1 diff --git a/ssl/ssl.h b/ssl/ssl.h
2 index 91a47a6..4e7d52e 100644
5 @@ -543,6 +543,48 @@ typedef SECStatus (PR_CALLBACK *SSLGetClientAuthData)(void *arg,
6 SSL_IMPORT SECStatus SSL_GetClientAuthDataHook(PRFileDesc *fd,
7 SSLGetClientAuthData f, void *a);
10 + * Prototype for SSL callback to get client auth data from the application,
11 + * optionally using the underlying platform's cryptographic primitives.
12 + * To use the platform cryptographic primitives, caNames and pRetCerts
13 + * should be set. To use NSS, pRetNSSCert and pRetNSSKey should be set.
14 + * Returning SECFailure will cause the socket to send no client certificate.
15 + * arg - application passed argument
16 + * caNames - pointer to distinguished names of CAs that the server likes
17 + * pRetCerts - pointer to pointer to list of certs, with the first being
18 + * the client cert, and any following being used for chain
20 + * pRetKey - pointer to native key pointer, for return of key
21 + * - Windows: A pointer to a PCERT_KEY_CONTEXT that was allocated
22 + * via PORT_Alloc(). Ownership of the PCERT_KEY_CONTEXT
23 + * is transferred to NSS, which will free via
25 + * - Mac OS X: A pointer to a SecKeyRef. Ownership is
26 + * transferred to NSS, which will free via CFRelease().
27 + * pRetNSSCert - pointer to pointer to NSS cert, for return of cert.
28 + * pRetNSSKey - pointer to NSS key pointer, for return of key.
30 +typedef SECStatus (PR_CALLBACK *SSLGetPlatformClientAuthData)(void *arg,
32 + CERTDistNames *caNames,
33 + CERTCertList **pRetCerts,/*return */
34 + void **pRetKey,/* return */
35 + CERTCertificate **pRetNSSCert,/*return */
36 + SECKEYPrivateKey **pRetNSSKey);/* return */
39 + * Set the client side callback for SSL to retrieve user's private key
41 + * Note: If a platform client auth callback is set, the callback configured by
42 + * SSL_GetClientAuthDataHook, if any, will not be called.
44 + * fd - the file descriptor for the connection in question
45 + * f - the application's callback that delivers the key and cert
46 + * a - application specific data
49 +SSL_GetPlatformClientAuthDataHook(PRFileDesc *fd,
50 + SSLGetPlatformClientAuthData f, void *a);
53 ** SNI extension processing callback function.
54 diff --git a/ssl/ssl3con.c b/ssl/ssl3con.c
55 index 60af5b0..b9014ef 100644
58 @@ -2503,6 +2503,9 @@ ssl3_ClientAuthTokenPresent(sslSessionID *sid) {
59 PRBool isPresent = PR_TRUE;
61 /* we only care if we are doing client auth */
62 + /* If NSS_PLATFORM_CLIENT_AUTH is defined and a platformClientKey is being
63 + * used, u.ssl3.clAuthValid will be false and this function will always
64 + * return PR_TRUE. */
65 if (!sid || !sid->u.ssl3.clAuthValid) {
68 @@ -6178,25 +6181,36 @@ ssl3_SendCertificateVerify(sslSocket *ss)
70 isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0);
71 isTLS12 = (PRBool)(ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
72 - keyType = ss->ssl3.clientPrivateKey->keyType;
73 - rv = ssl3_SignHashes(&hashes, ss->ssl3.clientPrivateKey, &buf, isTLS);
74 - if (rv == SECSuccess) {
75 - PK11SlotInfo * slot;
76 - sslSessionID * sid = ss->sec.ci.sid;
77 + if (ss->ssl3.platformClientKey) {
78 +#ifdef NSS_PLATFORM_CLIENT_AUTH
79 + keyType = CERT_GetCertKeyType(
80 + &ss->ssl3.clientCertificate->subjectPublicKeyInfo);
81 + rv = ssl3_PlatformSignHashes(
82 + &hashes, ss->ssl3.platformClientKey, &buf, isTLS, keyType);
83 + ssl_FreePlatformKey(ss->ssl3.platformClientKey);
84 + ss->ssl3.platformClientKey = (PlatformKey)NULL;
85 +#endif /* NSS_PLATFORM_CLIENT_AUTH */
87 + keyType = ss->ssl3.clientPrivateKey->keyType;
88 + rv = ssl3_SignHashes(&hashes, ss->ssl3.clientPrivateKey, &buf, isTLS);
89 + if (rv == SECSuccess) {
90 + PK11SlotInfo * slot;
91 + sslSessionID * sid = ss->sec.ci.sid;
93 - /* Remember the info about the slot that did the signing.
94 - ** Later, when doing an SSL restart handshake, verify this.
95 - ** These calls are mere accessors, and can't fail.
97 - slot = PK11_GetSlotFromPrivateKey(ss->ssl3.clientPrivateKey);
98 - sid->u.ssl3.clAuthSeries = PK11_GetSlotSeries(slot);
99 - sid->u.ssl3.clAuthSlotID = PK11_GetSlotID(slot);
100 - sid->u.ssl3.clAuthModuleID = PK11_GetModuleID(slot);
101 - sid->u.ssl3.clAuthValid = PR_TRUE;
102 - PK11_FreeSlot(slot);
103 + /* Remember the info about the slot that did the signing.
104 + ** Later, when doing an SSL restart handshake, verify this.
105 + ** These calls are mere accessors, and can't fail.
107 + slot = PK11_GetSlotFromPrivateKey(ss->ssl3.clientPrivateKey);
108 + sid->u.ssl3.clAuthSeries = PK11_GetSlotSeries(slot);
109 + sid->u.ssl3.clAuthSlotID = PK11_GetSlotID(slot);
110 + sid->u.ssl3.clAuthModuleID = PK11_GetModuleID(slot);
111 + sid->u.ssl3.clAuthValid = PR_TRUE;
112 + PK11_FreeSlot(slot);
114 + SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
115 + ss->ssl3.clientPrivateKey = NULL;
117 - SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
118 - ss->ssl3.clientPrivateKey = NULL;
119 if (rv != SECSuccess) {
120 goto done; /* err code was set by ssl3_SignHashes */
122 @@ -6275,6 +6289,12 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
123 SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
124 ss->ssl3.clientPrivateKey = NULL;
126 +#ifdef NSS_PLATFORM_CLIENT_AUTH
127 + if (ss->ssl3.platformClientKey) {
128 + ssl_FreePlatformKey(ss->ssl3.platformClientKey);
129 + ss->ssl3.platformClientKey = (PlatformKey)NULL;
131 +#endif /* NSS_PLATFORM_CLIENT_AUTH */
133 temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
135 @@ -6904,6 +6924,18 @@ ssl3_ExtractClientKeyInfo(sslSocket *ss,
139 +#if defined(NSS_PLATFORM_CLIENT_AUTH) && defined(_WIN32)
140 + /* If the key is in CAPI, assume conservatively that the CAPI service
141 + * provider may be unable to sign SHA-256 hashes.
143 + if (ss->ssl3.platformClientKey->dwKeySpec != CERT_NCRYPT_KEY_SPEC) {
144 + /* CAPI only supports RSA and DSA signatures, so we don't need to
145 + * check the key type. */
146 + *preferSha1 = PR_TRUE;
149 +#endif /* NSS_PLATFORM_CLIENT_AUTH && _WIN32 */
151 /* If the key is a 1024-bit RSA or DSA key, assume conservatively that
152 * it may be unable to sign SHA-256 hashes. This is the case for older
153 * Estonian ID cards that have 1024-bit RSA keys. In FIPS 186-2 and
154 @@ -7002,6 +7034,10 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
155 SECItem cert_types = {siBuffer, NULL, 0};
156 SECItem algorithms = {siBuffer, NULL, 0};
157 CERTDistNames ca_list;
158 +#ifdef NSS_PLATFORM_CLIENT_AUTH
159 + CERTCertList * platform_cert_list = NULL;
160 + CERTCertListNode * certNode = NULL;
161 +#endif /* NSS_PLATFORM_CLIENT_AUTH */
163 SSL_TRC(3, ("%d: SSL3[%d]: handle certificate_request handshake",
164 SSL_GETPID(), ss->fd));
165 @@ -7017,6 +7053,7 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
166 PORT_Assert(ss->ssl3.clientCertChain == NULL);
167 PORT_Assert(ss->ssl3.clientCertificate == NULL);
168 PORT_Assert(ss->ssl3.clientPrivateKey == NULL);
169 + PORT_Assert(ss->ssl3.platformClientKey == (PlatformKey)NULL);
171 isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0);
172 isTLS12 = (PRBool)(ss->ssl3.prSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
173 @@ -7096,6 +7133,18 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
174 desc = no_certificate;
175 ss->ssl3.hs.ws = wait_hello_done;
177 +#ifdef NSS_PLATFORM_CLIENT_AUTH
178 + if (ss->getPlatformClientAuthData != NULL) {
179 + /* XXX Should pass cert_types and algorithms in this call!! */
180 + rv = (SECStatus)(*ss->getPlatformClientAuthData)(
181 + ss->getPlatformClientAuthDataArg,
183 + &platform_cert_list,
184 + (void**)&ss->ssl3.platformClientKey,
185 + &ss->ssl3.clientCertificate,
186 + &ss->ssl3.clientPrivateKey);
189 if (ss->getClientAuthData != NULL) {
190 /* XXX Should pass cert_types and algorithms in this call!! */
191 rv = (SECStatus)(*ss->getClientAuthData)(ss->getClientAuthDataArg,
192 @@ -7105,12 +7154,55 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
194 rv = SECFailure; /* force it to send a no_certificate alert */
198 case SECWouldBlock: /* getClientAuthData has put up a dialog box. */
199 ssl3_SetAlwaysBlock(ss);
200 break; /* not an error */
203 +#ifdef NSS_PLATFORM_CLIENT_AUTH
204 + if (!platform_cert_list || CERT_LIST_EMPTY(platform_cert_list) ||
205 + !ss->ssl3.platformClientKey) {
206 + if (platform_cert_list) {
207 + CERT_DestroyCertList(platform_cert_list);
208 + platform_cert_list = NULL;
210 + if (ss->ssl3.platformClientKey) {
211 + ssl_FreePlatformKey(ss->ssl3.platformClientKey);
212 + ss->ssl3.platformClientKey = (PlatformKey)NULL;
214 + /* Fall through to NSS client auth check */
216 + certNode = CERT_LIST_HEAD(platform_cert_list);
217 + ss->ssl3.clientCertificate = CERT_DupCertificate(certNode->cert);
219 + /* Setting ssl3.clientCertChain non-NULL will cause
220 + * ssl3_HandleServerHelloDone to call SendCertificate.
221 + * Note: clientCertChain should include the EE cert as
222 + * clientCertificate is ignored during the actual sending
224 + ss->ssl3.clientCertChain =
225 + hack_NewCertificateListFromCertList(platform_cert_list);
226 + CERT_DestroyCertList(platform_cert_list);
227 + platform_cert_list = NULL;
228 + if (ss->ssl3.clientCertChain == NULL) {
229 + if (ss->ssl3.clientCertificate != NULL) {
230 + CERT_DestroyCertificate(ss->ssl3.clientCertificate);
231 + ss->ssl3.clientCertificate = NULL;
233 + if (ss->ssl3.platformClientKey) {
234 + ssl_FreePlatformKey(ss->ssl3.platformClientKey);
235 + ss->ssl3.platformClientKey = (PlatformKey)NULL;
237 + goto send_no_certificate;
239 + if (ss->ssl3.hs.hashType == handshake_hash_single) {
240 + ssl3_DestroyBackupHandshakeHashIfNotNeeded(ss, &algorithms);
242 + break; /* not an error */
244 +#endif /* NSS_PLATFORM_CLIENT_AUTH */
245 /* check what the callback function returned */
246 if ((!ss->ssl3.clientCertificate) || (!ss->ssl3.clientPrivateKey)) {
247 /* we are missing either the key or cert */
248 @@ -7172,6 +7264,10 @@ loser:
251 PORT_FreeArena(arena, PR_FALSE);
252 +#ifdef NSS_PLATFORM_CLIENT_AUTH
253 + if (platform_cert_list)
254 + CERT_DestroyCertList(platform_cert_list);
259 @@ -7288,7 +7384,8 @@ ssl3_SendClientSecondRound(sslSocket *ss)
261 sendClientCert = !ss->ssl3.sendEmptyCert &&
262 ss->ssl3.clientCertChain != NULL &&
263 - ss->ssl3.clientPrivateKey != NULL;
264 + (ss->ssl3.platformClientKey ||
265 + ss->ssl3.clientPrivateKey != NULL);
267 if (!sendClientCert &&
268 ss->ssl3.hs.hashType == handshake_hash_single &&
269 @@ -12148,6 +12245,10 @@ ssl3_DestroySSL3Info(sslSocket *ss)
271 if (ss->ssl3.clientPrivateKey != NULL)
272 SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
273 +#ifdef NSS_PLATFORM_CLIENT_AUTH
274 + if (ss->ssl3.platformClientKey)
275 + ssl_FreePlatformKey(ss->ssl3.platformClientKey);
276 +#endif /* NSS_PLATFORM_CLIENT_AUTH */
278 if (ss->ssl3.peerCertArena != NULL)
279 ssl3_CleanupPeerCerts(ss);
280 diff --git a/ssl/ssl3ext.c b/ssl/ssl3ext.c
281 index 28d21c4..0a2288a 100644
285 #include "nssrenam.h"
288 -#include "sslproto.h"
290 +#include "sslproto.h"
292 #ifdef NO_PKCS11_BYPASS
294 diff --git a/ssl/sslauth.c b/ssl/sslauth.c
295 index ed74d94..7f9c43b 100644
298 @@ -216,6 +216,28 @@ SSL_GetClientAuthDataHook(PRFileDesc *s, SSLGetClientAuthData func,
302 +#ifdef NSS_PLATFORM_CLIENT_AUTH
303 +/* NEED LOCKS IN HERE. */
305 +SSL_GetPlatformClientAuthDataHook(PRFileDesc *s,
306 + SSLGetPlatformClientAuthData func,
311 + ss = ssl_FindSocket(s);
313 + SSL_DBG(("%d: SSL[%d]: bad socket in GetPlatformClientAuthDataHook",
318 + ss->getPlatformClientAuthData = func;
319 + ss->getPlatformClientAuthDataArg = arg;
322 +#endif /* NSS_PLATFORM_CLIENT_AUTH */
324 /* NEED LOCKS IN HERE. */
326 SSL_SetPKCS11PinArg(PRFileDesc *s, void *arg)
327 diff --git a/ssl/sslimpl.h b/ssl/sslimpl.h
328 index 086f6d2..bbc9bd2 100644
333 #include "ssl3prot.h"
336 #include "nssilock.h"
338 #if defined(XP_UNIX) || defined(XP_BEOS)
341 #include "sslt.h" /* for some formerly private types, now public */
343 +#ifdef NSS_PLATFORM_CLIENT_AUTH
344 +#if defined(XP_WIN32)
345 +#include <windows.h>
346 +#include <wincrypt.h>
347 +#elif defined(XP_MACOSX)
348 +#include <Security/Security.h>
352 /* to make some of these old enums public without namespace pollution,
353 ** it was necessary to prepend ssl_ to the names.
354 ** These #defines preserve compatibility with the old code here in libssl.
355 @@ -443,6 +453,14 @@ struct sslGatherStr {
359 +#if defined(NSS_PLATFORM_CLIENT_AUTH) && defined(XP_WIN32)
360 +typedef PCERT_KEY_CONTEXT PlatformKey;
361 +#elif defined(NSS_PLATFORM_CLIENT_AUTH) && defined(XP_MACOSX)
362 +typedef SecKeyRef PlatformKey;
364 +typedef void *PlatformKey;
370 @@ -961,6 +979,10 @@ struct ssl3StateStr {
372 CERTCertificate * clientCertificate; /* used by client */
373 SECKEYPrivateKey * clientPrivateKey; /* used by client */
374 + /* platformClientKey is present even when NSS_PLATFORM_CLIENT_AUTH is not
375 + * defined in order to allow cleaner conditional code.
376 + * At most one of clientPrivateKey and platformClientKey may be set. */
377 + PlatformKey platformClientKey; /* used by client */
378 CERTCertificateList *clientCertChain; /* used by client */
379 PRBool sendEmptyCert; /* used by client */
381 @@ -1223,6 +1245,10 @@ const unsigned char * preferredCipher;
382 void *authCertificateArg;
383 SSLGetClientAuthData getClientAuthData;
384 void *getClientAuthDataArg;
385 +#ifdef NSS_PLATFORM_CLIENT_AUTH
386 + SSLGetPlatformClientAuthData getPlatformClientAuthData;
387 + void *getPlatformClientAuthDataArg;
388 +#endif /* NSS_PLATFORM_CLIENT_AUTH */
389 SSLSNISocketConfig sniSocketConfig;
390 void *sniSocketConfigArg;
391 SSLBadCertHandler handleBadCert;
392 @@ -1863,6 +1889,26 @@ extern SECStatus ssl_InitSessionCacheLocks(PRBool lazyInit);
394 extern SECStatus ssl_FreeSessionCacheLocks(void);
396 +/***************** platform client auth ****************/
398 +#ifdef NSS_PLATFORM_CLIENT_AUTH
399 +// Releases the platform key.
400 +extern void ssl_FreePlatformKey(PlatformKey key);
402 +// Implement the client CertificateVerify message for SSL3/TLS1.0
403 +extern SECStatus ssl3_PlatformSignHashes(SSL3Hashes *hash,
404 + PlatformKey key, SECItem *buf,
405 + PRBool isTLS, KeyType keyType);
407 +// Converts a CERTCertList* (A collection of CERTCertificates) into a
408 +// CERTCertificateList* (A collection of SECItems), or returns NULL if
409 +// it cannot be converted.
410 +// This is to allow the platform-supplied chain to be created with purely
411 +// public API functions, using the preferred CERTCertList mutators, rather
412 +// pushing this hack to clients.
413 +extern CERTCertificateList* hack_NewCertificateListFromCertList(
414 + CERTCertList* list);
415 +#endif /* NSS_PLATFORM_CLIENT_AUTH */
417 /**************** DTLS-specific functions **************/
418 extern void dtls_FreeQueuedMessage(DTLSQueuedMessage *msg);
419 diff --git a/ssl/sslsock.c b/ssl/sslsock.c
420 index 282bb85..6c09992 100644
423 @@ -275,6 +275,10 @@ ssl_DupSocket(sslSocket *os)
424 ss->authCertificateArg = os->authCertificateArg;
425 ss->getClientAuthData = os->getClientAuthData;
426 ss->getClientAuthDataArg = os->getClientAuthDataArg;
427 +#ifdef NSS_PLATFORM_CLIENT_AUTH
428 + ss->getPlatformClientAuthData = os->getPlatformClientAuthData;
429 + ss->getPlatformClientAuthDataArg = os->getPlatformClientAuthDataArg;
431 ss->sniSocketConfig = os->sniSocketConfig;
432 ss->sniSocketConfigArg = os->sniSocketConfigArg;
433 ss->handleBadCert = os->handleBadCert;
434 @@ -1709,6 +1713,12 @@ SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd)
435 ss->getClientAuthData = sm->getClientAuthData;
436 if (sm->getClientAuthDataArg)
437 ss->getClientAuthDataArg = sm->getClientAuthDataArg;
438 +#ifdef NSS_PLATFORM_CLIENT_AUTH
439 + if (sm->getPlatformClientAuthData)
440 + ss->getPlatformClientAuthData = sm->getPlatformClientAuthData;
441 + if (sm->getPlatformClientAuthDataArg)
442 + ss->getPlatformClientAuthDataArg = sm->getPlatformClientAuthDataArg;
444 if (sm->sniSocketConfig)
445 ss->sniSocketConfig = sm->sniSocketConfig;
446 if (sm->sniSocketConfigArg)
447 @@ -2974,6 +2984,10 @@ ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant)
448 ss->sniSocketConfig = NULL;
449 ss->sniSocketConfigArg = NULL;
450 ss->getClientAuthData = NULL;
451 +#ifdef NSS_PLATFORM_CLIENT_AUTH
452 + ss->getPlatformClientAuthData = NULL;
453 + ss->getPlatformClientAuthDataArg = NULL;
454 +#endif /* NSS_PLATFORM_CLIENT_AUTH */
455 ss->handleBadCert = NULL;
456 ss->badCertArg = NULL;
457 ss->pkcs11PinArg = NULL;