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 ***** */
44 extern PRBool dumpChain
;
45 extern void dumpCertChain(CERTCertificate
*, SECCertUsage
);
47 /* Declare SSL cipher suites. */
49 int ssl2CipherSuites
[] = {
50 SSL_EN_RC4_128_WITH_MD5
, /* A */
51 SSL_EN_RC4_128_EXPORT40_WITH_MD5
, /* B */
52 SSL_EN_RC2_128_CBC_WITH_MD5
, /* C */
53 SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5
, /* D */
54 SSL_EN_DES_64_CBC_WITH_MD5
, /* E */
55 SSL_EN_DES_192_EDE3_CBC_WITH_MD5
, /* F */
59 int ssl3CipherSuites
[] = {
60 -1, /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA* a */
61 -1, /* SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, * b */
62 SSL_RSA_WITH_RC4_128_MD5
, /* c */
63 SSL_RSA_WITH_3DES_EDE_CBC_SHA
, /* d */
64 SSL_RSA_WITH_DES_CBC_SHA
, /* e */
65 SSL_RSA_EXPORT_WITH_RC4_40_MD5
, /* f */
66 SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5
, /* g */
67 -1, /* SSL_FORTEZZA_DMS_WITH_NULL_SHA, * h */
68 SSL_RSA_WITH_NULL_MD5
, /* i */
69 SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA
, /* j */
70 SSL_RSA_FIPS_WITH_DES_CBC_SHA
, /* k */
71 TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA
, /* l */
72 TLS_RSA_EXPORT1024_WITH_RC4_56_SHA
, /* m */
73 SSL_RSA_WITH_RC4_128_SHA
, /* n */
74 TLS_DHE_DSS_WITH_RC4_128_SHA
, /* o */
75 SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
, /* p */
76 SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA
, /* q */
77 SSL_DHE_RSA_WITH_DES_CBC_SHA
, /* r */
78 SSL_DHE_DSS_WITH_DES_CBC_SHA
, /* s */
79 TLS_DHE_DSS_WITH_AES_128_CBC_SHA
, /* t */
80 TLS_DHE_RSA_WITH_AES_128_CBC_SHA
, /* u */
81 TLS_RSA_WITH_AES_128_CBC_SHA
, /* v */
82 TLS_DHE_DSS_WITH_AES_256_CBC_SHA
, /* w */
83 TLS_DHE_RSA_WITH_AES_256_CBC_SHA
, /* x */
84 TLS_RSA_WITH_AES_256_CBC_SHA
, /* y */
85 SSL_RSA_WITH_NULL_SHA
, /* z */
89 /**************************************************************************
91 ** SSL callback routines.
93 **************************************************************************/
95 /* Function: char * myPasswd()
97 * Purpose: This function is our custom password handler that is called by
98 * SSL when retreiving private certs and keys from the database. Returns a
99 * pointer to a string that with a password for the database. Password pointer
100 * should point to dynamically allocated memory that will be freed later.
103 myPasswd(PK11SlotInfo
*info
, PRBool retry
, void *arg
)
105 char * passwd
= NULL
;
107 if ( (!retry
) && arg
) {
108 passwd
= PORT_Strdup((char *)arg
);
113 /* Function: SECStatus myAuthCertificate()
115 * Purpose: This function is our custom certificate authentication handler.
117 * Note: This implementation is essentially the same as the default
118 * SSL_AuthCertificate().
121 myAuthCertificate(void *arg
, PRFileDesc
*socket
,
122 PRBool checksig
, PRBool isServer
)
125 SECCertificateUsage certUsage
;
126 CERTCertificate
* cert
;
131 if (!arg
|| !socket
) {
132 errWarn("myAuthCertificate");
136 /* Define how the cert is being used based upon the isServer flag. */
138 certUsage
= isServer
? certificateUsageSSLClient
: certificateUsageSSLServer
;
140 cert
= SSL_PeerCertificate(socket
);
142 pinArg
= SSL_RevealPinArg(socket
);
144 if (dumpChain
== PR_TRUE
) {
145 dumpCertChain(cert
, certUsage
);
148 secStatus
= CERT_VerifyCertificateNow((CERTCertDBHandle
*)arg
,
155 /* If this is a server, we're finished. */
156 if (isServer
|| secStatus
!= SECSuccess
) {
157 SECU_printCertProblems(stderr
, (CERTCertDBHandle
*)arg
, cert
,
158 checksig
, certUsage
, pinArg
, PR_FALSE
);
159 CERT_DestroyCertificate(cert
);
163 /* Certificate is OK. Since this is the client side of an SSL
164 * connection, we need to verify that the name field in the cert
165 * matches the desired hostname. This is our defense against
166 * man-in-the-middle attacks.
169 /* SSL_RevealURL returns a hostName, not an URL. */
170 hostName
= SSL_RevealURL(socket
);
172 if (hostName
&& hostName
[0]) {
173 secStatus
= CERT_VerifyCertName(cert
, hostName
);
175 PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN
, 0);
176 secStatus
= SECFailure
;
182 CERT_DestroyCertificate(cert
);
186 /* Function: SECStatus myBadCertHandler()
188 * Purpose: This callback is called when the incoming certificate is not
189 * valid. We define a certain set of parameters that still cause the
190 * certificate to be "valid" for this session, and return SECSuccess to cause
191 * the server to continue processing the request when any of these conditions
192 * are met. Otherwise, SECFailure is return and the server rejects the
196 myBadCertHandler(void *arg
, PRFileDesc
*socket
)
199 SECStatus secStatus
= SECFailure
;
202 /* log invalid cert here */
208 *(PRErrorCode
*)arg
= err
= PORT_GetError();
210 /* If any of the cases in the switch are met, then we will proceed */
211 /* with the processing of the request anyway. Otherwise, the default */
212 /* case will be reached and we will reject the request. */
215 case SEC_ERROR_INVALID_AVA
:
216 case SEC_ERROR_INVALID_TIME
:
217 case SEC_ERROR_BAD_SIGNATURE
:
218 case SEC_ERROR_EXPIRED_CERTIFICATE
:
219 case SEC_ERROR_UNKNOWN_ISSUER
:
220 case SEC_ERROR_UNTRUSTED_CERT
:
221 case SEC_ERROR_CERT_VALID
:
222 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE
:
223 case SEC_ERROR_CRL_EXPIRED
:
224 case SEC_ERROR_CRL_BAD_SIGNATURE
:
225 case SEC_ERROR_EXTENSION_VALUE_INVALID
:
226 case SEC_ERROR_CA_CERT_INVALID
:
227 case SEC_ERROR_CERT_USAGES_INVALID
:
228 case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION
:
229 secStatus
= SECSuccess
;
232 secStatus
= SECFailure
;
236 fprintf(stderr
, "Bad certificate: %d, %s\n", err
, SECU_Strerror(err
));
241 /* Function: SECStatus ownGetClientAuthData()
243 * Purpose: This callback is used by SSL to pull client certificate
244 * information upon server request.
247 myGetClientAuthData(void *arg
,
249 struct CERTDistNamesStr
*caNames
,
250 struct CERTCertificateStr
**pRetCert
,
251 struct SECKEYPrivateKeyStr
**pRetKey
)
254 CERTCertificate
* cert
;
255 SECKEYPrivateKey
* privKey
;
256 char * chosenNickName
= (char *)arg
;
257 void * proto_win
= NULL
;
258 SECStatus secStatus
= SECFailure
;
260 proto_win
= SSL_RevealPinArg(socket
);
262 if (chosenNickName
) {
263 cert
= PK11_FindCertFromNickname(chosenNickName
, proto_win
);
265 privKey
= PK11_FindKeyByAnyCert(cert
, proto_win
);
267 secStatus
= SECSuccess
;
269 CERT_DestroyCertificate(cert
);
272 } else { /* no nickname given, automatically find the right cert */
273 CERTCertNicknames
*names
;
276 names
= CERT_GetCertNicknames(CERT_GetDefaultCertDB(),
277 SEC_CERT_NICKNAMES_USER
, proto_win
);
280 for(i
= 0; i
< names
->numnicknames
; i
++ ) {
282 cert
= PK11_FindCertFromNickname(names
->nicknames
[i
],
288 /* Only check unexpired certs */
289 if (CERT_CheckCertValidTimes(cert
, PR_Now(), PR_FALSE
)
290 != secCertTimeValid
) {
291 CERT_DestroyCertificate(cert
);
295 secStatus
= NSS_CmpCertChainWCANames(cert
, caNames
);
296 if (secStatus
== SECSuccess
) {
297 privKey
= PK11_FindKeyByAnyCert(cert
, proto_win
);
301 secStatus
= SECFailure
;
303 CERT_DestroyCertificate(cert
);
305 CERT_FreeNicknames(names
);
309 if (secStatus
== SECSuccess
) {
317 /* Function: void myHandshakeCallback()
319 * Purpose: Called by SSL to inform application that the handshake is
320 * complete. This function is mostly used on the server side of an SSL
321 * connection, although it is provided for a client as well.
322 * Useful when a non-blocking SSL_ReHandshake or SSL_ResetHandshake
323 * is used to initiate a handshake.
325 * A typical scenario would be:
327 * 1. Server accepts an SSL connection from the client without client auth.
328 * 2. Client sends a request.
329 * 3. Server determines that to service request it needs to authenticate the
330 * client and initiates another handshake requesting client auth.
331 * 4. While handshake is in progress, server can do other work or spin waiting
332 * for the handshake to complete.
333 * 5. Server is notified that handshake has been successfully completed by
334 * the custom handshake callback function and it can service the client's
337 * Note: This function is not implemented in this sample, as we are using
341 myHandshakeCallback(PRFileDesc
*socket
, void *arg
)
343 fprintf(stderr
,"Handshake Complete: SERVER CONFIGURED CORRECTLY\n");
347 /**************************************************************************
349 ** Routines for disabling SSL ciphers.
351 **************************************************************************/
354 disableAllSSLCiphers(void)
356 const PRUint16
*cipherSuites
= SSL_ImplementedCiphers
;
357 int i
= SSL_NumImplementedCiphers
;
360 /* disable all the SSL3 cipher suites */
362 PRUint16 suite
= cipherSuites
[i
];
363 rv
= SSL_CipherPrefSetDefault(suite
, PR_FALSE
);
364 if (rv
!= SECSuccess
) {
366 "SSL_CipherPrefSetDefault didn't like value 0x%04x (i = %d)\n",
368 errWarn("SSL_CipherPrefSetDefault");
374 /**************************************************************************
376 ** Error and information routines.
378 **************************************************************************/
381 errWarn(char *function
)
383 PRErrorCode errorNumber
= PR_GetError();
384 const char * errorString
= SECU_Strerror(errorNumber
);
386 fprintf(stderr
, "Error in function %s: %d\n - %s\n",
387 function
, errorNumber
, errorString
);
391 exitErr(char *function
)
394 /* Exit gracefully. */
395 /* ignoring return value of NSS_Shutdown as code exits with 1 anyway*/
396 (void) NSS_Shutdown();
402 printSecurityInfo(FILE *outfile
, PRFileDesc
*fd
)
404 char * cp
; /* bulk cipher name */
405 char * ip
; /* cert issuer DN */
406 char * sp
; /* cert subject DN */
407 int op
; /* High, Low, Off */
408 int kp0
; /* total key bits */
409 int kp1
; /* secret key bits */
411 SSL3Statistics
* ssl3stats
= SSL_GetStatistics();
417 result
= SSL_SecurityStatus(fd
, &op
, &cp
, &kp0
, &kp1
, &ip
, &sp
);
418 if (result
!= SECSuccess
)
421 " bulk cipher %s, %d secret key bits, %d key bits, status: %d\n"
422 " subject DN:\n %s\n"
423 " issuer DN:\n %s\n", cp
, kp1
, kp0
, op
, sp
, ip
);
429 " %ld cache hits; %ld cache misses, %ld cache not reusable\n",
430 ssl3stats
->hch_sid_cache_hits
, ssl3stats
->hch_sid_cache_misses
,
431 ssl3stats
->hch_sid_cache_not_ok
);
436 /**************************************************************************
437 ** Begin thread management routines and data.
438 **************************************************************************/
441 thread_wrapper(void * arg
)
443 GlobalThreadMgr
*threadMGR
= (GlobalThreadMgr
*)arg
;
444 perThread
*slot
= &threadMGR
->threads
[threadMGR
->index
];
446 /* wait for parent to finish launching us before proceeding. */
447 PR_Lock(threadMGR
->threadLock
);
448 PR_Unlock(threadMGR
->threadLock
);
450 slot
->rv
= (* slot
->startFunc
)(slot
->a
, slot
->b
);
452 PR_Lock(threadMGR
->threadLock
);
453 slot
->running
= rs_zombie
;
455 /* notify the thread exit handler. */
456 PR_NotifyCondVar(threadMGR
->threadEndQ
);
458 PR_Unlock(threadMGR
->threadLock
);
462 launch_thread(GlobalThreadMgr
*threadMGR
,
470 if (!threadMGR
->threadStartQ
) {
471 threadMGR
->threadLock
= PR_NewLock();
472 threadMGR
->threadStartQ
= PR_NewCondVar(threadMGR
->threadLock
);
473 threadMGR
->threadEndQ
= PR_NewCondVar(threadMGR
->threadLock
);
475 PR_Lock(threadMGR
->threadLock
);
476 while (threadMGR
->numRunning
>= MAX_THREADS
) {
477 PR_WaitCondVar(threadMGR
->threadStartQ
, PR_INTERVAL_NO_TIMEOUT
);
479 for (i
= 0; i
< threadMGR
->numUsed
; ++i
) {
480 slot
= &threadMGR
->threads
[i
];
481 if (slot
->running
== rs_idle
)
484 if (i
>= threadMGR
->numUsed
) {
485 if (i
>= MAX_THREADS
) {
486 /* something's really wrong here. */
487 PORT_Assert(i
< MAX_THREADS
);
488 PR_Unlock(threadMGR
->threadLock
);
491 ++(threadMGR
->numUsed
);
492 PORT_Assert(threadMGR
->numUsed
== i
+ 1);
493 slot
= &threadMGR
->threads
[i
];
498 slot
->startFunc
= startFunc
;
500 threadMGR
->index
= i
;
502 slot
->prThread
= PR_CreateThread(PR_USER_THREAD
,
503 thread_wrapper
, threadMGR
,
504 PR_PRIORITY_NORMAL
, PR_GLOBAL_THREAD
,
505 PR_JOINABLE_THREAD
, 0);
507 if (slot
->prThread
== NULL
) {
508 PR_Unlock(threadMGR
->threadLock
);
509 printf("Failed to launch thread!\n");
515 ++(threadMGR
->numRunning
);
516 PR_Unlock(threadMGR
->threadLock
);
522 reap_threads(GlobalThreadMgr
*threadMGR
)
527 if (!threadMGR
->threadLock
)
529 PR_Lock(threadMGR
->threadLock
);
530 while (threadMGR
->numRunning
> 0) {
531 PR_WaitCondVar(threadMGR
->threadEndQ
, PR_INTERVAL_NO_TIMEOUT
);
532 for (i
= 0; i
< threadMGR
->numUsed
; ++i
) {
533 slot
= &threadMGR
->threads
[i
];
534 if (slot
->running
== rs_zombie
) {
535 /* Handle cleanup of thread here. */
537 /* Now make sure the thread has ended OK. */
538 PR_JoinThread(slot
->prThread
);
539 slot
->running
= rs_idle
;
540 --threadMGR
->numRunning
;
542 /* notify the thread launcher. */
543 PR_NotifyCondVar(threadMGR
->threadStartQ
);
548 /* Safety Sam sez: make sure count is right. */
549 for (i
= 0; i
< threadMGR
->numUsed
; ++i
) {
550 slot
= &threadMGR
->threads
[i
];
551 if (slot
->running
!= rs_idle
) {
552 fprintf(stderr
, "Thread in slot %d is in state %d!\n",
556 PR_Unlock(threadMGR
->threadLock
);
561 destroy_thread_data(GlobalThreadMgr
*threadMGR
)
563 PORT_Memset(threadMGR
->threads
, 0, sizeof(threadMGR
->threads
));
565 if (threadMGR
->threadEndQ
) {
566 PR_DestroyCondVar(threadMGR
->threadEndQ
);
567 threadMGR
->threadEndQ
= NULL
;
569 if (threadMGR
->threadStartQ
) {
570 PR_DestroyCondVar(threadMGR
->threadStartQ
);
571 threadMGR
->threadStartQ
= NULL
;
573 if (threadMGR
->threadLock
) {
574 PR_DestroyLock(threadMGR
->threadLock
);
575 threadMGR
->threadLock
= NULL
;
579 /**************************************************************************
580 ** End thread management routines.
581 **************************************************************************/
584 lockedVars_Init( lockedVars
* lv
)
588 lv
->lock
= PR_NewLock();
589 lv
->condVar
= PR_NewCondVar(lv
->lock
);
593 lockedVars_Destroy( lockedVars
* lv
)
595 PR_DestroyCondVar(lv
->condVar
);
598 PR_DestroyLock(lv
->lock
);
603 lockedVars_WaitForDone(lockedVars
* lv
)
606 while (lv
->count
> 0) {
607 PR_WaitCondVar(lv
->condVar
, PR_INTERVAL_NO_TIMEOUT
);
612 int /* returns count */
613 lockedVars_AddToCount(lockedVars
* lv
, int addend
)
618 rv
= lv
->count
+= addend
;
620 PR_NotifyCondVar(lv
->condVar
);
628 * Dump cert chain in to cert.* files. This function is will
629 * create collisions while dumping cert chains if called from
630 * multiple treads. But it should not be a problem since we
631 * consider vfyserv to be single threaded(see bug 353477).
635 dumpCertChain(CERTCertificate
*cert
, SECCertUsage usage
)
637 CERTCertificateList
*certList
;
640 certList
= CERT_CertChainFromCert(cert
, usage
, PR_TRUE
);
641 if (certList
== NULL
) {
642 errWarn("CERT_CertChainFromCert");
646 for(count
= 0; count
< (unsigned int)certList
->len
; count
++) {
647 char certFileName
[16];
650 PR_snprintf(certFileName
, sizeof certFileName
, "cert.%03d",
652 cfd
= PR_Open(certFileName
, PR_WRONLY
|PR_CREATE_FILE
|PR_TRUNCATE
,
655 PR_fprintf(PR_STDOUT
,
656 "Error: couldn't save cert der in file '%s'\n",
659 PR_Write(cfd
, certList
->certs
[count
].data
, certList
->certs
[count
].len
);
661 PR_fprintf(PR_STDOUT
, "Cert file %s was created.\n", certFileName
);
664 CERT_DestroyCertificateList(certList
);