1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
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>
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();
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
)
90 static bool bInitialized
= false;
91 bool bNSSInit
= false;
92 bInitialized
= nsscrypto_initialize( m_xContext
, 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
);
137 SECMOD_ReleaseReadLock(lock
);
142 if (SECSuccess
== SECMOD_DeleteModule(RootsModule
->commonName
, &modType
))
144 xmlsec_trace("Deleted module \"%s\".", RootsModule
->commonName
);
148 xmlsec_trace("Failed to delete \"%s\" : \n%s",
149 RootsModule
->commonName
, RootsModule
->dllName
);
151 SECMOD_DestroyModule(RootsModule
);
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");
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())
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
)
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
);
214 "xmlsecurity.xmlsec",
215 "Using Mozilla profile " << sProfilePath
);
216 return OUStringToOString(sProfilePath
, osl_getThreadTextEncoding());
221 SAL_INFO("xmlsecurity.xmlsec", "No Mozilla profile found");
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
253 #ifdef XMLSEC_CRYPTO_NSS
254 sCertDir
= getMozillaCurrentProfile(rxContext
);
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();
272 boost::scoped_array
<char> const error(new char[errlen
+ 1]);
273 PR_GetErrorText(error
.get());
274 xmlsec_trace("%s", error
.get());
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();
289 boost::scoped_array
<char> const error(new char[errlen
+ 1]);
290 PR_GetErrorText(error
.get());
291 xmlsec_trace("%s", error
.get());
298 #ifdef XMLSEC_CRYPTO_NSS
299 #if defined SYSTEM_NSS
300 if (!SECMOD_HasRootCerts())
305 #if defined SYSTEM_NSS
306 OUString
rootModule("libnssckbi" SAL_DLLEXTENSION
);
308 OUString
rootModule(RTL_CONSTASCII_USTRINGPARAM("${LO_LIB_DIR}/libnssckbi" SAL_DLLEXTENSION
));
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()),
327 PR_FALSE
); // do not recurse
332 bool found
= RootsModule
->loaded
;
334 SECMOD_DestroyModule(RootsModule
);
337 xmlsec_trace("Added new root certificate module "
338 "\"" ROOT_CERTS
"\" contained in \n%s", ospath
.getStr());
341 xmlsec_trace("FAILED to load the new root certificate module "
342 "\"" ROOT_CERTS
"\" contained in \n%s", ospath
.getStr());
343 return_value
= false;
348 xmlsec_trace("FAILED to add new root certifice module: "
349 "\"" ROOT_CERTS
"\" contained in \n%s", ospath
.getStr());
350 return_value
= false;
356 xmlsec_trace("Adding new root certificate module failed.");
357 return_value
= false;
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
);
374 if (SECSuccess
== SECMOD_UnloadUserModule(RootsModule
))
376 xmlsec_trace("Unloaded module \"" ROOT_CERTS
"\".");
380 xmlsec_trace("Failed unloading module \"" ROOT_CERTS
"\".");
382 SECMOD_DestroyModule(RootsModule
);
386 xmlsec_trace("Unloading module \"" ROOT_CERTS
387 "\" failed because it was not found.");
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
;
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
;
428 b1KData
= ( nDigestID
== css::xml::crypto::DigestID::SHA1_1K
);
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
);
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
;
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 );
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
);
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
);
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
) );
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: */