1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 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/. 8 */ 9 10 #include "SecurityEnvironment.hxx" 11 #include "CertificateImpl.hxx" 12 13 #include <com/sun/star/security/CertificateCharacters.hpp> 14 #include <com/sun/star/security/CertificateValidity.hpp> 15 16 #include <comphelper/servicehelper.hxx> 17 #include <vector> 18 19 #ifdef _WIN32 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> 25 #endif 26 27 #include <key.h> 28 #include <keylistresult.h> 29 #include <xmlsec-wrapper.h> 30 31 #if defined _MSC_VER && defined __clang__ 32 #pragma clang diagnostic push 33 #pragma clang diagnostic ignored "-Wundef" 34 #endif 35 #include <gpgme.h> 36 #if defined _MSC_VER && defined __clang__ 37 #pragma clang diagnostic pop 38 #endif 39 #include <context.h> 40 41 using namespace css; 42 using namespace css::security; 43 using namespace css::uno; 44 using namespace css::lang; 45 46 SecurityEnvironmentGpg::SecurityEnvironmentGpg() 47 { 48 #ifdef _WIN32 49 // On Windows, gpgme expects gpgme-w32spawn.exe to be in the same directory as the current 50 // process executable. This assumption might be wrong, e.g., for bundled python, which is 51 // in instdir/program/python-core-x.y.z/bin, while gpgme-w32spawn.exe is in instdir/program. 52 // If we can't find gpgme-w32spawn.exe in the current executable location, then try to find 53 // the spawn executable, and inform gpgme about actual location using gpgme_set_global_flag. 54 static bool bSpawnPathInitialized = [] { 55 auto accessUrl = [](const INetURLObject& url) { 56 osl::File file(url.GetMainURL(INetURLObject::DecodeMechanism::NONE)); 57 return file.open(osl_File_OpenFlag_Read) == osl::FileBase::E_None; 58 }; 59 OUString sPath; 60 osl_getExecutableFile(&sPath.pData); 61 INetURLObject aPathUrl(sPath); 62 aPathUrl.setName("gpgme-w32spawn.exe"); 63 if (!accessUrl(aPathUrl)) 64 { 65 sPath = "$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER "/gpgme-w32spawn.exe"; 66 rtl::Bootstrap::expandMacros(sPath); 67 aPathUrl.SetURL(sPath); 68 if (accessUrl(aPathUrl)) 69 { 70 aPathUrl.removeSegment(); 71 GpgME::setGlobalFlag("w32-inst-dir", 72 aPathUrl.getFSysPath(FSysStyle::Dos).toUtf8().getStr()); 73 } 74 } 75 return true; 76 }(); 77 #endif 78 GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP); 79 if (err) 80 throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); 81 82 m_ctx.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP) ); 83 if (m_ctx == nullptr) 84 throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); 85 m_ctx->setArmor(false); 86 } 87 88 SecurityEnvironmentGpg::~SecurityEnvironmentGpg() 89 { 90 } 91 92 /* XUnoTunnel */ 93 sal_Int64 SAL_CALL SecurityEnvironmentGpg::getSomething( const Sequence< sal_Int8 >& aIdentifier ) 94 { 95 if( aIdentifier.getLength() == 16 && 0 == memcmp( getUnoTunnelId().getConstArray(), aIdentifier.getConstArray(), 16 ) ) { 96 return sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_uIntPtr>(this)); 97 } 98 return 0 ; 99 } 100 101 /* XUnoTunnel extension */ 102 103 namespace 104 { 105 class theSecurityEnvironmentUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSecurityEnvironmentUnoTunnelId > {}; 106 } 107 108 const Sequence< sal_Int8>& SecurityEnvironmentGpg::getUnoTunnelId() { 109 return theSecurityEnvironmentUnoTunnelId::get().getSeq(); 110 } 111 112 OUString SecurityEnvironmentGpg::getSecurityEnvironmentInformation() 113 { 114 return OUString(); 115 } 116 117 Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::getCertificatesImpl( bool bPrivateOnly ) 118 { 119 CertificateImpl* xCert; 120 std::vector< GpgME::Key > keyList; 121 std::vector< CertificateImpl* > certsList; 122 123 m_ctx->setKeyListMode(GPGME_KEYLIST_MODE_LOCAL); 124 GpgME::Error err = m_ctx->startKeyListing("", bPrivateOnly ); 125 while (!err) { 126 GpgME::Key k = m_ctx->nextKey(err); 127 if (err) 128 break; 129 if (!k.isRevoked() && !k.isExpired() && !k.isDisabled() && !k.isInvalid()) { 130 // We can't create CertificateImpl here as CertificateImpl::setCertificate uses GpgME API 131 // which interrupts our key listing here. So first get the keys from GpgME, then create the CertificateImpls 132 keyList.push_back(k); 133 } 134 } 135 m_ctx->endKeyListing(); 136 137 for (auto const& key : keyList) { 138 xCert = new CertificateImpl(); 139 xCert->setCertificate(m_ctx.get(),key); 140 certsList.push_back(xCert); 141 } 142 143 Sequence< Reference< XCertificate > > xCertificateSequence(certsList.size()); 144 int i = 0; 145 for (auto const& cert : certsList) { 146 xCertificateSequence[i++] = cert; 147 } 148 149 return xCertificateSequence; 150 } 151 152 Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::getPersonalCertificates() 153 { 154 return getCertificatesImpl( true ); 155 } 156 157 Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::getAllCertificates() 158 { 159 return getCertificatesImpl( false ); 160 } 161 162 Reference< XCertificate > SecurityEnvironmentGpg::getCertificate( const OUString& keyId, const Sequence< sal_Int8 >& /*serialNumber*/ ) 163 { 164 CertificateImpl* xCert=nullptr; 165 166 //xmlChar* pSignatureValue=xmlNodeGetContent(cur); 167 OString ostr = OUStringToOString( keyId , RTL_TEXTENCODING_UTF8 ); 168 const xmlChar* strKeyId = reinterpret_cast<const xmlChar*>(ostr.getStr()); 169 if(xmlSecBase64Decode(strKeyId, const_cast<xmlSecByte*>(strKeyId), xmlStrlen(strKeyId)) < 0) 170 throw RuntimeException("Base64 decode failed"); 171 172 m_ctx->setKeyListMode(GPGME_KEYLIST_MODE_LOCAL); 173 GpgME::Error err = m_ctx->startKeyListing("", false); 174 while (!err) { 175 GpgME::Key k = m_ctx->nextKey(err); 176 if (err) 177 break; 178 if (!k.isInvalid() && strcmp(k.primaryFingerprint(), reinterpret_cast<const char*>(strKeyId)) == 0) { 179 xCert = new CertificateImpl(); 180 xCert->setCertificate(m_ctx.get(), k); 181 m_ctx->endKeyListing(); 182 return xCert; 183 } 184 } 185 m_ctx->endKeyListing(); 186 187 return nullptr; 188 } 189 190 Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::buildCertificatePath( const Reference< XCertificate >& /*begin*/ ) 191 { 192 return Sequence< Reference < XCertificate > >(); 193 } 194 195 Reference< XCertificate > SecurityEnvironmentGpg::createCertificateFromRaw( const Sequence< sal_Int8 >& /*rawCertificate*/ ) 196 { 197 return nullptr; 198 } 199 200 Reference< XCertificate > SecurityEnvironmentGpg::createCertificateFromAscii( const OUString& /*asciiCertificate*/ ) 201 { 202 return nullptr; 203 } 204 205 sal_Int32 SecurityEnvironmentGpg::verifyCertificate( const Reference< XCertificate >& aCert, 206 const Sequence< Reference< XCertificate > >& /*intermediateCerts*/ ) 207 { 208 const CertificateImpl* xCert = dynamic_cast<CertificateImpl*>(aCert.get()); 209 if (xCert == nullptr) { 210 // Can't find the key locally -> unknown owner 211 return security::CertificateValidity::ISSUER_UNKNOWN; 212 } 213 214 const GpgME::Key* key = xCert->getCertificate(); 215 if (key->ownerTrust() == GpgME::Key::OwnerTrust::Marginal || 216 key->ownerTrust() == GpgME::Key::OwnerTrust::Full || 217 key->ownerTrust() == GpgME::Key::OwnerTrust::Ultimate) 218 { 219 return security::CertificateValidity::VALID; 220 } 221 222 return security::CertificateValidity::ISSUER_UNTRUSTED; 223 } 224 225 sal_Int32 SecurityEnvironmentGpg::getCertificateCharacters( 226 const Reference< XCertificate >& aCert) 227 { 228 const CertificateImpl* xCert; 229 Reference< XUnoTunnel > xCertTunnel(aCert, UNO_QUERY_THROW) ; 230 xCert = reinterpret_cast<CertificateImpl*>(sal::static_int_cast<sal_uIntPtr>(xCertTunnel->getSomething(CertificateImpl::getUnoTunnelId()))) ; 231 if (xCert == nullptr) 232 throw RuntimeException(); 233 234 // we only listed private keys anyway, up in 235 // SecurityEnvironmentGpg::getPersonalCertificates 236 return CertificateCharacters::HAS_PRIVATE_KEY; 237 } 238 239 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 240
