summaryrefslogtreecommitdiffstats
path: root/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx')
-rw-r--r--xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx890
1 files changed, 890 insertions, 0 deletions
diff --git a/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx
new file mode 100644
index 000000000..f6c35257f
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx
@@ -0,0 +1,890 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "nssrenam.h"
+#include <cert.h>
+#include <secerr.h>
+#include <ocsp.h>
+
+#include <sal/config.h>
+#include <sal/macros.h>
+#include <osl/diagnose.h>
+#include "securityenvironment_nssimpl.hxx"
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/servicehelper.hxx>
+
+#include <xmlsec-wrapper.h>
+
+#include <rtl/ustrbuf.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/docpasswordrequest.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <vector>
+#include <memory>
+#include <osl/thread.h>
+#include <comphelper/sequence.hxx>
+
+#include "x509certificate_nssimpl.hxx"
+#include "secerror.hxx"
+#include <prerror.h>
+#include <keyhi.h>
+
+// added for password exception
+#include <com/sun/star/security/NoPasswordException.hpp>
+#include <com/sun/star/security/CertificateCharacters.hpp>
+#include <com/sun/star/security/CertificateValidity.hpp>
+
+namespace csss = ::com::sun::star::security;
+using namespace ::com::sun::star::security;
+using namespace com::sun::star;
+using namespace ::com::sun::star::uno ;
+using namespace ::com::sun::star::lang ;
+
+using ::com::sun::star::xml::crypto::XSecurityEnvironment ;
+using ::com::sun::star::security::XCertificate ;
+
+namespace std
+{
+template <> struct default_delete<PRArenaPool>
+{
+ void operator()(PRArenaPool* ptr) { PORT_FreeArena(ptr, PR_FALSE); }
+};
+}
+
+static rtl::Reference<X509Certificate_NssImpl> NssCertToXCert( CERTCertificate* cert ) ;
+static rtl::Reference<X509Certificate_NssImpl> NssPrivKeyToXCert( SECKEYPrivateKey* ) ;
+
+namespace {
+
+struct UsageDescription
+{
+ SECCertificateUsage usage;
+ char const* description;
+
+ UsageDescription()
+ : usage( certificateUsageCheckAllUsages )
+ , description( nullptr )
+ {}
+
+ UsageDescription( SECCertificateUsage i_usage, char const* i_description )
+ : usage( i_usage )
+ , description( i_description )
+ {}
+};
+
+}
+
+static char* GetPasswordFunction( PK11SlotInfo* pSlot, PRBool bRetry, void* /*arg*/ )
+{
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ uno::Reference < task::XInteractionHandler2 > xInteractionHandler(
+ task::InteractionHandler::createWithParent(xContext, nullptr) );
+
+ task::PasswordRequestMode eMode = bRetry ? task::PasswordRequestMode_PASSWORD_REENTER : task::PasswordRequestMode_PASSWORD_ENTER;
+ rtl::Reference<::comphelper::DocPasswordRequest> pPasswordRequest = new ::comphelper::DocPasswordRequest(
+ ::comphelper::DocPasswordRequestType::Standard, eMode, OUString::createFromAscii(PK11_GetTokenName(pSlot)) );
+
+ xInteractionHandler->handle( pPasswordRequest );
+
+ if ( pPasswordRequest->isPassword() )
+ {
+ OString aPassword(OUStringToOString(
+ pPasswordRequest->getPassword(),
+ osl_getThreadTextEncoding()));
+ sal_Int32 nLen = aPassword.getLength();
+ char* pPassword = static_cast<char*>(PORT_Alloc( nLen+1 ) );
+ pPassword[nLen] = 0;
+ memcpy( pPassword, aPassword.getStr(), nLen );
+ return pPassword;
+ }
+ return nullptr;
+}
+
+SecurityEnvironment_NssImpl::SecurityEnvironment_NssImpl() :
+m_pHandler( nullptr ) {
+ PK11_SetPasswordFunc( GetPasswordFunction ) ;
+}
+
+SecurityEnvironment_NssImpl::~SecurityEnvironment_NssImpl() {
+
+ PK11_SetPasswordFunc( nullptr ) ;
+
+ for (auto& slot : m_Slots)
+ {
+ PK11_FreeSlot(slot);
+ }
+
+ for( auto& symKey : m_tSymKeyList )
+ PK11_FreeSymKey( symKey ) ;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SecurityEnvironment_NssImpl::getImplementationName() {
+ return "com.sun.star.xml.crypto.SecurityEnvironment";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SecurityEnvironment_NssImpl::supportsService( const OUString& serviceName) {
+ return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL SecurityEnvironment_NssImpl::getSupportedServiceNames() {
+ Sequence<OUString> seqServiceNames{ "com.sun.star.xml.crypto.SecurityEnvironment" };
+ return seqServiceNames;
+}
+
+/* XUnoTunnel */
+sal_Int64 SAL_CALL SecurityEnvironment_NssImpl::getSomething( const Sequence< sal_Int8 >& aIdentifier )
+{
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+
+/* XUnoTunnel extension */
+
+const Sequence< sal_Int8>& SecurityEnvironment_NssImpl::getUnoTunnelId() {
+ static const comphelper::UnoIdInit theSecurityEnvironment_NssImplUnoTunnelId;
+ return theSecurityEnvironment_NssImplUnoTunnelId.getSeq();
+}
+
+OUString SecurityEnvironment_NssImpl::getSecurityEnvironmentInformation()
+{
+ OUStringBuffer buff;
+ for (auto& slot : m_Slots)
+ {
+ buff.append(OUString::createFromAscii(PK11_GetTokenName(slot)));
+ buff.append("\n");
+ }
+ return buff.makeStringAndClear();
+}
+
+void SecurityEnvironment_NssImpl::addCryptoSlot( PK11SlotInfo* aSlot)
+{
+ PK11_ReferenceSlot(aSlot);
+ m_Slots.push_back(aSlot);
+}
+
+//Could we have multiple cert dbs?
+void SecurityEnvironment_NssImpl::setCertDb( CERTCertDBHandle* aCertDb ) {
+ m_pHandler = aCertDb ;
+}
+
+void SecurityEnvironment_NssImpl::adoptSymKey( PK11SymKey* aSymKey ) {
+ if( aSymKey == nullptr ) return;
+
+ //First try to find the key in the list
+ if (std::find(m_tSymKeyList.begin(), m_tSymKeyList.end(), aSymKey) != m_tSymKeyList.end())
+ return;
+
+ //If we do not find the key in the list, add a new node
+ PK11SymKey* symkey = PK11_ReferenceSymKey( aSymKey ) ;
+ if( symkey == nullptr )
+ throw RuntimeException() ;
+
+ try {
+ m_tSymKeyList.push_back( symkey ) ;
+ } catch ( Exception& ) {
+ PK11_FreeSymKey( symkey ) ;
+ }
+}
+
+void SecurityEnvironment_NssImpl::updateSlots()
+{
+ //In case new tokens are present then we can obtain the corresponding slot
+ std::scoped_lock guard(m_mutex);
+
+ m_Slots.clear();
+ m_tSymKeyList.clear();
+
+ PK11SlotList * slotList = PK11_GetAllTokens( CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, nullptr ) ;
+ if( slotList == nullptr )
+ return;
+
+ for (PK11SlotListElement* slotEle = slotList->head ; slotEle != nullptr; slotEle = slotEle->next)
+ {
+ PK11SlotInfo * pSlot = slotEle->slot ;
+
+ if(pSlot != nullptr)
+ {
+ SAL_INFO(
+ "xmlsecurity.xmlsec",
+ "Found a slot: SlotName=" << PK11_GetSlotName(pSlot)
+ << ", TokenName=" << PK11_GetTokenName(pSlot));
+
+//The following code which is commented out checks if a slot, that is a smart card for example, is
+// able to generate a symmetric key of type CKM_DES3_CBC. If this fails then this token
+// will not be used. This key is possibly used for the encryption service. However, all
+// interfaces and services used for public key signature and encryption are not published
+// and the encryption is not used in OOo. Therefore it does not do any harm to remove
+// this code, hence allowing smart cards which cannot generate this type of key.
+//
+// By doing this, the encryption may fail if a smart card is being used which does not
+// support this key generation.
+//
+ PK11SymKey * pSymKey = PK11_KeyGen( pSlot , CKM_DES3_CBC, nullptr, 128, nullptr ) ;
+// if( pSymKey == NULL )
+// {
+// PK11_FreeSlot( pSlot ) ;
+// SAL_INFO( "xmlsecurity", "XMLSEC: Error - pSymKey is NULL" );
+// continue;
+// }
+ addCryptoSlot(pSlot);
+
+ if (pSymKey != nullptr)
+ {
+ adoptSymKey( pSymKey ) ;
+ PK11_FreeSymKey( pSymKey ) ;
+ pSymKey = nullptr;
+ }
+
+ }// end of if(pSlot != NULL)
+ }// end of for
+
+ PK11_FreeSlotList(slotList);
+}
+
+Sequence< Reference < XCertificate > >
+SecurityEnvironment_NssImpl::getPersonalCertificates()
+{
+ std::vector< rtl::Reference<X509Certificate_NssImpl> > certsList ;
+
+ updateSlots();
+ //firstly, we try to find private keys in slot
+ for (auto& slot : m_Slots)
+ {
+ SECKEYPrivateKeyList* priKeyList ;
+
+ if( PK11_NeedLogin(slot ) ) {
+ SECStatus nRet = PK11_Authenticate(slot, PR_TRUE, nullptr);
+ //PK11_Authenticate may fail in case the a slot has not been initialized.
+ //this is the case if the user has a new profile, so that they have never
+ //added a personal certificate.
+ if( nRet != SECSuccess && PORT_GetError() != SEC_ERROR_IO) {
+ throw NoPasswordException();
+ }
+ }
+
+ priKeyList = PK11_ListPrivateKeysInSlot(slot) ;
+ if( priKeyList != nullptr )
+ {
+ for (SECKEYPrivateKeyListNode* curPri = PRIVKEY_LIST_HEAD(priKeyList);
+ !PRIVKEY_LIST_END( curPri, priKeyList ) && curPri != nullptr;
+ curPri = PRIVKEY_LIST_NEXT(curPri))
+ {
+ rtl::Reference<X509Certificate_NssImpl> xcert = NssPrivKeyToXCert( curPri->key ) ;
+ if( xcert != nullptr )
+ certsList.push_back( xcert ) ;
+ }
+ SECKEY_DestroyPrivateKeyList( priKeyList ) ;
+ }
+
+
+ }
+
+ if( certsList.size() != 0 ) {
+ return comphelper::containerToSequence<Reference< XCertificate >>(certsList) ;
+ }
+
+ return Sequence< Reference < XCertificate > > ();
+}
+
+Reference< XCertificate > SecurityEnvironment_NssImpl::getCertificate( const OUString& issuerName, const Sequence< sal_Int8 >& serialNumber )
+{
+ if( !m_pHandler )
+ return nullptr;
+
+ rtl::Reference<X509Certificate_NssImpl> xcert;
+ CERTIssuerAndSN issuerAndSN ;
+ CERTCertificate* cert ;
+ CERTName* nmIssuer ;
+ char* chIssuer ;
+ SECItem* derIssuer ;
+ std::unique_ptr<PRArenaPool> arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ if( arena == nullptr )
+ throw RuntimeException() ;
+
+ // Create cert info from issue and serial
+ OString ostr = OUStringToOString( issuerName , RTL_TEXTENCODING_UTF8 ) ;
+ chIssuer = PL_strndup( ostr.getStr(), static_cast<int>(ostr.getLength()) ) ;
+ nmIssuer = CERT_AsciiToName( chIssuer ) ;
+ if( nmIssuer == nullptr ) {
+ PL_strfree( chIssuer ) ;
+ return nullptr; // no need for exception cf. i40394
+ }
+
+ derIssuer = SEC_ASN1EncodeItem( arena.get(), nullptr, static_cast<void*>(nmIssuer), SEC_ASN1_GET( CERT_NameTemplate ) ) ;
+ if( derIssuer == nullptr ) {
+ PL_strfree( chIssuer ) ;
+ CERT_DestroyName( nmIssuer ) ;
+ throw RuntimeException() ;
+ }
+
+ memset( &issuerAndSN, 0, sizeof( issuerAndSN ) ) ;
+
+ issuerAndSN.derIssuer.data = derIssuer->data ;
+ issuerAndSN.derIssuer.len = derIssuer->len ;
+
+ issuerAndSN.serialNumber.data = reinterpret_cast<unsigned char *>(const_cast<sal_Int8 *>(serialNumber.getConstArray()));
+ issuerAndSN.serialNumber.len = serialNumber.getLength() ;
+
+ cert = CERT_FindCertByIssuerAndSN( m_pHandler, &issuerAndSN ) ;
+ if( cert != nullptr ) {
+ xcert = NssCertToXCert( cert ) ;
+ }
+
+ PL_strfree( chIssuer ) ;
+ CERT_DestroyName( nmIssuer ) ;
+ //SECITEM_FreeItem( derIssuer, PR_FALSE ) ;
+ CERT_DestroyCertificate( cert ) ;
+
+ return xcert ;
+}
+
+Sequence< Reference < XCertificate > > SecurityEnvironment_NssImpl::buildCertificatePath( const Reference< XCertificate >& begin ) {
+ // Remember the signing certificate.
+ m_xSigningCertificate = begin;
+
+ Reference< XUnoTunnel > xCertTunnel( begin, UNO_QUERY_THROW ) ;
+ const X509Certificate_NssImpl* xcert = comphelper::getFromUnoTunnel<X509Certificate_NssImpl>(xCertTunnel);
+ if( xcert == nullptr ) {
+ throw RuntimeException() ;
+ }
+
+ const CERTCertificate* cert = xcert->getNssCert() ;
+ if (!cert)
+ return {};
+
+ //Get the system clock time
+ int64 timeboundary = PR_Now() ;
+ CERTCertList* certChain = CERT_GetCertChainFromCert( const_cast<CERTCertificate*>(cert), timeboundary, certUsageAnyCA ) ;
+
+ if( !certChain )
+ return {};
+
+ std::vector<uno::Reference<security::XCertificate>> aCertChain;
+
+ for (CERTCertListNode* node = CERT_LIST_HEAD(certChain); !CERT_LIST_END(node, certChain); node = CERT_LIST_NEXT(node)) {
+ rtl::Reference<X509Certificate_NssImpl> pCert = new X509Certificate_NssImpl();
+ if( pCert == nullptr ) {
+ CERT_DestroyCertList( certChain ) ;
+ throw RuntimeException() ;
+ }
+
+ pCert->setCert( node->cert ) ;
+
+ aCertChain.push_back(pCert);
+ }
+
+ CERT_DestroyCertList( certChain ) ;
+
+ return comphelper::containerToSequence(aCertChain);
+}
+
+rtl::Reference<X509Certificate_NssImpl> SecurityEnvironment_NssImpl::createAndAddCertificateFromPackage(
+ const css::uno::Sequence<sal_Int8>& raDERCertificate,
+ std::u16string_view raString)
+{
+ auto pCertificateBytes = reinterpret_cast<char *>(const_cast<sal_Int8 *>(raDERCertificate.getConstArray()));
+ CERTCertificate* pCERTCertificate = CERT_DecodeCertFromPackage(pCertificateBytes, raDERCertificate.getLength());
+
+ if (!pCERTCertificate)
+ return nullptr;
+
+ SECStatus aStatus;
+
+ OString aTrustString = OUStringToOString(raString, RTL_TEXTENCODING_ASCII_US);
+ CERTCertTrust aTrust;
+
+ aStatus = CERT_DecodeTrustString(&aTrust, aTrustString.getStr());
+
+ if (aStatus != SECSuccess)
+ {
+ PRIntn err = PR_GetError();
+ SAL_WARN("xmlsecurity.xmlsec", "Error: " << err << ": " << getCertError(err));
+ return nullptr;
+ }
+
+ PK11SlotInfo* pSlot = PK11_GetInternalKeySlot();
+
+ if (!pSlot)
+ return nullptr;
+
+ aStatus = PK11_ImportCert(pSlot, pCERTCertificate, CK_INVALID_HANDLE, nullptr, PR_FALSE);
+
+ if (aStatus != SECSuccess)
+ {
+ PRIntn err = PR_GetError();
+ SAL_WARN("xmlsecurity.xmlsec", "Error: " << err << ": " << getCertError(err));
+ return nullptr;
+ }
+
+ aStatus = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), pCERTCertificate, &aTrust);
+
+ if (aStatus != SECSuccess)
+ {
+ PRIntn err = PR_GetError();
+ SAL_WARN("xmlsecurity.xmlsec", "Error: " << err << ": " << getCertError(err));
+ return nullptr;
+ }
+
+ PK11_FreeSlot(pSlot);
+
+ rtl::Reference<X509Certificate_NssImpl> pX509Certificate = new X509Certificate_NssImpl();
+ pX509Certificate->setCert(pCERTCertificate);
+ return pX509Certificate;
+}
+
+rtl::Reference<X509Certificate_NssImpl> SecurityEnvironment_NssImpl::createX509CertificateFromDER(const css::uno::Sequence<sal_Int8>& aDerCertificate)
+{
+ rtl::Reference<X509Certificate_NssImpl> pX509Certificate;
+
+ if (aDerCertificate.hasElements())
+ {
+ pX509Certificate = new X509Certificate_NssImpl();
+ if (pX509Certificate == nullptr)
+ throw RuntimeException();
+ pX509Certificate->setRawCert(aDerCertificate);
+ }
+ return pX509Certificate;
+}
+
+Reference<XCertificate> SecurityEnvironment_NssImpl::createCertificateFromRaw(const Sequence< sal_Int8 >& rawCertificate)
+{
+ return createX509CertificateFromDER(rawCertificate);
+}
+
+Reference< XCertificate > SecurityEnvironment_NssImpl::createCertificateFromAscii( const OUString& asciiCertificate )
+{
+ OString oscert = OUStringToOString( asciiCertificate , RTL_TEXTENCODING_ASCII_US ) ;
+ xmlChar* chCert = xmlStrndup( reinterpret_cast<const xmlChar*>(oscert.getStr()), static_cast<int>(oscert.getLength()) ) ;
+ int certSize = xmlSecBase64Decode( chCert, reinterpret_cast<xmlSecByte*>(chCert), xmlStrlen( chCert ) ) ;
+ if (certSize == 0)
+ return nullptr;
+
+ Sequence< sal_Int8 > rawCert(comphelper::arrayToSequence<sal_Int8>(chCert, certSize)) ;
+
+ xmlFree( chCert ) ;
+
+ return createCertificateFromRaw( rawCert ) ;
+}
+
+sal_Int32 SecurityEnvironment_NssImpl ::
+verifyCertificate( const Reference< csss::XCertificate >& aCert,
+ const Sequence< Reference< csss::XCertificate > >& intermediateCerts )
+{
+ sal_Int32 validity = csss::CertificateValidity::INVALID;
+ const CERTCertificate* cert ;
+ Reference< XUnoTunnel > xCertTunnel( aCert, UNO_QUERY_THROW ) ;
+
+ SAL_INFO("xmlsecurity.xmlsec", "Start verification of certificate: " << aCert->getSubjectName());
+
+ const X509Certificate_NssImpl* xcert = comphelper::getFromUnoTunnel<X509Certificate_NssImpl>(xCertTunnel);
+ if( xcert == nullptr ) {
+ throw RuntimeException() ;
+ }
+
+ //CERT_PKIXVerifyCert does not take a db as argument. It will therefore
+ //internally use CERT_GetDefaultCertDB
+ //Make sure m_pHandler is the default DB
+ OSL_ASSERT(m_pHandler == CERT_GetDefaultCertDB());
+ CERTCertDBHandle * certDb = m_pHandler != nullptr ? m_pHandler : CERT_GetDefaultCertDB();
+ cert = xcert->getNssCert() ;
+ if( !cert )
+ return css::security::CertificateValidity::INVALID;
+
+
+ ::std::vector<CERTCertificate*> vecTmpNSSCertificates;
+
+ //prepare the intermediate certificates
+ for (const auto& rIntermediateCert : intermediateCerts)
+ {
+ Sequence<sal_Int8> der = rIntermediateCert->getEncoded();
+ SECItem item;
+ item.type = siBuffer;
+ item.data = reinterpret_cast<unsigned char*>(der.getArray());
+ item.len = der.getLength();
+
+ CERTCertificate* certTmp = CERT_NewTempCertificate(certDb, &item,
+ nullptr /* nickname */,
+ PR_FALSE /* isPerm */,
+ PR_TRUE /* copyDER */);
+ if (!certTmp)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Failed to add a temporary certificate: " << rIntermediateCert->getIssuerName());
+
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Added temporary certificate: " <<
+ (certTmp->subjectName ? certTmp->subjectName : ""));
+ vecTmpNSSCertificates.push_back(certTmp);
+ }
+ }
+
+
+ SECStatus status ;
+
+ CERTVerifyLog log;
+ log.arena = PORT_NewArena(512);
+ log.head = log.tail = nullptr;
+ log.count = 0;
+
+ CERT_EnableOCSPChecking(certDb);
+ CERT_DisableOCSPDefaultResponder(certDb);
+ CERTValOutParam cvout[5];
+ CERTValInParam cvin[3];
+ int ncvinCount=0;
+
+#if ( NSS_VMAJOR > 3 ) || ( NSS_VMAJOR == 3 && NSS_VMINOR > 12 ) || ( NSS_VMAJOR == 3 && NSS_VMINOR == 12 && NSS_VPATCH > 0 )
+ cvin[ncvinCount].type = cert_pi_useAIACertFetch;
+ cvin[ncvinCount].value.scalar.b = PR_TRUE;
+ ncvinCount++;
+#endif
+
+ PRUint64 revFlagsLeaf[2];
+ PRUint64 revFlagsChain[2];
+ CERTRevocationFlags rev;
+ rev.leafTests.number_of_defined_methods = 2;
+ rev.leafTests.cert_rev_flags_per_method = revFlagsLeaf;
+ //the flags are defined in cert.h
+ //We check both leaf and chain.
+ //It is enough if one revocation method has fresh info,
+ //but at least one must have some. Otherwise validation fails.
+ //!!! using leaf test and CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE
+ // when validating a root certificate will result in "revoked". Usually
+ //there is no revocation information available for the root cert because
+ //it must be trusted anyway and it does itself issue revocation information.
+ //When we use the flag here and OOo shows the certification path then the root
+ //cert is invalid while all other can be valid. It would probably best if
+ //this interface method returned the whole chain.
+ //Otherwise we need to check if the certificate is self-signed and if it is
+ //then not use the flag when doing the leaf-test.
+ rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
+ CERT_REV_M_TEST_USING_THIS_METHOD
+ | CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE;
+ rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
+ CERT_REV_M_TEST_USING_THIS_METHOD
+ | CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE;
+ rev.leafTests.number_of_preferred_methods = 0;
+ rev.leafTests.preferred_methods = nullptr;
+ rev.leafTests.cert_rev_method_independent_flags =
+ CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST;
+
+ rev.chainTests.number_of_defined_methods = 2;
+ rev.chainTests.cert_rev_flags_per_method = revFlagsChain;
+ rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
+ CERT_REV_M_TEST_USING_THIS_METHOD
+ | CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE;
+ rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
+ CERT_REV_M_TEST_USING_THIS_METHOD
+ | CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE;
+ rev.chainTests.number_of_preferred_methods = 0;
+ rev.chainTests.preferred_methods = nullptr;
+ rev.chainTests.cert_rev_method_independent_flags =
+ CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST;
+
+
+ cvin[ncvinCount].type = cert_pi_revocationFlags;
+ cvin[ncvinCount].value.pointer.revocation = &rev;
+ ncvinCount++;
+ // does not work, not implemented yet in 3.12.4
+// cvin[ncvinCount].type = cert_pi_keyusage;
+// cvin[ncvinCount].value.scalar.ui = KU_DIGITAL_SIGNATURE;
+// ncvinCount++;
+ cvin[ncvinCount].type = cert_pi_end;
+
+ cvout[0].type = cert_po_trustAnchor;
+ cvout[0].value.pointer.cert = nullptr;
+ cvout[1].type = cert_po_errorLog;
+ cvout[1].value.pointer.log = &log;
+ cvout[2].type = cert_po_end;
+
+ // We check SSL server certificates, CA certificates and signing certificates.
+ //
+ // ToDo check keyusage, looking at CERT_KeyUsageAndTypeForCertUsage (
+ // mozilla/security/nss/lib/certdb/certdb.c indicates that
+ // certificateUsageSSLClient, certificateUsageSSLServer and certificateUsageSSLCA
+ // are sufficient. They cover the key usages for digital signature, key agreement
+ // and encipherment and certificate signature
+
+ //never use the following usages because they are not checked properly
+ // certificateUsageUserCertImport
+ // certificateUsageVerifyCA
+ // certificateUsageAnyCA
+ // certificateUsageProtectedObjectSigner
+
+ UsageDescription arUsages[5];
+ arUsages[0] = UsageDescription( certificateUsageSSLClient, "certificateUsageSSLClient" );
+ arUsages[1] = UsageDescription( certificateUsageSSLServer, "certificateUsageSSLServer" );
+ arUsages[2] = UsageDescription( certificateUsageSSLCA, "certificateUsageSSLCA" );
+ arUsages[3] = UsageDescription( certificateUsageEmailSigner, "certificateUsageEmailSigner" );
+ arUsages[4] = UsageDescription( certificateUsageEmailRecipient, "certificateUsageEmailRecipient" );
+
+ int numUsages = SAL_N_ELEMENTS(arUsages);
+ for (int i = 0; i < numUsages; i++)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Testing usage " << i+1 <<
+ " of " << numUsages << ": " <<
+ arUsages[i].description <<
+ " (0x" << std::hex << static_cast<int>(arUsages[i].usage) << ")" << std::dec);
+
+ status = CERT_PKIXVerifyCert(const_cast<CERTCertificate *>(cert), arUsages[i].usage,
+ cvin, cvout, nullptr);
+ if( status == SECSuccess )
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "CERT_PKIXVerifyCert returned SECSuccess.");
+ //When an intermediate or root certificate is checked then we expect the usage
+ //certificateUsageSSLCA. This, however, will be only set when in the trust settings dialog
+ //the button "This certificate can identify websites" is checked. If for example only
+ //"This certificate can identify mail users" is set then the end certificate can
+ //be validated and the returned usage will contain certificateUsageEmailRecipient.
+ //But checking directly the root or intermediate certificate will fail. In the
+ //certificate path view the end certificate will be shown as valid but the others
+ //will be displayed as invalid.
+
+ validity = csss::CertificateValidity::VALID;
+ SAL_INFO("xmlsecurity.xmlsec", "Certificate is valid.");
+ CERTCertificate * issuerCert = cvout[0].value.pointer.cert;
+ if (issuerCert)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Root certificate: " << issuerCert->subjectName);
+ CERT_DestroyCertificate(issuerCert);
+ };
+
+ break;
+ }
+ else
+ {
+ PRIntn err = PR_GetError();
+ SAL_INFO("xmlsecurity.xmlsec", "Error: " << err << ": " << getCertError(err));
+
+ /* Display validation results */
+ if ( log.count > 0)
+ {
+ CERTVerifyLogNode *node = nullptr;
+ printChainFailure(&log);
+
+ for (node = log.head; node; node = node->next) {
+ if (node->cert)
+ CERT_DestroyCertificate(node->cert);
+ }
+ log.head = log.tail = nullptr;
+ log.count = 0;
+ }
+ SAL_INFO("xmlsecurity.xmlsec", "Certificate is invalid.");
+ }
+ }
+
+ //Destroying the temporary certificates
+ for (auto& tmpCert : vecTmpNSSCertificates)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Destroying temporary certificate");
+ CERT_DestroyCertificate(tmpCert);
+ }
+ PORT_FreeArena(log.arena, true);
+ return validity ;
+}
+
+sal_Int32 SecurityEnvironment_NssImpl::getCertificateCharacters(
+ const css::uno::Reference< css::security::XCertificate >& aCert ) {
+ sal_Int32 characters ;
+ const CERTCertificate* cert ;
+
+ Reference< XUnoTunnel > xCertTunnel( aCert, UNO_QUERY_THROW ) ;
+ const X509Certificate_NssImpl* xcert = comphelper::getFromUnoTunnel<X509Certificate_NssImpl>(xCertTunnel);
+ if( xcert == nullptr ) {
+ throw RuntimeException() ;
+ }
+
+ cert = xcert->getNssCert() ;
+
+ characters = 0x00000000 ;
+
+ //Firstly, find out whether or not the cert is self-signed.
+ if( SECITEM_CompareItem( &(cert->derIssuer), &(cert->derSubject) ) == SECEqual ) {
+ characters |= css::security::CertificateCharacters::SELF_SIGNED ;
+ } else {
+ characters &= ~ css::security::CertificateCharacters::SELF_SIGNED ;
+ }
+
+ //Secondly, find out whether or not the cert has a private key.
+
+ /*
+ * i40394
+ *
+ * mmi : need to check whether the cert's slot is valid first
+ */
+ SECKEYPrivateKey* priKey = nullptr;
+
+ if (cert->slot != nullptr)
+ {
+ priKey = PK11_FindPrivateKeyFromCert( cert->slot, const_cast<CERTCertificate*>(cert), nullptr ) ;
+ }
+ if(priKey == nullptr)
+ {
+ for (auto& slot : m_Slots)
+ {
+ priKey = PK11_FindPrivateKeyFromCert(slot, const_cast<CERTCertificate*>(cert), nullptr);
+ if (priKey)
+ break;
+ }
+ }
+ if( priKey != nullptr ) {
+ characters |= css::security::CertificateCharacters::HAS_PRIVATE_KEY ;
+
+ SECKEY_DestroyPrivateKey( priKey ) ;
+ } else {
+ characters &= ~ css::security::CertificateCharacters::HAS_PRIVATE_KEY ;
+ }
+
+ return characters ;
+}
+
+rtl::Reference<X509Certificate_NssImpl> NssCertToXCert( CERTCertificate* cert )
+{
+ if( cert != nullptr ) {
+ rtl::Reference<X509Certificate_NssImpl> xcert = new X509Certificate_NssImpl() ;
+ xcert->setCert( cert ) ;
+ return xcert;
+ }
+
+ return nullptr;
+}
+
+rtl::Reference<X509Certificate_NssImpl> NssPrivKeyToXCert( SECKEYPrivateKey* priKey )
+{
+ if( priKey != nullptr ) {
+ rtl::Reference<X509Certificate_NssImpl> xcert;
+ CERTCertificate* cert = PK11_GetCertFromPrivateKey( priKey ) ;
+
+ if( cert != nullptr ) {
+ xcert = NssCertToXCert( cert ) ;
+ }
+
+ CERT_DestroyCertificate( cert ) ;
+ return xcert;
+ }
+
+ return nullptr;
+}
+
+xmlSecKeysMngrPtr SecurityEnvironment_NssImpl::createKeysManager() {
+
+ /*-
+ * The following lines is based on the private version of xmlSec-NSS
+ * crypto engine
+ */
+ int cSlots = m_Slots.size();
+ std::unique_ptr<PK11SlotInfo*[]> sarSlots(new PK11SlotInfo*[cSlots]);
+ PK11SlotInfo** slots = sarSlots.get();
+ int count = 0;
+ for (const auto& slot : m_Slots)
+ {
+ slots[count] = slot;
+ ++count;
+ }
+
+ xmlSecKeysMngrPtr pKeysMngr = xmlSecKeysMngrCreate();
+ if (!pKeysMngr)
+ throw RuntimeException();
+
+ if (xmlSecNssAppDefaultKeysMngrInit(pKeysMngr) < 0)
+ throw RuntimeException();
+
+ // Adopt the private key of the signing certificate, if it has any.
+ if (auto pCertificate = dynamic_cast<X509Certificate_NssImpl*>(m_xSigningCertificate.get()))
+ {
+ SECKEYPrivateKey* pPrivateKey = SECKEY_CopyPrivateKey(pCertificate->getPrivateKey());
+ if (pPrivateKey)
+ {
+ xmlSecKeyDataPtr pKeyData = xmlSecNssPKIAdoptKey(pPrivateKey, nullptr);
+ xmlSecKeyPtr pKey = xmlSecKeyCreate();
+ xmlSecKeySetValue(pKey, pKeyData);
+ xmlSecNssAppDefaultKeysMngrAdoptKey(pKeysMngr, pKey);
+ }
+ else
+ {
+ SAL_WARN("xmlsecurity.xmlsec", "Can't get the private key from the certificate.");
+ }
+ }
+
+ return pKeysMngr;
+}
+
+void SecurityEnvironment_NssImpl::destroyKeysManager(xmlSecKeysMngrPtr pKeysMngr) {
+ if( pKeysMngr != nullptr ) {
+ xmlSecKeysMngrDestroy( pKeysMngr ) ;
+ }
+}
+
+SECKEYPrivateKey* SecurityEnvironment_NssImpl::insertPrivateKey(css::uno::Sequence<sal_Int8> const & raPrivateKey)
+{
+ PK11SlotInfo* pSlot = PK11_GetInternalKeySlot();
+
+ if (!pSlot)
+ return nullptr;
+
+ SECItem aDerPrivateKeyInfo;
+ aDerPrivateKeyInfo.data = reinterpret_cast<unsigned char *>(const_cast<sal_Int8 *>(raPrivateKey.getConstArray()));
+ aDerPrivateKeyInfo.len = raPrivateKey.getLength();
+
+ const unsigned int aKeyUsage = KU_ALL;
+ SECKEYPrivateKey* pPrivateKey = nullptr;
+
+ bool bPermanent = PR_FALSE;
+ bool bPrivate = PR_TRUE;
+
+ SECStatus nStatus = PK11_ImportDERPrivateKeyInfoAndReturnKey(
+ pSlot, &aDerPrivateKeyInfo, nullptr, nullptr, bPermanent, bPrivate,
+ aKeyUsage, &pPrivateKey, nullptr);
+
+ if (nStatus != SECSuccess)
+ return nullptr;
+
+ PK11_FreeSlot(pSlot);
+
+ return pPrivateKey;
+}
+
+uno::Reference<security::XCertificate> SecurityEnvironment_NssImpl::createDERCertificateWithPrivateKey(
+ Sequence<sal_Int8> const & raDERCertificate, Sequence<sal_Int8> const & raPrivateKey)
+{
+ SECKEYPrivateKey* pPrivateKey = insertPrivateKey(raPrivateKey);
+
+ if (!pPrivateKey)
+ return uno::Reference<security::XCertificate>();
+
+ return createAndAddCertificateFromPackage(raDERCertificate, u"TCu,TCu,TCu");
+}
+
+uno::Reference<security::XCertificate> SecurityEnvironment_NssImpl::addDERCertificateToTheDatabase(
+ uno::Sequence<sal_Int8> const & raDERCertificate, OUString const & raTrustString)
+{
+ return createAndAddCertificateFromPackage(raDERCertificate, raTrustString);
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_crypto_SecurityEnvironment_get_implementation(
+ uno::XComponentContext* /*pCtx*/, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SecurityEnvironment_NssImpl);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */