diff options
Diffstat (limited to 'xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx')
-rw-r--r-- | xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx | 289 |
1 files changed, 165 insertions, 124 deletions
diff --git a/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx index b4d3a78b1..89c05360f 100644 --- a/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx +++ b/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx @@ -33,12 +33,13 @@ #include "nssrenam.h" #include "cert.h" #include "secerr.h" +#include "ocsp.h" #include <sal/config.h> #include "securityenvironment_nssimpl.hxx" #include "x509certificate_nssimpl.hxx" #include <rtl/uuid.h> - +#include "../diagnose.hxx" #include <sal/types.h> //For reasons that escape me, this is what xmlsec does when size_t is not 4 @@ -62,9 +63,12 @@ #include <vector> #include "boost/scoped_array.hpp" +#include "secerror.hxx" + // MM : added for password exception #include <com/sun/star/security/NoPasswordException.hpp> namespace csss = ::com::sun::star::security; +using namespace xmlsecurity; using namespace ::com::sun::star::security; using namespace com::sun::star; using namespace ::com::sun::star::uno ; @@ -80,6 +84,14 @@ extern X509Certificate_NssImpl* NssCertToXCert( CERTCertificate* cert ) ; extern X509Certificate_NssImpl* NssPrivKeyToXCert( SECKEYPrivateKey* ) ; +struct UsageDescription +{ + SECCertificateUsage usage; + char const * const description; +}; + + + char* GetPasswordFunction( PK11SlotInfo* pSlot, PRBool bRetry, void* /*arg*/ ) { uno::Reference< lang::XMultiServiceFactory > xMSF( ::comphelper::getProcessServiceFactory() ); @@ -750,7 +762,7 @@ verifyCertificate( const Reference< csss::XCertificate >& aCert, const Sequence< Reference< csss::XCertificate > >& intermediateCerts ) throw( ::com::sun::star::uno::SecurityException, ::com::sun::star::uno::RuntimeException ) { - sal_Int32 validity = 0; + sal_Int32 validity = csss::CertificateValidity::INVALID; const X509Certificate_NssImpl* xcert ; const CERTCertificate* cert ; ::std::vector<CERTCertificate*> vecTmpNSSCertificates; @@ -759,23 +771,26 @@ verifyCertificate( const Reference< csss::XCertificate >& aCert, throw RuntimeException() ; } - OSL_TRACE("[xmlsecurity] Start verification of certificate: %s", + xmlsec_trace("Start verification of certificate: \n %s \n", OUStringToOString( - aCert->getIssuerName(), osl_getThreadTextEncoding()).getStr()); - - + aCert->getSubjectName(), osl_getThreadTextEncoding()).getStr()); + xcert = reinterpret_cast<X509Certificate_NssImpl*>( sal::static_int_cast<sal_uIntPtr>(xCertTunnel->getSomething( X509Certificate_NssImpl::getUnoTunnelId() ))) ; if( xcert == NULL ) { 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 != NULL ? m_pHandler : CERT_GetDefaultCertDB(); cert = xcert->getNssCert() ; if( cert != NULL ) { //prepare the intermediate certificates - CERTCertDBHandle * certDb = m_pHandler != NULL ? m_pHandler : CERT_GetDefaultCertDB(); for (sal_Int32 i = 0; i < intermediateCerts.getLength(); i++) { Sequence<sal_Int8> der = intermediateCerts[i]->getEncoded(); @@ -790,140 +805,172 @@ verifyCertificate( const Reference< csss::XCertificate >& aCert, PR_TRUE /* copyDER */); if (!certTmp) { - OSL_TRACE("[xmlsecurity] Failed to add a temporary certificate: %s", + xmlsec_trace("Failed to add a temporary certificate: %s", OUStringToOString(intermediateCerts[i]->getIssuerName(), osl_getThreadTextEncoding()).getStr()); } else { - OSL_TRACE("[xmlsecurity] Added temporary certificate: %s", + xmlsec_trace("Added temporary certificate: %s", certTmp->subjectName ? certTmp->subjectName : ""); vecTmpNSSCertificates.push_back(certTmp); } } + - - int64 timeboundary ; SECStatus status ; + + CERTVerifyLog log; + log.arena = PORT_NewArena(512); + log.head = log.tail = NULL; + log.count = 0; - //Get the system clock time - timeboundary = PR_Now() ; - SECCertificateUsage usage = 0; - - // create log - - CERTVerifyLog realLog; - CERTVerifyLog *log; - - log = &realLog; - - - log->count = 0; - log->head = NULL; - log->tail = NULL; - log->arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE ); + CERT_EnableOCSPChecking(certDb); + CERT_DisableOCSPDefaultResponder(certDb); + CERTValOutParam cvout[5]; + CERTValInParam cvin[3]; + + cvin[0].type = cert_pi_useAIACertFetch; + cvin[0].value.scalar.b = PR_TRUE; + + 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 = NULL; + rev.leafTests.cert_rev_method_independent_flags = + CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST; +// | CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE; + + 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 = NULL; + rev.chainTests.cert_rev_method_independent_flags = + CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST; +// | CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE; + - //CERTVerifyLog *log; - //PRArenaPool *arena; - - //arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE ); - //log = PORT_ArenaZNew( arena, CERTVerifyLog ); - //log->arena = arena; - validity = csss::CertificateValidity::INVALID; + cvin[1].type = cert_pi_revocationFlags; + cvin[1].value.pointer.revocation = &rev; + // does not work, not implemented yet in 3.12.4 +// cvin[2].type = cert_pi_keyusage; +// cvin[2].value.scalar.ui = KU_DIGITAL_SIGNATURE; + cvin[2].type = cert_pi_end; + + cvout[0].type = cert_po_trustAnchor; + cvout[0].value.pointer.cert = NULL; + cvout[1].type = cert_po_errorLog; + cvout[1].value.pointer.log = &log; + cvout[2].type = cert_po_end; - if( m_pHandler != NULL ) + // We check SSL server certificates, CA certificates and signing sertificates. + // + // 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[] = { - //JL: We must not pass a particular usage in the requiredUsages argument (the 4th) because, - //then ONLY these are verified. For example, we pass - //certificateUsageSSLClient | certificateUsageSSLServer. Then checking a certificate which - // is a valid certificateUsageEmailSigner but no certificateUsageSSLClient | certificateUsageSSLServer - //will result in CertificateValidity::INVALID. - //Only if the argument "requiredUsages" has a value (other than zero) - //then the function will return SECFailure in case - //the certificate is not suitable for the provided usage. That is, in the previous - //example the function returns SECFailure. - status = CERT_VerifyCertificate( - m_pHandler, ( CERTCertificate* )cert, PR_TRUE, - (SECCertificateUsage)0, timeboundary , NULL, log, &usage); - } - else + {certificateUsageSSLClient, "certificateUsageSSLClient" }, + {certificateUsageSSLServer, "certificateUsageSSLServer" }, + {certificateUsageSSLCA, "certificateUsageSSLCA" }, + {certificateUsageEmailSigner, "certificateUsageEmailSigner"}, //only usable for end certs + {certificateUsageEmailRecipient, "certificateUsageEmailRecipient"} + }; + + int numUsages = sizeof(arUsages) / sizeof(UsageDescription); + for (int i = 0; i < numUsages; i++) { - status = CERT_VerifyCertificate( - CERT_GetDefaultCertDB(), ( CERTCertificate* )cert, - PR_TRUE, (SECCertificateUsage)0, timeboundary ,NULL, log, &usage); - } - - if( status == SECSuccess ) - { - // JL & TKR : certificateUsageUserCertImport, - // certificateUsageVerifyCA and certificateUsageAnyCA dont check the chain + xmlsec_trace("Testing usage %d of %d: %s (0x%x)", i + 1, + numUsages, arUsages[i].description, (int) arUsages[i].usage); - //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 conain 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. - - if (usage & certificateUsageEmailSigner - || usage & certificateUsageEmailRecipient - || usage & certificateUsageSSLCA - || usage & certificateUsageSSLServer - || usage & certificateUsageSSLClient - // || usage & certificateUsageUserCertImport - // || usage & certificateUsageVerifyCA - || usage & certificateUsageStatusResponder ) - // || usage & certificateUsageAnyCA ) + status = CERT_PKIXVerifyCert(const_cast<CERTCertificate *>(cert), arUsages[i].usage, + cvin, cvout, NULL); + if( status == SECSuccess ) + { + xmlsec_trace("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 conain 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; + xmlsec_trace("Certificate is valid.\n"); + CERTCertificate * issuerCert = cvout[0].value.pointer.cert; + if (issuerCert) + { + xmlsec_trace("Root certificate: %s", issuerCert->subjectName); + CERT_DestroyCertificate(issuerCert); + }; + + break; + } else - validity = csss::CertificateValidity::INVALID; - + { + PRIntn err = PR_GetError(); + xmlsec_trace("Error: , %d = %s", err, getCertError(err)); + + /* Display validation results */ + if ( log.count > 0) + { + CERTVerifyLogNode *node = NULL; + printChainFailure(&log); + + for (node = log.head; node; node = node->next) { + if (node->cert) + CERT_DestroyCertificate(node->cert); + } + log.head = log.tail = NULL; + log.count = 0; + } + xmlsec_trace("Certificate is invalid.\n"); + } } - // always check what kind of error occured, even SECStatus says Success - //JL: When we call CERT_VerifyCertificate whit the parameter requiredUsages == 0 then all - //possible usages are checked. Then there are certainly usages for which the certificate - //is not intended. For these usages there will be NO flag set in the argument returnedUsages - // (the last arg) and there will be error codes set in the log. Therefore we cannot - //set the CertificateValidity to INVALID because there is a log entry. -// CERTVerifyLogNode *logNode = 0; - -// logNode = log->head; -// while ( logNode != NULL ) -// { -// sal_Int32 errorCode = 0; -// errorCode = logNode->error; -// switch ( errorCode ) -// { -// // JL & TKR: Any error are treated as invalid because we cannot say that we get all occurred errors from NSS -// /* -// case ( SEC_ERROR_REVOKED_CERTIFICATE ): -// validity |= csss::CertificateValidity::REVOKED; -// break; -// case ( SEC_ERROR_EXPIRED_CERTIFICATE ): -// validity |= csss::CertificateValidity::TIME_INVALID; -// break; -// case ( SEC_ERROR_CERT_USAGES_INVALID): -// validity |= csss::CertificateValidity::INVALID; -// break; -// case ( SEC_ERROR_UNTRUSTED_ISSUER ): -// case ( SEC_ERROR_UNTRUSTED_CERT ): -// validity |= csss::CertificateValidity::UNTRUSTED; -// break; -// */ -// default: -// validity |= csss::CertificateValidity::INVALID; -// break; -// } -// logNode = logNode->next; -// } } else - { - + { validity = ::com::sun::star::security::CertificateValidity::INVALID ; } @@ -931,15 +978,9 @@ verifyCertificate( const Reference< csss::XCertificate >& aCert, std::vector<CERTCertificate*>::const_iterator cert_i; for (cert_i = vecTmpNSSCertificates.begin(); cert_i != vecTmpNSSCertificates.end(); cert_i++) { - OSL_TRACE("[xmlsecurity] Destroying temporary certificate"); + xmlsec_trace("Destroying temporary certificate"); CERT_DestroyCertificate(*cert_i); } -#if OSL_DEBUG_LEVEL > 1 - if (validity == ::com::sun::star::security::CertificateValidity::VALID) - OSL_TRACE("[xmlsecurity] Certificate is valid."); - else - OSL_TRACE("[xmlsecurity] Certificate is invalid."); -#endif return validity ; } |