| diff --git a/lib/ssl/ssl.h b/lib/ssl/ssl.h |
| index 2a52769..48fa018 100644 |
| --- a/lib/ssl/ssl.h |
| +++ b/lib/ssl/ssl.h |
| @@ -636,6 +636,48 @@ 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, |
| + * 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 --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c |
| index 9aaf601..cc193cd 100644 |
| --- a/lib/ssl/ssl3con.c |
| +++ b/lib/ssl/ssl3con.c |
| @@ -2530,6 +2530,9 @@ ssl3_ClientAuthTokenPresent(sslSessionID *sid) { |
| 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; |
| } |
| @@ -6352,25 +6355,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 */ |
| } |
| @@ -6449,6 +6463,12 @@ ssl3_HandleServerHello(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 */ |
| |
| temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length); |
| if (temp < 0) { |
| @@ -7109,6 +7129,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 |
| @@ -7207,6 +7239,10 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length) |
| 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)); |
| @@ -7222,6 +7258,7 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length) |
| 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); |
| @@ -7301,6 +7338,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) { |
| + /* 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) { |
| PORT_Assert((ss->ssl3.hs.preliminaryInfo & ssl_preinfo_all) == |
| ssl_preinfo_all); |
| @@ -7312,12 +7361,55 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length) |
| } 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 */ |
| @@ -7379,6 +7471,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; |
| } |
| |
| @@ -7497,7 +7593,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 && |
| @@ -12910,6 +13007,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/lib/ssl/ssl3ext.c b/lib/ssl/ssl3ext.c |
| index cf04aba..5661a5c 100644 |
| --- a/lib/ssl/ssl3ext.c |
| +++ b/lib/ssl/ssl3ext.c |
| @@ -11,8 +11,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 --git a/lib/ssl/sslauth.c b/lib/ssl/sslauth.c |
| index b144336..e6981f0 100644 |
| --- a/lib/ssl/sslauth.c |
| +++ b/lib/ssl/sslauth.c |
| @@ -216,6 +216,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/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h |
| index 9dcc29e..94bb9f4 100644 |
| --- a/lib/ssl/sslimpl.h |
| +++ b/lib/ssl/sslimpl.h |
| @@ -21,6 +21,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) |
| @@ -32,6 +33,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. |
| @@ -453,6 +463,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 |
| + |
| |
| |
| /* |
| @@ -974,6 +992,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 */ |
| |
| @@ -1253,6 +1275,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; |
| @@ -1896,6 +1922,26 @@ 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); |
| + |
| +// 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 --git a/lib/ssl/sslsock.c b/lib/ssl/sslsock.c |
| index f735009..21754d6 100644 |
| --- a/lib/ssl/sslsock.c |
| +++ b/lib/ssl/sslsock.c |
| @@ -300,6 +300,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; |
| @@ -1963,6 +1967,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) |
| @@ -3232,6 +3242,10 @@ ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant) |
| 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; |