bump product version to 4.1.6.2
[LibreOffice.git] / xmlsecurity / source / xmlsec / nss / nssinitializer.cxx
blob123978769d3a10df47230d23cbf34732f782ad07
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 .
22 * and turn off the additional virtual methods which are part of some interfaces when compiled
23 * with debug
25 #ifdef DEBUG
26 #undef DEBUG
27 #endif
30 #include <com/sun/star/mozilla/XMozillaBootstrap.hpp>
31 #include <com/sun/star/xml/crypto/DigestID.hpp>
32 #include <com/sun/star/xml/crypto/CipherID.hpp>
34 #include <officecfg/Office/Common.hxx>
36 #include <sal/types.h>
37 #include <rtl/instance.hxx>
38 #include <rtl/bootstrap.hxx>
39 #include <rtl/string.hxx>
40 #include <rtl/strbuf.hxx>
41 #include <osl/file.hxx>
42 #include <osl/thread.h>
43 #include <sal/log.hxx>
45 #include "seinitializer_nssimpl.hxx"
46 #include "../diagnose.hxx"
48 #include "securityenvironment_nssimpl.hxx"
49 #include "digestcontext.hxx"
50 #include "ciphercontext.hxx"
52 #include <boost/scoped_array.hpp>
54 #include <nspr.h>
55 #include <cert.h>
56 #include <nss.h>
57 #include <pk11pub.h>
58 #include <secmod.h>
59 #include <nssckbi.h>
61 namespace cssu = css::uno;
62 namespace cssl = css::lang;
64 using namespace xmlsecurity;
65 using namespace com::sun::star;
67 #define IMPLEMENTATION_NAME "com.sun.star.xml.security.bridge.xmlsec.NSSInitializer_NssImpl"
69 #define ROOT_CERTS "Root Certs for OpenOffice.org"
71 extern "C" void nsscrypto_finalize();
74 namespace
77 bool nsscrypto_initialize( const css::uno::Reference< css::uno::XComponentContext > &rxContext, bool & out_nss_init );
79 struct InitNSSInitialize
81 css::uno::Reference< css::uno::XComponentContext > m_xContext;
83 InitNSSInitialize( const css::uno::Reference< css::uno::XComponentContext > &rxContext )
84 : m_xContext( rxContext )
88 bool * operator()()
90 static bool bInitialized = false;
91 bool bNSSInit = false;
92 bInitialized = nsscrypto_initialize( m_xContext, bNSSInit );
93 if (bNSSInit)
94 atexit(nsscrypto_finalize );
95 return & bInitialized;
99 struct GetNSSInitStaticMutex
101 ::osl::Mutex* operator()()
103 static ::osl::Mutex aNSSInitMutex;
104 return &aNSSInitMutex;
108 void deleteRootsModule()
110 SECMODModule *RootsModule = 0;
111 SECMODModuleList *list = SECMOD_GetDefaultModuleList();
112 SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
113 SECMOD_GetReadLock(lock);
115 while (!RootsModule && list)
117 SECMODModule *module = list->module;
119 for (int i=0; i < module->slotCount; i++)
121 PK11SlotInfo *slot = module->slots[i];
122 if (PK11_IsPresent(slot))
124 if (PK11_HasRootCerts(slot))
126 xmlsec_trace("The root certifificates module \"%s"
127 "\" is already loaded: \n%s",
128 module->commonName, module->dllName);
130 RootsModule = SECMOD_ReferenceModule(module);
131 break;
135 list = list->next;
137 SECMOD_ReleaseReadLock(lock);
139 if (RootsModule)
141 PRInt32 modType;
142 if (SECSuccess == SECMOD_DeleteModule(RootsModule->commonName, &modType))
144 xmlsec_trace("Deleted module \"%s\".", RootsModule->commonName);
146 else
148 xmlsec_trace("Failed to delete \"%s\" : \n%s",
149 RootsModule->commonName, RootsModule->dllName);
151 SECMOD_DestroyModule(RootsModule);
152 RootsModule = 0;
156 OString getMozillaCurrentProfile( const css::uno::Reference< css::uno::XComponentContext > &rxContext )
158 // first, try to get the profile from "MOZILLA_CERTIFICATE_FOLDER"
159 const char* pEnv = getenv("MOZILLA_CERTIFICATE_FOLDER");
160 if (pEnv)
162 SAL_INFO(
163 "xmlsecurity.xmlsec",
164 "Using Mozilla profile from MOZILLA_CERTIFICATE_FOLDER=" << pEnv);
165 return OString(pEnv);
168 // second, try to get saved user-preference
171 OUString sUserSetCertPath =
172 officecfg::Office::Common::Security::Scripting::CertDir::get().get_value_or(OUString());
174 if (!sUserSetCertPath.isEmpty())
176 SAL_INFO(
177 "xmlsecurity.xmlsec",
178 "Using Mozilla profile from /org.openoffice.Office.Common/"
179 "Security/Scripting/CertDir: " << sUserSetCertPath);
180 return OUStringToOString(sUserSetCertPath, osl_getThreadTextEncoding());
183 catch (const uno::Exception &e)
185 SAL_WARN(
186 "xmlsecurity.xmlsec",
187 "getMozillaCurrentProfile: caught exception " << e.Message);
190 // third, dig around to see if there's one available
191 mozilla::MozillaProductType productTypes[3] = {
192 mozilla::MozillaProductType_Thunderbird,
193 mozilla::MozillaProductType_Firefox,
194 mozilla::MozillaProductType_Mozilla };
195 int nProduct = SAL_N_ELEMENTS(productTypes);
197 uno::Reference<uno::XInterface> xInstance = rxContext->getServiceManager()->createInstanceWithContext("com.sun.star.mozilla.MozillaBootstrap", rxContext);
198 OSL_ENSURE( xInstance.is(), "failed to create instance" );
200 uno::Reference<mozilla::XMozillaBootstrap> xMozillaBootstrap
201 = uno::Reference<mozilla::XMozillaBootstrap>(xInstance,uno::UNO_QUERY);
202 OSL_ENSURE( xMozillaBootstrap.is(), "failed to create instance" );
204 if (xMozillaBootstrap.is())
206 for (int i=0; i<nProduct; ++i)
208 OUString profile = xMozillaBootstrap->getDefaultProfile(productTypes[i]);
210 if (!profile.isEmpty())
212 OUString sProfilePath = xMozillaBootstrap->getProfilePath( productTypes[i], profile );
213 SAL_INFO(
214 "xmlsecurity.xmlsec",
215 "Using Mozilla profile " << sProfilePath);
216 return OUStringToOString(sProfilePath, osl_getThreadTextEncoding());
221 SAL_INFO("xmlsecurity.xmlsec", "No Mozilla profile found");
222 return OString();
225 //Older versions of Firefox (FF), for example FF2, and Thunderbird (TB) 2 write
226 //the roots certificate module (libnssckbi.so), which they use, into the
227 //profile. This module will then already be loaded during NSS_Init (and the
228 //other init functions). This fails in two cases. First, FF3 was used to create
229 //the profile, or possibly used that profile before, and second the profile was
230 //used on a different platform.
232 //Then one needs to add the roots module oneself. This should be done with
233 //SECMOD_LoadUserModule rather then SECMOD_AddNewModule. The latter would write
234 //the location of the roots module to the profile, which makes FF2 and TB2 use
235 //it instead of there own module.
237 //When using SYSTEM_NSS then the libnss3.so lib is typically found in /usr/lib.
238 //This folder may, however, NOT contain the roots certificate module. That is,
239 //just providing the library name in SECMOD_LoadUserModule or
240 //SECMOD_AddNewModule will FAIL to load the mozilla unless the LD_LIBRARY_PATH
241 //contains an FF or TB installation.
242 //ATTENTION: DO NOT call this function directly instead use initNSS
243 //return true - whole initialization was successful
244 //param out_nss_init = true: at least the NSS initialization (NSS_InitReadWrite
245 //was successful and therefor NSS_Shutdown should be called when terminating.
246 bool nsscrypto_initialize( const css::uno::Reference< css::uno::XComponentContext > &rxContext, bool & out_nss_init )
248 bool return_value = true;
250 // this method must be called only once, no need for additional lock
251 OString sCertDir;
253 #ifdef XMLSEC_CRYPTO_NSS
254 sCertDir = getMozillaCurrentProfile(rxContext);
255 #else
256 (void) rxContext;
257 #endif
258 xmlsec_trace( "Using profile: %s", sCertDir.getStr() );
260 PR_Init( PR_USER_THREAD, PR_PRIORITY_NORMAL, 1 ) ;
262 bool bSuccess = true;
263 // there might be no profile
264 if ( !sCertDir.isEmpty() )
266 if( NSS_InitReadWrite( sCertDir.getStr() ) != SECSuccess )
268 xmlsec_trace("Initializing NSS with profile failed.");
269 int errlen = PR_GetErrorTextLength();
270 if(errlen > 0)
272 boost::scoped_array<char> const error(new char[errlen + 1]);
273 PR_GetErrorText(error.get());
274 xmlsec_trace("%s", error.get());
276 bSuccess = false;
280 if( sCertDir.isEmpty() || !bSuccess )
282 xmlsec_trace("Initializing NSS without profile.");
283 if ( NSS_NoDB_Init(NULL) != SECSuccess )
285 xmlsec_trace("Initializing NSS without profile failed.");
286 int errlen = PR_GetErrorTextLength();
287 if(errlen > 0)
289 boost::scoped_array<char> const error(new char[errlen + 1]);
290 PR_GetErrorText(error.get());
291 xmlsec_trace("%s", error.get());
293 return false ;
296 out_nss_init = true;
298 #ifdef XMLSEC_CRYPTO_NSS
299 #if defined SYSTEM_NSS
300 if (!SECMOD_HasRootCerts())
301 #endif
303 deleteRootsModule();
305 #if defined SYSTEM_NSS
306 OUString rootModule("libnssckbi" SAL_DLLEXTENSION);
307 #else
308 OUString rootModule(RTL_CONSTASCII_USTRINGPARAM("${LO_LIB_DIR}/libnssckbi" SAL_DLLEXTENSION));
309 #endif
310 ::rtl::Bootstrap::expandMacros(rootModule);
312 OUString rootModulePath;
313 if (::osl::File::E_None == ::osl::File::getSystemPathFromFileURL(rootModule, rootModulePath))
315 OString ospath = OUStringToOString(rootModulePath, osl_getThreadTextEncoding());
316 OStringBuffer pkcs11moduleSpec;
317 pkcs11moduleSpec.append("name=\"");
318 pkcs11moduleSpec.append(ROOT_CERTS);
319 pkcs11moduleSpec.append("\" library=\"");
320 pkcs11moduleSpec.append(ospath.getStr());
321 pkcs11moduleSpec.append("\"");
323 SECMODModule * RootsModule =
324 SECMOD_LoadUserModule(
325 const_cast<char*>(pkcs11moduleSpec.makeStringAndClear().getStr()),
326 0, // no parent
327 PR_FALSE); // do not recurse
329 if (RootsModule)
332 bool found = RootsModule->loaded;
334 SECMOD_DestroyModule(RootsModule);
335 RootsModule = 0;
336 if (found)
337 xmlsec_trace("Added new root certificate module "
338 "\"" ROOT_CERTS "\" contained in \n%s", ospath.getStr());
339 else
341 xmlsec_trace("FAILED to load the new root certificate module "
342 "\"" ROOT_CERTS "\" contained in \n%s", ospath.getStr());
343 return_value = false;
346 else
348 xmlsec_trace("FAILED to add new root certifice module: "
349 "\"" ROOT_CERTS "\" contained in \n%s", ospath.getStr());
350 return_value = false;
354 else
356 xmlsec_trace("Adding new root certificate module failed.");
357 return_value = false;
360 #endif
362 return return_value;
366 // must be extern "C" because we pass the function pointer to atexit
367 extern "C" void nsscrypto_finalize()
369 SECMODModule *RootsModule = SECMOD_FindModule(ROOT_CERTS);
371 if (RootsModule)
374 if (SECSuccess == SECMOD_UnloadUserModule(RootsModule))
376 xmlsec_trace("Unloaded module \"" ROOT_CERTS "\".");
378 else
380 xmlsec_trace("Failed unloading module \"" ROOT_CERTS "\".");
382 SECMOD_DestroyModule(RootsModule);
384 else
386 xmlsec_trace("Unloading module \"" ROOT_CERTS
387 "\" failed because it was not found.");
389 PK11_LogoutAll();
390 NSS_Shutdown();
392 } // namespace
394 ONSSInitializer::ONSSInitializer(
395 const css::uno::Reference< css::uno::XComponentContext > &rxContext)
396 :m_xContext( rxContext )
400 ONSSInitializer::~ONSSInitializer()
404 bool ONSSInitializer::initNSS( const css::uno::Reference< css::uno::XComponentContext > &rxContext )
406 return *rtl_Instance< bool, InitNSSInitialize, ::osl::MutexGuard, GetNSSInitStaticMutex >
407 ::create( InitNSSInitialize( rxContext ), GetNSSInitStaticMutex() );
410 css::uno::Reference< css::xml::crypto::XDigestContext > SAL_CALL ONSSInitializer::getDigestContext( ::sal_Int32 nDigestID, const css::uno::Sequence< css::beans::NamedValue >& aParams )
411 throw (css::lang::IllegalArgumentException, css::uno::RuntimeException)
413 SECOidTag nNSSDigestID = SEC_OID_UNKNOWN;
414 sal_Int32 nDigestLength = 0;
415 bool b1KData = false;
416 if ( nDigestID == css::xml::crypto::DigestID::SHA256
417 || nDigestID == css::xml::crypto::DigestID::SHA256_1K )
419 nNSSDigestID = SEC_OID_SHA256;
420 nDigestLength = 32;
421 b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA256_1K );
423 else if ( nDigestID == css::xml::crypto::DigestID::SHA1
424 || nDigestID == css::xml::crypto::DigestID::SHA1_1K )
426 nNSSDigestID = SEC_OID_SHA1;
427 nDigestLength = 20;
428 b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA1_1K );
430 else
431 throw css::lang::IllegalArgumentException( OUString( "Unexpected digest requested." ), css::uno::Reference< css::uno::XInterface >(), 1 );
433 if ( aParams.getLength() )
434 throw css::lang::IllegalArgumentException( OUString( "Unexpected arguments provided for digest creation." ), css::uno::Reference< css::uno::XInterface >(), 2 );
436 css::uno::Reference< css::xml::crypto::XDigestContext > xResult;
437 if( initNSS( m_xContext ) )
439 PK11Context* pContext = PK11_CreateDigestContext( nNSSDigestID );
440 if ( pContext && PK11_DigestBegin( pContext ) == SECSuccess )
441 xResult = new ODigestContext( pContext, nDigestLength, b1KData );
444 return xResult;
447 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 )
448 throw (css::lang::IllegalArgumentException, css::uno::RuntimeException)
450 CK_MECHANISM_TYPE nNSSCipherID = 0;
451 bool bW3CPadding = false;
452 if ( nCipherID == css::xml::crypto::CipherID::AES_CBC_W3C_PADDING )
454 nNSSCipherID = CKM_AES_CBC;
455 bW3CPadding = true;
457 if ( aKey.getLength() != 16 && aKey.getLength() != 24 && aKey.getLength() != 32 )
458 throw css::lang::IllegalArgumentException( OUString( "Unexpected key length." ), css::uno::Reference< css::uno::XInterface >(), 2 );
460 if ( aParams.getLength() )
461 throw css::lang::IllegalArgumentException( OUString( "Unexpected arguments provided for cipher creation." ), css::uno::Reference< css::uno::XInterface >(), 5 );
463 else
464 throw css::lang::IllegalArgumentException( OUString( "Unexpected cipher requested." ), css::uno::Reference< css::uno::XInterface >(), 1 );
466 css::uno::Reference< css::xml::crypto::XCipherContext > xResult;
467 if( initNSS( m_xContext ) )
469 if ( aInitializationVector.getLength() != PK11_GetIVLength( nNSSCipherID ) )
470 throw css::lang::IllegalArgumentException( OUString( "Unexpected length of initialization vector." ), css::uno::Reference< css::uno::XInterface >(), 3 );
472 xResult = OCipherContext::Create( nNSSCipherID, aKey, aInitializationVector, bEncryption, bW3CPadding );
475 return xResult;
478 OUString ONSSInitializer_getImplementationName ()
479 throw (cssu::RuntimeException)
482 return OUString ( IMPLEMENTATION_NAME );
485 sal_Bool SAL_CALL ONSSInitializer_supportsService( const OUString& ServiceName )
486 throw (cssu::RuntimeException)
488 return ServiceName == NSS_SERVICE_NAME;
491 cssu::Sequence< OUString > SAL_CALL ONSSInitializer_getSupportedServiceNames( )
492 throw (cssu::RuntimeException)
494 cssu::Sequence < OUString > aRet(1);
495 OUString* pArray = aRet.getArray();
496 pArray[0] = OUString ( NSS_SERVICE_NAME );
497 return aRet;
500 cssu::Reference< cssu::XInterface > SAL_CALL ONSSInitializer_createInstance( const cssu::Reference< cssl::XMultiServiceFactory > & rSMgr)
501 throw( cssu::Exception )
503 return (cppu::OWeakObject*) new ONSSInitializer( comphelper::getComponentContext(rSMgr) );
506 /* XServiceInfo */
507 OUString SAL_CALL ONSSInitializer::getImplementationName()
508 throw (cssu::RuntimeException)
510 return ONSSInitializer_getImplementationName();
512 sal_Bool SAL_CALL ONSSInitializer::supportsService( const OUString& rServiceName )
513 throw (cssu::RuntimeException)
515 return ONSSInitializer_supportsService( rServiceName );
517 cssu::Sequence< OUString > SAL_CALL ONSSInitializer::getSupportedServiceNames( )
518 throw (cssu::RuntimeException)
520 return ONSSInitializer_getSupportedServiceNames();
523 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */