Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / xmlsecurity / source / xmlsec / nss / nssinitializer.cxx
blob4288e1ed426d2b14f8719d48101ff4e6190c4d5f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/lang/IllegalArgumentException.hpp>
21 #include <com/sun/star/mozilla/XMozillaBootstrap.hpp>
22 #include <com/sun/star/xml/crypto/DigestID.hpp>
23 #include <com/sun/star/xml/crypto/CipherID.hpp>
24 #include <com/sun/star/uno/XComponentContext.hpp>
25 #include <cppuhelper/supportsservice.hxx>
26 #include <officecfg/Office/Common.hxx>
27 #include <sal/types.h>
28 #include <rtl/instance.hxx>
29 #include <rtl/bootstrap.hxx>
30 #include <rtl/string.hxx>
31 #include <osl/file.hxx>
32 #include <osl/thread.h>
33 #include <sal/log.hxx>
34 #include <tools/diagnose_ex.h>
35 #include <unotools/tempfile.hxx>
36 #include <salhelper/singletonref.hxx>
38 #include <nss/nssinitializer.hxx>
40 #include "digestcontext.hxx"
41 #include "ciphercontext.hxx"
43 #include <memory>
44 #include <vector>
46 #include <nss.h>
47 #include <pk11pub.h>
48 #include <secmod.h>
49 #include <prerror.h>
50 #include <prinit.h>
52 namespace cssu = css::uno;
53 namespace cssl = css::lang;
55 using namespace com::sun::star;
57 #define ROOT_CERTS "Root Certs for OpenOffice.org"
59 extern "C" {
61 static void nsscrypto_finalize();
65 namespace
68 class InitNSSPrivate
70 private:
71 std::unique_ptr<utl::TempFile> m_pTempFileDatabaseDirectory;
73 static void scanDirsAndFiles(OUString const & rDirURL, std::vector<OUString> & rDirs, std::vector<OUString> & rFiles)
75 if (rDirURL.isEmpty())
76 return;
77 osl::Directory aDirectory(rDirURL);
79 if (osl::FileBase::E_None != aDirectory.open())
80 return;
82 osl::DirectoryItem aDirectoryItem;
84 while (osl::FileBase::E_None == aDirectory.getNextItem(aDirectoryItem))
86 osl::FileStatus aFileStatus(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL | osl_FileStatus_Mask_FileName);
88 if (osl::FileBase::E_None == aDirectoryItem.getFileStatus(aFileStatus))
90 if (aFileStatus.isDirectory())
92 const OUString aFileName(aFileStatus.getFileName());
93 if (!aFileName.isEmpty())
94 rDirs.push_back(aFileName);
96 else if (aFileStatus.isRegular())
98 const OUString aFileName(aFileStatus.getFileName());
99 if (!aFileName.isEmpty())
100 rFiles.push_back(aFileName);
107 static bool deleteDirRecursively(OUString const & rDirURL)
109 std::vector<OUString> aDirs;
110 std::vector<OUString> aFiles;
111 bool bError(false);
113 scanDirsAndFiles(rDirURL, aDirs, aFiles);
115 for (const auto& sDir : aDirs)
117 const OUString aNewDirURL(rDirURL + "/" + sDir);
118 bError |= deleteDirRecursively(aNewDirURL);
121 for (const auto& sFile : aFiles)
123 OUString aNewFileURL(rDirURL + "/" + sFile);
124 bError |= (osl::FileBase::E_None != osl::File::remove(aNewFileURL));
127 bError |= (osl::FileBase::E_None != osl::Directory::remove(rDirURL));
129 return bError;
132 public:
133 OUString getTempDatabasePath()
135 if (!m_pTempFileDatabaseDirectory)
137 m_pTempFileDatabaseDirectory.reset(new utl::TempFile(nullptr, true));
138 m_pTempFileDatabaseDirectory->EnableKillingFile();
140 return m_pTempFileDatabaseDirectory->GetFileName();
143 void reset()
145 if (m_pTempFileDatabaseDirectory)
147 deleteDirRecursively(m_pTempFileDatabaseDirectory->GetURL());
148 m_pTempFileDatabaseDirectory.reset();
153 salhelper::SingletonRef<InitNSSPrivate>* getInitNSSPrivate()
155 static salhelper::SingletonRef<InitNSSPrivate> aInitNSSPrivate;
156 return &aInitNSSPrivate;
159 bool nsscrypto_initialize( const css::uno::Reference< css::uno::XComponentContext > &rxContext, bool & out_nss_init );
161 struct InitNSSInitialize
163 css::uno::Reference< css::uno::XComponentContext > m_xContext;
165 explicit InitNSSInitialize(const css::uno::Reference<css::uno::XComponentContext> &rxContext)
166 : m_xContext(rxContext)
170 bool * operator()()
172 static bool bInitialized = false;
173 bool bNSSInit = false;
174 bInitialized = nsscrypto_initialize( m_xContext, bNSSInit );
175 if (bNSSInit)
176 atexit(nsscrypto_finalize );
177 return & bInitialized;
181 struct GetNSSInitStaticMutex
183 ::osl::Mutex* operator()()
185 static ::osl::Mutex aNSSInitMutex;
186 return &aNSSInitMutex;
190 #ifdef XMLSEC_CRYPTO_NSS
192 void deleteRootsModule()
194 SECMODModule *RootsModule = nullptr;
195 SECMODModuleList *list = SECMOD_GetDefaultModuleList();
196 SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
197 SECMOD_GetReadLock(lock);
199 while (!RootsModule && list)
201 SECMODModule *module = list->module;
203 for (int i=0; i < module->slotCount; i++)
205 PK11SlotInfo *slot = module->slots[i];
206 if (PK11_IsPresent(slot))
208 if (PK11_HasRootCerts(slot))
210 SAL_INFO("xmlsecurity.xmlsec", "The root certificates module \"" << module->commonName << "\" is already loaded: " << module->dllName);
212 RootsModule = SECMOD_ReferenceModule(module);
213 break;
217 list = list->next;
219 SECMOD_ReleaseReadLock(lock);
221 if (RootsModule)
223 PRInt32 modType;
224 if (SECSuccess == SECMOD_DeleteModule(RootsModule->commonName, &modType))
226 SAL_INFO("xmlsecurity.xmlsec", "Deleted module \"" << RootsModule->commonName << "\".");
228 else
230 SAL_INFO("xmlsecurity.xmlsec", "Failed to delete \"" << RootsModule->commonName << "\": " << RootsModule->dllName);
232 SECMOD_DestroyModule(RootsModule);
233 RootsModule = nullptr;
237 OString getMozillaCurrentProfile( const css::uno::Reference< css::uno::XComponentContext > &rxContext )
239 // first, try to get the profile from "MOZILLA_CERTIFICATE_FOLDER"
240 const char* pEnv = getenv("MOZILLA_CERTIFICATE_FOLDER");
241 if (pEnv)
243 SAL_INFO(
244 "xmlsecurity.xmlsec",
245 "Using Mozilla profile from MOZILLA_CERTIFICATE_FOLDER=" << pEnv);
246 return pEnv;
249 // second, try to get saved user-preference
252 OUString sUserSetCertPath =
253 officecfg::Office::Common::Security::Scripting::CertDir::get().get_value_or(OUString());
255 if (!sUserSetCertPath.isEmpty())
257 SAL_INFO(
258 "xmlsecurity.xmlsec",
259 "Using Mozilla profile from /org.openoffice.Office.Common/"
260 "Security/Scripting/CertDir: " << sUserSetCertPath);
261 return OUStringToOString(sUserSetCertPath, osl_getThreadTextEncoding());
264 catch (const uno::Exception &)
266 TOOLS_WARN_EXCEPTION("xmlsecurity.xmlsec", "getMozillaCurrentProfile:");
269 // third, dig around to see if there's one available
270 mozilla::MozillaProductType productTypes[3] = {
271 mozilla::MozillaProductType_Thunderbird,
272 mozilla::MozillaProductType_Firefox,
273 mozilla::MozillaProductType_Mozilla };
275 uno::Reference<uno::XInterface> xInstance = rxContext->getServiceManager()->createInstanceWithContext("com.sun.star.mozilla.MozillaBootstrap", rxContext);
276 OSL_ENSURE( xInstance.is(), "failed to create instance" );
278 uno::Reference<mozilla::XMozillaBootstrap> xMozillaBootstrap(xInstance,uno::UNO_QUERY);
279 OSL_ENSURE( xMozillaBootstrap.is(), "failed to create instance" );
281 if (xMozillaBootstrap.is())
283 for (int i=0; i<int(SAL_N_ELEMENTS(productTypes)); ++i)
285 OUString profile = xMozillaBootstrap->getDefaultProfile(productTypes[i]);
287 if (!profile.isEmpty())
289 OUString sProfilePath = xMozillaBootstrap->getProfilePath( productTypes[i], profile );
290 SAL_INFO(
291 "xmlsecurity.xmlsec",
292 "Using Mozilla profile " << sProfilePath);
293 return OUStringToOString(sProfilePath, osl_getThreadTextEncoding());
298 SAL_INFO("xmlsecurity.xmlsec", "No Mozilla profile found");
299 return OString();
302 #endif
304 //Older versions of Firefox (FF), for example FF2, and Thunderbird (TB) 2 write
305 //the roots certificate module (libnssckbi.so), which they use, into the
306 //profile. This module will then already be loaded during NSS_Init (and the
307 //other init functions). This fails in two cases. First, FF3 was used to create
308 //the profile, or possibly used that profile before, and second the profile was
309 //used on a different platform.
311 //Then one needs to add the roots module oneself. This should be done with
312 //SECMOD_LoadUserModule rather than SECMOD_AddNewModule. The latter would write
313 //the location of the roots module to the profile, which makes FF2 and TB2 use
314 //it instead of their own module.
316 //When using SYSTEM_NSS then the libnss3.so lib is typically found in /usr/lib.
317 //This folder may, however, NOT contain the roots certificate module. That is,
318 //just providing the library name in SECMOD_LoadUserModule or
319 //SECMOD_AddNewModule will FAIL to load the mozilla unless the LD_LIBRARY_PATH
320 //contains an FF or TB installation.
321 //ATTENTION: DO NOT call this function directly instead use initNSS
322 //return true - whole initialization was successful
323 //param out_nss_init = true: at least the NSS initialization (NSS_InitReadWrite
324 //was successful and therefore NSS_Shutdown should be called when terminating.
325 bool nsscrypto_initialize(css::uno::Reference<css::uno::XComponentContext> const & rxContext, bool & out_nss_init)
327 // this method must be called only once, no need for additional lock
328 OString sCertDir;
330 #ifdef XMLSEC_CRYPTO_NSS
331 sCertDir = getMozillaCurrentProfile(rxContext);
332 #else
333 (void) rxContext;
334 #endif
335 SAL_INFO("xmlsecurity.xmlsec", "Using profile: " << sCertDir );
337 PR_Init( PR_USER_THREAD, PR_PRIORITY_NORMAL, 1 ) ;
339 bool bSuccess = false;
340 // there might be no profile
341 if (!sCertDir.isEmpty())
343 if (sCertDir.indexOf(':') == -1) //might be env var with explicit prefix
345 OUString sCertDirURL;
346 osl::FileBase::getFileURLFromSystemPath(
347 OStringToOUString(sCertDir, osl_getThreadTextEncoding()),
348 sCertDirURL);
349 osl::DirectoryItem item;
350 if (osl::FileBase::E_NOENT != osl::DirectoryItem::get(sCertDirURL + "/cert8.db", item) &&
351 osl::FileBase::E_NOENT == osl::DirectoryItem::get(sCertDirURL + "/cert9.db", item))
353 SAL_INFO("xmlsecurity.xmlsec", "nsscrypto_initialize: trying to avoid profile migration");
354 sCertDir = "dbm:" + sCertDir;
357 if (NSS_InitReadWrite(sCertDir.getStr()) != SECSuccess)
359 SAL_INFO("xmlsecurity.xmlsec", "Initializing NSS with profile failed.");
360 int errlen = PR_GetErrorTextLength();
361 if (errlen > 0)
363 std::unique_ptr<char[]> const error(new char[errlen + 1]);
364 PR_GetErrorText(error.get());
365 SAL_INFO("xmlsecurity.xmlsec", error.get());
368 else
370 bSuccess = true;
374 if (!bSuccess) // Try to create a database in temp dir
376 SAL_INFO("xmlsecurity.xmlsec", "Initializing NSS with a temporary profile.");
377 OUString rString = (*getInitNSSPrivate())->getTempDatabasePath();
379 if (NSS_InitReadWrite(rString.toUtf8().getStr()) != SECSuccess)
381 SAL_INFO("xmlsecurity.xmlsec", "Initializing NSS with a temporary profile.");
382 int errlen = PR_GetErrorTextLength();
383 if(errlen > 0)
385 std::unique_ptr<char[]> const error(new char[errlen + 1]);
386 PR_GetErrorText(error.get());
387 SAL_INFO("xmlsecurity.xmlsec", error.get());
389 return false;
391 // Initialize and set empty password if needed
392 PK11SlotInfo* pSlot = PK11_GetInternalKeySlot();
393 if (pSlot)
395 if (PK11_NeedUserInit(pSlot))
396 PK11_InitPin(pSlot, nullptr, nullptr);
397 PK11_FreeSlot(pSlot);
400 out_nss_init = true;
402 #ifdef XMLSEC_CRYPTO_NSS
403 bool return_value = true;
405 #if defined SYSTEM_NSS
406 if (!SECMOD_HasRootCerts())
407 #endif
409 deleteRootsModule();
411 #if defined SYSTEM_NSS
412 OUString rootModule("libnssckbi" SAL_DLLEXTENSION);
413 #else
414 OUString rootModule("${LO_LIB_DIR}/libnssckbi" SAL_DLLEXTENSION);
415 #endif
416 ::rtl::Bootstrap::expandMacros(rootModule);
418 OUString rootModulePath;
419 if (::osl::File::E_None == ::osl::File::getSystemPathFromFileURL(rootModule, rootModulePath))
421 OString ospath = OUStringToOString(rootModulePath, osl_getThreadTextEncoding());
422 OString aStr = "name=\"" ROOT_CERTS "\" library=\"" + ospath + "\"";
424 SECMODModule * RootsModule =
425 SECMOD_LoadUserModule(
426 const_cast<char*>(aStr.getStr()),
427 nullptr, // no parent
428 PR_FALSE); // do not recurse
430 if (RootsModule)
433 bool found = RootsModule->loaded;
435 SECMOD_DestroyModule(RootsModule);
436 RootsModule = nullptr;
437 if (found)
438 SAL_INFO("xmlsecurity.xmlsec", "Added new root certificate module " ROOT_CERTS " contained in " << ospath);
439 else
441 SAL_INFO("xmlsecurity.xmlsec", "FAILED to load the new root certificate module " ROOT_CERTS "contained in " << ospath);
442 return_value = false;
445 else
447 SAL_INFO("xmlsecurity.xmlsec", "FAILED to add new root certificate module " ROOT_CERTS " contained in " << ospath);
448 return_value = false;
452 else
454 SAL_INFO("xmlsecurity.xmlsec", "Adding new root certificate module failed.");
455 return_value = false;
459 return return_value;
460 #else
461 return true;
462 #endif
465 } // namespace
467 // must be extern "C" because we pass the function pointer to atexit
468 extern "C" void nsscrypto_finalize()
470 SECMODModule *RootsModule = SECMOD_FindModule(ROOT_CERTS);
472 if (RootsModule)
475 if (SECSuccess == SECMOD_UnloadUserModule(RootsModule))
477 SAL_INFO("xmlsecurity.xmlsec", "Unloaded module \"" ROOT_CERTS "\".");
479 else
481 SAL_INFO("xmlsecurity.xmlsec", "Failed unloading module \"" ROOT_CERTS "\".");
483 SECMOD_DestroyModule(RootsModule);
485 else
487 SAL_INFO("xmlsecurity.xmlsec", "Unloading module \"" ROOT_CERTS "\" failed because it was not found.");
489 PK11_LogoutAll();
490 (void)NSS_Shutdown();
492 (*getInitNSSPrivate())->reset();
495 ONSSInitializer::ONSSInitializer(
496 const css::uno::Reference< css::uno::XComponentContext > &rxContext)
497 :m_xContext( rxContext )
501 ONSSInitializer::~ONSSInitializer()
505 bool ONSSInitializer::initNSS( const css::uno::Reference< css::uno::XComponentContext > &rxContext )
507 return *rtl_Instance< bool, InitNSSInitialize, ::osl::MutexGuard, GetNSSInitStaticMutex >
508 ::create( InitNSSInitialize( rxContext ), GetNSSInitStaticMutex() );
511 css::uno::Reference< css::xml::crypto::XDigestContext > SAL_CALL ONSSInitializer::getDigestContext( ::sal_Int32 nDigestID, const css::uno::Sequence< css::beans::NamedValue >& aParams )
513 SECOidTag nNSSDigestID = SEC_OID_UNKNOWN;
514 sal_Int32 nDigestLength = 0;
515 bool b1KData = false;
516 if ( nDigestID == css::xml::crypto::DigestID::SHA256
517 || nDigestID == css::xml::crypto::DigestID::SHA256_1K )
519 nNSSDigestID = SEC_OID_SHA256;
520 nDigestLength = 32;
521 b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA256_1K );
523 else if ( nDigestID == css::xml::crypto::DigestID::SHA1
524 || nDigestID == css::xml::crypto::DigestID::SHA1_1K )
526 nNSSDigestID = SEC_OID_SHA1;
527 nDigestLength = 20;
528 b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA1_1K );
530 else if ( nDigestID == css::xml::crypto::DigestID::SHA512
531 || nDigestID == css::xml::crypto::DigestID::SHA512_1K )
533 nNSSDigestID = SEC_OID_SHA512;
534 nDigestLength = 64;
535 b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA512_1K );
537 else
538 throw css::lang::IllegalArgumentException("Unexpected digest requested.", css::uno::Reference< css::uno::XInterface >(), 1 );
540 if ( aParams.hasElements() )
541 throw css::lang::IllegalArgumentException("Unexpected arguments provided for digest creation.", css::uno::Reference< css::uno::XInterface >(), 2 );
543 css::uno::Reference< css::xml::crypto::XDigestContext > xResult;
544 if( initNSS( m_xContext ) )
546 PK11Context* pContext = PK11_CreateDigestContext( nNSSDigestID );
547 if ( pContext && PK11_DigestBegin( pContext ) == SECSuccess )
548 xResult = new ODigestContext( pContext, nDigestLength, b1KData );
551 return xResult;
554 css::uno::Reference< css::xml::crypto::XCipherContext > SAL_CALL ONSSInitializer::getCipherContext( ::sal_Int32 nCipherID, const css::uno::Sequence< ::sal_Int8 >& aKey, const css::uno::Sequence< ::sal_Int8 >& aInitializationVector, sal_Bool bEncryption, const css::uno::Sequence< css::beans::NamedValue >& aParams )
556 CK_MECHANISM_TYPE nNSSCipherID = 0;
557 bool bW3CPadding = false;
558 if ( nCipherID != css::xml::crypto::CipherID::AES_CBC_W3C_PADDING )
559 throw css::lang::IllegalArgumentException("Unexpected cipher requested.", css::uno::Reference< css::uno::XInterface >(), 1 );
561 nNSSCipherID = CKM_AES_CBC;
562 bW3CPadding = true;
564 if ( aKey.getLength() != 16 && aKey.getLength() != 24 && aKey.getLength() != 32 )
565 throw css::lang::IllegalArgumentException("Unexpected key length.", css::uno::Reference< css::uno::XInterface >(), 2 );
567 if ( aParams.hasElements() )
568 throw css::lang::IllegalArgumentException("Unexpected arguments provided for cipher creation.", css::uno::Reference< css::uno::XInterface >(), 5 );
570 css::uno::Reference< css::xml::crypto::XCipherContext > xResult;
571 if( initNSS( m_xContext ) )
573 if ( aInitializationVector.getLength() != PK11_GetIVLength( nNSSCipherID ) )
574 throw css::lang::IllegalArgumentException("Unexpected length of initialization vector.", css::uno::Reference< css::uno::XInterface >(), 3 );
576 xResult = OCipherContext::Create( nNSSCipherID, aKey, aInitializationVector, bEncryption, bW3CPadding );
579 return xResult;
582 /* XServiceInfo */
583 OUString SAL_CALL ONSSInitializer::getImplementationName()
585 return "com.sun.star.xml.crypto.NSSInitializer";
588 sal_Bool SAL_CALL ONSSInitializer::supportsService( const OUString& rServiceName )
590 return cppu::supportsService(this, rServiceName);
593 cssu::Sequence< OUString > SAL_CALL ONSSInitializer::getSupportedServiceNames( )
595 cssu::Sequence<OUString> aRet { NSS_SERVICE_NAME };
596 return aRet;
599 #ifndef XMLSEC_CRYPTO_NSS
600 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
601 com_sun_star_xml_crypto_NSSInitializer_get_implementation(
602 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
604 return cppu::acquire(new ONSSInitializer(pCtx));
606 #endif
608 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */