Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 :
21 : /*
22 : * and turn off the additional virtual methods which are part of some interfaces when compiled
23 : * with debug
24 : */
25 : #ifdef DEBUG
26 : #undef DEBUG
27 : #endif
28 :
29 :
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>
33 :
34 : #include <officecfg/Office/Common.hxx>
35 :
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>
44 :
45 : #include "seinitializer_nssimpl.hxx"
46 : #include "../diagnose.hxx"
47 :
48 : #include "securityenvironment_nssimpl.hxx"
49 : #include "digestcontext.hxx"
50 : #include "ciphercontext.hxx"
51 :
52 : #include <boost/scoped_array.hpp>
53 :
54 : #include <nspr.h>
55 : #include <cert.h>
56 : #include <nss.h>
57 : #include <pk11pub.h>
58 : #include <secmod.h>
59 : #include <nssckbi.h>
60 :
61 : namespace cssu = css::uno;
62 : namespace cssl = css::lang;
63 :
64 : using namespace xmlsecurity;
65 : using namespace com::sun::star;
66 :
67 : #define IMPLEMENTATION_NAME "com.sun.star.xml.security.bridge.xmlsec.NSSInitializer_NssImpl"
68 :
69 : #define ROOT_CERTS "Root Certs for OpenOffice.org"
70 :
71 : extern "C" void nsscrypto_finalize();
72 :
73 :
74 : namespace
75 : {
76 :
77 : bool nsscrypto_initialize( const css::uno::Reference< css::uno::XComponentContext > &rxContext, bool & out_nss_init );
78 :
79 41 : struct InitNSSInitialize
80 : {
81 : css::uno::Reference< css::uno::XComponentContext > m_xContext;
82 :
83 41 : InitNSSInitialize( const css::uno::Reference< css::uno::XComponentContext > &rxContext )
84 41 : : m_xContext( rxContext )
85 : {
86 41 : }
87 :
88 2 : bool * operator()()
89 : {
90 : static bool bInitialized = false;
91 2 : bool bNSSInit = false;
92 2 : bInitialized = nsscrypto_initialize( m_xContext, bNSSInit );
93 2 : if (bNSSInit)
94 2 : atexit(nsscrypto_finalize );
95 2 : return & bInitialized;
96 : }
97 : };
98 :
99 : struct GetNSSInitStaticMutex
100 : {
101 2 : ::osl::Mutex* operator()()
102 : {
103 2 : static ::osl::Mutex aNSSInitMutex;
104 2 : return &aNSSInitMutex;
105 : }
106 : };
107 :
108 2 : void deleteRootsModule()
109 : {
110 2 : SECMODModule *RootsModule = 0;
111 2 : SECMODModuleList *list = SECMOD_GetDefaultModuleList();
112 2 : SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
113 2 : SECMOD_GetReadLock(lock);
114 :
115 6 : while (!RootsModule && list)
116 : {
117 2 : SECMODModule *module = list->module;
118 :
119 6 : for (int i=0; i < module->slotCount; i++)
120 : {
121 4 : PK11SlotInfo *slot = module->slots[i];
122 4 : if (PK11_IsPresent(slot))
123 : {
124 4 : if (PK11_HasRootCerts(slot))
125 : {
126 : xmlsec_trace("The root certifificates module \"%s"
127 : "\" is already loaded: \n%s",
128 0 : module->commonName, module->dllName);
129 :
130 0 : RootsModule = SECMOD_ReferenceModule(module);
131 0 : break;
132 : }
133 : }
134 : }
135 2 : list = list->next;
136 : }
137 2 : SECMOD_ReleaseReadLock(lock);
138 :
139 2 : if (RootsModule)
140 : {
141 : PRInt32 modType;
142 0 : if (SECSuccess == SECMOD_DeleteModule(RootsModule->commonName, &modType))
143 : {
144 0 : xmlsec_trace("Deleted module \"%s\".", RootsModule->commonName);
145 : }
146 : else
147 : {
148 : xmlsec_trace("Failed to delete \"%s\" : \n%s",
149 0 : RootsModule->commonName, RootsModule->dllName);
150 : }
151 0 : SECMOD_DestroyModule(RootsModule);
152 0 : RootsModule = 0;
153 : }
154 2 : }
155 :
156 2 : OString getMozillaCurrentProfile( const css::uno::Reference< css::uno::XComponentContext > &rxContext )
157 : {
158 : // first, try to get the profile from "MOZILLA_CERTIFICATE_FOLDER"
159 2 : const char* pEnv = getenv("MOZILLA_CERTIFICATE_FOLDER");
160 2 : if (pEnv)
161 : {
162 : SAL_INFO(
163 : "xmlsecurity.xmlsec",
164 : "Using Mozilla profile from MOZILLA_CERTIFICATE_FOLDER=" << pEnv);
165 0 : return OString(pEnv);
166 : }
167 :
168 : // second, try to get saved user-preference
169 : try
170 : {
171 : OUString sUserSetCertPath =
172 2 : officecfg::Office::Common::Security::Scripting::CertDir::get().get_value_or(OUString());
173 :
174 2 : if (!sUserSetCertPath.isEmpty())
175 : {
176 : SAL_INFO(
177 : "xmlsecurity.xmlsec",
178 : "Using Mozilla profile from /org.openoffice.Office.Common/"
179 : "Security/Scripting/CertDir: " << sUserSetCertPath);
180 0 : return OUStringToOString(sUserSetCertPath, osl_getThreadTextEncoding());
181 2 : }
182 : }
183 0 : catch (const uno::Exception &e)
184 : {
185 : SAL_WARN(
186 : "xmlsecurity.xmlsec",
187 : "getMozillaCurrentProfile: caught exception " << e.Message);
188 : }
189 :
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 2 : mozilla::MozillaProductType_Mozilla };
195 2 : int nProduct = SAL_N_ELEMENTS(productTypes);
196 :
197 2 : uno::Reference<uno::XInterface> xInstance = rxContext->getServiceManager()->createInstanceWithContext("com.sun.star.mozilla.MozillaBootstrap", rxContext);
198 : OSL_ENSURE( xInstance.is(), "failed to create instance" );
199 :
200 : uno::Reference<mozilla::XMozillaBootstrap> xMozillaBootstrap
201 4 : = uno::Reference<mozilla::XMozillaBootstrap>(xInstance,uno::UNO_QUERY);
202 : OSL_ENSURE( xMozillaBootstrap.is(), "failed to create instance" );
203 :
204 2 : if (xMozillaBootstrap.is())
205 : {
206 0 : for (int i=0; i<nProduct; ++i)
207 : {
208 0 : OUString profile = xMozillaBootstrap->getDefaultProfile(productTypes[i]);
209 :
210 0 : if (!profile.isEmpty())
211 : {
212 0 : OUString sProfilePath = xMozillaBootstrap->getProfilePath( productTypes[i], profile );
213 : SAL_INFO(
214 : "xmlsecurity.xmlsec",
215 : "Using Mozilla profile " << sProfilePath);
216 0 : return OUStringToOString(sProfilePath, osl_getThreadTextEncoding());
217 : }
218 0 : }
219 : }
220 :
221 : SAL_INFO("xmlsecurity.xmlsec", "No Mozilla profile found");
222 4 : return OString();
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_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 2 : bool nsscrypto_initialize( const css::uno::Reference< css::uno::XComponentContext > &rxContext, bool & out_nss_init )
247 : {
248 2 : bool return_value = true;
249 :
250 : // this method must be called only once, no need for additional lock
251 2 : OString sCertDir;
252 :
253 : #ifdef XMLSEC_CRYPTO_NSS
254 2 : sCertDir = getMozillaCurrentProfile(rxContext);
255 : #else
256 : (void) rxContext;
257 : #endif
258 2 : xmlsec_trace( "Using profile: %s", sCertDir.getStr() );
259 :
260 2 : PR_Init( PR_USER_THREAD, PR_PRIORITY_NORMAL, 1 ) ;
261 :
262 2 : bool bSuccess = true;
263 : // there might be no profile
264 2 : if ( !sCertDir.isEmpty() )
265 : {
266 0 : if( NSS_InitReadWrite( sCertDir.getStr() ) != SECSuccess )
267 : {
268 0 : xmlsec_trace("Initializing NSS with profile failed.");
269 0 : int errlen = PR_GetErrorTextLength();
270 0 : if(errlen > 0)
271 : {
272 0 : boost::scoped_array<char> const error(new char[errlen + 1]);
273 0 : PR_GetErrorText(error.get());
274 0 : xmlsec_trace("%s", error.get());
275 : }
276 0 : bSuccess = false;
277 : }
278 : }
279 :
280 2 : if( sCertDir.isEmpty() || !bSuccess )
281 : {
282 2 : xmlsec_trace("Initializing NSS without profile.");
283 2 : if ( NSS_NoDB_Init(NULL) != SECSuccess )
284 : {
285 0 : xmlsec_trace("Initializing NSS without profile failed.");
286 0 : int errlen = PR_GetErrorTextLength();
287 0 : if(errlen > 0)
288 : {
289 0 : boost::scoped_array<char> const error(new char[errlen + 1]);
290 0 : PR_GetErrorText(error.get());
291 0 : xmlsec_trace("%s", error.get());
292 : }
293 0 : return false ;
294 : }
295 : }
296 2 : out_nss_init = true;
297 :
298 : #ifdef XMLSEC_CRYPTO_NSS
299 : #if defined SYSTEM_NSS
300 2 : if (!SECMOD_HasRootCerts())
301 : #endif
302 : {
303 2 : deleteRootsModule();
304 :
305 : #if defined SYSTEM_NSS
306 2 : OUString rootModule("libnssckbi" SAL_DLLEXTENSION);
307 : #else
308 : OUString rootModule(RTL_CONSTASCII_USTRINGPARAM("${LO_LIB_DIR}/libnssckbi" SAL_DLLEXTENSION));
309 : #endif
310 2 : ::rtl::Bootstrap::expandMacros(rootModule);
311 :
312 4 : OUString rootModulePath;
313 2 : if (::osl::File::E_None == ::osl::File::getSystemPathFromFileURL(rootModule, rootModulePath))
314 : {
315 2 : OString ospath = OUStringToOString(rootModulePath, osl_getThreadTextEncoding());
316 4 : OStringBuffer pkcs11moduleSpec;
317 2 : pkcs11moduleSpec.append("name=\"");
318 2 : pkcs11moduleSpec.append(ROOT_CERTS);
319 2 : pkcs11moduleSpec.append("\" library=\"");
320 2 : pkcs11moduleSpec.append(ospath.getStr());
321 2 : pkcs11moduleSpec.append("\"");
322 :
323 : SECMODModule * RootsModule =
324 : SECMOD_LoadUserModule(
325 4 : const_cast<char*>(pkcs11moduleSpec.makeStringAndClear().getStr()),
326 : 0, // no parent
327 2 : PR_FALSE); // do not recurse
328 :
329 2 : if (RootsModule)
330 : {
331 :
332 2 : bool found = RootsModule->loaded;
333 :
334 2 : SECMOD_DestroyModule(RootsModule);
335 2 : RootsModule = 0;
336 2 : if (found)
337 : xmlsec_trace("Added new root certificate module "
338 2 : "\"" ROOT_CERTS "\" contained in \n%s", ospath.getStr());
339 : else
340 : {
341 : xmlsec_trace("FAILED to load the new root certificate module "
342 0 : "\"" ROOT_CERTS "\" contained in \n%s", ospath.getStr());
343 0 : return_value = false;
344 : }
345 : }
346 : else
347 : {
348 : xmlsec_trace("FAILED to add new root certifice module: "
349 0 : "\"" ROOT_CERTS "\" contained in \n%s", ospath.getStr());
350 0 : return_value = false;
351 :
352 2 : }
353 : }
354 : else
355 : {
356 0 : xmlsec_trace("Adding new root certificate module failed.");
357 0 : return_value = false;
358 2 : }
359 : }
360 : #endif
361 :
362 2 : return return_value;
363 : }
364 :
365 :
366 : // must be extern "C" because we pass the function pointer to atexit
367 2 : extern "C" void nsscrypto_finalize()
368 : {
369 2 : SECMODModule *RootsModule = SECMOD_FindModule(ROOT_CERTS);
370 :
371 2 : if (RootsModule)
372 : {
373 :
374 2 : if (SECSuccess == SECMOD_UnloadUserModule(RootsModule))
375 : {
376 2 : xmlsec_trace("Unloaded module \"" ROOT_CERTS "\".");
377 : }
378 : else
379 : {
380 0 : xmlsec_trace("Failed unloading module \"" ROOT_CERTS "\".");
381 : }
382 2 : SECMOD_DestroyModule(RootsModule);
383 : }
384 : else
385 : {
386 : xmlsec_trace("Unloading module \"" ROOT_CERTS
387 0 : "\" failed because it was not found.");
388 : }
389 2 : PK11_LogoutAll();
390 2 : NSS_Shutdown();
391 2 : }
392 : } // namespace
393 :
394 0 : ONSSInitializer::ONSSInitializer(
395 : const css::uno::Reference< css::uno::XComponentContext > &rxContext)
396 0 : :m_xContext( rxContext )
397 : {
398 0 : }
399 :
400 41 : ONSSInitializer::~ONSSInitializer()
401 : {
402 41 : }
403 :
404 41 : bool ONSSInitializer::initNSS( const css::uno::Reference< css::uno::XComponentContext > &rxContext )
405 : {
406 : return *rtl_Instance< bool, InitNSSInitialize, ::osl::MutexGuard, GetNSSInitStaticMutex >
407 41 : ::create( InitNSSInitialize( rxContext ), GetNSSInitStaticMutex() );
408 : }
409 :
410 18 : 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)
412 : {
413 18 : SECOidTag nNSSDigestID = SEC_OID_UNKNOWN;
414 18 : sal_Int32 nDigestLength = 0;
415 18 : bool b1KData = false;
416 18 : if ( nDigestID == css::xml::crypto::DigestID::SHA256
417 14 : || nDigestID == css::xml::crypto::DigestID::SHA256_1K )
418 : {
419 18 : nNSSDigestID = SEC_OID_SHA256;
420 18 : nDigestLength = 32;
421 18 : b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA256_1K );
422 : }
423 0 : else if ( nDigestID == css::xml::crypto::DigestID::SHA1
424 0 : || nDigestID == css::xml::crypto::DigestID::SHA1_1K )
425 : {
426 0 : nNSSDigestID = SEC_OID_SHA1;
427 0 : nDigestLength = 20;
428 0 : b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA1_1K );
429 : }
430 : else
431 0 : throw css::lang::IllegalArgumentException("Unexpected digest requested.", css::uno::Reference< css::uno::XInterface >(), 1 );
432 :
433 18 : if ( aParams.getLength() )
434 0 : throw css::lang::IllegalArgumentException("Unexpected arguments provided for digest creation.", css::uno::Reference< css::uno::XInterface >(), 2 );
435 :
436 18 : css::uno::Reference< css::xml::crypto::XDigestContext > xResult;
437 18 : if( initNSS( m_xContext ) )
438 : {
439 18 : PK11Context* pContext = PK11_CreateDigestContext( nNSSDigestID );
440 18 : if ( pContext && PK11_DigestBegin( pContext ) == SECSuccess )
441 18 : xResult = new ODigestContext( pContext, nDigestLength, b1KData );
442 : }
443 :
444 18 : return xResult;
445 : }
446 :
447 23 : 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)
449 : {
450 23 : CK_MECHANISM_TYPE nNSSCipherID = 0;
451 23 : bool bW3CPadding = false;
452 23 : if ( nCipherID == css::xml::crypto::CipherID::AES_CBC_W3C_PADDING )
453 : {
454 23 : nNSSCipherID = CKM_AES_CBC;
455 23 : bW3CPadding = true;
456 :
457 23 : if ( aKey.getLength() != 16 && aKey.getLength() != 24 && aKey.getLength() != 32 )
458 0 : throw css::lang::IllegalArgumentException("Unexpected key length.", css::uno::Reference< css::uno::XInterface >(), 2 );
459 :
460 23 : if ( aParams.getLength() )
461 0 : throw css::lang::IllegalArgumentException("Unexpected arguments provided for cipher creation.", css::uno::Reference< css::uno::XInterface >(), 5 );
462 : }
463 : else
464 0 : throw css::lang::IllegalArgumentException("Unexpected cipher requested.", css::uno::Reference< css::uno::XInterface >(), 1 );
465 :
466 23 : css::uno::Reference< css::xml::crypto::XCipherContext > xResult;
467 23 : if( initNSS( m_xContext ) )
468 : {
469 23 : if ( aInitializationVector.getLength() != PK11_GetIVLength( nNSSCipherID ) )
470 0 : throw css::lang::IllegalArgumentException("Unexpected length of initialization vector.", css::uno::Reference< css::uno::XInterface >(), 3 );
471 :
472 23 : xResult = OCipherContext::Create( nNSSCipherID, aKey, aInitializationVector, bEncryption, bW3CPadding );
473 : }
474 :
475 23 : return xResult;
476 : }
477 :
478 0 : OUString ONSSInitializer_getImplementationName ()
479 : throw (cssu::RuntimeException)
480 : {
481 :
482 0 : return OUString ( IMPLEMENTATION_NAME );
483 : }
484 :
485 0 : sal_Bool SAL_CALL ONSSInitializer_supportsService( const OUString& ServiceName )
486 : throw (cssu::RuntimeException)
487 : {
488 0 : return ServiceName == NSS_SERVICE_NAME;
489 : }
490 :
491 0 : cssu::Sequence< OUString > SAL_CALL ONSSInitializer_getSupportedServiceNames( )
492 : throw (cssu::RuntimeException)
493 : {
494 0 : cssu::Sequence < OUString > aRet(1);
495 0 : OUString* pArray = aRet.getArray();
496 0 : pArray[0] = OUString ( NSS_SERVICE_NAME );
497 0 : return aRet;
498 : }
499 :
500 0 : cssu::Reference< cssu::XInterface > SAL_CALL ONSSInitializer_createInstance( const cssu::Reference< cssl::XMultiServiceFactory > & rSMgr)
501 : throw( cssu::Exception )
502 : {
503 0 : return (cppu::OWeakObject*) new ONSSInitializer( comphelper::getComponentContext(rSMgr) );
504 : }
505 :
506 : /* XServiceInfo */
507 0 : OUString SAL_CALL ONSSInitializer::getImplementationName()
508 : throw (cssu::RuntimeException)
509 : {
510 0 : return ONSSInitializer_getImplementationName();
511 : }
512 0 : sal_Bool SAL_CALL ONSSInitializer::supportsService( const OUString& rServiceName )
513 : throw (cssu::RuntimeException)
514 : {
515 0 : return ONSSInitializer_supportsService( rServiceName );
516 : }
517 0 : cssu::Sequence< OUString > SAL_CALL ONSSInitializer::getSupportedServiceNames( )
518 : throw (cssu::RuntimeException)
519 : {
520 0 : return ONSSInitializer_getSupportedServiceNames();
521 : }
522 :
523 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|