Chromium Code Reviews| 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(); |