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