Back to index

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