1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include "SecurityEnvironment.hxx"
11 #include "CertificateImpl.hxx"
13 #include <com/sun/star/security/CertificateCharacters.hpp>
14 #include <com/sun/star/security/CertificateValidity.hpp>
17 #include <rtl/ref.hxx>
20 #include <config_folders.h>
21 #include <osl/file.hxx>
22 #include <osl/process.h>
23 #include <rtl/bootstrap.hxx>
24 #include <tools/urlobj.hxx>
28 #include <keylistresult.h>
29 #include <libxml/xmlstring.h>
30 #include <xmlsec/base64.h>
31 #include <xmlsec/xmlsec.h>
33 #if defined _MSC_VER && defined __clang__
34 #pragma clang diagnostic push
35 #pragma clang diagnostic ignored "-Wundef"
38 #if defined _MSC_VER && defined __clang__
39 #pragma clang diagnostic pop
44 using namespace css::security
;
45 using namespace css::uno
;
46 using namespace css::lang
;
48 SecurityEnvironmentGpg::SecurityEnvironmentGpg()
51 // On Windows, gpgme expects gpgme-w32spawn.exe to be in the same directory as the current
52 // process executable. This assumption might be wrong, e.g., for bundled python, which is
53 // in instdir/program/python-core-x.y.z/bin, while gpgme-w32spawn.exe is in instdir/program.
54 // If we can't find gpgme-w32spawn.exe in the current executable location, then try to find
55 // the spawn executable, and inform gpgme about actual location using gpgme_set_global_flag.
56 [[maybe_unused
]] static bool bSpawnPathInitialized
= [] {
57 auto accessUrl
= [](const INetURLObject
& url
) {
58 osl::File
file(url
.GetMainURL(INetURLObject::DecodeMechanism::NONE
));
59 return file
.open(osl_File_OpenFlag_Read
) == osl::FileBase::E_None
;
62 osl_getExecutableFile(&sPath
.pData
);
63 INetURLObject
aPathUrl(sPath
);
64 aPathUrl
.setName(u
"gpgme-w32spawn.exe");
65 if (!accessUrl(aPathUrl
))
67 sPath
= "$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER
"/gpgme-w32spawn.exe";
68 rtl::Bootstrap::expandMacros(sPath
);
69 aPathUrl
.SetURL(sPath
);
70 if (accessUrl(aPathUrl
))
72 aPathUrl
.removeSegment();
73 GpgME::setGlobalFlag("w32-inst-dir",
74 aPathUrl
.getFSysPath(FSysStyle::Dos
).toUtf8().getStr());
80 GpgME::Error err
= GpgME::checkEngine(GpgME::OpenPGP
);
82 throw RuntimeException(u
"The GpgME library failed to initialize for the OpenPGP protocol."_ustr
);
84 m_ctx
.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP
) );
86 throw RuntimeException(u
"The GpgME library failed to initialize for the OpenPGP protocol."_ustr
);
87 m_ctx
->setArmor(false);
90 SecurityEnvironmentGpg::~SecurityEnvironmentGpg()
94 OUString
SecurityEnvironmentGpg::getSecurityEnvironmentInformation()
99 Sequence
< Reference
< XCertificate
> > SecurityEnvironmentGpg::getCertificatesImpl( bool bPrivateOnly
)
101 std::vector
< GpgME::Key
> keyList
;
103 m_ctx
->setKeyListMode(GPGME_KEYLIST_MODE_LOCAL
);
104 GpgME::Error err
= m_ctx
->startKeyListing("", bPrivateOnly
);
106 GpgME::Key k
= m_ctx
->nextKey(err
);
109 if (!k
.isRevoked() && !k
.isExpired() && !k
.isDisabled() && !k
.isInvalid()) {
110 // We can't create CertificateImpl here as CertificateImpl::setCertificate uses GpgME API
111 // which interrupts our key listing here. So first get the keys from GpgME, then create the CertificateImpls
112 keyList
.push_back(k
);
115 m_ctx
->endKeyListing();
117 Sequence
< Reference
< XCertificate
> > xCertificateSequence(keyList
.size());
118 auto xCertificateSequenceRange
= asNonConstRange(xCertificateSequence
);
120 for (auto const& key
: keyList
) {
121 rtl::Reference
<CertificateImpl
> xCert
= new CertificateImpl();
122 xCert
->setCertificate(m_ctx
, key
);
123 xCertificateSequenceRange
[i
++] = xCert
; // fills xCertificateSequence
126 return xCertificateSequence
;
129 Sequence
< Reference
< XCertificate
> > SecurityEnvironmentGpg::getPersonalCertificates()
131 return getCertificatesImpl( true );
134 Sequence
< Reference
< XCertificate
> > SecurityEnvironmentGpg::getAllCertificates()
136 return getCertificatesImpl( false );
139 Reference
< XCertificate
> SecurityEnvironmentGpg::getCertificate( const OUString
& keyId
, const Sequence
< sal_Int8
>& /*serialNumber*/ )
141 //xmlChar* pSignatureValue=xmlNodeGetContent(cur);
142 OString ostr
= OUStringToOString( keyId
, RTL_TEXTENCODING_UTF8
);
143 const xmlChar
* strKeyId
= reinterpret_cast<const xmlChar
*>(ostr
.getStr());
145 int nRet
= xmlSecBase64Decode_ex(strKeyId
, const_cast<xmlSecByte
*>(strKeyId
), xmlStrlen(strKeyId
), &nWritten
);
147 throw RuntimeException(u
"Base64 decode failed"_ustr
);
149 m_ctx
->setKeyListMode(GPGME_KEYLIST_MODE_LOCAL
);
150 GpgME::Error err
= m_ctx
->startKeyListing("", false);
152 GpgME::Key k
= m_ctx
->nextKey(err
);
155 if (!k
.isInvalid() && strcmp(k
.primaryFingerprint(), reinterpret_cast<const char*>(strKeyId
)) == 0) {
156 rtl::Reference
<CertificateImpl
> xCert
= new CertificateImpl();
157 xCert
->setCertificate(m_ctx
, k
);
158 m_ctx
->endKeyListing();
162 m_ctx
->endKeyListing();
167 Sequence
< Reference
< XCertificate
> > SecurityEnvironmentGpg::buildCertificatePath( const Reference
< XCertificate
>& /*begin*/ )
169 return Sequence
< Reference
< XCertificate
> >();
172 Reference
< XCertificate
> SecurityEnvironmentGpg::createCertificateFromRaw( const Sequence
< sal_Int8
>& /*rawCertificate*/ )
177 Reference
< XCertificate
> SecurityEnvironmentGpg::createCertificateFromAscii( const OUString
& /*asciiCertificate*/ )
182 sal_Int32
SecurityEnvironmentGpg::verifyCertificate( const Reference
< XCertificate
>& aCert
,
183 const Sequence
< Reference
< XCertificate
> >& /*intermediateCerts*/ )
185 const CertificateImpl
* xCert
= dynamic_cast<CertificateImpl
*>(aCert
.get());
186 if (xCert
== nullptr) {
187 // Can't find the key locally -> unknown owner
188 return security::CertificateValidity::ISSUER_UNKNOWN
;
191 const GpgME::Key
* key
= xCert
->getCertificate();
192 if (key
->ownerTrust() == GpgME::Key::OwnerTrust::Marginal
||
193 key
->ownerTrust() == GpgME::Key::OwnerTrust::Full
||
194 key
->ownerTrust() == GpgME::Key::OwnerTrust::Ultimate
)
196 return security::CertificateValidity::VALID
;
199 return security::CertificateValidity::ISSUER_UNTRUSTED
;
202 sal_Int32
SecurityEnvironmentGpg::getCertificateCharacters(
203 const Reference
< XCertificate
>& aCert
)
205 if (dynamic_cast<CertificateImpl
*>(aCert
.get()) == nullptr)
206 throw RuntimeException();
208 // we only listed private keys anyway, up in
209 // SecurityEnvironmentGpg::getPersonalCertificates
210 return CertificateCharacters::HAS_PRIVATE_KEY
;
213 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */