diff options
author | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-04 16:21:33 +0000 |
---|---|---|
committer | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-04 16:21:33 +0000 |
commit | 4a842345af02ac3ae84015683383539a84f66d8c (patch) | |
tree | 9b9b005ddea77d514f2bf0b32f83bf0e5f2bd746 /net | |
parent | 1fd26d8cdd166ec84c06bb7d4a8557293428fc0e (diff) | |
download | chromium_src-4a842345af02ac3ae84015683383539a84f66d8c.zip chromium_src-4a842345af02ac3ae84015683383539a84f66d8c.tar.gz chromium_src-4a842345af02ac3ae84015683383539a84f66d8c.tar.bz2 |
Support for using OS-native certificates for SSL client
auth.
Known Limitations:
- Only SSL3/TLS1.0 handshakes are supported. It's unlikely
SSLv2 will/should ever be implemented. NSS does not yet
support TLS1.1/1.2.
- On Windows, only CryptoAPI keys are supported. Keys that
can only be accessed via CNG will fail.
Technical Notes:
Windows:
- Only the AT_KEYEXCHANGE key is used, per
http://msdn.microsoft.com/en-us/library/aa387461(VS.85).aspx
- CryptSetHashParam is used to directly set the hash value.
This *should* be supported by all CSPs that are compatible
with RSA/SChannel, AFAICT, but testing is needed.
NSS:
- The define NSS_PLATFORM_CLIENT_AUTH is used to guard all
of the new/patched code. The primary implementation
details are in sslplatf.c.
Patch author: Ryan Sleevi <rsleevi@chromium.org>
Original review URL: http://codereview.chromium.org/2828002
BUG=148,37560,45369
TEST=Attempt to authenticate with a site that requires SSL
client authentication (e.g., https://foaf.me/simpleLogin.php
with a FOAF+SSL client certificate).
Review URL: http://codereview.chromium.org/3455019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@65064 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/socket/client_socket_factory.cc | 7 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss.cc | 185 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss.h | 8 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss_factory.cc | 12 | ||||
-rw-r--r-- | net/third_party/nss/ssl.gyp | 14 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl.h | 29 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3con.c | 103 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3ext.c | 2 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslauth.c | 22 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslimpl.h | 80 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslnonce.c | 5 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslplatf.c | 561 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslsnce.c | 5 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslsock.c | 14 |
14 files changed, 1008 insertions, 39 deletions
diff --git a/net/socket/client_socket_factory.cc b/net/socket/client_socket_factory.cc index 90bcdc5..9a7decf 100644 --- a/net/socket/client_socket_factory.cc +++ b/net/socket/client_socket_factory.cc @@ -14,7 +14,6 @@ #elif defined(USE_NSS) #include "net/socket/ssl_client_socket_nss.h" #elif defined(OS_MACOSX) -#include "net/socket/ssl_client_socket_mac.h" #include "net/socket/ssl_client_socket_nss.h" #endif #include "net/socket/ssl_host_info.h" @@ -38,12 +37,6 @@ SSLClientSocket* DefaultSSLClientSocketFactory( return new SSLClientSocketNSS(transport_socket, hostname, ssl_config, shi.release()); #elif defined(OS_MACOSX) - // TODO(wtc): SSLClientSocketNSS can't do SSL client authentication using - // Mac OS X CDSA/CSSM yet (http://crbug.com/45369), so fall back on - // SSLClientSocketMac. - if (ssl_config.send_client_cert) - return new SSLClientSocketMac(transport_socket, hostname, ssl_config); - return new SSLClientSocketNSS(transport_socket, hostname, ssl_config, shi.release()); #else diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index df2ac6a..a26e77d4 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -50,6 +50,9 @@ #if defined(USE_SYSTEM_SSL) #include <dlfcn.h> #endif +#if defined(OS_MACOSX) +#include <Security/Security.h> +#endif #include <certdb.h> #include <hasht.h> #include <keyhi.h> @@ -214,6 +217,8 @@ int MapNSPRError(PRErrorCode err) { return ERR_INVALID_ARGUMENT; case PR_END_OF_FILE_ERROR: return ERR_CONNECTION_CLOSED; + case PR_NOT_IMPLEMENTED_ERROR: + return ERR_NOT_IMPLEMENTED; case SEC_ERROR_INVALID_ARGS: return ERR_INVALID_ARGUMENT; @@ -800,7 +805,12 @@ int SSLClientSocketNSS::InitializeSSLOptions() { return ERR_UNEXPECTED; } +#if defined(NSS_PLATFORM_CLIENT_AUTH) + rv = SSL_GetPlatformClientAuthDataHook(nss_fd_, PlatformClientAuthHandler, + this); +#else rv = SSL_GetClientAuthDataHook(nss_fd_, ClientAuthHandler, this); +#endif if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_GetClientAuthDataHook", ""); return ERR_UNEXPECTED; @@ -1364,6 +1374,8 @@ static PRErrorCode MapErrorToNSS(int result) { case ERR_NETWORK_ACCESS_DENIED: // For connect, this could be mapped to PR_ADDRESS_NOT_SUPPORTED_ERROR. return PR_NO_ACCESS_RIGHTS_ERROR; + case ERR_NOT_IMPLEMENTED: + return PR_NOT_IMPLEMENTED_ERROR; case ERR_INTERNET_DISCONNECTED: // Equivalent to ENETDOWN. return PR_NETWORK_UNREACHABLE_ERROR; // Best approximation. case ERR_CONNECTION_TIMED_OUT: @@ -1650,29 +1662,69 @@ SECStatus SSLClientSocketNSS::OwnAuthCertHandler(void* arg, return SECSuccess; } +#if defined(NSS_PLATFORM_CLIENT_AUTH) // static // NSS calls this if a client certificate is needed. -// Based on Mozilla's NSS_GetClientAuthData. -SECStatus SSLClientSocketNSS::ClientAuthHandler( +SECStatus SSLClientSocketNSS::PlatformClientAuthHandler( void* arg, PRFileDesc* socket, CERTDistNames* ca_names, - CERTCertificate** result_certificate, - SECKEYPrivateKey** result_private_key) { - // NSS passes a null ca_names if SSL 2.0 is used. Just fail rather than - // trying to make this work, as we plan to remove SSL 2.0 support soon. - if (!ca_names) - return SECFailure; - + CERTCertList** result_certs, + void** result_private_key) { SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg); that->client_auth_cert_needed_ = !that->ssl_config_.send_client_cert; - #if defined(OS_WIN) if (that->ssl_config_.send_client_cert) { - // TODO(wtc): SSLClientSocketNSS can't do SSL client authentication using - // CryptoAPI yet (http://crbug.com/37560), so client_cert must be NULL. - DCHECK(!that->ssl_config_.client_cert); + if (that->ssl_config_.client_cert) { + PCCERT_CONTEXT cert_context = + that->ssl_config_.client_cert->os_cert_handle(); + HCRYPTPROV provider = NULL; + DWORD key_spec = AT_KEYEXCHANGE; + BOOL must_free = FALSE; + BOOL acquired_key = CryptAcquireCertificatePrivateKey( + cert_context, + CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_COMPARE_KEY_FLAG, + NULL, &provider, &key_spec, &must_free); + if (acquired_key && provider) { + DCHECK_NE(key_spec, CERT_NCRYPT_KEY_SPEC); + + // The certificate cache may have been updated/used, in which case, + // duplicate the existing handle, since NSS will free it when no + // longer in use. + if (!must_free) + CryptContextAddRef(provider, NULL, 0); + + SECItem der_cert; + der_cert.type = siDERCertBuffer; + der_cert.data = cert_context->pbCertEncoded; + der_cert.len = cert_context->cbCertEncoded; + + // TODO(rsleevi): Error checking for NSS allocation errors. + *result_certs = CERT_NewCertList(); + CERTCertDBHandle* db_handle = CERT_GetDefaultCertDB(); + CERTCertificate* user_cert = CERT_NewTempCertificate( + db_handle, &der_cert, NULL, PR_FALSE, PR_TRUE); + CERT_AddCertToListTail(*result_certs, user_cert); + + // Add the intermediates. + X509Certificate::OSCertHandles intermediates = + that->ssl_config_.client_cert->GetIntermediateCertificates(); + for (X509Certificate::OSCertHandles::const_iterator it = + intermediates.begin(); it != intermediates.end(); ++it) { + der_cert.data = (*it)->pbCertEncoded; + der_cert.len = (*it)->cbCertEncoded; + + CERTCertificate* intermediate = CERT_NewTempCertificate( + db_handle, &der_cert, NULL, PR_FALSE, PR_TRUE); + CERT_AddCertToListTail(*result_certs, intermediate); + } + // TODO(wtc): |key_spec| should be passed along with |provider|. + *result_private_key = reinterpret_cast<void*>(provider); + return SECSuccess; + } + LOG(WARNING) << "Client cert found without private key"; + } // Send no client certificate. return SECFailure; } @@ -1733,11 +1785,34 @@ SECStatus SSLClientSocketNSS::ClientAuthHandler( NOTREACHED(); continue; } + + // Copy the rest of the chain to our own store as well. Copying the chain + // stops gracefully if an error is encountered, with the partial chain + // being used as the intermediates, rather than failing to consider the + // client certificate. + net::X509Certificate::OSCertHandles intermediates; + for (DWORD i = 1; i < chain_context->rgpChain[0]->cElement; i++) { + PCCERT_CONTEXT intermediate_copy; + ok = CertAddCertificateContextToStore(X509Certificate::cert_store(), + chain_context->rgpChain[0]->rgpElement[i]->pCertContext, + CERT_STORE_ADD_USE_EXISTING, &intermediate_copy); + if (!ok) { + NOTREACHED(); + break; + } + intermediates.push_back(intermediate_copy); + } + scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle( cert_context2, X509Certificate::SOURCE_LONE_CERT_IMPORT, - X509Certificate::OSCertHandles()); - X509Certificate::FreeOSCertHandle(cert_context2); + intermediates); that->client_certs_.push_back(cert); + + X509Certificate::FreeOSCertHandle(cert_context2); + for (net::X509Certificate::OSCertHandles::iterator it = + intermediates.begin(); it != intermediates.end(); ++it) { + net::X509Certificate::FreeOSCertHandle(*it); + } } BOOL ok = CertCloseStore(my_cert_store, CERT_CLOSE_STORE_CHECK_FLAG); @@ -1748,9 +1823,63 @@ SECStatus SSLClientSocketNSS::ClientAuthHandler( return SECWouldBlock; #elif defined(OS_MACOSX) if (that->ssl_config_.send_client_cert) { - // TODO(wtc): SSLClientSocketNSS can't do SSL client authentication using - // CDSA/CSSM yet (http://crbug.com/45369), so client_cert must be NULL. - DCHECK(!that->ssl_config_.client_cert); + if (that->ssl_config_.client_cert) { + OSStatus os_error = noErr; + SecIdentityRef identity = NULL; + SecKeyRef private_key = NULL; + CFArrayRef chain = + that->ssl_config_.client_cert->CreateClientCertificateChain(); + if (chain) { + identity = reinterpret_cast<SecIdentityRef>( + const_cast<void*>(CFArrayGetValueAtIndex(chain, 0))); + } + if (identity) + os_error = SecIdentityCopyPrivateKey(identity, &private_key); + + if (chain && identity && os_error == noErr) { + // TODO(rsleevi): Error checking for NSS allocation errors. + *result_certs = CERT_NewCertList(); + *result_private_key = reinterpret_cast<void*>(private_key); + + for (CFIndex i = 0; i < CFArrayGetCount(chain); ++i) { + CSSM_DATA cert_data; + SecCertificateRef cert_ref; + if (i == 0) { + cert_ref = that->ssl_config_.client_cert->os_cert_handle(); + } else { + cert_ref = reinterpret_cast<SecCertificateRef>( + const_cast<void*>(CFArrayGetValueAtIndex(chain, i))); + } + os_error = SecCertificateGetData(cert_ref, &cert_data); + if (os_error != noErr) + break; + + SECItem der_cert; + der_cert.type = siDERCertBuffer; + der_cert.data = cert_data.Data; + der_cert.len = cert_data.Length; + CERTCertificate* nss_cert = CERT_NewTempCertificate( + CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE); + CERT_AddCertToListTail(*result_certs, nss_cert); + } + } + if (os_error == noErr) { + CFRelease(chain); + return SECSuccess; + } + LOG(WARNING) << "Client cert found, but could not be used: " + << os_error; + if (*result_certs) { + CERT_DestroyCertList(*result_certs); + *result_certs = NULL; + } + if (*result_private_key) + *result_private_key = NULL; + if (private_key) + CFRelease(private_key); + if (chain) + CFRelease(chain); + } // Send no client certificate. return SECFailure; } @@ -1778,6 +1907,24 @@ SECStatus SSLClientSocketNSS::ClientAuthHandler( // handshake by returning ERR_SSL_CLIENT_AUTH_CERT_NEEDED. return SECWouldBlock; #else + return SECFailure; +#endif +} + +#else // NSS_PLATFORM_CLIENT_AUTH + +// 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); + + that->client_auth_cert_needed_ = !that->ssl_config_.send_client_cert; void* wincx = SSL_RevealPinArg(socket); // Second pass: a client certificate should have been selected. @@ -1831,8 +1978,8 @@ SECStatus SSLClientSocketNSS::ClientAuthHandler( // Tell NSS to suspend the client authentication. We will then abort the // handshake by returning ERR_SSL_CLIENT_AUTH_CERT_NEEDED. return SECWouldBlock; -#endif } +#endif // NSS_PLATFORM_CLIENT_AUTH // static // NSS calls this when handshake is completed. diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h index 0acbb2a..821abe4 100644 --- a/net/socket/ssl_client_socket_nss.h +++ b/net/socket/ssl_client_socket_nss.h @@ -123,11 +123,19 @@ class SSLClientSocketNSS : public SSLClientSocket { static SECStatus OwnAuthCertHandler(void* arg, PRFileDesc* socket, PRBool checksig, PRBool is_server); // NSS calls this when client authentication is requested. +#if defined(NSS_PLATFORM_CLIENT_AUTH) + static SECStatus PlatformClientAuthHandler(void* arg, + PRFileDesc* socket, + CERTDistNames* ca_names, + CERTCertList** result_certs, + void** result_private_key); +#else static SECStatus ClientAuthHandler(void* arg, PRFileDesc* socket, CERTDistNames* ca_names, CERTCertificate** result_certificate, SECKEYPrivateKey** result_private_key); +#endif // NSS calls this when handshake is completed. We pass 'this' as the second // argument. static void HandshakeCallback(PRFileDesc* socket, void* arg); diff --git a/net/socket/ssl_client_socket_nss_factory.cc b/net/socket/ssl_client_socket_nss_factory.cc index efa6e23..a4c87fa 100644 --- a/net/socket/ssl_client_socket_nss_factory.cc +++ b/net/socket/ssl_client_socket_nss_factory.cc @@ -4,12 +4,8 @@ #include "net/socket/client_socket_factory.h" -#include "build/build_config.h" #include "net/socket/ssl_client_socket_nss.h" #include "net/socket/ssl_host_info.h" -#if defined(OS_WIN) -#include "net/socket/ssl_client_socket_win.h" -#endif // This file is only used on platforms where NSS is not the system SSL // library. When compiled, this file is the only object module that pulls @@ -24,14 +20,6 @@ SSLClientSocket* SSLClientSocketNSSFactory( const SSLConfig& ssl_config, SSLHostInfo* ssl_host_info) { scoped_ptr<SSLHostInfo> shi(ssl_host_info); - // TODO(wtc): SSLClientSocketNSS can't do SSL client authentication using - // CryptoAPI yet (http://crbug.com/37560), so we fall back on - // SSLClientSocketWin. -#if defined(OS_WIN) - if (ssl_config.send_client_cert) - return new SSLClientSocketWin(transport_socket, hostname, ssl_config); -#endif - return new SSLClientSocketNSS(transport_socket, hostname, ssl_config, shi.release()); } diff --git a/net/third_party/nss/ssl.gyp b/net/third_party/nss/ssl.gyp index a804e92..7a0d21e 100644 --- a/net/third_party/nss/ssl.gyp +++ b/net/third_party/nss/ssl.gyp @@ -53,6 +53,7 @@ 'ssl/sslmutex.c', 'ssl/sslmutex.h', 'ssl/sslnonce.c', + 'ssl/sslplatf.c', 'ssl/sslproto.h', 'ssl/sslreveal.c', 'ssl/sslsecur.c', @@ -83,6 +84,13 @@ 'NO_NSPR_10_SUPPORT', ], 'conditions': [ + [ 'OS=="mac"', { + 'defines': [ + 'XP_UNIX', + 'DARWIN', + 'XP_MACOSX', + ], + }], [ 'OS == "win"', { 'sources!': [ 'ssl/unix_err.c', @@ -122,6 +130,9 @@ 'sources/': [ ['exclude', 'ssl/bodge/'], ], + 'defines': [ + 'NSS_PLATFORM_CLIENT_AUTH', + ], 'dependencies': [ '../../../third_party/zlib/zlib.gyp:zlib', '../../../third_party/nss/nss.gyp:nss', @@ -130,6 +141,9 @@ 'include_dirs': [ 'ssl', ], + 'defines': [ + 'NSS_PLATFORM_CLIENT_AUTH', + ], }, }], ], diff --git a/net/third_party/nss/ssl/ssl.h b/net/third_party/nss/ssl/ssl.h index aac1aae..9d3da0c 100644 --- a/net/third_party/nss/ssl/ssl.h +++ b/net/third_party/nss/ssl/ssl.h @@ -323,6 +323,35 @@ typedef SECStatus (PR_CALLBACK *SSLGetClientAuthData)(void *arg, SSL_IMPORT SECStatus SSL_GetClientAuthDataHook(PRFileDesc *fd, SSLGetClientAuthData f, void *a); +/* + * Prototype for SSL callback to get client auth data from the application, + * when using the underlying platform's cryptographic primitives. Returning + * SECFailure will cause the socket to send no client certificate. + * arg - application passed argument + * caNames - pointer to distinguished names of CAs that the server likes + * pRetCerts - pointer to pointer to list of certs, with the first being + * the client cert, and any following being used for chain + * building + * pRetKey - pointer to native key pointer, for return of key + * - Windows: pointer to HCRYPTPROV + * - Mac OS X: pointer to SecKeyRef + */ +typedef SECStatus (PR_CALLBACK *SSLGetPlatformClientAuthData)(void *arg, + PRFileDesc *fd, + CERTDistNames *caNames, + CERTCertList **pRetCerts,/*return */ + void **pRetKey);/* return */ + +/* + * Set the client side callback for SSL to retrieve user's private key + * and certificate. + * fd - the file descriptor for the connection in question + * f - the application's callback that delivers the key and cert + * a - application specific data + */ +SSL_IMPORT SECStatus +SSL_GetPlatformClientAuthDataHook(PRFileDesc *fd, + SSLGetPlatformClientAuthData f, void *a); /* ** SNI extension processing callback function. diff --git a/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c index 9343b2b..d3d2727 100644 --- a/net/third_party/nss/ssl/ssl3con.c +++ b/net/third_party/nss/ssl/ssl3con.c @@ -2011,6 +2011,12 @@ ssl3_ComputeRecordMAC( static PRBool ssl3_ClientAuthTokenPresent(sslSessionID *sid) { +#ifdef NSS_PLATFORM_CLIENT_AUTH + if (!sid || !sid->u.ssl3.clPlatformAuthValid) { + return PR_TRUE; + } + return ssl_PlatformAuthTokenPresent(&sid->u.ssl3.clPlatformAuthInfo); +#else PK11SlotInfo *slot = NULL; PRBool isPresent = PR_TRUE; @@ -2034,6 +2040,7 @@ ssl3_ClientAuthTokenPresent(sslSessionID *sid) { PK11_FreeSlot(slot); } return isPresent; +#endif /* NSS_PLATFORM_CLIENT_AUTH */ } SECStatus @@ -4827,6 +4834,20 @@ ssl3_SendCertificateVerify(sslSocket *ss) } isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0); +#ifdef NSS_PLATFORM_CLIENT_AUTH + rv = ssl3_PlatformSignHashes(&hashes, ss->ssl3.platformClientKey, + &buf, isTLS); + if (rv == SECSuccess) { + sslSessionID * sid = ss->sec.ci.sid; + ssl_GetPlatformAuthInfoForKey(ss->ssl3.platformClientKey, + &sid->u.ssl3.clPlatformAuthInfo); + sid->u.ssl3.clPlatformAuthValid = PR_TRUE; + } + if (ss->ssl3.hs.kea_def->exchKeyType == kt_rsa) { + ssl_FreePlatformKey(ss->ssl3.platformClientKey); + ss->ssl3.platformClientKey = (PlatformKey)NULL; + } +#else /* NSS_PLATFORM_CLIENT_AUTH */ rv = ssl3_SignHashes(&hashes, ss->ssl3.clientPrivateKey, &buf, isTLS); if (rv == SECSuccess) { PK11SlotInfo * slot; @@ -4851,6 +4872,7 @@ ssl3_SendCertificateVerify(sslSocket *ss) SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); ss->ssl3.clientPrivateKey = NULL; } +#endif /* NSS_PLATFORM_CLIENT_AUTH */ if (rv != SECSuccess) { goto done; /* err code was set by ssl3_SignHashes */ } @@ -5481,6 +5503,10 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length) SSL3AlertDescription desc = illegal_parameter; SECItem cert_types = {siBuffer, NULL, 0}; CERTDistNames ca_list; +#ifdef NSS_PLATFORM_CLIENT_AUTH + CERTCertList * platform_cert_list = NULL; + CERTCertListNode * certNode = NULL; +#endif /* NSS_PLATFORM_CLIENT_AUTH */ SSL_TRC(3, ("%d: SSL3[%d]: handle certificate_request handshake", SSL_GETPID(), ss->fd)); @@ -5507,6 +5533,12 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length) SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); ss->ssl3.clientPrivateKey = NULL; } +#ifdef NSS_PLATFORM_CLIENT_AUTH + if (ss->ssl3.platformClientKey) { + ssl_FreePlatformKey(ss->ssl3.platformClientKey); + ss->ssl3.platformClientKey = (PlatformKey)NULL; + } +#endif /* NSS_PLATFORM_CLIENT_AUTH */ isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0); rv = ssl3_ConsumeHandshakeVariable(ss, &cert_types, 1, &b, &length); @@ -5573,6 +5605,18 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length) desc = no_certificate; ss->ssl3.hs.ws = wait_hello_done; +#ifdef NSS_PLATFORM_CLIENT_AUTH + if (ss->getPlatformClientAuthData == NULL) { + rv = SECFailure; /* force it to send a no_certificate alert */ + } else { + /* XXX Should pass cert_types in this call!! */ + rv = (SECStatus)(*ss->getPlatformClientAuthData)( + ss->getPlatformClientAuthDataArg, + ss->fd, &ca_list, + &platform_cert_list, + (void**)&ss->ssl3.platformClientKey); + } +#else if (ss->getClientAuthData == NULL) { rv = SECFailure; /* force it to send a no_certificate alert */ } else { @@ -5582,12 +5626,51 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length) &ss->ssl3.clientCertificate, &ss->ssl3.clientPrivateKey); } +#endif /* NSS_PLATFORM_CLIENT_AUTH */ switch (rv) { case SECWouldBlock: /* getClientAuthData has put up a dialog box. */ ssl_SetAlwaysBlock(ss); break; /* not an error */ case SECSuccess: +#ifdef NSS_PLATFORM_CLIENT_AUTH + if (!platform_cert_list || CERT_LIST_EMPTY(platform_cert_list) || + !ss->ssl3.platformClientKey) { + if (platform_cert_list) { + CERT_DestroyCertList(platform_cert_list); + platform_cert_list = NULL; + } + if (ss->ssl3.platformClientKey) { + ssl_FreePlatformKey(ss->ssl3.platformClientKey); + ss->ssl3.platformClientKey = (PlatformKey)NULL; + } + goto send_no_certificate; + } + + certNode = CERT_LIST_HEAD(platform_cert_list); + ss->ssl3.clientCertificate = CERT_DupCertificate(certNode->cert); + + /* Setting ssl3.clientCertChain non-NULL will cause + * ssl3_HandleServerHelloDone to call SendCertificate. + * Note: clientCertChain should include the EE cert as + * clientCertificate is ignored during the actual sending + */ + ss->ssl3.clientCertChain = + hack_NewCertificateListFromCertList(platform_cert_list); + CERT_DestroyCertList(platform_cert_list); + platform_cert_list = NULL; + if (ss->ssl3.clientCertChain == NULL) { + if (ss->ssl3.clientCertificate != NULL) { + CERT_DestroyCertificate(ss->ssl3.clientCertificate); + ss->ssl3.clientCertificate = NULL; + } + if (ss->ssl3.platformClientKey) { + ssl_FreePlatformKey(ss->ssl3.platformClientKey); + ss->ssl3.platformClientKey = (PlatformKey)NULL; + } + goto send_no_certificate; + } +#else /* check what the callback function returned */ if ((!ss->ssl3.clientCertificate) || (!ss->ssl3.clientPrivateKey)) { /* we are missing either the key or cert */ @@ -5620,6 +5703,7 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length) } goto send_no_certificate; } +#endif /* NSS_PLATFORM_CLIENT_AUTH */ break; /* not an error */ case SECFailure: @@ -5650,6 +5734,10 @@ loser: done: if (arena != NULL) PORT_FreeArena(arena, PR_FALSE); +#ifdef NSS_PLATFORM_CLIENT_AUTH + if (platform_cert_list) + CERT_DestroyCertList(platform_cert_list); +#endif return rv; } @@ -5758,6 +5846,16 @@ ssl3_HandleServerHelloDone(sslSocket *ss) goto loser; /* error code is set. */ } } else +#ifdef NSS_PLATFORM_CLIENT_AUTH + if (ss->ssl3.clientCertChain != NULL && + ss->ssl3.platformClientKey) { + send_verify = PR_TRUE; + rv = ssl3_SendCertificate(ss); + if (rv != SECSuccess) { + goto loser; /* error code is set. */ + } + } +#else if (ss->ssl3.clientCertChain != NULL && ss->ssl3.clientPrivateKey != NULL) { send_verify = PR_TRUE; @@ -5766,6 +5864,7 @@ ssl3_HandleServerHelloDone(sslSocket *ss) goto loser; /* error code is set. */ } } +#endif /* NSS_PLATFORM_CLIENT_AUTH */ rv = ssl3_SendClientKeyExchange(ss); if (rv != SECSuccess) { @@ -9626,6 +9725,10 @@ ssl3_DestroySSL3Info(sslSocket *ss) if (ss->ssl3.clientPrivateKey != NULL) SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); +#ifdef NSS_PLATFORM_CLIENT_AUTH + if (ss->ssl3.platformClientKey) + ssl_FreePlatformKey(ss->ssl3.platformClientKey); +#endif /* NSS_PLATFORM_CLIENT_AUTH */ if (ss->ssl3.peerCertArena != NULL) ssl3_CleanupPeerCerts(ss); diff --git a/net/third_party/nss/ssl/ssl3ext.c b/net/third_party/nss/ssl/ssl3ext.c index a7ae062..f044e1c 100644 --- a/net/third_party/nss/ssl/ssl3ext.c +++ b/net/third_party/nss/ssl/ssl3ext.c @@ -46,8 +46,8 @@ #include "nssrenam.h" #include "nss.h" #include "ssl.h" -#include "sslproto.h" #include "sslimpl.h" +#include "sslproto.h" #include "pk11pub.h" #include "blapi.h" #include "prinit.h" diff --git a/net/third_party/nss/ssl/sslauth.c b/net/third_party/nss/ssl/sslauth.c index e14bcc1..3f4924d 100644 --- a/net/third_party/nss/ssl/sslauth.c +++ b/net/third_party/nss/ssl/sslauth.c @@ -252,6 +252,28 @@ SSL_GetClientAuthDataHook(PRFileDesc *s, SSLGetClientAuthData func, return SECSuccess; } +#ifdef NSS_PLATFORM_CLIENT_AUTH +/* NEED LOCKS IN HERE. */ +SECStatus +SSL_GetPlatformClientAuthDataHook(PRFileDesc *s, + SSLGetPlatformClientAuthData func, + void *arg) +{ + sslSocket *ss; + + ss = ssl_FindSocket(s); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in GetPlatformClientAuthDataHook", + SSL_GETPID(), s)); + return SECFailure; + } + + ss->getPlatformClientAuthData = func; + ss->getPlatformClientAuthDataArg = arg; + return SECSuccess; +} +#endif /* NSS_PLATFORM_CLIENT_AUTH */ + /* NEED LOCKS IN HERE. */ SECStatus SSL_SetPKCS11PinArg(PRFileDesc *s, void *arg) diff --git a/net/third_party/nss/ssl/sslimpl.h b/net/third_party/nss/ssl/sslimpl.h index f708696..b84511b 100644 --- a/net/third_party/nss/ssl/sslimpl.h +++ b/net/third_party/nss/ssl/sslimpl.h @@ -65,6 +65,15 @@ #include "sslt.h" /* for some formerly private types, now public */ +#ifdef NSS_PLATFORM_CLIENT_AUTH +#if defined(XP_WIN32) +#include <windows.h> +#include <wincrypt.h> +#elif defined(XP_MACOSX) +#include <Security/Security.h> +#endif +#endif + /* to make some of these old enums public without namespace pollution, ** it was necessary to prepend ssl_ to the names. ** These #defines preserve compatibility with the old code here in libssl. @@ -573,6 +582,19 @@ typedef enum { never_cached, #define MAX_PEER_CERT_CHAIN_SIZE 8 +#ifdef NSS_PLATFORM_CLIENT_AUTH +typedef struct { +#if defined(XP_WIN32) + char * provider; + char * container; + DWORD provType; +#elif defined(XP_MACOSX) + SecKeychainRef keychain; + CFDataRef persistentKey; +#endif +} PlatformAuthInfo; +#endif /* NSS_PLATFORM_CLIENT_AUTH */ + struct sslSessionIDStr { sslSessionID * next; /* chain used for client sockets, only */ @@ -657,6 +679,11 @@ struct sslSessionIDStr { char masterValid; char clAuthValid; +#ifdef NSS_PLATFORM_CLIENT_AUTH + PlatformAuthInfo clPlatformAuthInfo; + char clPlatformAuthValid; +#endif /* NSS_PLATFORM_CLIENT_AUTH */ + /* Session ticket if we have one, is sent as an extension in the * ClientHello message. This field is used by clients. */ @@ -816,6 +843,15 @@ const ssl3CipherSuiteDef *suite_def; PRBool nextProtoNego;/* Our peer has sent this extension */ } SSL3HandshakeState; +#ifdef NSS_PLATFORM_CLIENT_AUTH +#if defined(XP_WIN32) +typedef HCRYPTPROV PlatformKey; +#elif defined(XP_MACOSX) +typedef SecKeyRef PlatformKey; +#else +typedef void *PlatformKey; +#endif +#endif /* @@ -839,6 +875,9 @@ struct ssl3StateStr { CERTCertificate * clientCertificate; /* used by client */ SECKEYPrivateKey * clientPrivateKey; /* used by client */ +#ifdef NSS_PLATFORM_CLIENT_AUTH + PlatformKey platformClientKey; /* used by client */ +#endif /* NSS_PLATFORM_CLIENT_AUTH */ CERTCertificateList *clientCertChain; /* used by client */ PRBool sendEmptyCert; /* used by client */ @@ -1100,6 +1139,10 @@ const unsigned char * preferredCipher; void *authCertificateArg; SSLGetClientAuthData getClientAuthData; void *getClientAuthDataArg; +#ifdef NSS_PLATFORM_CLIENT_AUTH + SSLGetPlatformClientAuthData getPlatformClientAuthData; + void *getPlatformClientAuthDataArg; +#endif /* NSS_PLATFORM_CLIENT_AUTH */ SSLSNISocketConfig sniSocketConfig; void *sniSocketConfigArg; SSLBadCertHandler handleBadCert; @@ -1691,6 +1734,43 @@ extern SECStatus ssl_InitSessionCacheLocks(PRBool lazyInit); extern SECStatus ssl_FreeSessionCacheLocks(void); +/***************** platform client auth ****************/ + +#ifdef NSS_PLATFORM_CLIENT_AUTH +// Releases the platform key. +extern void ssl_FreePlatformKey(PlatformKey key); + +// Frees any memory allocated to store a persistent reference to the +// platform key. +extern void ssl_FreePlatformAuthInfo(PlatformAuthInfo* info); + +// Initializes the PlatformAuthInfo to empty/invalid values. +extern void ssl_InitPlatformAuthInfo(PlatformAuthInfo* info); + +// Determine if the given key is still present in the system. This is used +// to check for things like smart cards being ejected after handshaking, +// since no further operations on the key will happen which would detect this. +extern PRBool ssl_PlatformAuthTokenPresent(PlatformAuthInfo* info); + +// Obtain a persistent reference to a key, sufficient for +// ssl_PlatformAuthTokenPresent to determine if the key is still present. +extern void ssl_GetPlatformAuthInfoForKey(PlatformKey key, + PlatformAuthInfo* info); + +// Implement the client CertificateVerify message for SSL3/TLS1.0 +extern SECStatus ssl3_PlatformSignHashes(SSL3Hashes *hash, + PlatformKey key, SECItem *buf, + PRBool isTLS); + +// Converts a CERTCertList* (A collection of CERTCertificates) into a +// CERTCertificateList* (A collection of SECItems), or returns NULL if +// it cannot be converted. +// This is to allow the platform-supplied chain to be created with purely +// public API functions, using the preferred CERTCertList mutators, rather +// pushing this hack to clients. +extern CERTCertificateList* hack_NewCertificateListFromCertList( + CERTCertList* list); +#endif /* NSS_PLATFORM_CLIENT_AUTH */ /********************** misc calls *********************/ diff --git a/net/third_party/nss/ssl/sslnonce.c b/net/third_party/nss/ssl/sslnonce.c index 64adc1f..345f041 100644 --- a/net/third_party/nss/ssl/sslnonce.c +++ b/net/third_party/nss/ssl/sslnonce.c @@ -226,6 +226,11 @@ ssl_DestroySID(sslSessionID *sid) if (sid->u.ssl3.sessionTicket.ticket.data) { SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE); } +#ifdef NSS_PLATFORM_CLIENT_AUTH + if (sid->u.ssl3.clPlatformAuthValid) { + ssl_FreePlatformAuthInfo(&sid->u.ssl3.clPlatformAuthInfo); + } +#endif /* NSS_PLATFORM_CLIENT_AUTH */ PORT_ZFree(sid, sizeof(sslSessionID)); } diff --git a/net/third_party/nss/ssl/sslplatf.c b/net/third_party/nss/ssl/sslplatf.c new file mode 100644 index 0000000..274ea02 --- /dev/null +++ b/net/third_party/nss/ssl/sslplatf.c @@ -0,0 +1,561 @@ +/* + * Platform specific crypto wrappers + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ryan Sleevi <ryan.sleevi@gmail.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* $Id$ */ +#include "ssl.h" +#include "certt.h" +#include "keythi.h" +#include "sslimpl.h" +#include "cryptohi.h" +#include "secitem.h" + +#ifdef NSS_PLATFORM_CLIENT_AUTH +CERTCertificateList* +hack_NewCertificateListFromCertList(CERTCertList* list) +{ + CERTCertificateList * chain = NULL; + PRArenaPool * arena = NULL; + CERTCertListNode * node; + int len; + + if (CERT_LIST_EMPTY(list)) + goto loser; + + arena = PORT_NewArena(4096); + if (arena == NULL) + goto loser; + + for (len = 0, node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list); + len++, node = CERT_LIST_NEXT(node)) { + } + + chain = PORT_ArenaNew(arena, CERTCertificateList); + if (chain == NULL) + goto loser; + + chain->certs = PORT_ArenaNewArray(arena, SECItem, len); + if (!chain->certs) + goto loser; + chain->len = len; + + for (len = 0, node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list); + len++, node = CERT_LIST_NEXT(node)) { + // Check to see if the last cert to be sent is a self-signed cert, + // and if so, omit it from the list of certificates. However, if + // there is only one cert (len == 0), include the cert, as it means + // the EE cert is self-signed. + if (len > 0 && (len == chain->len - 1) && node->cert->isRoot) { + chain->len = len; + break; + } + SECITEM_CopyItem(arena, &chain->certs[len], &node->cert->derCert); + } + + chain->arena = arena; + return chain; + +loser: + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + return NULL; +} + +#if defined(XP_WIN32) +void +ssl_FreePlatformKey(PlatformKey key) +{ + CryptReleaseContext(key, 0); +} + +void +ssl_FreePlatformAuthInfo(PlatformAuthInfo* info) +{ + if (info->provider != NULL) { + PORT_Free(info->provider); + info->provider = NULL; + } + if (info->container != NULL) { + PORT_Free(info->container); + info->container = NULL; + } + info->provType = 0; +} + +void +ssl_InitPlatformAuthInfo(PlatformAuthInfo* info) +{ + info->provider = NULL; + info->container = NULL; + info->provType = 0; +} + +PRBool +ssl_PlatformAuthTokenPresent(PlatformAuthInfo *info) +{ + HCRYPTPROV prov = 0; + + if (!info || !info->provider || !info->container) + return PR_FALSE; + + if (!CryptAcquireContextA(&prov, info->container, info->provider, + info->provType, 0)) + return PR_FALSE; + + CryptReleaseContext(prov, 0); + return PR_TRUE; +} + +void +ssl_GetPlatformAuthInfoForKey(PlatformKey key, + PlatformAuthInfo *info) +{ + DWORD bytesNeeded = 0; + ssl_InitPlatformAuthInfo(info); + bytesNeeded = sizeof(info->provType); + if (!CryptGetProvParam(key, PP_PROVTYPE, (BYTE*)&info->provType, + &bytesNeeded, 0)) + goto error; + + bytesNeeded = 0; + if (!CryptGetProvParam(key, PP_CONTAINER, NULL, &bytesNeeded, 0)) + goto error; + info->container = (char*)PORT_Alloc(bytesNeeded); + if (info->container == NULL) + goto error; + if (!CryptGetProvParam(key, PP_CONTAINER, (BYTE*)info->container, + &bytesNeeded, 0)) + goto error; + + bytesNeeded = 0; + if (!CryptGetProvParam(key, PP_NAME, NULL, &bytesNeeded, 0)) + goto error; + info->provider = (char*)PORT_Alloc(bytesNeeded); + if (info->provider == NULL) + goto error; + if (!CryptGetProvParam(key, PP_NAME, (BYTE*)info->provider, + &bytesNeeded, 0)) + goto error; + + goto done; +error: + ssl_FreePlatformAuthInfo(info); + +done: + return; +} + +SECStatus +ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf, + PRBool isTLS) +{ + SECStatus rv = SECFailure; + PRBool doDerEncode = PR_FALSE; + SECItem hashItem; + /* TODO(rsleevi): Should AT_SIGNATURE also be checked if doing client + * auth? + */ + DWORD keySpec = AT_KEYEXCHANGE; + HCRYPTKEY hKey = 0; + DWORD argLen = 0; + ALG_ID keyAlg = 0; + DWORD signatureLen = 0; + ALG_ID hashAlg = 0; + HCRYPTHASH hHash = 0; + DWORD hashLen = 0; + unsigned int i = 0; + + buf->data = NULL; + if (!CryptGetUserKey(key, keySpec, &hKey)) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + goto done; + } + + argLen = sizeof(keyAlg); + if (!CryptGetKeyParam(hKey, KP_ALGID, (BYTE*)&keyAlg, &argLen, 0)) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + goto done; + } + + switch (keyAlg) { + case CALG_RSA_KEYX: + case CALG_RSA_SIGN: + hashAlg = CALG_SSL3_SHAMD5; + hashItem.data = hash->md5; + hashItem.len = sizeof(SSL3Hashes); + break; + case CALG_DSS_SIGN: + /* TODO: Support CALG_ECDSA once tested */ + case CALG_ECDSA: + if (keyAlg == CALG_ECDSA) { + doDerEncode = PR_TRUE; + } else { + doDerEncode = isTLS; + } + hashAlg = CALG_SHA1; + hashItem.data = hash->sha; + hashItem.len = sizeof(hash->sha); + break; + default: + PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); + goto done; + } + PRINT_BUF(60, (NULL, "hash(es) to be signed", hashItem.data, hashItem.len)); + + if (!CryptCreateHash(key, hashAlg, 0, 0, &hHash)) { + ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE); + goto done; + } + argLen = sizeof(hashLen); + if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&hashLen, &argLen, 0)) { + ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE); + goto done; + } + if (hashLen != hashItem.len) { + ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE); + goto done; + } + if (!CryptSetHashParam(hHash, HP_HASHVAL, (BYTE*)hashItem.data, 0)) { + ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE); + goto done; + } + if (!CryptSignHash(hHash, keySpec, NULL, CRYPT_NOHASHOID, + NULL, &signatureLen) || signatureLen == 0) { + ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE); + goto done; + } + buf->len = signatureLen; + buf->data = (unsigned char *)PORT_Alloc(signatureLen); + if (!buf->data) + goto done; /* error code was set. */ + + if (!CryptSignHash(hHash, keySpec, NULL, CRYPT_NOHASHOID, + (BYTE*)buf->data, &signatureLen)) { + ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE); + goto done; + } + + /* CryptoAPI signs in little-endian, so reverse */ + for (i = 0; i < buf->len / 2; ++i) { + unsigned char tmp = buf->data[i]; + buf->data[i] = buf->data[buf->len - 1 - i]; + buf->data[buf->len - 1 - i] = tmp; + } + if (doDerEncode) { + SECItem derSig = {siBuffer, NULL, 0}; + + /* This also works for an ECDSA signature */ + rv = DSAU_EncodeDerSigWithLen(&derSig, buf, buf->len); + if (rv == SECSuccess) { + PORT_Free(buf->data); /* discard unencoded signature. */ + *buf = derSig; /* give caller encoded signature. */ + } else if (derSig.data) { + PORT_Free(derSig.data); + } + } + + PRINT_BUF(60, (NULL, "signed hashes", buf->data, buf->len)); + rv = SECSuccess; +done: + if (hHash) + CryptDestroyHash(hHash); + if (hKey) + CryptDestroyKey(hKey); + if (rv != SECSuccess && buf->data) { + PORT_Free(buf->data); + buf->data = NULL; + } + return rv; +} + +#elif defined(XP_MACOSX) +#include <Security/cssm.h> + +/* + * In Mac OS X 10.5, these two functions are private but implemented, and + * in Mac OS X 10.6, these are exposed publicly. To compile with the 10.5 + * SDK, we declare them here. + */ +OSStatus SecKeychainItemCreatePersistentReference(SecKeychainItemRef itemRef, CFDataRef *persistentItemRef); +OSStatus SecKeychainItemCopyFromPersistentReference(CFDataRef persistentItemRef, SecKeychainItemRef *itemRef); + +void +ssl_FreePlatformKey(PlatformKey key) +{ + CFRelease(key); +} + +void +ssl_FreePlatformAuthInfo(PlatformAuthInfo* info) +{ + if (info->keychain != NULL) { + CFRelease(info->keychain); + info->keychain = NULL; + } + if (info->persistentKey != NULL) { + CFRelease(info->persistentKey); + info->persistentKey = NULL; + } +} + +void +ssl_InitPlatformAuthInfo(PlatformAuthInfo* info) +{ + info->keychain = NULL; + info->persistentKey = NULL; +} + +PRBool +ssl_PlatformAuthTokenPresent(PlatformAuthInfo* info) +{ + if (!info || !info->keychain || !info->persistentKey) + return PR_FALSE; + + // Not actually interested in the status, but it can be used to make sure + // that the keychain still exists (as smart card ejection will remove + // the keychain) + SecKeychainStatus keychainStatus; + OSStatus rv = SecKeychainGetStatus(info->keychain, &keychainStatus); + if (rv != noErr) + return PR_FALSE; + + // Make sure the individual key still exists within the keychain, if + // the keychain is present + SecKeychainItemRef keychainItem; + rv = SecKeychainItemCopyFromPersistentReference(info->persistentKey, + &keychainItem); + if (rv != noErr) + return PR_FALSE; + + CFRelease(keychainItem); + return PR_TRUE; +} + +void +ssl_GetPlatformAuthInfoForKey(PlatformKey key, + PlatformAuthInfo *info) +{ + SecKeychainItemRef keychainItem = (SecKeychainItemRef)key; + OSStatus rv = SecKeychainItemCopyKeychain(keychainItem, &info->keychain); + if (rv == noErr) { + rv = SecKeychainItemCreatePersistentReference(keychainItem, + &info->persistentKey); + } + if (rv != noErr) { + ssl_FreePlatformAuthInfo(info); + } + return; +} + +SECStatus +ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf, + PRBool isTLS) +{ + SECStatus rv = SECFailure; + PRBool doDerEncode = PR_FALSE; + unsigned int signatureLen; + OSStatus status = noErr; + CSSM_CSP_HANDLE cspHandle = 0; + const CSSM_KEY *cssmKey = NULL; + CSSM_ALGORITHMS sigAlg; + const CSSM_ACCESS_CREDENTIALS * cssmCreds = NULL; + CSSM_RETURN cssmRv; + CSSM_DATA hashData; + CSSM_DATA signatureData; + CSSM_CC_HANDLE cssmSignature = 0; + + buf->data = NULL; + + status = SecKeyGetCSPHandle(key, &cspHandle); + if (status != noErr) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + goto done; + } + + status = SecKeyGetCSSMKey(key, &cssmKey); + if (status != noErr || !cssmKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + goto done; + } + + /* SecKeyGetBlockSize wasn't addeded until OS X 10.6 - but the + * needed information is readily available on the key itself. + */ + signatureLen = (cssmKey->KeyHeader.LogicalKeySizeInBits + 7) / 8; + + if (signatureLen == 0) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + goto done; + } + + buf->len = signatureLen; + buf->data = (unsigned char *)PORT_Alloc(signatureLen); + if (!buf->data) + goto done; /* error code was set. */ + + sigAlg = cssmKey->KeyHeader.AlgorithmId; + switch (sigAlg) { + case CSSM_ALGID_RSA: + hashData.Data = hash->md5; + hashData.Length = sizeof(SSL3Hashes); + break; + case CSSM_ALGID_ECDSA: + case CSSM_ALGID_DSA: + if (sigAlg == CSSM_ALGID_ECDSA) { + doDerEncode = PR_TRUE; + } else { + doDerEncode = isTLS; + } + hashData.Data = hash->sha; + hashData.Length = sizeof(hash->sha); + break; + default: + PORT_SetError(SEC_ERROR_INVALID_KEY); + goto done; + } + PRINT_BUF(60, (NULL, "hash(es) to be signed", hashData.Data, hashData.Length)); + + /* TODO(rsleevi): Should it be kSecCredentialTypeNoUI? In Win32, at least, + * you can prevent the UI by setting the provider handle on the + * certificate to be opened with CRYPT_SILENT, but is there an equivalent? + */ + status = SecKeyGetCredentials(key, CSSM_ACL_AUTHORIZATION_SIGN, + kSecCredentialTypeDefault, &cssmCreds); + if (status != noErr) { + ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE); + goto done; + } + + signatureData.Length = buf->len; + signatureData.Data = (uint8*)buf->data; + + cssmRv = CSSM_CSP_CreateSignatureContext(cspHandle, sigAlg, cssmCreds, + cssmKey, &cssmSignature); + if (cssmRv) { + ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE); + goto done; + } + + /* See "Apple Cryptographic Service Provider Functional Specification" */ + if (cssmKey->KeyHeader.AlgorithmId == CSSM_ALGID_RSA) { + /* To set RSA blinding for RSA keys */ + CSSM_CONTEXT_ATTRIBUTE blindingAttr; + blindingAttr.AttributeType = CSSM_ATTRIBUTE_RSA_BLINDING; + blindingAttr.AttributeLength = sizeof(uint32); + blindingAttr.Attribute.Uint32 = 1; + cssmRv = CSSM_UpdateContextAttributes(cssmSignature, 1, &blindingAttr); + if (cssmRv) { + ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE); + goto done; + } + } + + cssmRv = CSSM_SignData(cssmSignature, &hashData, 1, CSSM_ALGID_NONE, + &signatureData); + if (cssmRv) { + ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE); + goto done; + } + + if (doDerEncode) { + SECItem derSig = {siBuffer, NULL, 0}; + + /* This also works for an ECDSA signature */ + rv = DSAU_EncodeDerSigWithLen(&derSig, buf, buf->len); + if (rv == SECSuccess) { + PORT_Free(buf->data); /* discard unencoded signature. */ + *buf = derSig; /* give caller encoded signature. */ + } else if (derSig.data) { + PORT_Free(derSig.data); + } + } + + PRINT_BUF(60, (NULL, "signed hashes", buf->data, buf->len)); + rv = SECSuccess; +done: + /* cspHandle, cssmKey, and cssmCreds are owned by the SecKeyRef and
+ * should not be freed. When the PlatformKey is freed, they will be
+ * released. + */ + if (cssmSignature) + CSSM_DeleteContext(cssmSignature); + + if (rv != SECSuccess && buf->data) { + PORT_Free(buf->data); + buf->data = NULL; + } + return rv; +} +#else +void +ssl_FreePlatformKey(PlatformKey key) +{ +} + +void +ssl_FreePlatformAuthInfo(PlatformAuthInfo *info) +{ +} + +void +ssl_InitPlatformAuthInfo(PlatformAuthInfo *info) +{ +} + +PRBool +ssl_PlatformAuthTokenPresent(PlatformAuthInfo *info) +{ + return PR_FALSE; +} + +void +ssl_GetPlatformAuthInfoForKey(PlatformKey key, PlatformAuthInfo *info) +{ +} + +SECStatus +ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf, + PRBool isTLS) +{ + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +} +#endif + +#endif /* NSS_PLATFORM_CLIENT_AUTH */ diff --git a/net/third_party/nss/ssl/sslsnce.c b/net/third_party/nss/ssl/sslsnce.c index 6c73f25..4176ac8 100644 --- a/net/third_party/nss/ssl/sslsnce.c +++ b/net/third_party/nss/ssl/sslsnce.c @@ -638,6 +638,11 @@ ConvertToSID(sidCacheEntry * from, to->u.ssl3.clAuthSeries = 0; to->u.ssl3.clAuthValid = PR_FALSE; +#ifdef NSS_PLATFORM_CLIENT_AUTH + ssl_InitPlatformAuthInfo(&to->u.ssl3.clPlatformAuthInfo); + to->u.ssl3.clPlatformAuthValid = PR_FALSE; +#endif /* NSS_PLATFORM_CLIENT_AUTH */ + if (from->u.ssl3.certIndex != -1 && pcce) { SECItem derCert; diff --git a/net/third_party/nss/ssl/sslsock.c b/net/third_party/nss/ssl/sslsock.c index 2898b88..33e7f3e 100644 --- a/net/third_party/nss/ssl/sslsock.c +++ b/net/third_party/nss/ssl/sslsock.c @@ -336,6 +336,10 @@ ssl_DupSocket(sslSocket *os) ss->authCertificateArg = os->authCertificateArg; ss->getClientAuthData = os->getClientAuthData; ss->getClientAuthDataArg = os->getClientAuthDataArg; +#ifdef NSS_PLATFORM_CLIENT_AUTH + ss->getPlatformClientAuthData = os->getPlatformClientAuthData; + ss->getPlatformClientAuthDataArg = os->getPlatformClientAuthDataArg; +#endif ss->sniSocketConfig = os->sniSocketConfig; ss->sniSocketConfigArg = os->sniSocketConfigArg; ss->handleBadCert = os->handleBadCert; @@ -1443,6 +1447,12 @@ SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd) ss->getClientAuthData = sm->getClientAuthData; if (sm->getClientAuthDataArg) ss->getClientAuthDataArg = sm->getClientAuthDataArg; +#ifdef NSS_PLATFORM_CLIENT_AUTH + if (sm->getPlatformClientAuthData) + ss->getPlatformClientAuthData = sm->getPlatformClientAuthData; + if (sm->getPlatformClientAuthDataArg) + ss->getPlatformClientAuthDataArg = sm->getPlatformClientAuthDataArg; +#endif if (sm->sniSocketConfig) ss->sniSocketConfig = sm->sniSocketConfig; if (sm->sniSocketConfigArg) @@ -2456,6 +2466,10 @@ ssl_NewSocket(PRBool makeLocks) ss->sniSocketConfig = NULL; ss->sniSocketConfigArg = NULL; ss->getClientAuthData = NULL; +#ifdef NSS_PLATFORM_CLIENT_AUTH + ss->getPlatformClientAuthData = NULL; + ss->getPlatformClientAuthDataArg = NULL; +#endif /* NSS_PLATFORM_CLIENT_AUTH */ ss->handleBadCert = NULL; ss->badCertArg = NULL; ss->pkcs11PinArg = NULL; |