summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorwtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-04 16:21:33 +0000
committerwtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-04 16:21:33 +0000
commit4a842345af02ac3ae84015683383539a84f66d8c (patch)
tree9b9b005ddea77d514f2bf0b32f83bf0e5f2bd746 /net
parent1fd26d8cdd166ec84c06bb7d4a8557293428fc0e (diff)
downloadchromium_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.cc7
-rw-r--r--net/socket/ssl_client_socket_nss.cc185
-rw-r--r--net/socket/ssl_client_socket_nss.h8
-rw-r--r--net/socket/ssl_client_socket_nss_factory.cc12
-rw-r--r--net/third_party/nss/ssl.gyp14
-rw-r--r--net/third_party/nss/ssl/ssl.h29
-rw-r--r--net/third_party/nss/ssl/ssl3con.c103
-rw-r--r--net/third_party/nss/ssl/ssl3ext.c2
-rw-r--r--net/third_party/nss/ssl/sslauth.c22
-rw-r--r--net/third_party/nss/ssl/sslimpl.h80
-rw-r--r--net/third_party/nss/ssl/sslnonce.c5
-rw-r--r--net/third_party/nss/ssl/sslplatf.c561
-rw-r--r--net/third_party/nss/ssl/sslsnce.c5
-rw-r--r--net/third_party/nss/ssl/sslsock.c14
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;