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