Index: net/socket/ssl_client_socket_nss.cc |
=================================================================== |
--- net/socket/ssl_client_socket_nss.cc (revisiĆ³n: 28534) |
+++ net/socket/ssl_client_socket_nss.cc (copia de trabajo) |
@@ -51,6 +51,7 @@ |
#include "net/socket/ssl_client_socket_nss.h" |
#include <certdb.h> |
+#include <key.h> |
#include <nspr.h> |
#include <nss.h> |
#include <secerr.h> |
@@ -69,6 +70,7 @@ |
#include "net/base/cert_verifier.h" |
#include "net/base/io_buffer.h" |
#include "net/base/net_errors.h" |
+#include "net/base/ssl_cert_request_info.h" |
#include "net/base/ssl_info.h" |
#include "net/ocsp/nss_ocsp.h" |
@@ -200,6 +202,8 @@ |
user_connect_callback_(NULL), |
user_callback_(NULL), |
user_buf_len_(0), |
+ client_auth_ca_names_(NULL), |
+ client_auth_cert_needed_(false), |
completed_handshake_(false), |
next_state_(STATE_NONE), |
nss_fd_(NULL), |
@@ -311,6 +315,10 @@ |
if (rv != SECSuccess) |
return ERR_UNEXPECTED; |
+ rv = SSL_GetClientAuthDataHook(nss_fd_, ClientAuthHandler, this); |
+ if (rv != SECSuccess) |
+ return ERR_UNEXPECTED; |
+ |
rv = SSL_HandshakeCallback(nss_fd_, HandshakeCallback, this); |
if (rv != SECSuccess) |
return ERR_UNEXPECTED; |
@@ -363,6 +371,8 @@ |
server_cert_verify_result_.Reset(); |
completed_handshake_ = false; |
nss_bufs_ = NULL; |
+ client_auth_ca_names_ = NULL; |
Jaime Soriano
2009/10/13 11:13:39
If client_auth_ca_names_ is freed here with CERT_F
|
+ client_auth_cert_needed_ = false; |
LeaveFunction(""); |
} |
@@ -488,7 +498,52 @@ |
void SSLClientSocketNSS::GetSSLCertRequestInfo( |
SSLCertRequestInfo* cert_request_info) { |
- // TODO(wtc): implement this. |
+ CERTCertNicknames *names; |
+ void *wincx = NULL; |
+ CERTCertificate* cert = NULL; |
+ X509Certificate* x509_cert = NULL; |
+ SECKEYPrivateKey* privkey = NULL; |
+ EnterFunction(""); |
+ |
+ cert_request_info->host_and_port = hostname_; |
+ cert_request_info->client_certs.clear(); |
+ |
+ wincx = SSL_RevealPinArg(nss_fd_); |
+ |
+ names = CERT_GetCertNicknames(CERT_GetDefaultCertDB(), |
+ SEC_CERT_NICKNAMES_USER, wincx); |
+ |
+ if (names != NULL) { |
+ for (int i = 0; i < names->numnicknames; i++) { |
+ cert = CERT_FindUserCertByUsage( |
+ CERT_GetDefaultCertDB(), |
+ names->nicknames[i], |
+ certUsageSSLClient, |
+ PR_FALSE, |
+ wincx); |
+ if (!cert) |
+ continue; |
+ // Only check unexpired certs |
+ if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_TRUE) != |
+ secCertTimeValid ) { |
+ CERT_DestroyCertificate(cert); |
+ continue; |
+ } |
+ if (NSS_CmpCertChainWCANames(cert, client_auth_ca_names_) == SECSuccess) { |
+ privkey = PK11_FindKeyByAnyCert(cert, wincx); |
+ if (privkey) { |
+ x509_cert = X509Certificate::CreateFromHandle( |
+ cert, X509Certificate::SOURCE_LONE_CERT_IMPORT); |
+ cert_request_info->client_certs.push_back(x509_cert); |
+ SECKEY_DestroyPrivateKey(privkey); |
+ continue; |
+ } |
+ } |
+ CERT_DestroyCertificate(cert); |
+ } |
+ CERT_FreeNicknames(names); |
+ } |
+ LeaveFunction(cert_request_info->client_certs.size()); |
} |
void SSLClientSocketNSS::DoCallback(int rv) { |
@@ -691,6 +746,63 @@ |
} |
// static |
+// NSS calls this if a client certificate is needed. |
+// Based on Mozilla's NSS_GetClientAuthData |
+SECStatus SSLClientSocketNSS::ClientAuthHandler( |
+ void* arg, |
+ PRFileDesc* socket, |
+ CERTDistNames* ca_names, |
+ CERTCertificate** result_certificate, |
+ SECKEYPrivateKey** result_private_key) { |
+ SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg); |
+ CERTCertificate* cert = NULL; |
+ SECKEYPrivateKey* privkey = NULL; |
+ PRArenaPool* arena = NULL; |
+ CERTDistNames* ca_names_copy = NULL; |
+ void *wincx = NULL; |
+ |
+ wincx = SSL_RevealPinArg(socket); |
+ |
+ that->client_auth_cert_needed_ = !that->ssl_config_.send_client_cert; |
+ |
+ // Second pass, a client certificate should have been selected |
+ if (that->ssl_config_.send_client_cert) { |
+ if (that->ssl_config_.client_cert) { |
+ cert = CERT_DupCertificate( |
+ that->ssl_config_.client_cert->os_cert_handle()); |
+ if (cert) { |
+ privkey = PK11_FindKeyByAnyCert(cert, wincx); |
+ if (privkey) { |
+ // TODO(jsorianopastor): We should wait for server certificate |
+ // verification before sending our credentials. |
+ // (http://code.google.com/p/chromium/issues/detail?id=13934) |
+ result_certificate[0] = cert; |
+ result_private_key[0] = privkey; |
+ return SECSuccess; |
+ } else { |
+ LOG(WARNING) << "Client cert found without private key"; |
+ } |
+ } else { |
+ LOG(WARNING) << "Invalid client cert to send"; |
+ } |
+ } |
+ // If not, client authentication failed |
+ return SECFailure; |
+ } |
+ |
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
+ ca_names_copy = PORT_ArenaZNew(arena, CERTDistNames); |
+ |
+ ca_names_copy->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
+ ca_names_copy->head = NULL; |
+ ca_names_copy->nnames = ca_names->nnames; |
+ ca_names_copy->names = SECITEM_ArenaDupItem(arena, ca_names->names); |
+ |
+ that->client_auth_ca_names_ = ca_names_copy; |
Jaime Soriano
2009/10/13 11:13:39
I'm not sure if this is the correct way to duplica
|
+ return SECFailure; |
+} |
+ |
+// static |
// NSS calls this when handshake is completed. |
// After the SSL handshake is finished, use CertVerifier to verify |
// the saved server certificate. |
@@ -707,9 +819,16 @@ |
int rv = SSL_ForceHandshake(nss_fd_); |
if (rv == SECSuccess) { |
- // SSL handshake is completed. Let's verify the certificate. |
- GotoState(STATE_VERIFY_CERT); |
- // Done! |
+ if (client_auth_cert_needed_) { |
+ net_error = ERR_SSL_CLIENT_AUTH_CERT_NEEDED; |
+ if (SECSuccess != SSL_ReHandshake(nss_fd_, PR_TRUE)) { |
Jaime Soriano
2009/10/13 11:13:39
I tried to remove this, but then it didn't request
|
+ LOG(WARNING) << "Couldn't restart handshake: " << PR_GetError(); |
+ } |
+ } else { |
+ // SSL handshake is completed. Let's verify the certificate. |
+ GotoState(STATE_VERIFY_CERT); |
+ // Done! |
+ } |
} else { |
PRErrorCode prerr = PR_GetError(); |