Back to index

lightning-sunbird  0.9+nobinonly
vfyserv.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  *   Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /****************************************************************************
00039  *  SSL client program that tests  a server for proper operation of SSL2,   *
00040  *  SSL3, and TLS. Test propder certificate installation.                   *
00041  *                                                                          *
00042  *  This code was modified from the SSLSample code also kept in the NSS     *
00043  *  directory.                                                              *
00044  ****************************************************************************/ 
00045 
00046 #include <stdio.h>
00047 #include <string.h>
00048 
00049 #if defined(XP_UNIX)
00050 #include <unistd.h>
00051 #endif
00052 
00053 #include "prerror.h"
00054 
00055 #include "pk11func.h"
00056 #include "secmod.h"
00057 #include "secitem.h"
00058 
00059 
00060 #include <stdlib.h>
00061 #include <errno.h>
00062 #include <fcntl.h>
00063 #include <stdarg.h>
00064 
00065 #include "nspr.h"
00066 #include "plgetopt.h"
00067 #include "prio.h"
00068 #include "prnetdb.h"
00069 #include "nss.h"
00070 #include "secutil.h"
00071 #include "ocsp.h"
00072 
00073 #include "vfyserv.h"
00074 
00075 #define RD_BUF_SIZE (60 * 1024)
00076 
00077 extern int ssl2CipherSuites[];
00078 extern int ssl3CipherSuites[];
00079 
00080 GlobalThreadMgr threadMGR;
00081 char *certNickname = NULL;
00082 char *hostName = NULL;
00083 char *password = NULL;
00084 unsigned short port = 0;
00085 PRBool dumpChain;
00086 
00087 static void
00088 Usage(const char *progName)
00089 {
00090     PRFileDesc *pr_stderr;
00091 
00092     pr_stderr = PR_STDERR;
00093 
00094     PR_fprintf(pr_stderr, "Usage:\n"
00095                "   %s  [-c ] [-o] [-p port] [-d dbdir] [-w password]\n"
00096                "   \t\t[-C cipher(s)]  [-l <url> -t <nickname> ] hostname",
00097                progName);
00098     PR_fprintf (pr_stderr, "\nWhere:\n");
00099     PR_fprintf (pr_stderr,
00100                 "  %-13s dump server cert chain into files\n",
00101                 "-c");
00102     PR_fprintf (pr_stderr,
00103                 "  %-13s perform server cert OCSP check\n",
00104                 "-o");
00105     PR_fprintf (pr_stderr,
00106                 "  %-13s server port to be used\n",
00107                 "-p");
00108     PR_fprintf (pr_stderr,
00109                 "  %-13s use security databases in \"dbdir\"\n",
00110                 "-d dbdir");
00111     PR_fprintf (pr_stderr,
00112                 "  %-13s key database password\n",
00113                 "-w password");
00114     PR_fprintf (pr_stderr,
00115                 "  %-13s communication cipher list\n",
00116                 "-C cipher(s)");
00117     PR_fprintf (pr_stderr,
00118                 "  %-13s OCSP responder location. This location is used to\n"
00119                 "  %-13s check  status  of a server  certificate.  If  not \n"
00120                 "  %-13s specified, location  will  be taken  from the AIA\n"
00121                 "  %-13s server certificate extension.\n",
00122                 "-l url", "", "", "");
00123     PR_fprintf (pr_stderr,
00124                 "  %-13s OCSP Trusted Responder Cert nickname\n\n",
00125                 "-t nickname");
00126 
00127        exit(1);
00128 }
00129 
00130 PRFileDesc *
00131 setupSSLSocket(PRNetAddr *addr)
00132 {
00133        PRFileDesc         *tcpSocket;
00134        PRFileDesc         *sslSocket;
00135        PRSocketOptionData   socketOption;
00136        PRStatus            prStatus;
00137        SECStatus           secStatus;
00138 
00139 
00140        tcpSocket = PR_NewTCPSocket();
00141        if (tcpSocket == NULL) {
00142               errWarn("PR_NewTCPSocket");
00143        }
00144 
00145        /* Make the socket blocking. */
00146        socketOption.option              = PR_SockOpt_Nonblocking;
00147        socketOption.value.non_blocking = PR_FALSE;
00148 
00149        prStatus = PR_SetSocketOption(tcpSocket, &socketOption);
00150        if (prStatus != PR_SUCCESS) {
00151               errWarn("PR_SetSocketOption");
00152               goto loser;
00153        } 
00154 
00155 
00156        /* Import the socket into the SSL layer. */
00157        sslSocket = SSL_ImportFD(NULL, tcpSocket);
00158        if (!sslSocket) {
00159               errWarn("SSL_ImportFD");
00160               goto loser;
00161        }
00162 
00163        /* Set configuration options. */
00164        secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE);
00165        if (secStatus != SECSuccess) {
00166               errWarn("SSL_OptionSet:SSL_SECURITY");
00167               goto loser;
00168        }
00169 
00170        secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
00171        if (secStatus != SECSuccess) {
00172               errWarn("SSL_OptionSet:SSL_HANDSHAKE_AS_CLIENT");
00173               goto loser;
00174        }
00175 
00176        /* Set SSL callback routines. */
00177        secStatus = SSL_GetClientAuthDataHook(sslSocket,
00178                                  (SSLGetClientAuthData)myGetClientAuthData,
00179                                  (void *)certNickname);
00180        if (secStatus != SECSuccess) {
00181               errWarn("SSL_GetClientAuthDataHook");
00182               goto loser;
00183        }
00184 
00185        secStatus = SSL_AuthCertificateHook(sslSocket,
00186                                           (SSLAuthCertificate)myAuthCertificate,
00187                                        (void *)CERT_GetDefaultCertDB());
00188        if (secStatus != SECSuccess) {
00189               errWarn("SSL_AuthCertificateHook");
00190               goto loser;
00191        }
00192 
00193        secStatus = SSL_BadCertHook(sslSocket, 
00194                                   (SSLBadCertHandler)myBadCertHandler, NULL);
00195        if (secStatus != SECSuccess) {
00196               errWarn("SSL_BadCertHook");
00197               goto loser;
00198        }
00199 
00200        secStatus = SSL_HandshakeCallback(sslSocket, 
00201                                         (SSLHandshakeCallback)myHandshakeCallback,
00202                                         NULL);
00203        if (secStatus != SECSuccess) {
00204               errWarn("SSL_HandshakeCallback");
00205               goto loser;
00206        }
00207 
00208        return sslSocket;
00209 
00210 loser:
00211 
00212        PR_Close(tcpSocket);
00213        return NULL;
00214 }
00215 
00216 
00217 const char requestString[] = {"GET /testfile HTTP/1.0\r\n\r\n" };
00218 
00219 SECStatus
00220 handle_connection(PRFileDesc *sslSocket, int connection)
00221 {
00222        int    countRead = 0;
00223        PRInt32  numBytes;
00224        char    *readBuffer;
00225 
00226        readBuffer = PORT_Alloc(RD_BUF_SIZE);
00227        if (!readBuffer) {
00228               exitErr("PORT_Alloc");
00229        }
00230 
00231        /* compose the http request here. */
00232 
00233        numBytes = PR_Write(sslSocket, requestString, strlen(requestString));
00234        if (numBytes <= 0) {
00235               errWarn("PR_Write");
00236               PR_Free(readBuffer);
00237               readBuffer = NULL;
00238               return SECFailure;
00239        }
00240 
00241        /* read until EOF */
00242        while (PR_TRUE) {
00243               numBytes = PR_Read(sslSocket, readBuffer, RD_BUF_SIZE);
00244               if (numBytes == 0) {
00245                      break; /* EOF */
00246               }
00247               if (numBytes < 0) {
00248                      errWarn("PR_Read");
00249                      break;
00250               }
00251               countRead += numBytes;
00252        }
00253 
00254        printSecurityInfo(stderr, sslSocket);
00255        
00256        PR_Free(readBuffer);
00257        readBuffer = NULL;
00258 
00259        /* Caller closes the socket. */
00260 
00261        fprintf(stderr, 
00262                "***** Connection %d read %d bytes total.\n", 
00263                connection, countRead);
00264 
00265        return SECSuccess;   /* success */
00266 }
00267 
00268 #define BYTE(n,i) (((i)>>((n)*8))&0xff)
00269 
00270 /* one copy of this function is launched in a separate thread for each
00271 ** connection to be made.
00272 */
00273 SECStatus
00274 do_connects(void *a, int connection)
00275 {
00276        PRNetAddr  *addr = (PRNetAddr *)a;
00277        PRFileDesc *sslSocket;
00278        PRHostEnt   hostEntry;
00279        char        buffer[PR_NETDB_BUF_SIZE];
00280        PRStatus    prStatus;
00281        PRIntn      hostenum;
00282        PRInt32    ip;
00283        SECStatus   secStatus;
00284 
00285        /* Set up SSL secure socket. */
00286        sslSocket = setupSSLSocket(addr);
00287        if (sslSocket == NULL) {
00288               errWarn("setupSSLSocket");
00289               return SECFailure;
00290        }
00291 
00292        secStatus = SSL_SetPKCS11PinArg(sslSocket, password);
00293        if (secStatus != SECSuccess) {
00294               errWarn("SSL_SetPKCS11PinArg");
00295               return secStatus;
00296        }
00297 
00298        secStatus = SSL_SetURL(sslSocket, hostName);
00299        if (secStatus != SECSuccess) {
00300               errWarn("SSL_SetURL");
00301               return secStatus;
00302        }
00303 
00304        /* Prepare and setup network connection. */
00305        prStatus = PR_GetHostByName(hostName, buffer, sizeof(buffer), &hostEntry);
00306        if (prStatus != PR_SUCCESS) {
00307               errWarn("PR_GetHostByName");
00308               return SECFailure;
00309        }
00310 
00311        hostenum = PR_EnumerateHostEnt(0, &hostEntry, port, addr);
00312        if (hostenum == -1) {
00313               errWarn("PR_EnumerateHostEnt");
00314               return SECFailure;
00315        }
00316 
00317        ip = PR_ntohl(addr->inet.ip);
00318        fprintf(stderr,
00319               "Connecting to host %s (addr %d.%d.%d.%d) on port %d\n",
00320                      hostName, BYTE(3,ip), BYTE(2,ip), BYTE(1,ip), 
00321                      BYTE(0,ip), PR_ntohs(addr->inet.port)); 
00322 
00323        prStatus = PR_Connect(sslSocket, addr, PR_INTERVAL_NO_TIMEOUT);
00324        if (prStatus != PR_SUCCESS) {
00325               errWarn("PR_Connect");
00326               return SECFailure;
00327        }
00328 
00329        /* Established SSL connection, ready to send data. */
00330 #if 0
00331        secStatus = SSL_ForceHandshake(sslSocket);
00332        if (secStatus != SECSuccess) {
00333               errWarn("SSL_ForceHandshake");
00334               return secStatus;
00335        }
00336 #endif
00337 
00338        secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_FALSE);
00339        if (secStatus != SECSuccess) {
00340               errWarn("SSL_ResetHandshake");
00341               prStatus = PR_Close(sslSocket);
00342               if (prStatus != PR_SUCCESS) {
00343                      errWarn("PR_Close");
00344               }
00345               return secStatus;
00346        }
00347 
00348        secStatus = handle_connection(sslSocket, connection);
00349        if (secStatus != SECSuccess) {
00350               /* error already printed out in handle_connection */
00351               /* errWarn("handle_connection"); */
00352               prStatus = PR_Close(sslSocket);
00353               if (prStatus != PR_SUCCESS) {
00354                      errWarn("PR_Close");
00355               }
00356               return secStatus;
00357        }
00358 
00359        PR_Close(sslSocket);
00360        return SECSuccess;
00361 }
00362 
00363 void
00364 client_main(unsigned short      port, 
00365             int                      connections, 
00366             const char *        hostName)
00367 {
00368        int                  i;
00369        SECStatus     secStatus;
00370        PRStatus    prStatus;
00371        PRInt32     rv;
00372        PRNetAddr     addr;
00373        PRHostEnt   hostEntry;
00374        char        buffer[PR_NETDB_BUF_SIZE];
00375 
00376        /* Setup network connection. */
00377        prStatus = PR_GetHostByName(hostName, buffer, sizeof(buffer), &hostEntry);
00378        if (prStatus != PR_SUCCESS) {
00379               exitErr("PR_GetHostByName");
00380        }
00381 
00382        rv = PR_EnumerateHostEnt(0, &hostEntry, port, &addr);
00383        if (rv < 0) {
00384               exitErr("PR_EnumerateHostEnt");
00385        }
00386 
00387        secStatus = launch_thread(&threadMGR, do_connects, &addr, 1);
00388        if (secStatus != SECSuccess) {
00389               exitErr("launch_thread");
00390        }
00391 
00392        if (connections > 1) {
00393               /* wait for the first connection to terminate, then launch the rest. */
00394               reap_threads(&threadMGR);
00395               /* Start up the connections */
00396               for (i = 2; i <= connections; ++i) {
00397                      secStatus = launch_thread(&threadMGR, do_connects, &addr, i);
00398                      if (secStatus != SECSuccess) {
00399                             errWarn("launch_thread");
00400                      }
00401               }
00402        }
00403 
00404        reap_threads(&threadMGR);
00405        destroy_thread_data(&threadMGR);
00406 }
00407 
00408 #define HEXCHAR_TO_INT(c, i) \
00409     if (((c) >= '0') && ((c) <= '9')) { \
00410        i = (c) - '0'; \
00411     } else if (((c) >= 'a') && ((c) <= 'f')) { \
00412        i = (c) - 'a' + 10; \
00413     } else if (((c) >= 'A') && ((c) <= 'F')) { \
00414        i = (c) - 'A' + 10; \
00415     } else { \
00416        Usage(progName); \
00417     }
00418 
00419 int
00420 main(int argc, char **argv)
00421 {
00422        char *               certDir = NULL;
00423        char *               progName     = NULL;
00424        int                  connections  = 1;
00425        char *               cipherString = NULL;
00426        char *               respUrl = NULL;
00427        char *               respCertName = NULL;
00428        SECStatus            secStatus;
00429        PLOptState *         optstate;
00430        PLOptStatus          status;
00431        PRBool               doOcspCheck = PR_FALSE;
00432 
00433        /* Call the NSPR initialization routines */
00434        PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
00435 
00436        progName = PORT_Strdup(argv[0]);
00437 
00438        hostName = NULL;
00439        optstate = PL_CreateOptState(argc, argv, "C:cd:l:n:p:ot:w:");
00440        while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
00441               switch(optstate->option) {
00442               case 'C' : cipherString = PL_strdup(optstate->value); break;
00443               case 'c' : dumpChain = PR_TRUE;                       break;
00444               case 'd' : certDir = PL_strdup(optstate->value);      break;
00445               case 'l' : respUrl = PL_strdup(optstate->value);      break;
00446               case 'p' : port = PORT_Atoi(optstate->value);         break;
00447               case 'o' : doOcspCheck = PR_TRUE;                     break;
00448               case 't' : respCertName = PL_strdup(optstate->value); break;
00449               case 'w' : password = PL_strdup(optstate->value);     break;
00450               case '\0': hostName = PL_strdup(optstate->value);     break;
00451               default  : Usage(progName);
00452               }
00453        }
00454 
00455        if (port == 0) {
00456               port = 443;
00457        }
00458 
00459        if (port == 0 || hostName == NULL)
00460               Usage(progName);
00461 
00462         if (doOcspCheck &&
00463             ((respCertName != NULL && respUrl == NULL) ||
00464              (respUrl != NULL && respCertName == NULL))) {
00465            SECU_PrintError (progName, "options -l <url> and -t "
00466                             "<responder> must be used together");
00467            Usage(progName);
00468         }
00469     
00470        /* Set our password function callback. */
00471        PK11_SetPasswordFunc(myPasswd);
00472 
00473        /* Initialize the NSS libraries. */
00474        if (certDir) {
00475            secStatus = NSS_Init(certDir);
00476        } else {
00477            secStatus = NSS_NoDB_Init(NULL);
00478 
00479            /* load the builtins */
00480            SECMOD_AddNewModule("Builtins",
00481                             DLL_PREFIX"nssckbi."DLL_SUFFIX, 0, 0);
00482        }
00483        if (secStatus != SECSuccess) {
00484               exitErr("NSS_Init");
00485        }
00486        SECU_RegisterDynamicOids();
00487 
00488        if (doOcspCheck == PR_TRUE) {
00489             SECStatus rv;
00490             CERTCertDBHandle *handle = CERT_GetDefaultCertDB();
00491             if (handle == NULL) {
00492                 SECU_PrintError (progName, "problem getting certdb handle");
00493                 goto cleanup;
00494             }
00495             
00496             rv = CERT_EnableOCSPChecking (handle);
00497             if (rv != SECSuccess) {
00498                 SECU_PrintError (progName, "error enabling OCSP checking");
00499                 goto cleanup;
00500             }
00501 
00502             if (respUrl != NULL) {
00503                 rv = CERT_SetOCSPDefaultResponder (handle, respUrl,
00504                                                    respCertName);
00505                 if (rv != SECSuccess) {
00506                     SECU_PrintError (progName,
00507                                      "error setting default responder");
00508                     goto cleanup;
00509                 }
00510                 
00511                 rv = CERT_EnableOCSPDefaultResponder (handle);
00512                 if (rv != SECSuccess) {
00513                     SECU_PrintError (progName,
00514                                      "error enabling default responder");
00515                     goto cleanup;
00516                 }
00517             }
00518        }
00519 
00520        /* All cipher suites except RSA_NULL_MD5 are enabled by 
00521         * Domestic Policy. */
00522        NSS_SetDomesticPolicy();
00523        SSL_CipherPrefSetDefault(SSL_RSA_WITH_NULL_MD5, PR_TRUE);
00524 
00525        /* all the SSL2 and SSL3 cipher suites are enabled by default. */
00526        if (cipherString) {
00527            int ndx;
00528 
00529            /* disable all the ciphers, then enable the ones we want. */
00530            disableAllSSLCiphers();
00531 
00532            while (0 != (ndx = *cipherString++)) {
00533               int  cipher;
00534 
00535               if (ndx == ':') {
00536                   int ctmp;
00537 
00538                   cipher = 0;
00539                   HEXCHAR_TO_INT(*cipherString, ctmp)
00540                   cipher |= (ctmp << 12);
00541                   cipherString++;
00542                   HEXCHAR_TO_INT(*cipherString, ctmp)
00543                   cipher |= (ctmp << 8);
00544                   cipherString++;
00545                   HEXCHAR_TO_INT(*cipherString, ctmp)
00546                   cipher |= (ctmp << 4);
00547                   cipherString++;
00548                   HEXCHAR_TO_INT(*cipherString, ctmp)
00549                   cipher |= ctmp;
00550                   cipherString++;
00551               } else {
00552                   const int *cptr;
00553                   if (! isalpha(ndx))
00554                      Usage(progName);
00555                   cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites;
00556                   for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; )
00557                      /* do nothing */;
00558               }
00559               if (cipher > 0) {
00560                   SSL_CipherPrefSetDefault(cipher, PR_TRUE);
00561               } else {
00562                   Usage(progName);
00563               }
00564            }
00565        }
00566 
00567        client_main(port, connections, hostName);
00568 
00569 cleanup:
00570         if (doOcspCheck) {
00571             CERTCertDBHandle *handle = CERT_GetDefaultCertDB();
00572             CERT_DisableOCSPDefaultResponder(handle);        
00573             CERT_DisableOCSPChecking (handle);
00574         }
00575 
00576         if (NSS_Shutdown() != SECSuccess) {
00577             exit(1);
00578         }
00579 
00580        PR_Cleanup();
00581        PORT_Free(progName);
00582        return 0;
00583 }
00584