1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_xmlsecurity.hxx"
26 
27 /*
28  * Turn off DEBUG Assertions
29  */
30 #ifdef _DEBUG
31     #define _DEBUG_WAS_DEFINED _DEBUG
32     #undef _DEBUG
33 #else
34     #undef _DEBUG_WAS_DEFINED
35 #endif
36 
37 /*
38  * and turn off the additional virtual methods which are part of some interfaces when compiled
39  * with debug
40  */
41 #ifdef DEBUG
42     #define DEBUG_WAS_DEFINED DEBUG
43     #undef DEBUG
44 #else
45     #undef DEBUG_WAS_DEFINED
46 #endif
47 
48 
49 #include <com/sun/star/mozilla/XMozillaBootstrap.hpp>
50 #include <com/sun/star/xml/crypto/DigestID.hpp>
51 #include <com/sun/star/xml/crypto/CipherID.hpp>
52 
53 #include <sal/types.h>
54 #include <rtl/instance.hxx>
55 #include <rtl/bootstrap.hxx>
56 #include <rtl/string.hxx>
57 #include <rtl/strbuf.hxx>
58 #include <osl/file.hxx>
59 #include <osl/thread.h>
60 #include <tools/debug.hxx>
61 #include <rtl/logfile.hxx>
62 
63 #include "seinitializer_nssimpl.hxx"
64 #include "../diagnose.hxx"
65 
66 #include "securityenvironment_nssimpl.hxx"
67 #include "digestcontext.hxx"
68 #include "ciphercontext.hxx"
69 
70 #include <nspr.h>
71 #include <cert.h>
72 #include <nss.h>
73 #include <pk11pub.h>
74 #include <secmod.h>
75 #include <nssckbi.h>
76 
77 
78 namespace css = ::com::sun::star;
79 namespace cssu = css::uno;
80 namespace cssl = css::lang;
81 namespace cssxc = css::xml::crypto;
82 
83 using namespace xmlsecurity;
84 using namespace com::sun::star;
85 using ::rtl::OUString;
86 using ::rtl::OString;
87 
88 #define IMPLEMENTATION_NAME "com.sun.star.xml.security.bridge.xmlsec.NSSInitializer_NssImpl"
89 
90 #define ROOT_CERTS "Root Certs for OpenOffice.org"
91 
92 extern "C" void nsscrypto_finalize();
93 
94 
95 namespace
96 {
97 
98 bool nsscrypto_initialize( const css::uno::Reference< css::lang::XMultiServiceFactory > &xMSF, bool & out_nss_init );
99 
100 struct InitNSSInitialize
101 {
102     css::uno::Reference< css::lang::XMultiServiceFactory > mxMSF;
103 
104     InitNSSInitialize( const css::uno::Reference< css::lang::XMultiServiceFactory > &xMSF )
105     : mxMSF( xMSF )
106     {
107     }
108 
109     bool * operator()()
110         {
111             static bool bInitialized = false;
112             bool bNSSInit = false;
113             bInitialized = nsscrypto_initialize( mxMSF, bNSSInit );
114             if (bNSSInit)
115                 atexit(nsscrypto_finalize );
116              return & bInitialized;
117         }
118 };
119 
120 struct GetNSSInitStaticMutex
121 {
122     ::osl::Mutex* operator()()
123     {
124         static ::osl::Mutex aNSSInitMutex;
125         return &aNSSInitMutex;
126     }
127 };
128 
129 void deleteRootsModule()
130 {
131     SECMODModule *RootsModule = 0;
132     SECMODModuleList *list = SECMOD_GetDefaultModuleList();
133     SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
134     SECMOD_GetReadLock(lock);
135 
136     while (!RootsModule && list)
137     {
138         SECMODModule *module = list->module;
139 
140         for (int i=0; i < module->slotCount; i++)
141         {
142             PK11SlotInfo *slot = module->slots[i];
143             if (PK11_IsPresent(slot))
144             {
145                 if (PK11_HasRootCerts(slot))
146                 {
147                     xmlsec_trace("The root certifificates module \"%s"
148                               "\" is already loaded: \n%s",
149                               module->commonName,  module->dllName);
150 
151                     RootsModule = SECMOD_ReferenceModule(module);
152                     break;
153                 }
154             }
155         }
156         list = list->next;
157     }
158     SECMOD_ReleaseReadLock(lock);
159 
160     if (RootsModule)
161     {
162         PRInt32 modType;
163         if (SECSuccess == SECMOD_DeleteModule(RootsModule->commonName, &modType))
164         {
165             xmlsec_trace("Deleted module \"%s\".", RootsModule->commonName);
166         }
167         else
168         {
169             xmlsec_trace("Failed to delete \"%s\" : \n%s",
170                       RootsModule->commonName, RootsModule->dllName);
171         }
172         SECMOD_DestroyModule(RootsModule);
173         RootsModule = 0;
174     }
175 }
176 
177 ::rtl::OString getMozillaCurrentProfile( const css::uno::Reference< css::lang::XMultiServiceFactory > &rxMSF )
178 {
179     ::rtl::OString sResult;
180     // first, try to get the profile from "MOZILLA_CERTIFICATE_FOLDER"
181     char* pEnv = getenv( "MOZILLA_CERTIFICATE_FOLDER" );
182     if ( pEnv )
183     {
184         sResult = ::rtl::OString( pEnv );
185         RTL_LOGFILE_PRODUCT_TRACE1( "XMLSEC: Using env MOZILLA_CERTIFICATE_FOLDER: %s", sResult.getStr() );
186     }
187     else
188     {
189         mozilla::MozillaProductType productTypes[4] = {
190             mozilla::MozillaProductType_Thunderbird,
191             mozilla::MozillaProductType_Mozilla,
192             mozilla::MozillaProductType_Firefox,
193             mozilla::MozillaProductType_Default };
194         int nProduct = 4;
195 
196         uno::Reference<uno::XInterface> xInstance = rxMSF->createInstance(
197             ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.mozilla.MozillaBootstrap")) );
198         OSL_ENSURE( xInstance.is(), "failed to create instance" );
199 
200         uno::Reference<mozilla::XMozillaBootstrap> xMozillaBootstrap
201             =  uno::Reference<mozilla::XMozillaBootstrap>(xInstance,uno::UNO_QUERY);
202         OSL_ENSURE( xMozillaBootstrap.is(), "failed to create instance" );
203 
204         if (xMozillaBootstrap.is())
205         {
206             for (int i=0; i<nProduct; i++)
207             {
208                 ::rtl::OUString profile = xMozillaBootstrap->getDefaultProfile(productTypes[i]);
209 
210                 if (profile != NULL && profile.getLength()>0)
211                 {
212                     ::rtl::OUString sProfilePath = xMozillaBootstrap->getProfilePath( productTypes[i], profile );
213                     sResult = ::rtl::OUStringToOString( sProfilePath, osl_getThreadTextEncoding() );
214                     RTL_LOGFILE_PRODUCT_TRACE1( "XMLSEC: Using Mozilla Profile: %s", sResult.getStr() );
215                 }
216             }
217         }
218 
219         RTL_LOGFILE_PRODUCT_TRACE( "XMLSEC: No Mozilla Profile found!" );
220     }
221 
222     return sResult;
223 }
224 
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.
231 //
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.
236 //
237 //When using SYSTEM_MOZILLA then the libnss3.so lib is typically found in
238 ///usr/lib. This folder may, however, NOT contain the roots certificate
239 //module. That is, 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::lang::XMultiServiceFactory > &xMSF, bool & out_nss_init )
247 {
248     bool return_value = true;
249 
250     // this method must be called only once, no need for additional lock
251     rtl::OString sCertDir;
252 
253     (void) xMSF;
254 #ifdef XMLSEC_CRYPTO_NSS
255     if ( xMSF.is() )
256         sCertDir = getMozillaCurrentProfile( xMSF );
257 #endif
258     xmlsec_trace( "Using profile: %s", sCertDir.getStr() );
259 
260     PR_Init( PR_USER_THREAD, PR_PRIORITY_NORMAL, 1 ) ;
261 
262     // there might be no profile
263     if ( sCertDir.getLength() > 0 )
264     {
265         if( NSS_InitReadWrite( sCertDir.getStr() ) != SECSuccess )
266         {
267             xmlsec_trace("Initializing NSS with profile failed.");
268             char * error = NULL;
269 
270             PR_GetErrorText(error);
271             if (error)
272                 xmlsec_trace("%s",error);
273             return false ;
274         }
275     }
276     else
277     {
278         xmlsec_trace("Initializing NSS without profile.");
279         if ( NSS_NoDB_Init(NULL) != SECSuccess )
280         {
281             xmlsec_trace("Initializing NSS without profile failed.");
282             char * error = NULL;
283             PR_GetErrorText(error);
284             if (error)
285                 xmlsec_trace("%s",error);
286             return false ;
287         }
288     }
289     out_nss_init = true;
290 
291 #ifdef XMLSEC_CRYPTO_NSS
292 #if defined SYSTEM_MOZILLA
293     if (!SECMOD_HasRootCerts())
294     {
295 #endif
296         deleteRootsModule();
297 
298 #if defined SYSTEM_MOZILLA
299         OUString rootModule(RTL_CONSTASCII_USTRINGPARAM("libnssckbi"SAL_DLLEXTENSION));
300 #else
301         OUString rootModule(RTL_CONSTASCII_USTRINGPARAM("${OOO_BASE_DIR}/program/libnssckbi"SAL_DLLEXTENSION));
302 #endif
303         ::rtl::Bootstrap::expandMacros(rootModule);
304 
305         OUString rootModulePath;
306         if (::osl::File::E_None == ::osl::File::getSystemPathFromFileURL(rootModule, rootModulePath))
307         {
308             ::rtl::OString ospath = ::rtl::OUStringToOString(rootModulePath, osl_getThreadTextEncoding());
309             ::rtl::OStringBuffer pkcs11moduleSpec;
310             pkcs11moduleSpec.append("name=\"");
311             pkcs11moduleSpec.append(ROOT_CERTS);
312             pkcs11moduleSpec.append("\" library=\"");
313             pkcs11moduleSpec.append(ospath.getStr());
314             pkcs11moduleSpec.append("\"");
315 
316             SECMODModule * RootsModule =
317                 SECMOD_LoadUserModule(
318                     const_cast<char*>(pkcs11moduleSpec.makeStringAndClear().getStr()),
319                     0, // no parent
320                     PR_FALSE); // do not recurse
321 
322             if (RootsModule)
323             {
324 
325                 bool found = RootsModule->loaded;
326 
327                 SECMOD_DestroyModule(RootsModule);
328                 RootsModule = 0;
329                 if (found)
330                     xmlsec_trace("Added new root certificate module "
331                               "\""ROOT_CERTS"\" contained in \n%s", ospath.getStr());
332                 else
333                 {
334                     xmlsec_trace("FAILED to load the new root certificate module "
335                               "\""ROOT_CERTS"\" contained in \n%s", ospath.getStr());
336                     return_value = false;
337                 }
338             }
339             else
340             {
341                 xmlsec_trace("FAILED to add new root certifice module: "
342                           "\""ROOT_CERTS"\" contained in \n%s", ospath.getStr());
343                 return_value = false;
344 
345             }
346         }
347         else
348         {
349             xmlsec_trace("Adding new root certificate module failed.");
350             return_value = false;
351         }
352 #if SYSTEM_MOZILLA
353     }
354 #endif
355 #endif
356 
357     return return_value;
358 }
359 
360 
361 // must be extern "C" because we pass the function pointer to atexit
362 extern "C" void nsscrypto_finalize()
363 {
364     SECMODModule *RootsModule = SECMOD_FindModule(ROOT_CERTS);
365 
366     if (RootsModule)
367     {
368 
369         if (SECSuccess == SECMOD_UnloadUserModule(RootsModule))
370         {
371             xmlsec_trace("Unloaded module \""ROOT_CERTS"\".");
372         }
373         else
374         {
375             xmlsec_trace("Failed unloadeding module \""ROOT_CERTS"\".");
376         }
377         SECMOD_DestroyModule(RootsModule);
378     }
379     else
380     {
381         xmlsec_trace("Unloading module \""ROOT_CERTS
382                   "\" failed because it was not found.");
383     }
384     PK11_LogoutAll();
385     NSS_Shutdown();
386 }
387 } // namespace
388 
389 ONSSInitializer::ONSSInitializer(
390     const css::uno::Reference< css::lang::XMultiServiceFactory > &rxMSF)
391     :mxMSF( rxMSF )
392 {
393 }
394 
395 ONSSInitializer::~ONSSInitializer()
396 {
397 }
398 
399 bool ONSSInitializer::initNSS( const css::uno::Reference< css::lang::XMultiServiceFactory > &xMSF )
400 {
401     return *rtl_Instance< bool, InitNSSInitialize, ::osl::MutexGuard, GetNSSInitStaticMutex >
402                 ::create( InitNSSInitialize( xMSF ), GetNSSInitStaticMutex() );
403 }
404 
405 css::uno::Reference< css::xml::crypto::XDigestContext > SAL_CALL ONSSInitializer::getDigestContext( ::sal_Int32 nDigestID, const css::uno::Sequence< css::beans::NamedValue >& aParams )
406     throw (css::lang::IllegalArgumentException, css::uno::RuntimeException)
407 {
408     SECOidTag nNSSDigestID = SEC_OID_UNKNOWN;
409     sal_Int32 nDigestLength = 0;
410     bool b1KData = false;
411     if ( nDigestID == css::xml::crypto::DigestID::SHA256
412       || nDigestID == css::xml::crypto::DigestID::SHA256_1K )
413     {
414         nNSSDigestID = SEC_OID_SHA256;
415         nDigestLength = 32;
416         b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA256_1K );
417     }
418     else if ( nDigestID == css::xml::crypto::DigestID::SHA1
419            || nDigestID == css::xml::crypto::DigestID::SHA1_1K )
420     {
421         nNSSDigestID = SEC_OID_SHA1;
422         nDigestLength = 20;
423         b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA1_1K );
424     }
425     else
426         throw css::lang::IllegalArgumentException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected digest requested." ) ), css::uno::Reference< css::uno::XInterface >(), 1 );
427 
428     if ( aParams.getLength() )
429         throw css::lang::IllegalArgumentException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected arguments provided for digest creation." ) ), css::uno::Reference< css::uno::XInterface >(), 2 );
430 
431     css::uno::Reference< css::xml::crypto::XDigestContext > xResult;
432     if( initNSS( mxMSF ) )
433     {
434         PK11Context* pContext = PK11_CreateDigestContext( nNSSDigestID );
435         if ( pContext && PK11_DigestBegin( pContext ) == SECSuccess )
436             xResult = new ODigestContext( pContext, nDigestLength, b1KData );
437     }
438 
439     return xResult;
440 }
441 
442 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 )
443     throw (css::lang::IllegalArgumentException, css::uno::RuntimeException)
444 {
445     CK_MECHANISM_TYPE nNSSCipherID = 0;
446     bool bW3CPadding = false;
447     if ( nCipherID == css::xml::crypto::CipherID::AES_CBC_W3C_PADDING )
448     {
449         nNSSCipherID = CKM_AES_CBC;
450         bW3CPadding = true;
451 
452         if ( aKey.getLength() != 16 && aKey.getLength() != 24 && aKey.getLength() != 32 )
453             throw css::lang::IllegalArgumentException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected key length." ) ), css::uno::Reference< css::uno::XInterface >(), 2 );
454 
455         if ( aParams.getLength() )
456             throw css::lang::IllegalArgumentException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected arguments provided for cipher creation." ) ), css::uno::Reference< css::uno::XInterface >(), 5 );
457     }
458     else
459         throw css::lang::IllegalArgumentException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected cipher requested." ) ), css::uno::Reference< css::uno::XInterface >(), 1 );
460 
461     css::uno::Reference< css::xml::crypto::XCipherContext > xResult;
462     if( initNSS( mxMSF ) )
463     {
464         if ( aInitializationVector.getLength() != PK11_GetIVLength( nNSSCipherID ) )
465             throw css::lang::IllegalArgumentException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected length of initialization vector." ) ), css::uno::Reference< css::uno::XInterface >(), 3 );
466 
467         xResult = OCipherContext::Create( nNSSCipherID, aKey, aInitializationVector, bEncryption, bW3CPadding );
468     }
469 
470     return xResult;
471 }
472 
473 rtl::OUString ONSSInitializer_getImplementationName ()
474     throw (cssu::RuntimeException)
475 {
476 
477     return rtl::OUString ( RTL_CONSTASCII_USTRINGPARAM ( IMPLEMENTATION_NAME ) );
478 }
479 
480 sal_Bool SAL_CALL ONSSInitializer_supportsService( const rtl::OUString& ServiceName )
481     throw (cssu::RuntimeException)
482 {
483     return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( NSS_SERVICE_NAME ));
484 }
485 
486 cssu::Sequence< rtl::OUString > SAL_CALL ONSSInitializer_getSupportedServiceNames(  )
487     throw (cssu::RuntimeException)
488 {
489     cssu::Sequence < rtl::OUString > aRet(1);
490     rtl::OUString* pArray = aRet.getArray();
491     pArray[0] =  rtl::OUString ( RTL_CONSTASCII_USTRINGPARAM ( NSS_SERVICE_NAME ) );
492     return aRet;
493 }
494 
495 cssu::Reference< cssu::XInterface > SAL_CALL ONSSInitializer_createInstance( const cssu::Reference< cssl::XMultiServiceFactory > & rSMgr)
496     throw( cssu::Exception )
497 {
498     return (cppu::OWeakObject*) new ONSSInitializer( rSMgr );
499 }
500 
501 /* XServiceInfo */
502 rtl::OUString SAL_CALL ONSSInitializer::getImplementationName()
503     throw (cssu::RuntimeException)
504 {
505     return ONSSInitializer_getImplementationName();
506 }
507 sal_Bool SAL_CALL ONSSInitializer::supportsService( const rtl::OUString& rServiceName )
508     throw (cssu::RuntimeException)
509 {
510     return ONSSInitializer_supportsService( rServiceName );
511 }
512 cssu::Sequence< rtl::OUString > SAL_CALL ONSSInitializer::getSupportedServiceNames(  )
513     throw (cssu::RuntimeException)
514 {
515     return ONSSInitializer_getSupportedServiceNames();
516 }
517 
518