Back to index

lightning-sunbird  0.9+nobinonly
vfyutil.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #include "vfyserv.h"
00038 #include "secerr.h"
00039 #include "sslerr.h"
00040 #include "nspr.h"
00041 #include "secutil.h"
00042 
00043 
00044 extern PRBool dumpChain;
00045 extern void dumpCertChain(CERTCertificate *, SECCertUsage);
00046 
00047 /* Declare SSL cipher suites. */
00048 
00049 int ssl2CipherSuites[] = {
00050     SSL_EN_RC4_128_WITH_MD5,              /* A */
00051     SSL_EN_RC4_128_EXPORT40_WITH_MD5,     /* B */
00052     SSL_EN_RC2_128_CBC_WITH_MD5,          /* C */
00053     SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */
00054     SSL_EN_DES_64_CBC_WITH_MD5,           /* E */
00055     SSL_EN_DES_192_EDE3_CBC_WITH_MD5,     /* F */
00056     0
00057 };
00058 
00059 int ssl3CipherSuites[] = {
00060     -1, /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA* a */
00061     -1, /* SSL_FORTEZZA_DMS_WITH_RC4_128_SHA,     * b */
00062     SSL_RSA_WITH_RC4_128_MD5,                    /* c */
00063     SSL_RSA_WITH_3DES_EDE_CBC_SHA,        /* d */
00064     SSL_RSA_WITH_DES_CBC_SHA,                    /* e */
00065     SSL_RSA_EXPORT_WITH_RC4_40_MD5,              /* f */
00066     SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5,          /* g */
00067     -1, /* SSL_FORTEZZA_DMS_WITH_NULL_SHA,        * h */
00068     SSL_RSA_WITH_NULL_MD5,                /* i */
00069     SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA,          /* j */
00070     SSL_RSA_FIPS_WITH_DES_CBC_SHA,        /* k */
00071     TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA,  /* l */
00072     TLS_RSA_EXPORT1024_WITH_RC4_56_SHA,           /* m */
00073     SSL_RSA_WITH_RC4_128_SHA,                    /* n */
00074     TLS_DHE_DSS_WITH_RC4_128_SHA,         /* o */
00075     SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,           /* p */
00076     SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA,           /* q */
00077     SSL_DHE_RSA_WITH_DES_CBC_SHA,         /* r */
00078     SSL_DHE_DSS_WITH_DES_CBC_SHA,         /* s */
00079     TLS_DHE_DSS_WITH_AES_128_CBC_SHA,            /* t */
00080     TLS_DHE_RSA_WITH_AES_128_CBC_SHA,            /* u */
00081     TLS_RSA_WITH_AES_128_CBC_SHA,                /* v */
00082     TLS_DHE_DSS_WITH_AES_256_CBC_SHA,            /* w */
00083     TLS_DHE_RSA_WITH_AES_256_CBC_SHA,            /* x */
00084     TLS_RSA_WITH_AES_256_CBC_SHA,                /* y */
00085     SSL_RSA_WITH_NULL_SHA,                /* z */
00086     0
00087 };
00088 
00089 /**************************************************************************
00090 ** 
00091 ** SSL callback routines.
00092 **
00093 **************************************************************************/
00094 
00095 /* Function: char * myPasswd()
00096  * 
00097  * Purpose: This function is our custom password handler that is called by
00098  * SSL when retreiving private certs and keys from the database. Returns a
00099  * pointer to a string that with a password for the database. Password pointer
00100  * should point to dynamically allocated memory that will be freed later.
00101  */
00102 char *
00103 myPasswd(PK11SlotInfo *info, PRBool retry, void *arg)
00104 {
00105     char * passwd = NULL;
00106 
00107     if ( (!retry) && arg ) {
00108        passwd = PORT_Strdup((char *)arg);
00109     }
00110     return passwd;
00111 }
00112 
00113 /* Function: SECStatus myAuthCertificate()
00114  *
00115  * Purpose: This function is our custom certificate authentication handler.
00116  * 
00117  * Note: This implementation is essentially the same as the default 
00118  *       SSL_AuthCertificate().
00119  */
00120 SECStatus 
00121 myAuthCertificate(void *arg, PRFileDesc *socket, 
00122                   PRBool checksig, PRBool isServer) 
00123 {
00124 
00125     SECCertificateUsage certUsage;
00126     CERTCertificate *   cert;
00127     void *              pinArg;
00128     char *              hostName;
00129     SECStatus           secStatus;
00130 
00131     if (!arg || !socket) {
00132        errWarn("myAuthCertificate");
00133        return SECFailure;
00134     }
00135 
00136     /* Define how the cert is being used based upon the isServer flag. */
00137 
00138     certUsage = isServer ? certificateUsageSSLClient : certificateUsageSSLServer;
00139 
00140     cert = SSL_PeerCertificate(socket);
00141        
00142     pinArg = SSL_RevealPinArg(socket);
00143     
00144     if (dumpChain == PR_TRUE) {
00145         dumpCertChain(cert, certUsage);
00146     }
00147 
00148     secStatus = CERT_VerifyCertificateNow((CERTCertDBHandle *)arg,
00149                                cert,
00150                                checksig,
00151                                certUsage,
00152                                pinArg,
00153                                    NULL);
00154 
00155     /* If this is a server, we're finished. */
00156     if (isServer || secStatus != SECSuccess) {
00157        SECU_printCertProblems(stderr, (CERTCertDBHandle *)arg, cert, 
00158                        checksig, certUsage, pinArg, PR_FALSE);
00159        CERT_DestroyCertificate(cert);
00160        return secStatus;
00161     }
00162 
00163     /* Certificate is OK.  Since this is the client side of an SSL
00164      * connection, we need to verify that the name field in the cert
00165      * matches the desired hostname.  This is our defense against
00166      * man-in-the-middle attacks.
00167      */
00168 
00169     /* SSL_RevealURL returns a hostName, not an URL. */
00170     hostName = SSL_RevealURL(socket);
00171 
00172     if (hostName && hostName[0]) {
00173        secStatus = CERT_VerifyCertName(cert, hostName);
00174     } else {
00175        PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
00176        secStatus = SECFailure;
00177     }
00178 
00179     if (hostName)
00180        PR_Free(hostName);
00181 
00182     CERT_DestroyCertificate(cert);
00183     return secStatus;
00184 }
00185 
00186 /* Function: SECStatus myBadCertHandler()
00187  *
00188  * Purpose: This callback is called when the incoming certificate is not
00189  * valid. We define a certain set of parameters that still cause the
00190  * certificate to be "valid" for this session, and return SECSuccess to cause
00191  * the server to continue processing the request when any of these conditions
00192  * are met. Otherwise, SECFailure is return and the server rejects the 
00193  * request.
00194  */
00195 SECStatus 
00196 myBadCertHandler(void *arg, PRFileDesc *socket) 
00197 {
00198 
00199     SECStatus secStatus = SECFailure;
00200     PRErrorCode      err;
00201 
00202     /* log invalid cert here */
00203 
00204     if (!arg) {
00205        return secStatus;
00206     }
00207 
00208     *(PRErrorCode *)arg = err = PORT_GetError();
00209 
00210     /* If any of the cases in the switch are met, then we will proceed   */
00211     /* with the processing of the request anyway. Otherwise, the default */  
00212     /* case will be reached and we will reject the request.              */
00213 
00214     switch (err) {
00215     case SEC_ERROR_INVALID_AVA:
00216     case SEC_ERROR_INVALID_TIME:
00217     case SEC_ERROR_BAD_SIGNATURE:
00218     case SEC_ERROR_EXPIRED_CERTIFICATE:
00219     case SEC_ERROR_UNKNOWN_ISSUER:
00220     case SEC_ERROR_UNTRUSTED_CERT:
00221     case SEC_ERROR_CERT_VALID:
00222     case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
00223     case SEC_ERROR_CRL_EXPIRED:
00224     case SEC_ERROR_CRL_BAD_SIGNATURE:
00225     case SEC_ERROR_EXTENSION_VALUE_INVALID:
00226     case SEC_ERROR_CA_CERT_INVALID:
00227     case SEC_ERROR_CERT_USAGES_INVALID:
00228     case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
00229        secStatus = SECSuccess;
00230        break;
00231     default:
00232        secStatus = SECFailure;
00233        break;
00234     }
00235 
00236     fprintf(stderr, "Bad certificate: %d, %s\n", err, SECU_Strerror(err));
00237 
00238     return secStatus;
00239 }
00240 
00241 /* Function: SECStatus ownGetClientAuthData()
00242  *
00243  * Purpose: This callback is used by SSL to pull client certificate 
00244  * information upon server request.
00245  */
00246 SECStatus 
00247 myGetClientAuthData(void *arg,
00248                     PRFileDesc *socket,
00249                     struct CERTDistNamesStr *caNames,
00250                     struct CERTCertificateStr **pRetCert,
00251                     struct SECKEYPrivateKeyStr **pRetKey) 
00252 {
00253 
00254     CERTCertificate *  cert;
00255     SECKEYPrivateKey * privKey;
00256     char *             chosenNickName = (char *)arg;
00257     void *             proto_win      = NULL;
00258     SECStatus          secStatus      = SECFailure;
00259 
00260     proto_win = SSL_RevealPinArg(socket);
00261 
00262     if (chosenNickName) {
00263        cert = PK11_FindCertFromNickname(chosenNickName, proto_win);
00264        if (cert) {
00265            privKey = PK11_FindKeyByAnyCert(cert, proto_win);
00266            if (privKey) {
00267               secStatus = SECSuccess;
00268            } else {
00269               CERT_DestroyCertificate(cert);
00270            }
00271        }
00272     } else { /* no nickname given, automatically find the right cert */
00273        CERTCertNicknames *names;
00274        int                i;
00275 
00276        names = CERT_GetCertNicknames(CERT_GetDefaultCertDB(), 
00277                                   SEC_CERT_NICKNAMES_USER, proto_win);
00278 
00279        if (names != NULL) {
00280            for(i = 0; i < names->numnicknames; i++ ) {
00281 
00282               cert = PK11_FindCertFromNickname(names->nicknames[i], 
00283                                            proto_win);
00284               if (!cert) {
00285                   continue;
00286               }
00287 
00288               /* Only check unexpired certs */
00289               if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE)
00290                     != secCertTimeValid ) {
00291                   CERT_DestroyCertificate(cert);
00292                   continue;
00293               }
00294 
00295               secStatus = NSS_CmpCertChainWCANames(cert, caNames);
00296               if (secStatus == SECSuccess) {
00297                   privKey = PK11_FindKeyByAnyCert(cert, proto_win);
00298                   if (privKey) {
00299                      break;
00300                   }
00301                   secStatus = SECFailure;
00302               }
00303               CERT_DestroyCertificate(cert);
00304            } /* for loop */
00305            CERT_FreeNicknames(names);
00306        }
00307     }
00308 
00309     if (secStatus == SECSuccess) {
00310        *pRetCert = cert;
00311        *pRetKey  = privKey;
00312     }
00313 
00314     return secStatus;
00315 }
00316 
00317 /* Function: SECStatus myHandshakeCallback()
00318  *
00319  * Purpose: Called by SSL to inform application that the handshake is
00320  * complete. This function is mostly used on the server side of an SSL
00321  * connection, although it is provided for a client as well.
00322  * Useful when a non-blocking SSL_ReHandshake or SSL_ResetHandshake 
00323  * is used to initiate a handshake.
00324  *
00325  * A typical scenario would be:
00326  *
00327  * 1. Server accepts an SSL connection from the client without client auth.
00328  * 2. Client sends a request.
00329  * 3. Server determines that to service request it needs to authenticate the
00330  * client and initiates another handshake requesting client auth.
00331  * 4. While handshake is in progress, server can do other work or spin waiting
00332  * for the handshake to complete.
00333  * 5. Server is notified that handshake has been successfully completed by
00334  * the custom handshake callback function and it can service the client's
00335  * request.
00336  *
00337  * Note: This function is not implemented in this sample, as we are using
00338  * blocking sockets.
00339  */
00340 SECStatus 
00341 myHandshakeCallback(PRFileDesc *socket, void *arg) 
00342 {
00343     fprintf(stderr,"Handshake Complete: SERVER CONFIGURED CORRECTLY\n");
00344     return SECSuccess;
00345 }
00346 
00347 
00348 /**************************************************************************
00349 ** 
00350 ** Routines for disabling SSL ciphers.
00351 **
00352 **************************************************************************/
00353 
00354 void
00355 disableAllSSLCiphers(void)
00356 {
00357     const PRUint16 *cipherSuites = SSL_ImplementedCiphers;
00358     int             i            = SSL_NumImplementedCiphers;
00359     SECStatus       rv;
00360 
00361     /* disable all the SSL3 cipher suites */
00362     while (--i >= 0) {
00363        PRUint16 suite = cipherSuites[i];
00364         rv = SSL_CipherPrefSetDefault(suite, PR_FALSE);
00365        if (rv != SECSuccess) {
00366            fprintf(stderr,
00367               "SSL_CipherPrefSetDefault didn't like value 0x%04x (i = %d)\n",
00368                  suite, i);
00369            errWarn("SSL_CipherPrefSetDefault");
00370            exit(2);
00371        }
00372     }
00373 }
00374 
00375 /**************************************************************************
00376 ** 
00377 ** Error and information routines.
00378 **
00379 **************************************************************************/
00380 
00381 void
00382 errWarn(char *function)
00383 {
00384     PRErrorCode  errorNumber = PR_GetError();
00385     const char * errorString = SECU_Strerror(errorNumber);
00386 
00387     fprintf(stderr, "Error in function %s: %d\n - %s\n",
00388                   function, errorNumber, errorString);
00389 }
00390 
00391 void
00392 exitErr(char *function)
00393 {
00394     errWarn(function);
00395     /* Exit gracefully. */
00396     /* ignoring return value of NSS_Shutdown as code exits with 1 anyway*/
00397     (void) NSS_Shutdown();
00398     PR_Cleanup();
00399     exit(1);
00400 }
00401 
00402 void 
00403 printSecurityInfo(FILE *outfile, PRFileDesc *fd)
00404 {
00405     char * cp;       /* bulk cipher name */
00406     char * ip;       /* cert issuer DN */
00407     char * sp;       /* cert subject DN */
00408     int    op;       /* High, Low, Off */
00409     int    kp0;      /* total key bits */
00410     int    kp1;      /* secret key bits */
00411     int    result;
00412     SSL3Statistics * ssl3stats = SSL_GetStatistics();
00413 
00414     if (!outfile) {
00415        outfile = stdout;
00416     }
00417 
00418     result = SSL_SecurityStatus(fd, &op, &cp, &kp0, &kp1, &ip, &sp);
00419     if (result != SECSuccess)
00420        return;
00421     fprintf(outfile,
00422      "   bulk cipher %s, %d secret key bits, %d key bits, status: %d\n"
00423      "   subject DN:\n %s\n"
00424      "   issuer  DN:\n %s\n", cp, kp1, kp0, op, sp, ip);
00425     PR_Free(cp);
00426     PR_Free(ip);
00427     PR_Free(sp);
00428 
00429     fprintf(outfile,
00430       "   %ld cache hits; %ld cache misses, %ld cache not reusable\n",
00431            ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses,
00432     ssl3stats->hch_sid_cache_not_ok);
00433 
00434 }
00435 
00436 
00437 /**************************************************************************
00438 ** Begin thread management routines and data.
00439 **************************************************************************/
00440 
00441 void
00442 thread_wrapper(void * arg)
00443 {
00444     GlobalThreadMgr *threadMGR = (GlobalThreadMgr *)arg;
00445     perThread *slot = &threadMGR->threads[threadMGR->index];
00446 
00447     /* wait for parent to finish launching us before proceeding. */
00448     PR_Lock(threadMGR->threadLock);
00449     PR_Unlock(threadMGR->threadLock);
00450 
00451     slot->rv = (* slot->startFunc)(slot->a, slot->b);
00452 
00453     PR_Lock(threadMGR->threadLock);
00454     slot->running = rs_zombie;
00455 
00456     /* notify the thread exit handler. */
00457     PR_NotifyCondVar(threadMGR->threadEndQ);
00458 
00459     PR_Unlock(threadMGR->threadLock);
00460 }
00461 
00462 SECStatus
00463 launch_thread(GlobalThreadMgr *threadMGR,
00464               startFn         *startFunc,
00465               void            *a,
00466               int              b)
00467 {
00468     perThread *slot;
00469     int        i;
00470 
00471     if (!threadMGR->threadStartQ) {
00472        threadMGR->threadLock   = PR_NewLock();
00473        threadMGR->threadStartQ = PR_NewCondVar(threadMGR->threadLock);
00474        threadMGR->threadEndQ   = PR_NewCondVar(threadMGR->threadLock);
00475     }
00476     PR_Lock(threadMGR->threadLock);
00477     while (threadMGR->numRunning >= MAX_THREADS) {
00478        PR_WaitCondVar(threadMGR->threadStartQ, PR_INTERVAL_NO_TIMEOUT);
00479     }
00480     for (i = 0; i < threadMGR->numUsed; ++i) {
00481        slot = &threadMGR->threads[i];
00482        if (slot->running == rs_idle) 
00483            break;
00484     }
00485     if (i >= threadMGR->numUsed) {
00486        if (i >= MAX_THREADS) {
00487            /* something's really wrong here. */
00488            PORT_Assert(i < MAX_THREADS);
00489            PR_Unlock(threadMGR->threadLock);
00490            return SECFailure;
00491        }
00492        ++(threadMGR->numUsed);
00493        PORT_Assert(threadMGR->numUsed == i + 1);
00494        slot = &threadMGR->threads[i];
00495     }
00496 
00497     slot->a = a;
00498     slot->b = b;
00499     slot->startFunc = startFunc;
00500 
00501     threadMGR->index = i;
00502 
00503     slot->prThread = PR_CreateThread(PR_USER_THREAD,
00504                                  thread_wrapper, threadMGR,
00505                                  PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
00506                                  PR_JOINABLE_THREAD, 0);
00507 
00508     if (slot->prThread == NULL) {
00509        PR_Unlock(threadMGR->threadLock);
00510        printf("Failed to launch thread!\n");
00511        return SECFailure;
00512     } 
00513 
00514     slot->inUse   = 1;
00515     slot->running = 1;
00516     ++(threadMGR->numRunning);
00517     PR_Unlock(threadMGR->threadLock);
00518 
00519     return SECSuccess;
00520 }
00521 
00522 SECStatus 
00523 reap_threads(GlobalThreadMgr *threadMGR)
00524 {
00525     perThread * slot;
00526     int                     i;
00527 
00528     if (!threadMGR->threadLock)
00529        return SECSuccess;
00530     PR_Lock(threadMGR->threadLock);
00531     while (threadMGR->numRunning > 0) {
00532        PR_WaitCondVar(threadMGR->threadEndQ, PR_INTERVAL_NO_TIMEOUT);
00533        for (i = 0; i < threadMGR->numUsed; ++i) {
00534            slot = &threadMGR->threads[i];
00535            if (slot->running == rs_zombie)  {
00536               /* Handle cleanup of thread here. */
00537 
00538               /* Now make sure the thread has ended OK. */
00539               PR_JoinThread(slot->prThread);
00540               slot->running = rs_idle;
00541               --threadMGR->numRunning;
00542 
00543               /* notify the thread launcher. */
00544               PR_NotifyCondVar(threadMGR->threadStartQ);
00545            }
00546        }
00547     }
00548 
00549     /* Safety Sam sez: make sure count is right. */
00550     for (i = 0; i < threadMGR->numUsed; ++i) {
00551        slot = &threadMGR->threads[i];
00552        if (slot->running != rs_idle)  {
00553            fprintf(stderr, "Thread in slot %d is in state %d!\n", 
00554                           i, slot->running);
00555        }
00556     }
00557     PR_Unlock(threadMGR->threadLock);
00558     return SECSuccess;
00559 }
00560 
00561 void
00562 destroy_thread_data(GlobalThreadMgr *threadMGR)
00563 {
00564     PORT_Memset(threadMGR->threads, 0, sizeof(threadMGR->threads));
00565 
00566     if (threadMGR->threadEndQ) {
00567        PR_DestroyCondVar(threadMGR->threadEndQ);
00568        threadMGR->threadEndQ = NULL;
00569     }
00570     if (threadMGR->threadStartQ) {
00571        PR_DestroyCondVar(threadMGR->threadStartQ);
00572        threadMGR->threadStartQ = NULL;
00573     }
00574     if (threadMGR->threadLock) {
00575        PR_DestroyLock(threadMGR->threadLock);
00576        threadMGR->threadLock = NULL;
00577     }
00578 }
00579 
00580 /**************************************************************************
00581 ** End  thread management routines.
00582 **************************************************************************/
00583 
00584 void 
00585 lockedVars_Init( lockedVars * lv)
00586 {
00587     lv->count = 0;
00588     lv->waiters = 0;
00589     lv->lock  = PR_NewLock();
00590     lv->condVar = PR_NewCondVar(lv->lock);
00591 }
00592 
00593 void
00594 lockedVars_Destroy( lockedVars * lv)
00595 {
00596     PR_DestroyCondVar(lv->condVar);
00597     lv->condVar = NULL;
00598 
00599     PR_DestroyLock(lv->lock);
00600     lv->lock = NULL;
00601 }
00602 
00603 void
00604 lockedVars_WaitForDone(lockedVars * lv)
00605 {
00606     PR_Lock(lv->lock);
00607     while (lv->count > 0) {
00608        PR_WaitCondVar(lv->condVar, PR_INTERVAL_NO_TIMEOUT);
00609     }
00610     PR_Unlock(lv->lock);
00611 }
00612 
00613 int    /* returns count */
00614 lockedVars_AddToCount(lockedVars * lv, int addend)
00615 {
00616     int rv;
00617 
00618     PR_Lock(lv->lock);
00619     rv = lv->count += addend;
00620     if (rv <= 0) {
00621        PR_NotifyCondVar(lv->condVar);
00622     }
00623     PR_Unlock(lv->lock);
00624     return rv;
00625 }
00626 
00627 
00628 /*
00629  * Dump cert chain in to cert.* files. This function is will
00630  * create collisions while dumping cert chains if called from
00631  * multiple treads. But it should not be a problem since we
00632  * consider vfyserv to be single threaded(see bug 353477).
00633  */
00634 
00635 void
00636 dumpCertChain(CERTCertificate *cert, SECCertUsage usage)
00637 {
00638     CERTCertificateList *certList;
00639     int count = 0;
00640 
00641     certList = CERT_CertChainFromCert(cert, usage, PR_TRUE);
00642     if (certList == NULL) {
00643         errWarn("CERT_CertChainFromCert");
00644         return;
00645     }
00646 
00647     for(count = 0; count < (unsigned int)certList->len; count++) {
00648         char certFileName[16];
00649         PRFileDesc *cfd;
00650 
00651         PR_snprintf(certFileName, sizeof certFileName, "cert.%03d",
00652                     count);
00653         cfd = PR_Open(certFileName, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 
00654                       0664);
00655         if (!cfd) {
00656             PR_fprintf(PR_STDOUT,
00657                        "Error: couldn't save cert der in file '%s'\n",
00658                        certFileName);
00659         } else {
00660             PR_Write(cfd,  certList->certs[count].data,  certList->certs[count].len);
00661             PR_Close(cfd);
00662             PR_fprintf(PR_STDOUT, "Cert file %s was created.\n", certFileName);
00663         }
00664     }
00665     CERT_DestroyCertificateList(certList);
00666 }