Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / xmlsecurity / source / xmlsec / nss / nssinitializer.cxx
blob2647f7991657a4ee7dc4fb7bb8ec18791179db4e
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/lang/XMultiComponentFactory.hpp>
26 #include <cppuhelper/supportsservice.hxx>
27 #include <officecfg/Office/Common.hxx>
28 #include <sal/types.h>
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 <comphelper/diagnose_ex.hxx>
35 #include <unotools/tempfile.hxx>
36 #include <comphelper/singletonref.hxx>
37 #include <comphelper/sequence.hxx>
39 #include <nss/nssinitializer.hxx>
41 #include "digestcontext.hxx"
42 #include "ciphercontext.hxx"
43 #include <memory>
44 #include <utility>
45 #include <vector>
47 #include <nss.h>
48 #include <pk11pub.h>
49 #include <secmod.h>
50 #include <prerror.h>
51 #include <prinit.h>
53 namespace cssu = css::uno;
54 namespace cssl = css::lang;
56 using namespace com::sun::star;
58 #define ROOT_CERTS "Root Certs for OpenOffice.org"
60 extern "C" {
62 static void nsscrypto_finalize();
66 namespace
69 class InitNSSPrivate
71 private:
72 std::optional<utl::TempFileNamed> m_oTempFileDatabaseDirectory;
74 public:
75 OUString getTempDatabasePath()
77 if (!m_oTempFileDatabaseDirectory)
79 m_oTempFileDatabaseDirectory.emplace(nullptr, true);
80 m_oTempFileDatabaseDirectory->EnableKillingFile();
82 return m_oTempFileDatabaseDirectory->GetFileName();
85 void reset()
87 if (m_oTempFileDatabaseDirectory)
89 m_oTempFileDatabaseDirectory.reset();
94 comphelper::SingletonRef<InitNSSPrivate>* getInitNSSPrivate()
96 static comphelper::SingletonRef<InitNSSPrivate> aInitNSSPrivate;
97 return &aInitNSSPrivate;
100 bool nsscrypto_initialize( const css::uno::Reference< css::uno::XComponentContext > &rxContext, bool & out_nss_init );
102 #ifdef XMLSEC_CRYPTO_NSS
104 void deleteRootsModule()
106 SECMODModule *RootsModule = nullptr;
107 SECMODModuleList *list = SECMOD_GetDefaultModuleList();
108 SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
109 SECMOD_GetReadLock(lock);
111 while (!RootsModule && list)
113 SECMODModule *module = list->module;
115 for (int i=0; i < module->slotCount; i++)
117 PK11SlotInfo *slot = module->slots[i];
118 if (PK11_IsPresent(slot))
120 if (PK11_HasRootCerts(slot))
122 SAL_INFO("xmlsecurity.xmlsec", "The root certificates module \"" << module->commonName << "\" is already loaded: " << module->dllName);
124 RootsModule = SECMOD_ReferenceModule(module);
125 break;
129 list = list->next;
131 SECMOD_ReleaseReadLock(lock);
133 if (!RootsModule)
134 return;
136 PRInt32 modType;
137 if (SECSuccess == SECMOD_DeleteModule(RootsModule->commonName, &modType))
139 SAL_INFO("xmlsecurity.xmlsec", "Deleted module \"" << RootsModule->commonName << "\".");
141 else
143 SAL_INFO("xmlsecurity.xmlsec", "Failed to delete \"" << RootsModule->commonName << "\": " << RootsModule->dllName);
145 SECMOD_DestroyModule(RootsModule);
146 RootsModule = nullptr;
149 #endif
151 bool lcl_pathExists(const OUString& sPath)
153 if (sPath.isEmpty())
154 return false;
156 ::osl::DirectoryItem aPathItem;
157 OUString sURL;
158 osl::FileBase::getFileURLFromSystemPath(sPath, sURL);
159 if (::osl::FileBase::E_None == ::osl::DirectoryItem::get(sURL, aPathItem))
161 ::osl::FileStatus aStatus = osl_FileStatus_Mask_Validate;
162 if (::osl::FileBase::E_None == aPathItem.getFileStatus(aStatus))
163 return true;
166 return false;
169 } // namespace
171 const OUString & ONSSInitializer::getMozillaCurrentProfile(const css::uno::Reference< css::uno::XComponentContext > &rxContext, bool bSetActive)
173 if (m_bIsNSSinitialized)
174 return m_sNSSPath;
175 if (bSetActive)
176 m_bIsNSSinitialized = true;
178 // first, try to get the profile from "MOZILLA_CERTIFICATE_FOLDER"
179 const char* pEnv = getenv("MOZILLA_CERTIFICATE_FOLDER");
180 if (pEnv)
182 SAL_INFO(
183 "xmlsecurity.xmlsec",
184 "Using Mozilla profile from MOZILLA_CERTIFICATE_FOLDER=" << pEnv);
185 m_sNSSPath = OStringToOUString(pEnv, osl_getThreadTextEncoding());
188 // second, try to get saved user-preference
189 if (m_sNSSPath.isEmpty())
193 OUString sUserSetCertPath =
194 officecfg::Office::Common::Security::Scripting::CertDir::get().value_or(OUString());
196 if (lcl_pathExists(sUserSetCertPath))
198 SAL_INFO(
199 "xmlsecurity.xmlsec",
200 "Using Mozilla profile from /org.openoffice.Office.Common/"
201 "Security/Scripting/CertDir: " << sUserSetCertPath);
202 m_sNSSPath = sUserSetCertPath;
205 catch (const uno::Exception &)
207 TOOLS_WARN_EXCEPTION("xmlsecurity.xmlsec", "getMozillaCurrentProfile:");
211 // third, dig around to see if there's one default available
212 mozilla::MozillaProductType productTypes[3] = {
213 mozilla::MozillaProductType_Thunderbird,
214 mozilla::MozillaProductType_Firefox,
215 mozilla::MozillaProductType_Mozilla };
217 uno::Reference<uno::XInterface> xInstance = rxContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.mozilla.MozillaBootstrap"_ustr, rxContext);
218 OSL_ENSURE( xInstance.is(), "failed to create instance" );
220 uno::Reference<mozilla::XMozillaBootstrap> xMozillaBootstrap(xInstance,uno::UNO_QUERY);
221 OSL_ENSURE( xMozillaBootstrap.is(), "failed to create instance" );
223 if (xMozillaBootstrap.is())
225 for (auto const productTypeIter : productTypes)
227 OUString profile = xMozillaBootstrap->getDefaultProfile(productTypeIter);
229 if (!profile.isEmpty())
231 OUString sProfilePath = xMozillaBootstrap->getProfilePath(productTypeIter, profile);
232 if (m_sNSSPath.isEmpty())
234 SAL_INFO("xmlsecurity.xmlsec", "Using Mozilla profile " << sProfilePath);
235 m_sNSSPath = sProfilePath;
237 break;
242 SAL_INFO_IF(m_sNSSPath.isEmpty(), "xmlsecurity.xmlsec", "No Mozilla profile found");
243 return m_sNSSPath;
246 css::uno::Sequence<css::xml::crypto::NSSProfile> SAL_CALL ONSSInitializer::getNSSProfiles()
248 ONSSInitializer::getMozillaCurrentProfile(m_xContext);
250 std::vector<xml::crypto::NSSProfile> aProfileList;
251 aProfileList.reserve(10);
253 mozilla::MozillaProductType productTypes[3] = {
254 mozilla::MozillaProductType_Thunderbird,
255 mozilla::MozillaProductType_Firefox,
256 mozilla::MozillaProductType_Mozilla };
258 uno::Reference<uno::XInterface> xInstance = m_xContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.mozilla.MozillaBootstrap"_ustr, m_xContext);
259 OSL_ENSURE(xInstance.is(), "failed to create instance" );
261 uno::Reference<mozilla::XMozillaBootstrap> xMozillaBootstrap(xInstance,uno::UNO_QUERY);
263 if (xMozillaBootstrap.is())
265 for (auto const productTypeIter : productTypes)
267 uno::Sequence<OUString> aProductProfileList;
268 xMozillaBootstrap->getProfileList(productTypeIter, aProductProfileList);
269 for (const auto& sProfile : aProductProfileList)
270 aProfileList.push_back({sProfile, xMozillaBootstrap->getProfilePath(productTypeIter, sProfile), productTypeIter});
274 OUString sUserSelect;
277 sUserSelect = officecfg::Office::Common::Security::Scripting::CertDir::get().value_or(OUString());;
278 if (!lcl_pathExists(sUserSelect))
279 sUserSelect = OUString();
281 catch (const uno::Exception &)
283 TOOLS_WARN_EXCEPTION("xmlsecurity.xmlsec", "getMozillaCurrentProfile:");
285 aProfileList.push_back({u"MANUAL"_ustr, sUserSelect, mozilla::MozillaProductType_Default});
287 const char* pEnv = getenv("MOZILLA_CERTIFICATE_FOLDER");
288 aProfileList.push_back({u"MOZILLA_CERTIFICATE_FOLDER"_ustr,
289 pEnv ? OStringToOUString(pEnv, osl_getThreadTextEncoding()) : OUString(),
290 mozilla::MozillaProductType_Default});
292 return comphelper::containerToSequence(aProfileList);
295 bool ONSSInitializer::m_bIsNSSinitialized = false;
296 OUString ONSSInitializer::m_sNSSPath;
298 OUString SAL_CALL ONSSInitializer::getNSSPath()
300 ONSSInitializer::getMozillaCurrentProfile(m_xContext);
301 return m_sNSSPath;
304 sal_Bool SAL_CALL ONSSInitializer::getIsNSSinitialized()
306 return m_bIsNSSinitialized;
309 ONSSInitializer::ONSSInitializer(css::uno::Reference< css::uno::XComponentContext > xContext)
310 : m_xContext(std::move(xContext))
314 ONSSInitializer::ONSSInitializer()
318 namespace
321 //Older versions of Firefox (FF), for example FF2, and Thunderbird (TB) 2 write
322 //the roots certificate module (libnssckbi.so), which they use, into the
323 //profile. This module will then already be loaded during NSS_Init (and the
324 //other init functions). This fails in two cases. First, FF3 was used to create
325 //the profile, or possibly used that profile before, and second the profile was
326 //used on a different platform.
328 //Then one needs to add the roots module oneself. This should be done with
329 //SECMOD_LoadUserModule rather than SECMOD_AddNewModule. The latter would write
330 //the location of the roots module to the profile, which makes FF2 and TB2 use
331 //it instead of their own module.
333 //When using SYSTEM_NSS then the libnss3.so lib is typically found in /usr/lib.
334 //This folder may, however, NOT contain the roots certificate module. That is,
335 //just providing the library name in SECMOD_LoadUserModule or
336 //SECMOD_AddNewModule will FAIL to load the mozilla unless the LD_LIBRARY_PATH
337 //contains an FF or TB installation.
338 //ATTENTION: DO NOT call this function directly instead use initNSS
339 //return true - whole initialization was successful
340 //param out_nss_init = true: at least the NSS initialization (NSS_InitReadWrite
341 //was successful and therefore NSS_Shutdown should be called when terminating.
342 bool nsscrypto_initialize(css::uno::Reference<css::uno::XComponentContext> const & rxContext, bool & out_nss_init)
344 // this method must be called only once, no need for additional lock
345 OString sCertDir;
347 #ifdef XMLSEC_CRYPTO_NSS
348 sCertDir = OUStringToOString(ONSSInitializer::getMozillaCurrentProfile(rxContext, true), osl_getThreadTextEncoding());
349 #else
350 (void) rxContext;
351 #endif
352 SAL_INFO("xmlsecurity.xmlsec", "Using profile: " << sCertDir );
354 PR_Init( PR_USER_THREAD, PR_PRIORITY_NORMAL, 1 ) ;
356 bool bSuccess = false;
357 // there might be no profile
358 if (!sCertDir.isEmpty())
360 if (sCertDir.indexOf(':') == -1) //might be env var with explicit prefix
362 OUString sCertDirURL;
363 osl::FileBase::getFileURLFromSystemPath(
364 OStringToOUString(sCertDir, osl_getThreadTextEncoding()),
365 sCertDirURL);
366 osl::DirectoryItem item;
367 if (osl::FileBase::E_NOENT != osl::DirectoryItem::get(sCertDirURL + "/cert8.db", item) &&
368 osl::FileBase::E_NOENT == osl::DirectoryItem::get(sCertDirURL + "/cert9.db", item))
370 SAL_INFO("xmlsecurity.xmlsec", "nsscrypto_initialize: trying to avoid profile migration");
371 sCertDir = "dbm:" + sCertDir;
374 if (NSS_InitReadWrite(sCertDir.getStr()) != SECSuccess)
376 SAL_INFO("xmlsecurity.xmlsec", "Initializing NSS with profile failed.");
377 int errlen = PR_GetErrorTextLength();
378 if (errlen > 0)
380 std::unique_ptr<char[]> const error(new char[errlen + 1]);
381 PR_GetErrorText(error.get());
382 SAL_INFO("xmlsecurity.xmlsec", error.get());
385 else
387 bSuccess = true;
391 if (!bSuccess) // Try to create a database in temp dir
393 SAL_INFO("xmlsecurity.xmlsec", "Initializing NSS with a temporary profile.");
394 OUString rString = (*getInitNSSPrivate())->getTempDatabasePath();
396 if (NSS_InitReadWrite(rString.toUtf8().getStr()) != SECSuccess)
398 SAL_INFO("xmlsecurity.xmlsec", "Initializing NSS with a temporary profile.");
399 int errlen = PR_GetErrorTextLength();
400 if(errlen > 0)
402 std::unique_ptr<char[]> const error(new char[errlen + 1]);
403 PR_GetErrorText(error.get());
404 SAL_INFO("xmlsecurity.xmlsec", error.get());
406 return false;
410 // Initialize and set empty password if needed
411 // note: it's possible that the first NSS_InitReadWrite() succeeds by
412 // creating a new DB; in this case it may also be necessary to call
413 // PK11_InitPin()
414 PK11SlotInfo* pSlot = PK11_GetInternalKeySlot();
415 if (pSlot)
417 if (PK11_NeedUserInit(pSlot))
418 PK11_InitPin(pSlot, nullptr, nullptr);
419 PK11_FreeSlot(pSlot);
422 out_nss_init = true;
424 #ifdef XMLSEC_CRYPTO_NSS
425 bool return_value = true;
427 #if defined SYSTEM_NSS || defined IOS // The statically linked nss on iOS acts as a "system" nss in this regards
428 if (!SECMOD_HasRootCerts())
429 #endif
431 deleteRootsModule();
433 #ifdef IOS // Use statically linked NSS
434 OUString rootModulePath("NSSCKBI");
436 if (true)
437 #else
438 #if defined SYSTEM_NSS || defined ANDROID
439 OUString rootModule(u"libnssckbi" SAL_DLLEXTENSION ""_ustr);
440 #else
441 OUString rootModule("${LO_LIB_DIR}/libnssckbi" SAL_DLLEXTENSION);
442 #endif
443 ::rtl::Bootstrap::expandMacros(rootModule);
445 OUString rootModulePath;
446 if (::osl::File::E_None == ::osl::File::getSystemPathFromFileURL(rootModule, rootModulePath))
447 #endif
449 OString ospath = OUStringToOString(rootModulePath, osl_getThreadTextEncoding());
450 OString aStr = "name=\"" ROOT_CERTS "\" library=\"" + ospath + "\"";
452 SECMODModule * RootsModule =
453 SECMOD_LoadUserModule(
454 const_cast<char*>(aStr.getStr()),
455 nullptr, // no parent
456 PR_FALSE); // do not recurse
458 if (RootsModule)
461 bool found = RootsModule->loaded;
463 SECMOD_DestroyModule(RootsModule);
464 RootsModule = nullptr;
465 if (found)
466 SAL_INFO("xmlsecurity.xmlsec", "Added new root certificate module " ROOT_CERTS " contained in " << ospath);
467 else
469 SAL_INFO("xmlsecurity.xmlsec", "FAILED to load the new root certificate module " ROOT_CERTS "contained in " << ospath);
470 return_value = false;
473 else
475 SAL_INFO("xmlsecurity.xmlsec", "FAILED to add new root certificate module " ROOT_CERTS " contained in " << ospath);
476 return_value = false;
480 else
482 SAL_INFO("xmlsecurity.xmlsec", "Adding new root certificate module failed.");
483 return_value = false;
487 return return_value;
488 #else
489 return true;
490 #endif
493 } // namespace
495 // must be extern "C" because we pass the function pointer to atexit
496 extern "C" void nsscrypto_finalize()
498 SECMODModule *RootsModule = SECMOD_FindModule(ROOT_CERTS);
500 if (RootsModule)
503 if (SECSuccess == SECMOD_UnloadUserModule(RootsModule))
505 SAL_INFO("xmlsecurity.xmlsec", "Unloaded module \"" ROOT_CERTS "\".");
507 else
509 SAL_INFO("xmlsecurity.xmlsec", "Failed unloading module \"" ROOT_CERTS "\".");
511 SECMOD_DestroyModule(RootsModule);
513 else
515 SAL_INFO("xmlsecurity.xmlsec", "Unloading module \"" ROOT_CERTS "\" failed because it was not found.");
517 PK11_LogoutAll();
518 (void)NSS_Shutdown();
520 (*getInitNSSPrivate())->reset();
524 ONSSInitializer::~ONSSInitializer()
528 bool ONSSInitializer::initNSS( const css::uno::Reference< css::uno::XComponentContext > &rxContext )
530 static bool gbInitialized = [&rxContext]()
532 bool bNSSInit = false;
533 bool bInitialized = nsscrypto_initialize( rxContext, bNSSInit );
534 if (bNSSInit)
535 atexit(nsscrypto_finalize);
536 return bInitialized;
537 }();
538 return gbInitialized;
541 css::uno::Reference< css::xml::crypto::XDigestContext > SAL_CALL ONSSInitializer::getDigestContext( ::sal_Int32 nDigestID, const css::uno::Sequence< css::beans::NamedValue >& aParams )
543 SECOidTag nNSSDigestID = SEC_OID_UNKNOWN;
544 sal_Int32 nDigestLength = 0;
545 bool b1KData = false;
546 if ( nDigestID == css::xml::crypto::DigestID::SHA256
547 || nDigestID == css::xml::crypto::DigestID::SHA256_1K )
549 nNSSDigestID = SEC_OID_SHA256;
550 nDigestLength = 32;
551 b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA256_1K );
553 else if ( nDigestID == css::xml::crypto::DigestID::SHA1
554 || nDigestID == css::xml::crypto::DigestID::SHA1_1K )
556 nNSSDigestID = SEC_OID_SHA1;
557 nDigestLength = 20;
558 b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA1_1K );
560 else if ( nDigestID == css::xml::crypto::DigestID::SHA512
561 || nDigestID == css::xml::crypto::DigestID::SHA512_1K )
563 nNSSDigestID = SEC_OID_SHA512;
564 nDigestLength = 64;
565 b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA512_1K );
567 else
568 throw css::lang::IllegalArgumentException(u"Unexpected digest requested."_ustr, css::uno::Reference< css::uno::XInterface >(), 1 );
570 if ( aParams.hasElements() )
571 throw css::lang::IllegalArgumentException(u"Unexpected arguments provided for digest creation."_ustr, css::uno::Reference< css::uno::XInterface >(), 2 );
573 css::uno::Reference< css::xml::crypto::XDigestContext > xResult;
574 if( initNSS( m_xContext ) )
576 PK11Context* pContext = PK11_CreateDigestContext( nNSSDigestID );
577 if ( pContext && PK11_DigestBegin( pContext ) == SECSuccess )
578 xResult = new ODigestContext( pContext, nDigestLength, b1KData );
581 return xResult;
584 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 )
586 CK_MECHANISM_TYPE nNSSCipherID = 0;
587 bool bW3CPadding = false;
588 switch (nCipherID)
590 case css::xml::crypto::CipherID::AES_CBC_W3C_PADDING:
591 nNSSCipherID = CKM_AES_CBC;
592 bW3CPadding = true;
593 break;
594 case css::xml::crypto::CipherID::AES_GCM_W3C:
595 nNSSCipherID = CKM_AES_GCM;
596 break;
597 default:
598 throw css::lang::IllegalArgumentException(u"Unexpected cipher requested."_ustr, css::uno::Reference< css::uno::XInterface >(), 1);
601 if ( aKey.getLength() != 16 && aKey.getLength() != 24 && aKey.getLength() != 32 )
602 throw css::lang::IllegalArgumentException(u"Unexpected key length."_ustr, css::uno::Reference< css::uno::XInterface >(), 2 );
604 if ( aParams.hasElements() )
605 throw css::lang::IllegalArgumentException(u"Unexpected arguments provided for cipher creation."_ustr, css::uno::Reference< css::uno::XInterface >(), 5 );
607 css::uno::Reference< css::xml::crypto::XCipherContext > xResult;
608 if( initNSS( m_xContext ) )
610 if ( aInitializationVector.getLength() != PK11_GetIVLength( nNSSCipherID ) )
611 throw css::lang::IllegalArgumentException(u"Unexpected length of initialization vector."_ustr, css::uno::Reference< css::uno::XInterface >(), 3 );
613 xResult = OCipherContext::Create( nNSSCipherID, aKey, aInitializationVector, bEncryption, bW3CPadding );
614 assert(xResult.is());
617 return xResult;
620 /* XServiceInfo */
621 OUString SAL_CALL ONSSInitializer::getImplementationName()
623 return u"com.sun.star.xml.crypto.NSSInitializer"_ustr;
626 sal_Bool SAL_CALL ONSSInitializer::supportsService( const OUString& rServiceName )
628 return cppu::supportsService(this, rServiceName);
631 cssu::Sequence< OUString > SAL_CALL ONSSInitializer::getSupportedServiceNames( )
633 return { NSS_SERVICE_NAME };
636 #ifndef XMLSEC_CRYPTO_NSS
637 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
638 com_sun_star_xml_crypto_NSSInitializer_get_implementation(
639 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
641 return cppu::acquire(new ONSSInitializer(pCtx));
643 #endif
645 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */