/* Called when the server certificate verification fails. This gives us the chance to trust the server anyway and add the certificate to the local database. */ static SECStatus badCertHandler(void *arg, PRFileDesc *sslSocket) { SECStatus secStatus; PRErrorCode errorNumber; CERTCertificate *serverCert; /* By default, don't trust the certificate. */ secStatus = SECFailure; errorNumber = PR_GetError (); if (errorNumber == SEC_ERROR_CA_CERT_INVALID) { /* The server's certificate is not trusted. Should we trust it? */ if (trustNewServer_p == NULL) return SECFailure; /* Do not trust this server */ /* Trust it for this session only? */ if (strcmp (trustNewServer_p, "session") == 0) return SECSuccess; /* Trust it permanently? */ if (strcmp (trustNewServer_p, "permanent") == 0) { /* The user wants to trust this server. Get the server's certificate so and add it to our database. */ serverCert = SSL_PeerCertificate (sslSocket); if (serverCert != NULL) secStatus = trustNewServer (serverCert); } } return secStatus; }
/* This invokes the "default" AuthCert handler in libssl. ** The only reason to use this one is that it prints out info as it goes. */ static SECStatus mySSLAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer) { SECStatus rv; CERTCertificate * peerCert; const SECItemArray *csa; if (MakeCertOK>=2) { return SECSuccess; } peerCert = SSL_PeerCertificate(fd); PRINTF("strsclnt: Subject: %s\nstrsclnt: Issuer : %s\n", peerCert->subjectName, peerCert->issuerName); csa = SSL_PeerStapledOCSPResponses(fd); if (csa) { PRINTF("Received %d Cert Status items (OCSP stapled data)\n", csa->len); } /* invoke the "default" AuthCert handler. */ rv = SSL_AuthCertificate(arg, fd, checkSig, isServer); PR_ATOMIC_INCREMENT(&certsTested); if (rv == SECSuccess) { fputs("strsclnt: -- SSL: Server Certificate Validated.\n", stderr); } CERT_DestroyCertificate(peerCert); /* error, if any, will be displayed by the Bad Cert Handler. */ return rv; }
/** * * Check that the Peer certificate's issuer certificate matches the one found * by issuer_nickname. This is not exactly the way OpenSSL and GNU TLS do the * issuer check, so we provide comments that mimic the OpenSSL * X509_check_issued function (in x509v3/v3_purp.c) */ static SECStatus check_issuer_cert(PRFileDesc *sock, char *issuer_nickname) { CERTCertificate *cert,*cert_issuer,*issuer; SECStatus res=SECSuccess; void *proto_win = NULL; /* PRArenaPool *tmpArena = NULL; CERTAuthKeyID *authorityKeyID = NULL; SECITEM *caname = NULL; */ cert = SSL_PeerCertificate(sock); cert_issuer = CERT_FindCertIssuer(cert,PR_Now(),certUsageObjectSigner); proto_win = SSL_RevealPinArg(sock); issuer = NULL; issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win); if ((!cert_issuer) || (!issuer)) res = SECFailure; else if (SECITEM_CompareItem(&cert_issuer->derCert, &issuer->derCert)!=SECEqual) res = SECFailure; CERT_DestroyCertificate(cert); CERT_DestroyCertificate(issuer); CERT_DestroyCertificate(cert_issuer); return res; }
static SECStatus nss_bad_cert_cb(void *arg, PRFileDesc *fd) { struct tls_connection *conn = arg; SECStatus res = SECSuccess; PRErrorCode err; CERTCertificate *cert; char *subject, *issuer; err = PR_GetError(); if (IS_SEC_ERROR(err)) wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (sec err " "%d)", err - SEC_ERROR_BASE); else wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (err %d)", err); cert = SSL_PeerCertificate(fd); subject = CERT_NameToAscii(&cert->subject); issuer = CERT_NameToAscii(&cert->issuer); wpa_printf(MSG_DEBUG, "NSS: Peer certificate subject='%s' issuer='%s'", subject, issuer); CERT_DestroyCertificate(cert); PR_Free(subject); PR_Free(issuer); if (conn->verify_peer) res = SECFailure; return res; }
SECStatus TransportLayerDtls::AuthCertificateHook(PRFileDesc *fd, PRBool checksig, PRBool isServer) { CheckThread(); ScopedCERTCertificate peer_cert; peer_cert = SSL_PeerCertificate(fd); // We are not set up to take this being called multiple // times. Change this if we ever add renegotiation. MOZ_ASSERT(!auth_hook_called_); if (auth_hook_called_) { PR_SetError(PR_UNKNOWN_ERROR, 0); return SECFailure; } auth_hook_called_ = true; MOZ_ASSERT(verification_mode_ != VERIFY_UNSET); MOZ_ASSERT(peer_cert_ == nullptr); switch (verification_mode_) { case VERIFY_UNSET: // Break out to error exit PR_SetError(PR_UNKNOWN_ERROR, 0); break; case VERIFY_ALLOW_ALL: peer_cert_ = peer_cert.forget(); cert_ok_ = true; return SECSuccess; case VERIFY_DIGEST: { MOZ_ASSERT(digests_.size() != 0); // Check all the provided digests // Checking functions call PR_SetError() SECStatus rv = SECFailure; for (size_t i = 0; i < digests_.size(); i++) { RefPtr<VerificationDigest> digest = digests_[i]; rv = CheckDigest(digest, peer_cert); if (rv != SECSuccess) break; } if (rv == SECSuccess) { // Matches all digests, we are good to go cert_ok_ = true; peer_cert = peer_cert.forget(); return SECSuccess; } } break; default: MOZ_CRASH(); // Can't happen } return SECFailure; }
static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) { SECStatus result = SECFailure; struct connectdata *conn = (struct connectdata *)arg; PRErrorCode err = PR_GetError(); CERTCertificate *cert = NULL; char *subject, *subject_cn, *issuer; conn->data->set.ssl.certverifyresult=err; cert = SSL_PeerCertificate(sock); subject = CERT_NameToAscii(&cert->subject); subject_cn = CERT_GetCommonName(&cert->subject); issuer = CERT_NameToAscii(&cert->issuer); CERT_DestroyCertificate(cert); switch(err) { case SEC_ERROR_CA_CERT_INVALID: infof(conn->data, "Issuer certificate is invalid: '%s'\n", issuer); break; case SEC_ERROR_UNTRUSTED_ISSUER: infof(conn->data, "Certificate is signed by an untrusted issuer: '%s'\n", issuer); break; case SSL_ERROR_BAD_CERT_DOMAIN: if(conn->data->set.ssl.verifyhost) { failf(conn->data, "SSL: certificate subject name '%s' does not match " "target host name '%s'", subject_cn, conn->host.dispname); } else { result = SECSuccess; infof(conn->data, "warning: SSL: certificate subject name '%s' does not " "match target host name '%s'\n", subject_cn, conn->host.dispname); } break; case SEC_ERROR_EXPIRED_CERTIFICATE: infof(conn->data, "Remote Certificate has expired.\n"); break; case SEC_ERROR_UNKNOWN_ISSUER: infof(conn->data, "Peer's certificate issuer is not recognized: '%s'\n", issuer); break; default: infof(conn->data, "Bad certificate received. Subject = '%s', " "Issuer = '%s'\n", subject, issuer); break; } if(result == SECSuccess) infof(conn->data, "SSL certificate verify ok.\n"); PR_Free(subject); PR_Free(subject_cn); PR_Free(issuer); return result; }
static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) { SECStatus success = SECSuccess; struct connectdata *conn = (struct connectdata *)arg; PRErrorCode err = PR_GetError(); CERTCertificate *cert = NULL; char *subject, *issuer; if(conn->data->set.ssl.certverifyresult!=0) return success; conn->data->set.ssl.certverifyresult=err; cert = SSL_PeerCertificate(sock); subject = CERT_NameToAscii(&cert->subject); issuer = CERT_NameToAscii(&cert->issuer); CERT_DestroyCertificate(cert); switch(err) { case SEC_ERROR_CA_CERT_INVALID: infof(conn->data, "Issuer certificate is invalid: '%s'\n", issuer); if(conn->data->set.ssl.verifypeer) success = SECFailure; break; case SEC_ERROR_UNTRUSTED_ISSUER: if(conn->data->set.ssl.verifypeer) success = SECFailure; infof(conn->data, "Certificate is signed by an untrusted issuer: '%s'\n", issuer); break; case SSL_ERROR_BAD_CERT_DOMAIN: if(conn->data->set.ssl.verifypeer) success = SECFailure; infof(conn->data, "common name: %s (does not match '%s')\n", subject, conn->host.dispname); break; case SEC_ERROR_EXPIRED_CERTIFICATE: if(conn->data->set.ssl.verifypeer) success = SECFailure; infof(conn->data, "Remote Certificate has expired.\n"); break; default: if(conn->data->set.ssl.verifypeer) success = SECFailure; infof(conn->data, "Bad certificate received. Subject = '%s', " "Issuer = '%s'\n", subject, issuer); break; } if(success == SECSuccess) infof(conn->data, "SSL certificate verify ok.\n"); PR_Free(subject); PR_Free(issuer); return success; }
/* * Callback from SSL for checking a (possibly) expired * certificate the peer presents. */ SECStatus JSSL_ConfirmExpiredPeerCert(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer) { SECStatus rv=SECFailure; SECCertUsage certUsage; CERTCertificate* peerCert=NULL; int64 notAfter, notBefore; certUsage = isServer ? certUsageSSLClient : certUsageSSLServer; peerCert = SSL_PeerCertificate(fd); if (peerCert) { rv = CERT_GetCertTimes(peerCert, ¬Before, ¬After); if (rv != SECSuccess) goto finish; /* * Verify the certificate based on it's expiry date. This should * always succeed, if the cert is trusted. It doesn't care if * the cert has expired. */ rv = CERT_VerifyCert(CERT_GetDefaultCertDB(), peerCert, checkSig, certUsage, notAfter, NULL /*pinarg*/, NULL /* log */); } if ( rv != SECSuccess ) goto finish; if( ! isServer ) { /* This is the client side of an SSL connection. * Now check the name field in the cert against the desired hostname. * NB: This is our only defense against Man-In-The-Middle (MITM) attacks! */ if( peerCert == NULL ) { rv = SECFailure; } else { char* hostname = NULL; hostname = SSL_RevealURL(fd); /* really is a hostname, not a URL */ if (hostname && hostname[0]) { rv = CERT_VerifyCertName(peerCert, hostname); PORT_Free(hostname); } else { rv = SECFailure; } } } finish: if (peerCert!=NULL) CERT_DestroyCertificate(peerCert); return rv; }
/* * Callback from SSL for checking certificate the peer (other end of * the socket) presents. */ SECStatus JSSL_DefaultCertAuthCallback(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer) { char * hostname = NULL; SECStatus rv = SECFailure; SECCertUsage certUsage; CERTCertificate *peerCert=NULL; certUsage = isServer ? certUsageSSLClient : certUsageSSLServer; /* SSL_PeerCertificate() returns a shallow copy of the cert, so we must destroy it before we exit this function */ peerCert = SSL_PeerCertificate(fd); if (peerCert) { rv = CERT_VerifyCertNow(CERT_GetDefaultCertDB(), peerCert, checkSig, certUsage, NULL /*pinarg*/); } /* if we're a server, then we don't need to check the CN of the certificate, so we can just return whatever returncode we have now */ if ( rv != SECSuccess || isServer ) { if (peerCert) CERT_DestroyCertificate(peerCert); return (int)rv; } /* cert is OK. This is the client side of an SSL connection. * Now check the name field in the cert against the desired hostname. * NB: This is our only defense against Man-In-The-Middle (MITM) attacks! */ hostname = SSL_RevealURL(fd); /* really is a hostname, not a URL */ if (hostname && hostname[0]) { rv = CERT_VerifyCertName(peerCert, hostname); PORT_Free(hostname); } else rv = SECFailure; if (peerCert) CERT_DestroyCertificate(peerCert); return rv; }
static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) { SSLChannelInfo channel; SSLCipherSuiteInfo suite; CERTCertificate *cert; char *subject, *issuer, *common_name; PRExplodedTime printableTime; char timeString[256]; PRTime notBefore, notAfter; if(SSL_GetChannelInfo(sock, &channel, sizeof channel) == SECSuccess && channel.length == sizeof channel && channel.cipherSuite) { if(SSL_GetCipherSuiteInfo(channel.cipherSuite, &suite, sizeof suite) == SECSuccess) { infof(conn->data, "SSL connection using %s\n", suite.cipherSuiteName); } } infof(conn->data, "Server certificate:\n"); cert = SSL_PeerCertificate(sock); subject = CERT_NameToAscii(&cert->subject); issuer = CERT_NameToAscii(&cert->issuer); common_name = CERT_GetCommonName(&cert->subject); infof(conn->data, "\tsubject: %s\n", subject); CERT_GetCertTimes(cert, ¬Before, ¬After); PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime); PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); infof(conn->data, "\tstart date: %s\n", timeString); PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime); PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); infof(conn->data, "\texpire date: %s\n", timeString); infof(conn->data, "\tcommon name: %s\n", common_name); infof(conn->data, "\tissuer: %s\n", issuer); PR_Free(subject); PR_Free(issuer); PR_Free(common_name); CERT_DestroyCertificate(cert); return; }
static int cert_from_sessioninfo(sxc_client_t *sx, const struct curl_tlssessioninfo *info, CERTCertificate **cert) { if (info->backend != CURLSSLBACKEND_NSS) { curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); sxi_seterr(sx, SXE_ECURL, "SSL backend mismatch: NSS expected, got %d (curl %s)", info->backend, data->ssl_version ? data->ssl_version : "N/A"); return -1; } PRFileDesc *desc = info->internals; if(!desc) { SXDEBUG("NULL PRFileDesc context"); return -EAGAIN; } *cert = SSL_PeerCertificate(desc); if (!*cert) { PRInt32 err = PR_GetError(); SXDEBUG("Unable to retrieve certificate for cluster: %s", PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT)); return -EAGAIN; } return 0; }
static GList * ssl_nss_get_peer_certificates(PRFileDesc *socket, PurpleSslConnection * gsc) { CERTCertificate *curcert; CERTCertificate *issuerCert; PurpleCertificate * newcrt; /* List of Certificate instances to return */ GList * peer_certs = NULL; int count; int64 now = PR_Now(); curcert = SSL_PeerCertificate(socket); if (curcert == NULL) { purple_debug_error("nss", "could not DupCertificate\n"); return NULL; } for (count = 0 ; count < CERT_MAX_CERT_CHAIN ; count++) { purple_debug_info("nss", "subject=%s issuer=%s\n", curcert->subjectName, curcert->issuerName ? curcert->issuerName : "(null)"); newcrt = x509_import_from_nss(curcert); peer_certs = g_list_append(peer_certs, newcrt); if (curcert->isRoot) { break; } issuerCert = CERT_FindCertIssuer(curcert, now, certUsageSSLServer); if (!issuerCert) { purple_debug_error("nss", "partial certificate chain\n"); break; } CERT_DestroyCertificate(curcert); curcert = issuerCert; } CERT_DestroyCertificate(curcert); return peer_certs; }
std::string SslSocket::getClientAuthId() const { std::string authId; CERTCertificate* cert = SSL_PeerCertificate(nssSocket); if (cert) { char *cn = CERT_GetCommonName(&(cert->subject)); if (cn) { authId = std::string(cn); /* * The NSS function CERT_GetDomainComponentName only returns * the last component of the domain name, so we have to parse * the subject manually to extract the full domain. */ std::string domain = getDomainFromSubject(cert->subjectName); if (!domain.empty()) { authId += DOMAIN_SEPARATOR; authId += domain; } } CERT_DestroyCertificate(cert); } return authId; }
static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) { SSLChannelInfo channel; SSLCipherSuiteInfo suite; CERTCertificate *cert; if(SSL_GetChannelInfo(sock, &channel, sizeof channel) == SECSuccess && channel.length == sizeof channel && channel.cipherSuite) { if(SSL_GetCipherSuiteInfo(channel.cipherSuite, &suite, sizeof suite) == SECSuccess) { infof(conn->data, "SSL connection using %s\n", suite.cipherSuiteName); } } infof(conn->data, "Server certificate:\n"); cert = SSL_PeerCertificate(sock); display_cert_info(conn->data, cert); CERT_DestroyCertificate(cert); return; }
static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) { struct connectdata *conn = (struct connectdata *)arg; struct SessionHandle *data = conn->data; PRErrorCode err = PR_GetError(); CERTCertificate *cert; /* remember the cert verification result */ data->set.ssl.certverifyresult = err; if(err == SSL_ERROR_BAD_CERT_DOMAIN && !data->set.ssl.verifyhost) /* we are asked not to verify the host name */ return SECSuccess; /* print only info about the cert, the error is printed off the callback */ cert = SSL_PeerCertificate(sock); if(cert) { infof(data, "Server certificate:\n"); display_cert_info(data, cert); CERT_DestroyCertificate(cert); } return SECFailure; }
static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) { SSLChannelInfo channel; SSLCipherSuiteInfo suite; CERTCertificate *cert; CERTCertificate *cert2; CERTCertificate *cert3; PRTime now; int i; if(SSL_GetChannelInfo(sock, &channel, sizeof channel) == SECSuccess && channel.length == sizeof channel && channel.cipherSuite) { if(SSL_GetCipherSuiteInfo(channel.cipherSuite, &suite, sizeof suite) == SECSuccess) { infof(conn->data, "SSL connection using %s\n", suite.cipherSuiteName); } } cert = SSL_PeerCertificate(sock); if(cert) { infof(conn->data, "Server certificate:\n"); if(!conn->data->set.ssl.certinfo) { display_cert_info(conn->data, cert); CERT_DestroyCertificate(cert); } else { /* Count certificates in chain. */ now = PR_Now(); i = 1; if(!cert->isRoot) { cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); while(cert2) { i++; if(cert2->isRoot) { CERT_DestroyCertificate(cert2); break; } cert3 = CERT_FindCertIssuer(cert2, now, certUsageSSLCA); CERT_DestroyCertificate(cert2); cert2 = cert3; } } Curl_ssl_init_certinfo(conn->data, i); for(i = 0; cert; cert = cert2) { Curl_extract_certinfo(conn, i++, (char *)cert->derCert.data, (char *)cert->derCert.data + cert->derCert.len); if(cert->isRoot) { CERT_DestroyCertificate(cert); break; } cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); CERT_DestroyCertificate(cert); } } } return; }
void PR_CALLBACK HandshakeCallback(PRFileDesc* fd, void* client_data) { nsNSSShutDownPreventionLock locker; PRInt32 sslStatus; char* signer = nsnull; char* cipherName = nsnull; PRInt32 keyLength; nsresult rv; PRInt32 encryptBits; if (SECSuccess != SSL_SecurityStatus(fd, &sslStatus, &cipherName, &keyLength, &encryptBits, &signer, nsnull)) { return; } PRInt32 secStatus; if (sslStatus == SSL_SECURITY_STATUS_OFF) secStatus = nsIWebProgressListener::STATE_IS_BROKEN; else if (encryptBits >= 90) secStatus = (nsIWebProgressListener::STATE_IS_SECURE | nsIWebProgressListener::STATE_SECURE_HIGH); else secStatus = (nsIWebProgressListener::STATE_IS_SECURE | nsIWebProgressListener::STATE_SECURE_LOW); CERTCertificate *peerCert = SSL_PeerCertificate(fd); const char* caName = nsnull; // caName is a pointer only, no ownership char* certOrgName = CERT_GetOrgName(&peerCert->issuer); CERT_DestroyCertificate(peerCert); caName = certOrgName ? certOrgName : signer; const char* verisignName = "Verisign, Inc."; // If the CA name is RSA Data Security, then change the name to the real // name of the company i.e. VeriSign, Inc. if (nsCRT::strcmp((const char*)caName, "RSA Data Security, Inc.") == 0) { caName = verisignName; } nsAutoString shortDesc; const PRUnichar* formatStrings[1] = { ToNewUnicode(NS_ConvertUTF8toUTF16(caName)) }; nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_SUCCEEDED(rv)) { rv = nssComponent->PIPBundleFormatStringFromName("SignedBy", formatStrings, 1, shortDesc); nsMemory::Free(const_cast<PRUnichar*>(formatStrings[0])); nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret; infoObject->SetSecurityState(secStatus); infoObject->SetShortSecurityDescription(shortDesc.get()); /* Set the SSL Status information */ nsRefPtr<nsSSLStatus> status = infoObject->SSLStatus(); if (!status) { status = new nsSSLStatus(); infoObject->SetSSLStatus(status); } CERTCertificate *serverCert = SSL_PeerCertificate(fd); if (serverCert) { nsRefPtr<nsNSSCertificate> nssc = new nsNSSCertificate(serverCert); CERT_DestroyCertificate(serverCert); serverCert = nsnull; nsCOMPtr<nsIX509Cert> prevcert; infoObject->GetPreviousCert(getter_AddRefs(prevcert)); PRBool equals_previous = PR_FALSE; if (prevcert) { nsresult rv = nssc->Equals(prevcert, &equals_previous); if (NS_FAILED(rv)) { equals_previous = PR_FALSE; } } if (equals_previous) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("HandshakeCallback using PREV cert %p\n", prevcert.get())); infoObject->SetCert(prevcert); status->mServerCert = prevcert; } else { if (status->mServerCert) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("HandshakeCallback KEEPING cert %p\n", status->mServerCert.get())); infoObject->SetCert(status->mServerCert); } else { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("HandshakeCallback using NEW cert %p\n", nssc.get())); infoObject->SetCert(nssc); status->mServerCert = nssc; } } } status->mHaveKeyLengthAndCipher = PR_TRUE; status->mKeyLength = keyLength; status->mSecretKeyLength = encryptBits; status->mCipherName.Adopt(cipherName); } PR_FREEIF(certOrgName); PR_Free(signer); }
SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd, PRBool checksig, PRBool isServer) { nsNSSShutDownPreventionLock locker; // first the default action SECStatus rv = SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer); // We want to remember the CA certs in the temp db, so that the application can find the // complete chain at any time it might need it. // But we keep only those CA certs in the temp db, that we didn't already know. CERTCertificate *serverCert = SSL_PeerCertificate(fd); CERTCertificateCleaner serverCertCleaner(serverCert); if (serverCert) { nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret; nsRefPtr<nsSSLStatus> status = infoObject->SSLStatus(); nsRefPtr<nsNSSCertificate> nsc; if (!status || !status->mServerCert) { nsc = new nsNSSCertificate(serverCert); } if (SECSuccess == rv) { if (nsc) { PRBool dummyIsEV; nsc->GetIsExtendedValidation(&dummyIsEV); // the nsc object will cache the status } CERTCertList *certList = CERT_GetCertChainFromCert(serverCert, PR_Now(), certUsageSSLCA); nsCOMPtr<nsINSSComponent> nssComponent; for (CERTCertListNode *node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList); node = CERT_LIST_NEXT(node)) { if (node->cert->slot) { // This cert was found on a token, no need to remember it in the temp db. continue; } if (node->cert->isperm) { // We don't need to remember certs already stored in perm db. continue; } if (node->cert == serverCert) { // We don't want to remember the server cert, // the code that cares for displaying page info does this already. continue; } // We have found a signer cert that we want to remember. nsCAutoString nickname; nickname = nsNSSCertificate::defaultServerNickname(node->cert); if (!nickname.IsEmpty()) { PK11SlotInfo *slot = PK11_GetInternalKeySlot(); if (slot) { PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE, const_cast<char*>(nickname.get()), PR_FALSE); PK11_FreeSlot(slot); } } } CERT_DestroyCertList(certList); } // The connection may get terminated, for example, if the server requires // a client cert. Let's provide a minimal SSLStatus // to the caller that contains at least the cert and its status. if (!status) { status = new nsSSLStatus(); infoObject->SetSSLStatus(status); } if (status && !status->mServerCert) { status->mServerCert = nsc; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("AuthCertificateCallback setting NEW cert %p\n", status->mServerCert.get())); } } return rv; }
// Extracts whatever information we need out of fd (using SSL_*) and passes it // to SSLServerCertVerificationJob::Dispatch. SSLServerCertVerificationJob should // never do anything with fd except logging. SECStatus AuthCertificateHook(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer) { // Runs on the socket transport thread PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] starting AuthCertificateHook\n", fd)); // Modern libssl always passes PR_TRUE for checkSig, and we have no means of // doing verification without checking signatures. NS_ASSERTION(checkSig, "AuthCertificateHook: checkSig unexpectedly false"); // PSM never causes libssl to call this function with PR_TRUE for isServer, // and many things in PSM assume that we are a client. NS_ASSERTION(!isServer, "AuthCertificateHook: isServer unexpectedly true"); nsNSSSocketInfo *socketInfo = static_cast<nsNSSSocketInfo*>(arg); if (socketInfo) { // This is the first callback during full handshakes. socketInfo->SetFirstServerHelloReceived(); } CERTCertificate *serverCert = SSL_PeerCertificate(fd); CERTCertificateCleaner serverCertCleaner(serverCert); if (!checkSig || isServer || !socketInfo || !serverCert) { PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure; } if (BlockServerCertChangeForSpdy(socketInfo, serverCert) != SECSuccess) return SECFailure; bool onSTSThread; nsresult nrv; nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv); if (NS_SUCCEEDED(nrv)) { nrv = sts->IsOnCurrentThread(&onSTSThread); } if (NS_FAILED(nrv)) { NS_ERROR("Could not get STS service or IsOnCurrentThread failed"); PR_SetError(PR_UNKNOWN_ERROR, 0); return SECFailure; } if (onSTSThread) { // We *must* do certificate verification on a background thread because // we need the socket transport thread to be free for our OCSP requests, // and we *want* to do certificate verification on a background thread // because of the performance benefits of doing so. socketInfo->SetCertVerificationWaiting(); SECStatus rv = SSLServerCertVerificationJob::Dispatch( static_cast<const void *>(fd), socketInfo, serverCert); return rv; } // We can't do certificate verification on a background thread, because the // thread doing the network I/O may not interrupt its network I/O on receipt // of our SSLServerCertVerificationResult event, and/or it might not even be // a non-blocking socket. SECStatus rv = AuthCertificate(socketInfo, serverCert); if (rv == SECSuccess) { return SECSuccess; } PRErrorCode error = PR_GetError(); if (error != 0) { nsRefPtr<CertErrorRunnable> runnable = CreateCertErrorRunnable( error, socketInfo, serverCert, static_cast<const void *>(fd)); if (!runnable) { // CreateCertErrorRunnable sets a new error code when it fails error = PR_GetError(); } else { // We have to return SECSuccess or SECFailure based on the result of the // override processing, so we must block this thread waiting for it. The // CertErrorRunnable will NOT dispatch the result at all, since we passed // false for CreateCertErrorRunnable's async parameter nrv = runnable->DispatchToMainThreadAndWait(); if (NS_FAILED(nrv)) { NS_ERROR("Failed to dispatch CertErrorRunnable"); PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure; } if (!runnable->mResult) { NS_ERROR("CertErrorRunnable did not set result"); PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure; } if (runnable->mResult->mErrorCode == 0) { return SECSuccess; // cert error override occurred. } // We must call SetCanceled here to set the error message type // in case it isn't PlainErrorMessage, which is what we would // default to if we just called // PR_SetError(runnable->mResult->mErrorCode, 0) and returned // SECFailure without doing this. socketInfo->SetCanceled(runnable->mResult->mErrorCode, runnable->mResult->mErrorMessageType); error = runnable->mResult->mErrorCode; } } if (error == 0) { NS_ERROR("error code not set"); error = PR_UNKNOWN_ERROR; } PR_SetError(error, 0); return SECFailure; }
JNIEXPORT jobject JNICALL Java_org_mozilla_jss_ssl_SSLSocket_getStatus (JNIEnv *env, jobject self) { SECStatus secstatus; JSSL_SocketData *sock=NULL; int on; char *cipher=NULL; jobject cipherString; jint keySize; jint secretKeySize; char *issuer=NULL; jobject issuerString; char *subject=NULL; jobject subjectString; jobject statusObj = NULL; jclass statusClass; jmethodID statusCons; CERTCertificate *peerCert=NULL; jobject peerCertObj = NULL; char *serialNum = NULL; jobject serialNumObj = NULL; /* get the fd */ if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) { /* exception was thrown */ goto finish; } /* get the status */ secstatus = SSL_SecurityStatus( sock->fd, &on, &cipher, (int*)&keySize, (int*)&secretKeySize, &issuer, &subject); if(secstatus != SECSuccess) { JSSL_throwSSLSocketException(env, "Failed to retrieve socket security status"); goto finish; } /* * get the peer certificate */ peerCert = SSL_PeerCertificate(sock->fd); if( peerCert != NULL ) { /* the peer cert might be null, for example if this is the server * side and the client didn't auth. */ serialNum = CERT_Hexify(&peerCert->serialNumber, PR_FALSE /*do_colon*/); PR_ASSERT(serialNum != NULL); serialNumObj = (*env)->NewStringUTF(env, serialNum); if( serialNumObj == NULL ) { goto finish; } /* this call will wipe out peerCert */ peerCertObj = JSS_PK11_wrapCert(env, &peerCert); if( peerCertObj == NULL) { goto finish; } } /* * convert char*s to Java Strings */ cipherString = issuerString = subjectString = NULL; if( cipher != NULL ) cipherString = (*env)->NewStringUTF(env, cipher); if( issuer != NULL ) issuerString = (*env)->NewStringUTF(env, issuer); if( subject != NULL ) subjectString = (*env)->NewStringUTF(env, subject); /* * package the status into a new SSLSecurityStatus object */ statusClass = (*env)->FindClass(env, SSL_SECURITY_STATUS_CLASS_NAME); PR_ASSERT(statusClass != NULL); if( statusClass == NULL ) { /* exception was thrown */ goto finish; } statusCons = (*env)->GetMethodID(env, statusClass, SSL_SECURITY_STATUS_CONSTRUCTOR_NAME, SSL_SECURITY_STATUS_CONSTRUCTOR_SIG); PR_ASSERT(statusCons != NULL); if(statusCons == NULL ) { /* exception was thrown */ goto finish; } statusObj = (*env)->NewObject(env, statusClass, statusCons, on, cipherString, keySize, secretKeySize, issuerString, subjectString, serialNumObj, peerCertObj); finish: if( cipher != NULL ) { PR_Free(cipher); } if( issuer != NULL ) { PORT_Free(issuer); } if ( subject != NULL) { PORT_Free(subject); } if( peerCert != NULL ) { CERT_DestroyCertificate(peerCert); } if( serialNum != NULL ) { PR_Free(serialNum); } EXCEPTION_CHECK(env, sock) return statusObj; }
/* * Callback from SSL for checking a (possibly) expired * certificate the peer presents. * * obj - a jobject -> instance of a class implementing * the SSLCertificateApprovalCallback interface */ SECStatus JSSL_JavaCertAuthCallback(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer) { CERTCertificate *peerCert=NULL; CERTVerifyLog log; JNIEnv *env; jobject validityStatus; jmethodID addReasonMethod; int certUsage; int checkcn_rv; jmethodID approveMethod; jboolean result; char *hostname=NULL; SECStatus retval = SECFailure; SECStatus verificationResult; PR_ASSERT(arg != NULL); PR_ASSERT(fd != NULL); /* initialize logging structures */ log.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if( log.arena == NULL ) return SECFailure; log.head = NULL; log.tail = NULL; log.count = 0; /* get the JNI environment */ if((*JSS_javaVM)->AttachCurrentThread(JSS_javaVM, (void**)&env, NULL) != 0){ PR_ASSERT(PR_FALSE); goto finish; } /* First, get a handle on the cert that the peer presented */ peerCert = SSL_PeerCertificate(fd); /* if peer didn't present a cert, why am I called? */ if (peerCert == NULL) goto finish; certUsage = isServer ? certUsageSSLClient : certUsageSSLServer; /* * verify it against current time - (can't use * CERT_VerifyCertNow() since it doesn't allow passing of * logging parameter) */ verificationResult = CERT_VerifyCert( CERT_GetDefaultCertDB(), peerCert, checkSig, certUsage, PR_Now(), NULL /*pinarg*/, &log); if (verificationResult == SECSuccess && log.count > 0) { verificationResult = SECFailure; } /* * Verify the domain name of the cert. */ hostname = SSL_RevealURL(fd); /* really is a hostname, not a URL */ if (hostname && hostname[0]) { checkcn_rv = CERT_VerifyCertName(peerCert, hostname); PORT_Free(hostname); } else { checkcn_rv = SECFailure; } if (checkcn_rv != SECSuccess) { addToVerifyLog(env, &log,peerCert,SSL_ERROR_BAD_CERT_DOMAIN,0); if((*env)->ExceptionOccurred(env) != NULL) goto finish; verificationResult = SECFailure; } /* * create a new ValidityStatus object */ { jclass clazz; jmethodID cons; clazz = (*env)->FindClass(env, SSLCERT_APP_CB_VALIDITY_STATUS_CLASS); if( clazz == NULL ) goto finish; cons = (*env)->GetMethodID(env, clazz, PLAIN_CONSTRUCTOR, PLAIN_CONSTRUCTOR_SIG); if( cons == NULL ) goto finish; validityStatus = (*env)->NewObject(env, clazz, cons); if( validityStatus == NULL ) { goto finish; } /* get the addReason methodID while we're at it */ addReasonMethod = (*env)->GetMethodID(env, clazz, SSLCERT_APP_CB_VALIDITY_STATUS_ADD_REASON_NAME, SSLCERT_APP_CB_VALIDITY_STATUS_ADD_REASON_SIG); if( addReasonMethod == NULL ) { goto finish; } } /* * Load up the ValidityStatus object with all the reasons for failure */ if (verificationResult == SECFailure) { CERTVerifyLogNode *node; int error; CERTCertificate *errorcert=NULL; int depth; jobject ninjacert; node = log.head; while (node) { error = node->error; errorcert = node->cert; node->cert = NULL; depth = node->depth; ninjacert = JSS_PK11_wrapCert(env,&errorcert); (*env)->CallVoidMethod(env, validityStatus, addReasonMethod, error, ninjacert, depth ); node = node->next; } } /* * Call the approval callback */ { jobject approvalCallbackObj; jclass approvalCallbackClass; jobject peerninjacert; approvalCallbackObj = (jobject) arg; approvalCallbackClass = (*env)->GetObjectClass(env,approvalCallbackObj); approveMethod = (*env)->GetMethodID( env, approvalCallbackClass, SSLCERT_APP_CB_APPROVE_NAME, SSLCERT_APP_CB_APPROVE_SIG); if( approveMethod == NULL ) { PR_ASSERT(PR_FALSE); goto finish; } peerninjacert = JSS_PK11_wrapCert(env,&peerCert); if( peerninjacert == NULL) { PR_ASSERT(PR_FALSE); goto finish; } result = (*env)->CallBooleanMethod(env, approvalCallbackObj, approveMethod, peerninjacert, validityStatus); if( result == JNI_TRUE ) { retval = SECSuccess; } } finish: if( peerCert != NULL ) { CERT_DestroyCertificate(peerCert); } PORT_FreeArena(log.arena, PR_FALSE); return retval; }
// Extracts whatever information we need out of fd (using SSL_*) and passes it // to SSLServerCertVerificationJob::Dispatch. SSLServerCertVerificationJob should // never do anything with fd except logging. SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer) { RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier()); if (!certVerifier) { PR_SetError(SEC_ERROR_NOT_INITIALIZED, 0); return SECFailure; } // Runs on the socket transport thread PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] starting AuthCertificateHook\n", fd)); // Modern libssl always passes PR_TRUE for checkSig, and we have no means of // doing verification without checking signatures. NS_ASSERTION(checkSig, "AuthCertificateHook: checkSig unexpectedly false"); // PSM never causes libssl to call this function with PR_TRUE for isServer, // and many things in PSM assume that we are a client. NS_ASSERTION(!isServer, "AuthCertificateHook: isServer unexpectedly true"); nsNSSSocketInfo* socketInfo = static_cast<nsNSSSocketInfo*>(arg); ScopedCERTCertificate serverCert(SSL_PeerCertificate(fd)); if (!checkSig || isServer || !socketInfo || !serverCert) { PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure; } socketInfo->SetFullHandshake(); // This value of "now" is used both here for OCSP stapling and later // when calling CreateCertErrorRunnable. PRTime now = PR_Now(); if (BlockServerCertChangeForSpdy(socketInfo, serverCert) != SECSuccess) return SECFailure; bool onSTSThread; nsresult nrv; nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv); if (NS_SUCCEEDED(nrv)) { nrv = sts->IsOnCurrentThread(&onSTSThread); } if (NS_FAILED(nrv)) { NS_ERROR("Could not get STS service or IsOnCurrentThread failed"); PR_SetError(PR_UNKNOWN_ERROR, 0); return SECFailure; } // SSL_PeerStapledOCSPResponses will never return a non-empty response if // OCSP stapling wasn't enabled because libssl wouldn't have let the server // return a stapled OCSP response. // We don't own these pointers. const SECItemArray* csa = SSL_PeerStapledOCSPResponses(fd); SECItem* stapledOCSPResponse = nullptr; // we currently only support single stapled responses if (csa && csa->len == 1) { stapledOCSPResponse = &csa->items[0]; } uint32_t providerFlags = 0; socketInfo->GetProviderFlags(&providerFlags); if (onSTSThread) { // We *must* do certificate verification on a background thread because // we need the socket transport thread to be free for our OCSP requests, // and we *want* to do certificate verification on a background thread // because of the performance benefits of doing so. socketInfo->SetCertVerificationWaiting(); SECStatus rv = SSLServerCertVerificationJob::Dispatch( certVerifier, static_cast<const void*>(fd), socketInfo, serverCert, stapledOCSPResponse, providerFlags, now); return rv; } // We can't do certificate verification on a background thread, because the // thread doing the network I/O may not interrupt its network I/O on receipt // of our SSLServerCertVerificationResult event, and/or it might not even be // a non-blocking socket. SECStatus rv = AuthCertificate(*certVerifier, socketInfo, serverCert, stapledOCSPResponse, providerFlags, now); if (rv == SECSuccess) { Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, 1); return SECSuccess; } PRErrorCode error = PR_GetError(); if (error != 0) { RefPtr<CertErrorRunnable> runnable( CreateCertErrorRunnable(*certVerifier, error, socketInfo, serverCert, static_cast<const void*>(fd), providerFlags, now)); if (!runnable) { // CreateCertErrorRunnable sets a new error code when it fails error = PR_GetError(); } else { // We have to return SECSuccess or SECFailure based on the result of the // override processing, so we must block this thread waiting for it. The // CertErrorRunnable will NOT dispatch the result at all, since we passed // false for CreateCertErrorRunnable's async parameter nrv = runnable->DispatchToMainThreadAndWait(); if (NS_FAILED(nrv)) { NS_ERROR("Failed to dispatch CertErrorRunnable"); PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure; } if (!runnable->mResult) { NS_ERROR("CertErrorRunnable did not set result"); PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure; } if (runnable->mResult->mErrorCode == 0) { return SECSuccess; // cert error override occurred. } // We must call SetCanceled here to set the error message type // in case it isn't PlainErrorMessage, which is what we would // default to if we just called // PR_SetError(runnable->mResult->mErrorCode, 0) and returned // SECFailure without doing this. socketInfo->SetCanceled(runnable->mResult->mErrorCode, runnable->mResult->mErrorMessageType); error = runnable->mResult->mErrorCode; } } if (error == 0) { NS_ERROR("error code not set"); error = PR_UNKNOWN_ERROR; } PR_SetError(error, 0); return SECFailure; }
/* Function: SECStatus myAuthCertificate() * * Purpose: This function is our custom certificate authentication handler. * * Note: This implementation is essentially the same as the default * SSL_AuthCertificate(). */ SECStatus myAuthCertificate(void *arg, PRFileDesc *socket, PRBool checksig, PRBool isServer) { SECCertificateUsage certUsage; CERTCertificate * cert; void * pinArg; char * hostName; SECStatus secStatus; if (!arg || !socket) { errWarn("myAuthCertificate"); return SECFailure; } /* Define how the cert is being used based upon the isServer flag. */ certUsage = isServer ? certificateUsageSSLClient : certificateUsageSSLServer; cert = SSL_PeerCertificate(socket); pinArg = SSL_RevealPinArg(socket); if (dumpChain == PR_TRUE) { dumpCertChain(cert, certUsage); } secStatus = CERT_VerifyCertificateNow((CERTCertDBHandle *)arg, cert, checksig, certUsage, pinArg, NULL); /* If this is a server, we're finished. */ if (isServer || secStatus != SECSuccess) { SECU_printCertProblems(stderr, (CERTCertDBHandle *)arg, cert, checksig, certUsage, pinArg, PR_FALSE); CERT_DestroyCertificate(cert); return secStatus; } /* Certificate is OK. Since this is the client side of an SSL * connection, we need to verify that the name field in the cert * matches the desired hostname. This is our defense against * man-in-the-middle attacks. */ /* SSL_RevealURL returns a hostName, not an URL. */ hostName = SSL_RevealURL(socket); if (hostName && hostName[0]) { secStatus = CERT_VerifyCertName(cert, hostName); } else { PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0); secStatus = SECFailure; } if (hostName) PR_Free(hostName); CERT_DestroyCertificate(cert); return secStatus; }