diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c --- a/nss/lib/ssl/ssl3con.c 2014-01-17 17:52:00.295082288 -0800 +++ b/nss/lib/ssl/ssl3con.c 2014-01-17 17:52:19.745405758 -0800 @@ -2471,6 +2471,9 @@ ssl3_ClientAuthTokenPresent(sslSessionID PRBool isPresent = PR_TRUE; /* we only care if we are doing client auth */ + /* If NSS_PLATFORM_CLIENT_AUTH is defined and a platformClientKey is being + * used, u.ssl3.clAuthValid will be false and this function will always + * return PR_TRUE. */ if (!sid || !sid->u.ssl3.clAuthValid) { return PR_TRUE; } @@ -6103,25 +6106,36 @@ ssl3_SendCertificateVerify(sslSocket *ss isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0); isTLS12 = (PRBool)(ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2); - keyType = ss->ssl3.clientPrivateKey->keyType; - rv = ssl3_SignHashes(&hashes, ss->ssl3.clientPrivateKey, &buf, isTLS); - if (rv == SECSuccess) { - PK11SlotInfo * slot; - sslSessionID * sid = ss->sec.ci.sid; + if (ss->ssl3.platformClientKey) { +#ifdef NSS_PLATFORM_CLIENT_AUTH + keyType = CERT_GetCertKeyType( + &ss->ssl3.clientCertificate->subjectPublicKeyInfo); + rv = ssl3_PlatformSignHashes( + &hashes, ss->ssl3.platformClientKey, &buf, isTLS, keyType); + ssl_FreePlatformKey(ss->ssl3.platformClientKey); + ss->ssl3.platformClientKey = (PlatformKey)NULL; +#endif /* NSS_PLATFORM_CLIENT_AUTH */ + } else { + keyType = ss->ssl3.clientPrivateKey->keyType; + rv = ssl3_SignHashes(&hashes, ss->ssl3.clientPrivateKey, &buf, isTLS); + if (rv == SECSuccess) { + PK11SlotInfo * slot; + sslSessionID * sid = ss->sec.ci.sid; - /* Remember the info about the slot that did the signing. - ** Later, when doing an SSL restart handshake, verify this. - ** These calls are mere accessors, and can't fail. - */ - slot = PK11_GetSlotFromPrivateKey(ss->ssl3.clientPrivateKey); - sid->u.ssl3.clAuthSeries = PK11_GetSlotSeries(slot); - sid->u.ssl3.clAuthSlotID = PK11_GetSlotID(slot); - sid->u.ssl3.clAuthModuleID = PK11_GetModuleID(slot); - sid->u.ssl3.clAuthValid = PR_TRUE; - PK11_FreeSlot(slot); + /* Remember the info about the slot that did the signing. + ** Later, when doing an SSL restart handshake, verify this. + ** These calls are mere accessors, and can't fail. + */ + slot = PK11_GetSlotFromPrivateKey(ss->ssl3.clientPrivateKey); + sid->u.ssl3.clAuthSeries = PK11_GetSlotSeries(slot); + sid->u.ssl3.clAuthSlotID = PK11_GetSlotID(slot); + sid->u.ssl3.clAuthModuleID = PK11_GetModuleID(slot); + sid->u.ssl3.clAuthValid = PR_TRUE; + PK11_FreeSlot(slot); + } + SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); + ss->ssl3.clientPrivateKey = NULL; } - SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); - ss->ssl3.clientPrivateKey = NULL; if (rv != SECSuccess) { goto done; /* err code was set by ssl3_SignHashes */ } @@ -6200,6 +6214,12 @@ ssl3_HandleServerHello(sslSocket *ss, SS 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 */ temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length); if (temp < 0) { @@ -6827,6 +6847,18 @@ ssl3_ExtractClientKeyInfo(sslSocket *ss, goto done; } +#if defined(NSS_PLATFORM_CLIENT_AUTH) && defined(_WIN32) + /* If the key is in CAPI, assume conservatively that the CAPI service + * provider may be unable to sign SHA-256 hashes. + */ + if (ss->ssl3.platformClientKey->dwKeySpec != CERT_NCRYPT_KEY_SPEC) { + /* CAPI only supports RSA and DSA signatures, so we don't need to + * check the key type. */ + *preferSha1 = PR_TRUE; + goto done; + } +#endif /* NSS_PLATFORM_CLIENT_AUTH && _WIN32 */ + /* If the key is a 1024-bit RSA or DSA key, assume conservatively that * it may be unable to sign SHA-256 hashes. This is the case for older * Estonian ID cards that have 1024-bit RSA keys. In FIPS 186-2 and @@ -6925,6 +6957,10 @@ ssl3_HandleCertificateRequest(sslSocket SECItem cert_types = {siBuffer, NULL, 0}; SECItem algorithms = {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)); @@ -6941,6 +6977,7 @@ ssl3_HandleCertificateRequest(sslSocket PORT_Assert(ss->ssl3.clientCertChain == NULL); PORT_Assert(ss->ssl3.clientCertificate == NULL); PORT_Assert(ss->ssl3.clientPrivateKey == NULL); + PORT_Assert(ss->ssl3.platformClientKey == (PlatformKey)NULL); isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0); isTLS12 = (PRBool)(ss->ssl3.prSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2); @@ -7020,6 +7057,18 @@ ssl3_HandleCertificateRequest(sslSocket desc = no_certificate; ss->ssl3.hs.ws = wait_hello_done; +#ifdef NSS_PLATFORM_CLIENT_AUTH + if (ss->getPlatformClientAuthData != NULL) { + /* XXX Should pass cert_types and algorithms in this call!! */ + rv = (SECStatus)(*ss->getPlatformClientAuthData)( + ss->getPlatformClientAuthDataArg, + ss->fd, &ca_list, + &platform_cert_list, + (void**)&ss->ssl3.platformClientKey, + &ss->ssl3.clientCertificate, + &ss->ssl3.clientPrivateKey); + } else +#endif if (ss->getClientAuthData != NULL) { /* XXX Should pass cert_types and algorithms in this call!! */ rv = (SECStatus)(*ss->getClientAuthData)(ss->getClientAuthDataArg, @@ -7029,12 +7078,55 @@ ssl3_HandleCertificateRequest(sslSocket } else { rv = SECFailure; /* force it to send a no_certificate alert */ } + switch (rv) { case SECWouldBlock: /* getClientAuthData has put up a dialog box. */ ssl3_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; + } + /* Fall through to NSS client auth check */ + } else { + 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; + } + if (ss->ssl3.hs.hashType == handshake_hash_single) { + ssl3_DestroyBackupHandshakeHashIfNotNeeded(ss, &algorithms); + } + break; /* not an error */ + } +#endif /* NSS_PLATFORM_CLIENT_AUTH */ /* check what the callback function returned */ if ((!ss->ssl3.clientCertificate) || (!ss->ssl3.clientPrivateKey)) { /* we are missing either the key or cert */ @@ -7096,6 +7188,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; } @@ -7213,7 +7309,8 @@ ssl3_SendClientSecondRound(sslSocket *ss sendClientCert = !ss->ssl3.sendEmptyCert && ss->ssl3.clientCertChain != NULL && - ss->ssl3.clientPrivateKey != NULL; + (ss->ssl3.platformClientKey || + ss->ssl3.clientPrivateKey != NULL); if (!sendClientCert && ss->ssl3.hs.hashType == handshake_hash_single && @@ -12052,6 +12149,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 -pu a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c --- a/nss/lib/ssl/ssl3ext.c 2014-01-17 17:49:26.072517368 -0800 +++ b/nss/lib/ssl/ssl3ext.c 2014-01-17 17:52:19.745405758 -0800 @@ -10,8 +10,8 @@ #include "nssrenam.h" #include "nss.h" #include "ssl.h" -#include "sslproto.h" #include "sslimpl.h" +#include "sslproto.h" #include "pk11pub.h" #ifdef NO_PKCS11_BYPASS #include "blapit.h" diff -pu a/nss/lib/ssl/sslauth.c b/nss/lib/ssl/sslauth.c --- a/nss/lib/ssl/sslauth.c 2014-01-17 17:49:26.072517368 -0800 +++ b/nss/lib/ssl/sslauth.c 2014-01-17 17:52:19.755405924 -0800 @@ -216,6 +216,28 @@ SSL_GetClientAuthDataHook(PRFileDesc *s, 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 -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h --- a/nss/lib/ssl/ssl.h 2014-01-17 17:49:26.062517203 -0800 +++ b/nss/lib/ssl/ssl.h 2014-01-17 17:52:19.755405924 -0800 @@ -533,6 +533,48 @@ typedef SECStatus (PR_CALLBACK *SSLGetCl SSL_IMPORT SECStatus SSL_GetClientAuthDataHook(PRFileDesc *fd, SSLGetClientAuthData f, void *a); +/* + * Prototype for SSL callback to get client auth data from the application, + * optionally using the underlying platform's cryptographic primitives. + * To use the platform cryptographic primitives, caNames and pRetCerts + * should be set. To use NSS, pRetNSSCert and pRetNSSKey should be set. + * 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: A pointer to a PCERT_KEY_CONTEXT that was allocated + * via PORT_Alloc(). Ownership of the PCERT_KEY_CONTEXT + * is transferred to NSS, which will free via + * PORT_Free(). + * - Mac OS X: A pointer to a SecKeyRef. Ownership is + * transferred to NSS, which will free via CFRelease(). + * pRetNSSCert - pointer to pointer to NSS cert, for return of cert. + * pRetNSSKey - pointer to NSS key pointer, for return of key. + */ +typedef SECStatus (PR_CALLBACK *SSLGetPlatformClientAuthData)(void *arg, + PRFileDesc *fd, + CERTDistNames *caNames, + CERTCertList **pRetCerts,/*return */ + void **pRetKey,/* return */ + CERTCertificate **pRetNSSCert,/*return */ + SECKEYPrivateKey **pRetNSSKey);/* return */ + +/* + * Set the client side callback for SSL to retrieve user's private key + * and certificate. + * Note: If a platform client auth callback is set, the callback configured by + * SSL_GetClientAuthDataHook, if any, will not be called. + * + * 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 -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h --- a/nss/lib/ssl/sslimpl.h 2014-01-17 17:52:00.295082288 -0800 +++ b/nss/lib/ssl/sslimpl.h 2014-01-17 17:52:19.755405924 -0800 @@ -20,6 +20,7 @@ #include "sslerr.h" #include "ssl3prot.h" #include "hasht.h" +#include "keythi.h" #include "nssilock.h" #include "pkcs11t.h" #if defined(XP_UNIX) || defined(XP_BEOS) @@ -31,6 +32,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. @@ -441,6 +451,14 @@ struct sslGatherStr { #define GS_DATA 3 #define GS_PAD 4 +#if defined(NSS_PLATFORM_CLIENT_AUTH) && defined(XP_WIN32) +typedef PCERT_KEY_CONTEXT PlatformKey; +#elif defined(NSS_PLATFORM_CLIENT_AUTH) && defined(XP_MACOSX) +typedef SecKeyRef PlatformKey; +#else +typedef void *PlatformKey; +#endif + /* @@ -953,6 +971,10 @@ struct ssl3StateStr { CERTCertificate * clientCertificate; /* used by client */ SECKEYPrivateKey * clientPrivateKey; /* used by client */ + /* platformClientKey is present even when NSS_PLATFORM_CLIENT_AUTH is not + * defined in order to allow cleaner conditional code. + * At most one of clientPrivateKey and platformClientKey may be set. */ + PlatformKey platformClientKey; /* used by client */ CERTCertificateList *clientCertChain; /* used by client */ PRBool sendEmptyCert; /* used by client */ @@ -1214,6 +1236,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; @@ -1852,6 +1878,26 @@ extern SECStatus ssl_InitSessionCacheLoc extern SECStatus ssl_FreeSessionCacheLocks(void); +/***************** platform client auth ****************/ + +#ifdef NSS_PLATFORM_CLIENT_AUTH +// Releases the platform key. +extern void ssl_FreePlatformKey(PlatformKey key); + +// Implement the client CertificateVerify message for SSL3/TLS1.0 +extern SECStatus ssl3_PlatformSignHashes(SSL3Hashes *hash, + PlatformKey key, SECItem *buf, + PRBool isTLS, KeyType keyType); + +// 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 */ /**************** DTLS-specific functions **************/ extern void dtls_FreeQueuedMessage(DTLSQueuedMessage *msg); diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c --- a/nss/lib/ssl/sslsock.c 2014-01-17 17:49:40.942764689 -0800 +++ b/nss/lib/ssl/sslsock.c 2014-01-17 17:52:19.755405924 -0800 @@ -263,6 +263,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; @@ -1667,6 +1671,12 @@ SSL_ReconfigFD(PRFileDesc *model, PRFile 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) @@ -2921,6 +2931,10 @@ ssl_NewSocket(PRBool makeLocks, SSLProto 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;