1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #include "sslsample.h"
40 /* Declare SSL cipher suites. */
42 int ssl2CipherSuites
[] = {
43 SSL_EN_RC4_128_WITH_MD5
, /* A */
44 SSL_EN_RC4_128_EXPORT40_WITH_MD5
, /* B */
45 SSL_EN_RC2_128_CBC_WITH_MD5
, /* C */
46 SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5
, /* D */
47 SSL_EN_DES_64_CBC_WITH_MD5
, /* E */
48 SSL_EN_DES_192_EDE3_CBC_WITH_MD5
, /* F */
52 int ssl3CipherSuites
[] = {
53 -1, /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA a */
54 -1, /* SSL_FORTEZZA_DMS_WITH_RC4_128_SHA * b */
55 SSL_RSA_WITH_RC4_128_MD5
, /* c */
56 SSL_RSA_WITH_3DES_EDE_CBC_SHA
, /* d */
57 SSL_RSA_WITH_DES_CBC_SHA
, /* e */
58 SSL_RSA_EXPORT_WITH_RC4_40_MD5
, /* f */
59 SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5
, /* g */
60 -1, /* SSL_FORTEZZA_DMS_WITH_NULL_SHA, * h */
61 SSL_RSA_WITH_NULL_MD5
, /* i */
62 SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA
, /* j */
63 SSL_RSA_FIPS_WITH_DES_CBC_SHA
, /* k */
64 TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA
, /* l */
65 TLS_RSA_EXPORT1024_WITH_RC4_56_SHA
, /* m */
69 /**************************************************************************
71 ** SSL callback routines.
73 **************************************************************************/
75 /* Function: char * myPasswd()
77 * Purpose: This function is our custom password handler that is called by
78 * SSL when retreiving private certs and keys from the database. Returns a
79 * pointer to a string that with a password for the database. Password pointer
80 * should point to dynamically allocated memory that will be freed later.
83 myPasswd(PK11SlotInfo
*info
, PRBool retry
, void *arg
)
87 if ( (!retry
) && arg
) {
88 passwd
= PORT_Strdup((char *)arg
);
94 /* Function: SECStatus myAuthCertificate()
96 * Purpose: This function is our custom certificate authentication handler.
98 * Note: This implementation is essentially the same as the default
99 * SSL_AuthCertificate().
102 myAuthCertificate(void *arg
, PRFileDesc
*socket
,
103 PRBool checksig
, PRBool isServer
)
106 SECCertUsage certUsage
;
107 CERTCertificate
* cert
;
112 if (!arg
|| !socket
) {
113 errWarn("myAuthCertificate");
117 /* Define how the cert is being used based upon the isServer flag. */
119 certUsage
= isServer
? certUsageSSLClient
: certUsageSSLServer
;
121 cert
= SSL_PeerCertificate(socket
);
123 pinArg
= SSL_RevealPinArg(socket
);
125 secStatus
= CERT_VerifyCertNow((CERTCertDBHandle
*)arg
,
131 /* If this is a server, we're finished. */
132 if (isServer
|| secStatus
!= SECSuccess
) {
133 CERT_DestroyCertificate(cert
);
137 /* Certificate is OK. Since this is the client side of an SSL
138 * connection, we need to verify that the name field in the cert
139 * matches the desired hostname. This is our defense against
140 * man-in-the-middle attacks.
143 /* SSL_RevealURL returns a hostName, not an URL. */
144 hostName
= SSL_RevealURL(socket
);
146 if (hostName
&& hostName
[0]) {
147 secStatus
= CERT_VerifyCertName(cert
, hostName
);
149 PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN
, 0);
150 secStatus
= SECFailure
;
156 CERT_DestroyCertificate(cert
);
160 /* Function: SECStatus myBadCertHandler()
162 * Purpose: This callback is called when the incoming certificate is not
163 * valid. We define a certain set of parameters that still cause the
164 * certificate to be "valid" for this session, and return SECSuccess to cause
165 * the server to continue processing the request when any of these conditions
166 * are met. Otherwise, SECFailure is return and the server rejects the
170 myBadCertHandler(void *arg
, PRFileDesc
*socket
)
173 SECStatus secStatus
= SECFailure
;
176 /* log invalid cert here */
182 *(PRErrorCode
*)arg
= err
= PORT_GetError();
184 /* If any of the cases in the switch are met, then we will proceed */
185 /* with the processing of the request anyway. Otherwise, the default */
186 /* case will be reached and we will reject the request. */
189 case SEC_ERROR_INVALID_AVA
:
190 case SEC_ERROR_INVALID_TIME
:
191 case SEC_ERROR_BAD_SIGNATURE
:
192 case SEC_ERROR_EXPIRED_CERTIFICATE
:
193 case SEC_ERROR_UNKNOWN_ISSUER
:
194 case SEC_ERROR_UNTRUSTED_CERT
:
195 case SEC_ERROR_CERT_VALID
:
196 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE
:
197 case SEC_ERROR_CRL_EXPIRED
:
198 case SEC_ERROR_CRL_BAD_SIGNATURE
:
199 case SEC_ERROR_EXTENSION_VALUE_INVALID
:
200 case SEC_ERROR_CA_CERT_INVALID
:
201 case SEC_ERROR_CERT_USAGES_INVALID
:
202 case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION
:
203 secStatus
= SECSuccess
;
206 secStatus
= SECFailure
;
210 printf("Bad certificate: %d, %s\n", err
, SSL_Strerror(err
));
215 /* Function: SECStatus ownGetClientAuthData()
217 * Purpose: This callback is used by SSL to pull client certificate
218 * information upon server request.
221 myGetClientAuthData(void *arg
,
223 struct CERTDistNamesStr
*caNames
,
224 struct CERTCertificateStr
**pRetCert
,
225 struct SECKEYPrivateKeyStr
**pRetKey
)
228 CERTCertificate
* cert
;
229 SECKEYPrivateKey
* privKey
;
230 char * chosenNickName
= (char *)arg
;
231 void * proto_win
= NULL
;
232 SECStatus secStatus
= SECFailure
;
234 proto_win
= SSL_RevealPinArg(socket
);
236 if (chosenNickName
) {
237 cert
= PK11_FindCertFromNickname(chosenNickName
, proto_win
);
239 privKey
= PK11_FindKeyByAnyCert(cert
, proto_win
);
241 secStatus
= SECSuccess
;
243 CERT_DestroyCertificate(cert
);
246 } else { /* no nickname given, automatically find the right cert */
247 CERTCertNicknames
*names
;
250 names
= CERT_GetCertNicknames(CERT_GetDefaultCertDB(),
251 SEC_CERT_NICKNAMES_USER
, proto_win
);
254 for(i
= 0; i
< names
->numnicknames
; i
++ ) {
256 cert
= PK11_FindCertFromNickname(names
->nicknames
[i
],
262 /* Only check unexpired certs */
263 if (CERT_CheckCertValidTimes(cert
, PR_Now(), PR_FALSE
)
264 != secCertTimeValid
) {
265 CERT_DestroyCertificate(cert
);
269 secStatus
= NSS_CmpCertChainWCANames(cert
, caNames
);
270 if (secStatus
== SECSuccess
) {
271 privKey
= PK11_FindKeyByAnyCert(cert
, proto_win
);
275 secStatus
= SECFailure
;
279 CERT_FreeNicknames(names
);
283 if (secStatus
== SECSuccess
) {
291 /* Function: SECStatus myHandshakeCallback()
293 * Purpose: Called by SSL to inform application that the handshake is
294 * complete. This function is mostly used on the server side of an SSL
295 * connection, although it is provided for a client as well.
296 * Useful when a non-blocking SSL_ReHandshake or SSL_ResetHandshake
297 * is used to initiate a handshake.
299 * A typical scenario would be:
301 * 1. Server accepts an SSL connection from the client without client auth.
302 * 2. Client sends a request.
303 * 3. Server determines that to service request it needs to authenticate the
304 * client and initiates another handshake requesting client auth.
305 * 4. While handshake is in progress, server can do other work or spin waiting
306 * for the handshake to complete.
307 * 5. Server is notified that handshake has been successfully completed by
308 * the custom handshake callback function and it can service the client's
311 * Note: This function is not implemented in this sample, as we are using
315 myHandshakeCallback(PRFileDesc
*socket
, void *arg
)
317 printf("Handshake has completed, ready to send data securely.\n");
321 /**************************************************************************
323 ** Routines for disabling SSL ciphers.
325 **************************************************************************/
328 disableAllSSLCiphers(void)
330 const PRUint16
*cipherSuites
= SSL_ImplementedCiphers
;
331 int i
= SSL_NumImplementedCiphers
;
334 /* disable all the SSL3 cipher suites */
336 PRUint16 suite
= cipherSuites
[i
];
337 rv
= SSL_CipherPrefSetDefault(suite
, PR_FALSE
);
338 if (rv
!= SECSuccess
) {
339 printf("SSL_CipherPrefSetDefault didn't like value 0x%04x (i = %d)\n",
341 errWarn("SSL_CipherPrefSetDefault");
347 /**************************************************************************
349 ** Error and information routines.
351 **************************************************************************/
354 errWarn(char *function
)
356 PRErrorCode errorNumber
= PR_GetError();
357 const char * errorString
= SSL_Strerror(errorNumber
);
359 printf("Error in function %s: %d\n - %s\n",
360 function
, errorNumber
, errorString
);
364 exitErr(char *function
)
367 /* Exit gracefully. */
368 /* ignoring return value of NSS_Shutdown as code exits with 1*/
369 (void) NSS_Shutdown();
375 printSecurityInfo(PRFileDesc
*fd
)
377 char * cp
; /* bulk cipher name */
378 char * ip
; /* cert issuer DN */
379 char * sp
; /* cert subject DN */
380 int op
; /* High, Low, Off */
381 int kp0
; /* total key bits */
382 int kp1
; /* secret key bits */
384 SSL3Statistics
* ssl3stats
= SSL_GetStatistics();
386 result
= SSL_SecurityStatus(fd
, &op
, &cp
, &kp0
, &kp1
, &ip
, &sp
);
387 if (result
!= SECSuccess
)
389 printf("bulk cipher %s, %d secret key bits, %d key bits, status: %d\n"
391 "issuer DN: %s\n", cp
, kp1
, kp0
, op
, sp
, ip
);
396 printf("%ld cache hits; %ld cache misses, %ld cache not reusable\n",
397 ssl3stats
->hch_sid_cache_hits
, ssl3stats
->hch_sid_cache_misses
,
398 ssl3stats
->hch_sid_cache_not_ok
);
403 /**************************************************************************
404 ** Begin thread management routines and data.
405 **************************************************************************/
408 thread_wrapper(void * arg
)
410 GlobalThreadMgr
*threadMGR
= (GlobalThreadMgr
*)arg
;
411 perThread
*slot
= &threadMGR
->threads
[threadMGR
->index
];
413 /* wait for parent to finish launching us before proceeding. */
414 PR_Lock(threadMGR
->threadLock
);
415 PR_Unlock(threadMGR
->threadLock
);
417 slot
->rv
= (* slot
->startFunc
)(slot
->a
, slot
->b
);
419 PR_Lock(threadMGR
->threadLock
);
420 slot
->running
= rs_zombie
;
422 /* notify the thread exit handler. */
423 PR_NotifyCondVar(threadMGR
->threadEndQ
);
425 PR_Unlock(threadMGR
->threadLock
);
429 launch_thread(GlobalThreadMgr
*threadMGR
,
437 if (!threadMGR
->threadStartQ
) {
438 threadMGR
->threadLock
= PR_NewLock();
439 threadMGR
->threadStartQ
= PR_NewCondVar(threadMGR
->threadLock
);
440 threadMGR
->threadEndQ
= PR_NewCondVar(threadMGR
->threadLock
);
442 PR_Lock(threadMGR
->threadLock
);
443 while (threadMGR
->numRunning
>= MAX_THREADS
) {
444 PR_WaitCondVar(threadMGR
->threadStartQ
, PR_INTERVAL_NO_TIMEOUT
);
446 for (i
= 0; i
< threadMGR
->numUsed
; ++i
) {
447 slot
= &threadMGR
->threads
[i
];
448 if (slot
->running
== rs_idle
)
451 if (i
>= threadMGR
->numUsed
) {
452 if (i
>= MAX_THREADS
) {
453 /* something's really wrong here. */
454 PORT_Assert(i
< MAX_THREADS
);
455 PR_Unlock(threadMGR
->threadLock
);
458 ++(threadMGR
->numUsed
);
459 PORT_Assert(threadMGR
->numUsed
== i
+ 1);
460 slot
= &threadMGR
->threads
[i
];
465 slot
->startFunc
= startFunc
;
467 threadMGR
->index
= i
;
469 slot
->prThread
= PR_CreateThread(PR_USER_THREAD
,
470 thread_wrapper
, threadMGR
,
471 PR_PRIORITY_NORMAL
, PR_GLOBAL_THREAD
,
472 PR_JOINABLE_THREAD
, 0);
474 if (slot
->prThread
== NULL
) {
475 PR_Unlock(threadMGR
->threadLock
);
476 printf("Failed to launch thread!\n");
482 ++(threadMGR
->numRunning
);
483 PR_Unlock(threadMGR
->threadLock
);
484 printf("Launched thread in slot %d \n", threadMGR
->index
);
490 reap_threads(GlobalThreadMgr
*threadMGR
)
495 if (!threadMGR
->threadLock
)
497 PR_Lock(threadMGR
->threadLock
);
498 while (threadMGR
->numRunning
> 0) {
499 PR_WaitCondVar(threadMGR
->threadEndQ
, PR_INTERVAL_NO_TIMEOUT
);
500 for (i
= 0; i
< threadMGR
->numUsed
; ++i
) {
501 slot
= &threadMGR
->threads
[i
];
502 if (slot
->running
== rs_zombie
) {
503 /* Handle cleanup of thread here. */
504 printf("Thread in slot %d returned %d\n", i
, slot
->rv
);
506 /* Now make sure the thread has ended OK. */
507 PR_JoinThread(slot
->prThread
);
508 slot
->running
= rs_idle
;
509 --threadMGR
->numRunning
;
511 /* notify the thread launcher. */
512 PR_NotifyCondVar(threadMGR
->threadStartQ
);
517 /* Safety Sam sez: make sure count is right. */
518 for (i
= 0; i
< threadMGR
->numUsed
; ++i
) {
519 slot
= &threadMGR
->threads
[i
];
520 if (slot
->running
!= rs_idle
) {
521 fprintf(stderr
, "Thread in slot %d is in state %d!\n",
525 PR_Unlock(threadMGR
->threadLock
);
530 destroy_thread_data(GlobalThreadMgr
*threadMGR
)
532 PORT_Memset(threadMGR
->threads
, 0, sizeof(threadMGR
->threads
));
534 if (threadMGR
->threadEndQ
) {
535 PR_DestroyCondVar(threadMGR
->threadEndQ
);
536 threadMGR
->threadEndQ
= NULL
;
538 if (threadMGR
->threadStartQ
) {
539 PR_DestroyCondVar(threadMGR
->threadStartQ
);
540 threadMGR
->threadStartQ
= NULL
;
542 if (threadMGR
->threadLock
) {
543 PR_DestroyLock(threadMGR
->threadLock
);
544 threadMGR
->threadLock
= NULL
;
548 /**************************************************************************
549 ** End thread management routines.
550 **************************************************************************/
553 lockedVars_Init( lockedVars
* lv
)
557 lv
->lock
= PR_NewLock();
558 lv
->condVar
= PR_NewCondVar(lv
->lock
);
562 lockedVars_Destroy( lockedVars
* lv
)
564 PR_DestroyCondVar(lv
->condVar
);
567 PR_DestroyLock(lv
->lock
);
572 lockedVars_WaitForDone(lockedVars
* lv
)
575 while (lv
->count
> 0) {
576 PR_WaitCondVar(lv
->condVar
, PR_INTERVAL_NO_TIMEOUT
);
581 int /* returns count */
582 lockedVars_AddToCount(lockedVars
* lv
, int addend
)
587 rv
= lv
->count
+= addend
;
589 PR_NotifyCondVar(lv
->condVar
);