nss: import at 3.0.1 beta 1
[mozilla-nss.git] / security / nss / cmd / vfyserv / vfyutil.c
blob101152d6b4597b5f35e2c6467e8edb1132ca8b6f
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
12 * License.
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.
21 * Contributor(s):
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 "vfyserv.h"
38 #include "secerr.h"
39 #include "sslerr.h"
40 #include "nspr.h"
41 #include "secutil.h"
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 /**************************************************************************
90 **
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.
102 char *
103 myPasswd(PK11SlotInfo *info, PRBool retry, void *arg)
105 char * passwd = NULL;
107 if ( (!retry) && arg ) {
108 passwd = PORT_Strdup((char *)arg);
110 return passwd;
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().
120 SECStatus
121 myAuthCertificate(void *arg, PRFileDesc *socket,
122 PRBool checksig, PRBool isServer)
125 SECCertificateUsage certUsage;
126 CERTCertificate * cert;
127 void * pinArg;
128 char * hostName;
129 SECStatus secStatus;
131 if (!arg || !socket) {
132 errWarn("myAuthCertificate");
133 return SECFailure;
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,
149 cert,
150 checksig,
151 certUsage,
152 pinArg,
153 NULL);
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);
160 return secStatus;
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);
174 } else {
175 PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
176 secStatus = SECFailure;
179 if (hostName)
180 PR_Free(hostName);
182 CERT_DestroyCertificate(cert);
183 return secStatus;
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
193 * request.
195 SECStatus
196 myBadCertHandler(void *arg, PRFileDesc *socket)
199 SECStatus secStatus = SECFailure;
200 PRErrorCode err;
202 /* log invalid cert here */
204 if (!arg) {
205 return secStatus;
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. */
214 switch (err) {
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;
230 break;
231 default:
232 secStatus = SECFailure;
233 break;
236 fprintf(stderr, "Bad certificate: %d, %s\n", err, SECU_Strerror(err));
238 return secStatus;
241 /* Function: SECStatus ownGetClientAuthData()
243 * Purpose: This callback is used by SSL to pull client certificate
244 * information upon server request.
246 SECStatus
247 myGetClientAuthData(void *arg,
248 PRFileDesc *socket,
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);
264 if (cert) {
265 privKey = PK11_FindKeyByAnyCert(cert, proto_win);
266 if (privKey) {
267 secStatus = SECSuccess;
268 } else {
269 CERT_DestroyCertificate(cert);
272 } else { /* no nickname given, automatically find the right cert */
273 CERTCertNicknames *names;
274 int i;
276 names = CERT_GetCertNicknames(CERT_GetDefaultCertDB(),
277 SEC_CERT_NICKNAMES_USER, proto_win);
279 if (names != NULL) {
280 for(i = 0; i < names->numnicknames; i++ ) {
282 cert = PK11_FindCertFromNickname(names->nicknames[i],
283 proto_win);
284 if (!cert) {
285 continue;
288 /* Only check unexpired certs */
289 if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE)
290 != secCertTimeValid ) {
291 CERT_DestroyCertificate(cert);
292 continue;
295 secStatus = NSS_CmpCertChainWCANames(cert, caNames);
296 if (secStatus == SECSuccess) {
297 privKey = PK11_FindKeyByAnyCert(cert, proto_win);
298 if (privKey) {
299 break;
301 secStatus = SECFailure;
303 CERT_DestroyCertificate(cert);
304 } /* for loop */
305 CERT_FreeNicknames(names);
309 if (secStatus == SECSuccess) {
310 *pRetCert = cert;
311 *pRetKey = privKey;
314 return secStatus;
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
335 * request.
337 * Note: This function is not implemented in this sample, as we are using
338 * blocking sockets.
340 void
341 myHandshakeCallback(PRFileDesc *socket, void *arg)
343 fprintf(stderr,"Handshake Complete: SERVER CONFIGURED CORRECTLY\n");
347 /**************************************************************************
349 ** Routines for disabling SSL ciphers.
351 **************************************************************************/
353 void
354 disableAllSSLCiphers(void)
356 const PRUint16 *cipherSuites = SSL_ImplementedCiphers;
357 int i = SSL_NumImplementedCiphers;
358 SECStatus rv;
360 /* disable all the SSL3 cipher suites */
361 while (--i >= 0) {
362 PRUint16 suite = cipherSuites[i];
363 rv = SSL_CipherPrefSetDefault(suite, PR_FALSE);
364 if (rv != SECSuccess) {
365 fprintf(stderr,
366 "SSL_CipherPrefSetDefault didn't like value 0x%04x (i = %d)\n",
367 suite, i);
368 errWarn("SSL_CipherPrefSetDefault");
369 exit(2);
374 /**************************************************************************
376 ** Error and information routines.
378 **************************************************************************/
380 void
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);
390 void
391 exitErr(char *function)
393 errWarn(function);
394 /* Exit gracefully. */
395 /* ignoring return value of NSS_Shutdown as code exits with 1 anyway*/
396 (void) NSS_Shutdown();
397 PR_Cleanup();
398 exit(1);
401 void
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 */
410 int result;
411 SSL3Statistics * ssl3stats = SSL_GetStatistics();
413 if (!outfile) {
414 outfile = stdout;
417 result = SSL_SecurityStatus(fd, &op, &cp, &kp0, &kp1, &ip, &sp);
418 if (result != SECSuccess)
419 return;
420 fprintf(outfile,
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);
424 PR_Free(cp);
425 PR_Free(ip);
426 PR_Free(sp);
428 fprintf(outfile,
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 **************************************************************************/
440 void
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);
461 SECStatus
462 launch_thread(GlobalThreadMgr *threadMGR,
463 startFn *startFunc,
464 void *a,
465 int b)
467 perThread *slot;
468 int i;
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)
482 break;
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);
489 return SECFailure;
491 ++(threadMGR->numUsed);
492 PORT_Assert(threadMGR->numUsed == i + 1);
493 slot = &threadMGR->threads[i];
496 slot->a = a;
497 slot->b = b;
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");
510 return SECFailure;
513 slot->inUse = 1;
514 slot->running = 1;
515 ++(threadMGR->numRunning);
516 PR_Unlock(threadMGR->threadLock);
518 return SECSuccess;
521 SECStatus
522 reap_threads(GlobalThreadMgr *threadMGR)
524 perThread * slot;
525 int i;
527 if (!threadMGR->threadLock)
528 return SECSuccess;
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",
553 i, slot->running);
556 PR_Unlock(threadMGR->threadLock);
557 return SECSuccess;
560 void
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 **************************************************************************/
583 void
584 lockedVars_Init( lockedVars * lv)
586 lv->count = 0;
587 lv->waiters = 0;
588 lv->lock = PR_NewLock();
589 lv->condVar = PR_NewCondVar(lv->lock);
592 void
593 lockedVars_Destroy( lockedVars * lv)
595 PR_DestroyCondVar(lv->condVar);
596 lv->condVar = NULL;
598 PR_DestroyLock(lv->lock);
599 lv->lock = NULL;
602 void
603 lockedVars_WaitForDone(lockedVars * lv)
605 PR_Lock(lv->lock);
606 while (lv->count > 0) {
607 PR_WaitCondVar(lv->condVar, PR_INTERVAL_NO_TIMEOUT);
609 PR_Unlock(lv->lock);
612 int /* returns count */
613 lockedVars_AddToCount(lockedVars * lv, int addend)
615 int rv;
617 PR_Lock(lv->lock);
618 rv = lv->count += addend;
619 if (rv <= 0) {
620 PR_NotifyCondVar(lv->condVar);
622 PR_Unlock(lv->lock);
623 return rv;
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).
634 void
635 dumpCertChain(CERTCertificate *cert, SECCertUsage usage)
637 CERTCertificateList *certList;
638 int count = 0;
640 certList = CERT_CertChainFromCert(cert, usage, PR_TRUE);
641 if (certList == NULL) {
642 errWarn("CERT_CertChainFromCert");
643 return;
646 for(count = 0; count < (unsigned int)certList->len; count++) {
647 char certFileName[16];
648 PRFileDesc *cfd;
650 PR_snprintf(certFileName, sizeof certFileName, "cert.%03d",
651 count);
652 cfd = PR_Open(certFileName, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE,
653 0664);
654 if (!cfd) {
655 PR_fprintf(PR_STDOUT,
656 "Error: couldn't save cert der in file '%s'\n",
657 certFileName);
658 } else {
659 PR_Write(cfd, certList->certs[count].data, certList->certs[count].len);
660 PR_Close(cfd);
661 PR_fprintf(PR_STDOUT, "Cert file %s was created.\n", certFileName);
664 CERT_DestroyCertificateList(certList);