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 : #include <cppuhelper/supportsservice.hxx>
34 :
35 : #include <officecfg/Office/Common.hxx>
36 :
37 : #include <sal/types.h>
38 : #include <rtl/instance.hxx>
39 : #include <rtl/bootstrap.hxx>
40 : #include <rtl/string.hxx>
41 : #include <rtl/strbuf.hxx>
42 : #include <osl/file.hxx>
43 : #include <osl/thread.h>
44 : #include <sal/log.hxx>
45 :
46 : #include "seinitializer_nssimpl.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 com::sun::star;
65 :
66 : #define IMPLEMENTATION_NAME "com.sun.star.xml.security.bridge.xmlsec.NSSInitializer_NssImpl"
67 :
68 : #define ROOT_CERTS "Root Certs for OpenOffice.org"
69 :
70 : extern "C" void nsscrypto_finalize();
71 :
72 :
73 : namespace
74 : {
75 :
76 : bool nsscrypto_initialize( const css::uno::Reference< css::uno::XComponentContext > &rxContext, bool & out_nss_init );
77 :
78 65 : struct InitNSSInitialize
79 : {
80 : css::uno::Reference< css::uno::XComponentContext > m_xContext;
81 :
82 65 : explicit InitNSSInitialize(const css::uno::Reference<css::uno::XComponentContext> &rxContext)
83 65 : : m_xContext(rxContext)
84 : {
85 65 : }
86 :
87 3 : bool * operator()()
88 : {
89 : static bool bInitialized = false;
90 3 : bool bNSSInit = false;
91 3 : bInitialized = nsscrypto_initialize( m_xContext, bNSSInit );
92 3 : if (bNSSInit)
93 3 : atexit(nsscrypto_finalize );
94 3 : return & bInitialized;
95 : }
96 : };
97 :
98 : struct GetNSSInitStaticMutex
99 : {
100 3 : ::osl::Mutex* operator()()
101 : {
102 3 : static ::osl::Mutex aNSSInitMutex;
103 3 : return &aNSSInitMutex;
104 : }
105 : };
106 :
107 3 : void deleteRootsModule()
108 : {
109 3 : SECMODModule *RootsModule = 0;
110 3 : SECMODModuleList *list = SECMOD_GetDefaultModuleList();
111 3 : SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
112 3 : SECMOD_GetReadLock(lock);
113 :
114 9 : while (!RootsModule && list)
115 : {
116 3 : SECMODModule *module = list->module;
117 :
118 9 : for (int i=0; i < module->slotCount; i++)
119 : {
120 6 : PK11SlotInfo *slot = module->slots[i];
121 6 : if (PK11_IsPresent(slot))
122 : {
123 6 : if (PK11_HasRootCerts(slot))
124 : {
125 : SAL_INFO("xmlsecurity.xmlsec", "The root certifificates module \"" << module->commonName << "\" is already loaded: " << module->dllName);
126 :
127 0 : RootsModule = SECMOD_ReferenceModule(module);
128 0 : break;
129 : }
130 : }
131 : }
132 3 : list = list->next;
133 : }
134 3 : SECMOD_ReleaseReadLock(lock);
135 :
136 3 : if (RootsModule)
137 : {
138 : PRInt32 modType;
139 0 : if (SECSuccess == SECMOD_DeleteModule(RootsModule->commonName, &modType))
140 : {
141 : SAL_INFO("xmlsecurity.xmlsec", "Deleted module \"" << RootsModule->commonName << "\".");
142 : }
143 : else
144 : {
145 : SAL_INFO("xmlsecurity.xmlsec", "Failed to delete \"" << RootsModule->commonName << "\": " << RootsModule->dllName);
146 : }
147 0 : SECMOD_DestroyModule(RootsModule);
148 0 : RootsModule = 0;
149 : }
150 3 : }
151 :
152 3 : OString getMozillaCurrentProfile( const css::uno::Reference< css::uno::XComponentContext > &rxContext )
153 : {
154 : // first, try to get the profile from "MOZILLA_CERTIFICATE_FOLDER"
155 3 : const char* pEnv = getenv("MOZILLA_CERTIFICATE_FOLDER");
156 3 : if (pEnv)
157 : {
158 : SAL_INFO(
159 : "xmlsecurity.xmlsec",
160 : "Using Mozilla profile from MOZILLA_CERTIFICATE_FOLDER=" << pEnv);
161 0 : return OString(pEnv);
162 : }
163 :
164 : // second, try to get saved user-preference
165 : try
166 : {
167 : OUString sUserSetCertPath =
168 3 : officecfg::Office::Common::Security::Scripting::CertDir::get().get_value_or(OUString());
169 :
170 3 : if (!sUserSetCertPath.isEmpty())
171 : {
172 : SAL_INFO(
173 : "xmlsecurity.xmlsec",
174 : "Using Mozilla profile from /org.openoffice.Office.Common/"
175 : "Security/Scripting/CertDir: " << sUserSetCertPath);
176 0 : return OUStringToOString(sUserSetCertPath, osl_getThreadTextEncoding());
177 3 : }
178 : }
179 0 : catch (const uno::Exception &e)
180 : {
181 : SAL_WARN(
182 : "xmlsecurity.xmlsec",
183 : "getMozillaCurrentProfile: caught exception " << e.Message);
184 : }
185 :
186 : // third, dig around to see if there's one available
187 : mozilla::MozillaProductType productTypes[3] = {
188 : mozilla::MozillaProductType_Thunderbird,
189 : mozilla::MozillaProductType_Firefox,
190 3 : mozilla::MozillaProductType_Mozilla };
191 3 : int nProduct = SAL_N_ELEMENTS(productTypes);
192 :
193 3 : uno::Reference<uno::XInterface> xInstance = rxContext->getServiceManager()->createInstanceWithContext("com.sun.star.mozilla.MozillaBootstrap", rxContext);
194 : OSL_ENSURE( xInstance.is(), "failed to create instance" );
195 :
196 : uno::Reference<mozilla::XMozillaBootstrap> xMozillaBootstrap
197 6 : = uno::Reference<mozilla::XMozillaBootstrap>(xInstance,uno::UNO_QUERY);
198 : OSL_ENSURE( xMozillaBootstrap.is(), "failed to create instance" );
199 :
200 3 : if (xMozillaBootstrap.is())
201 : {
202 0 : for (int i=0; i<nProduct; ++i)
203 : {
204 0 : OUString profile = xMozillaBootstrap->getDefaultProfile(productTypes[i]);
205 :
206 0 : if (!profile.isEmpty())
207 : {
208 0 : OUString sProfilePath = xMozillaBootstrap->getProfilePath( productTypes[i], profile );
209 : SAL_INFO(
210 : "xmlsecurity.xmlsec",
211 : "Using Mozilla profile " << sProfilePath);
212 0 : return OUStringToOString(sProfilePath, osl_getThreadTextEncoding());
213 : }
214 0 : }
215 : }
216 :
217 : SAL_INFO("xmlsecurity.xmlsec", "No Mozilla profile found");
218 6 : return OString();
219 : }
220 :
221 : //Older versions of Firefox (FF), for example FF2, and Thunderbird (TB) 2 write
222 : //the roots certificate module (libnssckbi.so), which they use, into the
223 : //profile. This module will then already be loaded during NSS_Init (and the
224 : //other init functions). This fails in two cases. First, FF3 was used to create
225 : //the profile, or possibly used that profile before, and second the profile was
226 : //used on a different platform.
227 : //
228 : //Then one needs to add the roots module oneself. This should be done with
229 : //SECMOD_LoadUserModule rather then SECMOD_AddNewModule. The latter would write
230 : //the location of the roots module to the profile, which makes FF2 and TB2 use
231 : //it instead of there own module.
232 : //
233 : //When using SYSTEM_NSS then the libnss3.so lib is typically found in /usr/lib.
234 : //This folder may, however, NOT contain the roots certificate module. That is,
235 : //just providing the library name in SECMOD_LoadUserModule or
236 : //SECMOD_AddNewModule will FAIL to load the mozilla unless the LD_LIBRARY_PATH
237 : //contains an FF or TB installation.
238 : //ATTENTION: DO NOT call this function directly instead use initNSS
239 : //return true - whole initialization was successful
240 : //param out_nss_init = true: at least the NSS initialization (NSS_InitReadWrite
241 : //was successful and therefore NSS_Shutdown should be called when terminating.
242 3 : bool nsscrypto_initialize( const css::uno::Reference< css::uno::XComponentContext > &rxContext, bool & out_nss_init )
243 : {
244 3 : bool return_value = true;
245 :
246 : // this method must be called only once, no need for additional lock
247 3 : OString sCertDir;
248 :
249 : #ifdef XMLSEC_CRYPTO_NSS
250 3 : sCertDir = getMozillaCurrentProfile(rxContext);
251 : #else
252 : (void) rxContext;
253 : #endif
254 : SAL_INFO("xmlsecurity.xmlsec", "Using profile: " << sCertDir.getStr() );
255 :
256 3 : PR_Init( PR_USER_THREAD, PR_PRIORITY_NORMAL, 1 ) ;
257 :
258 3 : bool bSuccess = true;
259 : // there might be no profile
260 3 : if ( !sCertDir.isEmpty() )
261 : {
262 0 : if( NSS_InitReadWrite( sCertDir.getStr() ) != SECSuccess )
263 : {
264 : SAL_INFO("xmlsecurity.xmlsec", "Initializing NSS with profile failed.");
265 0 : int errlen = PR_GetErrorTextLength();
266 0 : if(errlen > 0)
267 : {
268 0 : boost::scoped_array<char> const error(new char[errlen + 1]);
269 0 : PR_GetErrorText(error.get());
270 0 : SAL_INFO("xmlsecurity.xmlsec", error.get());
271 : }
272 0 : bSuccess = false;
273 : }
274 : }
275 :
276 3 : if( sCertDir.isEmpty() || !bSuccess )
277 : {
278 : SAL_INFO("xmlsecurity.xmlsec", "Initializing NSS without profile.");
279 3 : if ( NSS_NoDB_Init(NULL) != SECSuccess )
280 : {
281 : SAL_INFO("xmlsecurity.xmlsec", "Initializing NSS without profile failed.");
282 0 : int errlen = PR_GetErrorTextLength();
283 0 : if(errlen > 0)
284 : {
285 0 : boost::scoped_array<char> const error(new char[errlen + 1]);
286 0 : PR_GetErrorText(error.get());
287 0 : SAL_INFO("xmlsecurity.xmlsec", error.get());
288 : }
289 0 : return false ;
290 : }
291 : }
292 3 : out_nss_init = true;
293 :
294 : #ifdef XMLSEC_CRYPTO_NSS
295 : #if defined SYSTEM_NSS
296 3 : if (!SECMOD_HasRootCerts())
297 : #endif
298 : {
299 3 : deleteRootsModule();
300 :
301 : #if defined SYSTEM_NSS
302 3 : OUString rootModule("libnssckbi" SAL_DLLEXTENSION);
303 : #else
304 : OUString rootModule("${LO_LIB_DIR}/libnssckbi" SAL_DLLEXTENSION);
305 : #endif
306 3 : ::rtl::Bootstrap::expandMacros(rootModule);
307 :
308 6 : OUString rootModulePath;
309 3 : if (::osl::File::E_None == ::osl::File::getSystemPathFromFileURL(rootModule, rootModulePath))
310 : {
311 3 : OString ospath = OUStringToOString(rootModulePath, osl_getThreadTextEncoding());
312 6 : OString aStr = "name=\"" ROOT_CERTS "\" library=\"" + ospath + "\"";
313 :
314 : SECMODModule * RootsModule =
315 : SECMOD_LoadUserModule(
316 3 : const_cast<char*>(aStr.getStr()),
317 : 0, // no parent
318 3 : PR_FALSE); // do not recurse
319 :
320 3 : if (RootsModule)
321 : {
322 :
323 3 : bool found = RootsModule->loaded;
324 :
325 3 : SECMOD_DestroyModule(RootsModule);
326 3 : RootsModule = 0;
327 3 : if (found)
328 : SAL_INFO("xmlsecurity.xmlsec", "Added new root certificate module " ROOT_CERTS " contained in " << ospath);
329 : else
330 : {
331 : SAL_INFO("xmlsecurity.xmlsec", "FAILED to load the new root certificate module " ROOT_CERTS "contained in " << ospath);
332 0 : return_value = false;
333 : }
334 : }
335 : else
336 : {
337 : SAL_INFO("xmlsecurity.xmlsec", "FAILED to add new root certifice module " ROOT_CERTS " contained in " << ospath);
338 0 : return_value = false;
339 :
340 3 : }
341 : }
342 : else
343 : {
344 : SAL_INFO("xmlsecurity.xmlsec", "Adding new root certificate module failed.");
345 0 : return_value = false;
346 3 : }
347 : }
348 : #endif
349 :
350 3 : return return_value;
351 : }
352 :
353 : } // namespace
354 :
355 : // must be extern "C" because we pass the function pointer to atexit
356 3 : extern "C" void nsscrypto_finalize()
357 : {
358 3 : SECMODModule *RootsModule = SECMOD_FindModule(ROOT_CERTS);
359 :
360 3 : if (RootsModule)
361 : {
362 :
363 3 : if (SECSuccess == SECMOD_UnloadUserModule(RootsModule))
364 : {
365 : SAL_INFO("xmlsecurity.xmlsec", "Unloaded module \"" ROOT_CERTS "\".");
366 : }
367 : else
368 : {
369 : SAL_INFO("xmlsecurity.xmlsec", "Failed unloading module \"" ROOT_CERTS "\".");
370 : }
371 3 : SECMOD_DestroyModule(RootsModule);
372 : }
373 : else
374 : {
375 : SAL_INFO("xmlsecurity.xmlsec", "Unloading module \"" ROOT_CERTS "\" failed because it was not found.");
376 : }
377 3 : PK11_LogoutAll();
378 3 : (void)NSS_Shutdown();
379 3 : }
380 :
381 0 : ONSSInitializer::ONSSInitializer(
382 : const css::uno::Reference< css::uno::XComponentContext > &rxContext)
383 0 : :m_xContext( rxContext )
384 : {
385 0 : }
386 :
387 67 : ONSSInitializer::~ONSSInitializer()
388 : {
389 67 : }
390 :
391 65 : bool ONSSInitializer::initNSS( const css::uno::Reference< css::uno::XComponentContext > &rxContext )
392 : {
393 : return *rtl_Instance< bool, InitNSSInitialize, ::osl::MutexGuard, GetNSSInitStaticMutex >
394 65 : ::create( InitNSSInitialize( rxContext ), GetNSSInitStaticMutex() );
395 : }
396 :
397 29 : css::uno::Reference< css::xml::crypto::XDigestContext > SAL_CALL ONSSInitializer::getDigestContext( ::sal_Int32 nDigestID, const css::uno::Sequence< css::beans::NamedValue >& aParams )
398 : throw (css::lang::IllegalArgumentException, css::uno::RuntimeException, std::exception)
399 : {
400 29 : SECOidTag nNSSDigestID = SEC_OID_UNKNOWN;
401 29 : sal_Int32 nDigestLength = 0;
402 29 : bool b1KData = false;
403 29 : if ( nDigestID == css::xml::crypto::DigestID::SHA256
404 22 : || nDigestID == css::xml::crypto::DigestID::SHA256_1K )
405 : {
406 29 : nNSSDigestID = SEC_OID_SHA256;
407 29 : nDigestLength = 32;
408 29 : b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA256_1K );
409 : }
410 0 : else if ( nDigestID == css::xml::crypto::DigestID::SHA1
411 0 : || nDigestID == css::xml::crypto::DigestID::SHA1_1K )
412 : {
413 0 : nNSSDigestID = SEC_OID_SHA1;
414 0 : nDigestLength = 20;
415 0 : b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA1_1K );
416 : }
417 : else
418 0 : throw css::lang::IllegalArgumentException("Unexpected digest requested.", css::uno::Reference< css::uno::XInterface >(), 1 );
419 :
420 29 : if ( aParams.getLength() )
421 0 : throw css::lang::IllegalArgumentException("Unexpected arguments provided for digest creation.", css::uno::Reference< css::uno::XInterface >(), 2 );
422 :
423 29 : css::uno::Reference< css::xml::crypto::XDigestContext > xResult;
424 29 : if( initNSS( m_xContext ) )
425 : {
426 29 : PK11Context* pContext = PK11_CreateDigestContext( nNSSDigestID );
427 29 : if ( pContext && PK11_DigestBegin( pContext ) == SECSuccess )
428 29 : xResult = new ODigestContext( pContext, nDigestLength, b1KData );
429 : }
430 :
431 29 : return xResult;
432 : }
433 :
434 36 : 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 )
435 : throw (css::lang::IllegalArgumentException, css::uno::RuntimeException, std::exception)
436 : {
437 36 : CK_MECHANISM_TYPE nNSSCipherID = 0;
438 36 : bool bW3CPadding = false;
439 36 : if ( nCipherID == css::xml::crypto::CipherID::AES_CBC_W3C_PADDING )
440 : {
441 36 : nNSSCipherID = CKM_AES_CBC;
442 36 : bW3CPadding = true;
443 :
444 36 : if ( aKey.getLength() != 16 && aKey.getLength() != 24 && aKey.getLength() != 32 )
445 0 : throw css::lang::IllegalArgumentException("Unexpected key length.", css::uno::Reference< css::uno::XInterface >(), 2 );
446 :
447 36 : if ( aParams.getLength() )
448 0 : throw css::lang::IllegalArgumentException("Unexpected arguments provided for cipher creation.", css::uno::Reference< css::uno::XInterface >(), 5 );
449 : }
450 : else
451 0 : throw css::lang::IllegalArgumentException("Unexpected cipher requested.", css::uno::Reference< css::uno::XInterface >(), 1 );
452 :
453 36 : css::uno::Reference< css::xml::crypto::XCipherContext > xResult;
454 36 : if( initNSS( m_xContext ) )
455 : {
456 36 : if ( aInitializationVector.getLength() != PK11_GetIVLength( nNSSCipherID ) )
457 0 : throw css::lang::IllegalArgumentException("Unexpected length of initialization vector.", css::uno::Reference< css::uno::XInterface >(), 3 );
458 :
459 36 : xResult = OCipherContext::Create( nNSSCipherID, aKey, aInitializationVector, bEncryption, bW3CPadding );
460 : }
461 :
462 36 : return xResult;
463 : }
464 :
465 0 : OUString ONSSInitializer_getImplementationName ()
466 : throw (cssu::RuntimeException)
467 : {
468 :
469 0 : return OUString ( IMPLEMENTATION_NAME );
470 : }
471 :
472 0 : cssu::Sequence< OUString > SAL_CALL ONSSInitializer_getSupportedServiceNames( )
473 : throw (cssu::RuntimeException)
474 : {
475 0 : cssu::Sequence < OUString > aRet(1);
476 0 : OUString* pArray = aRet.getArray();
477 0 : pArray[0] = NSS_SERVICE_NAME;
478 0 : return aRet;
479 : }
480 :
481 0 : cssu::Reference< cssu::XInterface > SAL_CALL ONSSInitializer_createInstance( const cssu::Reference< cssl::XMultiServiceFactory > & rSMgr)
482 : throw( cssu::Exception )
483 : {
484 0 : return static_cast<cppu::OWeakObject*>(new ONSSInitializer( comphelper::getComponentContext(rSMgr) ));
485 : }
486 :
487 : /* XServiceInfo */
488 0 : OUString SAL_CALL ONSSInitializer::getImplementationName()
489 : throw (cssu::RuntimeException, std::exception)
490 : {
491 0 : return ONSSInitializer_getImplementationName();
492 : }
493 :
494 0 : sal_Bool SAL_CALL ONSSInitializer::supportsService( const OUString& rServiceName )
495 : throw (cssu::RuntimeException, std::exception)
496 : {
497 0 : return cppu::supportsService(this, rServiceName);
498 : }
499 :
500 0 : cssu::Sequence< OUString > SAL_CALL ONSSInitializer::getSupportedServiceNames( )
501 : throw (cssu::RuntimeException, std::exception)
502 : {
503 0 : return ONSSInitializer_getSupportedServiceNames();
504 : }
505 :
506 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|