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");
322 /**************************************************************************
324 ** Routines for disabling SSL ciphers.
326 **************************************************************************/
329 disableAllSSLCiphers(void)
331 const PRUint16
*cipherSuites
= SSL_ImplementedCiphers
;
332 int i
= SSL_NumImplementedCiphers
;
335 /* disable all the SSL3 cipher suites */
337 PRUint16 suite
= cipherSuites
[i
];
338 rv
= SSL_CipherPrefSetDefault(suite
, PR_FALSE
);
339 if (rv
!= SECSuccess
) {
340 printf("SSL_CipherPrefSetDefault didn't like value 0x%04x (i = %d)\n",
342 errWarn("SSL_CipherPrefSetDefault");
348 /**************************************************************************
350 ** Error and information routines.
352 **************************************************************************/
355 errWarn(char *function
)
357 PRErrorCode errorNumber
= PR_GetError();
358 const char * errorString
= SSL_Strerror(errorNumber
);
360 printf("Error in function %s: %d\n - %s\n",
361 function
, errorNumber
, errorString
);
365 exitErr(char *function
)
368 /* Exit gracefully. */
369 /* ignoring return value of NSS_Shutdown as code exits with 1*/
370 (void) NSS_Shutdown();
376 printSecurityInfo(PRFileDesc
*fd
)
378 char * cp
; /* bulk cipher name */
379 char * ip
; /* cert issuer DN */
380 char * sp
; /* cert subject DN */
381 int op
; /* High, Low, Off */
382 int kp0
; /* total key bits */
383 int kp1
; /* secret key bits */
385 SSL3Statistics
* ssl3stats
= SSL_GetStatistics();
387 result
= SSL_SecurityStatus(fd
, &op
, &cp
, &kp0
, &kp1
, &ip
, &sp
);
388 if (result
!= SECSuccess
)
390 printf("bulk cipher %s, %d secret key bits, %d key bits, status: %d\n"
392 "issuer DN: %s\n", cp
, kp1
, kp0
, op
, sp
, ip
);
397 printf("%ld cache hits; %ld cache misses, %ld cache not reusable\n",
398 ssl3stats
->hch_sid_cache_hits
, ssl3stats
->hch_sid_cache_misses
,
399 ssl3stats
->hch_sid_cache_not_ok
);
404 /**************************************************************************
405 ** Begin thread management routines and data.
406 **************************************************************************/
409 thread_wrapper(void * arg
)
411 GlobalThreadMgr
*threadMGR
= (GlobalThreadMgr
*)arg
;
412 perThread
*slot
= &threadMGR
->threads
[threadMGR
->index
];
414 /* wait for parent to finish launching us before proceeding. */
415 PR_Lock(threadMGR
->threadLock
);
416 PR_Unlock(threadMGR
->threadLock
);
418 slot
->rv
= (* slot
->startFunc
)(slot
->a
, slot
->b
);
420 PR_Lock(threadMGR
->threadLock
);
421 slot
->running
= rs_zombie
;
423 /* notify the thread exit handler. */
424 PR_NotifyCondVar(threadMGR
->threadEndQ
);
426 PR_Unlock(threadMGR
->threadLock
);
430 launch_thread(GlobalThreadMgr
*threadMGR
,
438 if (!threadMGR
->threadStartQ
) {
439 threadMGR
->threadLock
= PR_NewLock();
440 threadMGR
->threadStartQ
= PR_NewCondVar(threadMGR
->threadLock
);
441 threadMGR
->threadEndQ
= PR_NewCondVar(threadMGR
->threadLock
);
443 PR_Lock(threadMGR
->threadLock
);
444 while (threadMGR
->numRunning
>= MAX_THREADS
) {
445 PR_WaitCondVar(threadMGR
->threadStartQ
, PR_INTERVAL_NO_TIMEOUT
);
447 for (i
= 0; i
< threadMGR
->numUsed
; ++i
) {
448 slot
= &threadMGR
->threads
[i
];
449 if (slot
->running
== rs_idle
)
452 if (i
>= threadMGR
->numUsed
) {
453 if (i
>= MAX_THREADS
) {
454 /* something's really wrong here. */
455 PORT_Assert(i
< MAX_THREADS
);
456 PR_Unlock(threadMGR
->threadLock
);
459 ++(threadMGR
->numUsed
);
460 PORT_Assert(threadMGR
->numUsed
== i
+ 1);
461 slot
= &threadMGR
->threads
[i
];
466 slot
->startFunc
= startFunc
;
468 threadMGR
->index
= i
;
470 slot
->prThread
= PR_CreateThread(PR_USER_THREAD
,
471 thread_wrapper
, threadMGR
,
472 PR_PRIORITY_NORMAL
, PR_GLOBAL_THREAD
,
473 PR_JOINABLE_THREAD
, 0);
475 if (slot
->prThread
== NULL
) {
476 PR_Unlock(threadMGR
->threadLock
);
477 printf("Failed to launch thread!\n");
483 ++(threadMGR
->numRunning
);
484 PR_Unlock(threadMGR
->threadLock
);
485 printf("Launched thread in slot %d \n", threadMGR
->index
);
491 reap_threads(GlobalThreadMgr
*threadMGR
)
496 if (!threadMGR
->threadLock
)
498 PR_Lock(threadMGR
->threadLock
);
499 while (threadMGR
->numRunning
> 0) {
500 PR_WaitCondVar(threadMGR
->threadEndQ
, PR_INTERVAL_NO_TIMEOUT
);
501 for (i
= 0; i
< threadMGR
->numUsed
; ++i
) {
502 slot
= &threadMGR
->threads
[i
];
503 if (slot
->running
== rs_zombie
) {
504 /* Handle cleanup of thread here. */
505 printf("Thread in slot %d returned %d\n", i
, slot
->rv
);
507 /* Now make sure the thread has ended OK. */
508 PR_JoinThread(slot
->prThread
);
509 slot
->running
= rs_idle
;
510 --threadMGR
->numRunning
;
512 /* notify the thread launcher. */
513 PR_NotifyCondVar(threadMGR
->threadStartQ
);
518 /* Safety Sam sez: make sure count is right. */
519 for (i
= 0; i
< threadMGR
->numUsed
; ++i
) {
520 slot
= &threadMGR
->threads
[i
];
521 if (slot
->running
!= rs_idle
) {
522 fprintf(stderr
, "Thread in slot %d is in state %d!\n",
526 PR_Unlock(threadMGR
->threadLock
);
531 destroy_thread_data(GlobalThreadMgr
*threadMGR
)
533 PORT_Memset(threadMGR
->threads
, 0, sizeof(threadMGR
->threads
));
535 if (threadMGR
->threadEndQ
) {
536 PR_DestroyCondVar(threadMGR
->threadEndQ
);
537 threadMGR
->threadEndQ
= NULL
;
539 if (threadMGR
->threadStartQ
) {
540 PR_DestroyCondVar(threadMGR
->threadStartQ
);
541 threadMGR
->threadStartQ
= NULL
;
543 if (threadMGR
->threadLock
) {
544 PR_DestroyLock(threadMGR
->threadLock
);
545 threadMGR
->threadLock
= NULL
;
549 /**************************************************************************
550 ** End thread management routines.
551 **************************************************************************/
554 lockedVars_Init( lockedVars
* lv
)
558 lv
->lock
= PR_NewLock();
559 lv
->condVar
= PR_NewCondVar(lv
->lock
);
563 lockedVars_Destroy( lockedVars
* lv
)
565 PR_DestroyCondVar(lv
->condVar
);
568 PR_DestroyLock(lv
->lock
);
573 lockedVars_WaitForDone(lockedVars
* lv
)
576 while (lv
->count
> 0) {
577 PR_WaitCondVar(lv
->condVar
, PR_INTERVAL_NO_TIMEOUT
);
582 int /* returns count */
583 lockedVars_AddToCount(lockedVars
* lv
, int addend
)
588 rv
= lv
->count
+= addend
;
590 PR_NotifyCondVar(lv
->condVar
);