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