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 
00072 #include "vfyserv.h"
00073 
00074 #define RD_BUF_SIZE (60 * 1024)
00075 
00076 extern int ssl2CipherSuites[];
00077 extern int ssl3CipherSuites[];
00078 
00079 GlobalThreadMgr threadMGR;
00080 char *certNickname = NULL;
00081 char *hostName = NULL;
00082 char *password = NULL;
00083 unsigned short port = 0;
00084 
00085 static void
00086 Usage(const char *progName)
00087 {
00088        fprintf(stderr, 
00089 "Usage: %s [-p port] [-c connections] [-d dbdir] [-w password]\n"
00090 "\t\t[-C cipher(s)] hostname\n",
00091        progName);
00092        exit(1);
00093 }
00094 
00095 PRFileDesc *
00096 setupSSLSocket(PRNetAddr *addr)
00097 {
00098        PRFileDesc         *tcpSocket;
00099        PRFileDesc         *sslSocket;
00100        PRSocketOptionData   socketOption;
00101        PRStatus            prStatus;
00102        SECStatus           secStatus;
00103 
00104 
00105        tcpSocket = PR_NewTCPSocket();
00106        if (tcpSocket == NULL) {
00107               errWarn("PR_NewTCPSocket");
00108        }
00109 
00110        /* Make the socket blocking. */
00111        socketOption.option              = PR_SockOpt_Nonblocking;
00112        socketOption.value.non_blocking = PR_FALSE;
00113 
00114        prStatus = PR_SetSocketOption(tcpSocket, &socketOption);
00115        if (prStatus != PR_SUCCESS) {
00116               errWarn("PR_SetSocketOption");
00117               goto loser;
00118        } 
00119 
00120 
00121        /* Import the socket into the SSL layer. */
00122        sslSocket = SSL_ImportFD(NULL, tcpSocket);
00123        if (!sslSocket) {
00124               errWarn("SSL_ImportFD");
00125               goto loser;
00126        }
00127 
00128        /* Set configuration options. */
00129        secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE);
00130        if (secStatus != SECSuccess) {
00131               errWarn("SSL_OptionSet:SSL_SECURITY");
00132               goto loser;
00133        }
00134 
00135        secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
00136        if (secStatus != SECSuccess) {
00137               errWarn("SSL_OptionSet:SSL_HANDSHAKE_AS_CLIENT");
00138               goto loser;
00139        }
00140 
00141        /* Set SSL callback routines. */
00142        secStatus = SSL_GetClientAuthDataHook(sslSocket,
00143                                  (SSLGetClientAuthData)myGetClientAuthData,
00144                                  (void *)certNickname);
00145        if (secStatus != SECSuccess) {
00146               errWarn("SSL_GetClientAuthDataHook");
00147               goto loser;
00148        }
00149 
00150        secStatus = SSL_AuthCertificateHook(sslSocket,
00151                                           (SSLAuthCertificate)myAuthCertificate,
00152                                        (void *)CERT_GetDefaultCertDB());
00153        if (secStatus != SECSuccess) {
00154               errWarn("SSL_AuthCertificateHook");
00155               goto loser;
00156        }
00157 
00158        secStatus = SSL_BadCertHook(sslSocket, 
00159                                   (SSLBadCertHandler)myBadCertHandler, NULL);
00160        if (secStatus != SECSuccess) {
00161               errWarn("SSL_BadCertHook");
00162               goto loser;
00163        }
00164 
00165        secStatus = SSL_HandshakeCallback(sslSocket, 
00166                                         (SSLHandshakeCallback)myHandshakeCallback,
00167                                         NULL);
00168        if (secStatus != SECSuccess) {
00169               errWarn("SSL_HandshakeCallback");
00170               goto loser;
00171        }
00172 
00173        return sslSocket;
00174 
00175 loser:
00176 
00177        PR_Close(tcpSocket);
00178        return NULL;
00179 }
00180 
00181 
00182 const char requestString[] = {"GET /testfile HTTP/1.0\r\n\r\n" };
00183 
00184 SECStatus
00185 handle_connection(PRFileDesc *sslSocket, int connection)
00186 {
00187        int    countRead = 0;
00188        PRInt32  numBytes;
00189        char    *readBuffer;
00190 
00191        readBuffer = PORT_Alloc(RD_BUF_SIZE);
00192        if (!readBuffer) {
00193               exitErr("PORT_Alloc");
00194        }
00195 
00196        /* compose the http request here. */
00197 
00198        numBytes = PR_Write(sslSocket, requestString, strlen(requestString));
00199        if (numBytes <= 0) {
00200               errWarn("PR_Write");
00201               PR_Free(readBuffer);
00202               readBuffer = NULL;
00203               return SECFailure;
00204        }
00205 
00206        /* read until EOF */
00207        while (PR_TRUE) {
00208               numBytes = PR_Read(sslSocket, readBuffer, RD_BUF_SIZE);
00209               if (numBytes == 0) {
00210                      break; /* EOF */
00211               }
00212               if (numBytes < 0) {
00213                      errWarn("PR_Read");
00214                      break;
00215               }
00216               countRead += numBytes;
00217        }
00218 
00219        printSecurityInfo(stderr, sslSocket);
00220        
00221        PR_Free(readBuffer);
00222        readBuffer = NULL;
00223 
00224        /* Caller closes the socket. */
00225 
00226        fprintf(stderr, 
00227                "***** Connection %d read %d bytes total.\n", 
00228                connection, countRead);
00229 
00230        return SECSuccess;   /* success */
00231 }
00232 
00233 #define BYTE(n,i) (((i)>>((n)*8))&0xff)
00234 
00235 /* one copy of this function is launched in a separate thread for each
00236 ** connection to be made.
00237 */
00238 SECStatus
00239 do_connects(void *a, int connection)
00240 {
00241        PRNetAddr  *addr = (PRNetAddr *)a;
00242        PRFileDesc *sslSocket;
00243        PRHostEnt   hostEntry;
00244        char        buffer[PR_NETDB_BUF_SIZE];
00245        PRStatus    prStatus;
00246        PRIntn      hostenum;
00247        PRInt32    ip;
00248        SECStatus   secStatus;
00249 
00250        /* Set up SSL secure socket. */
00251        sslSocket = setupSSLSocket(addr);
00252        if (sslSocket == NULL) {
00253               errWarn("setupSSLSocket");
00254               return SECFailure;
00255        }
00256 
00257        secStatus = SSL_SetPKCS11PinArg(sslSocket, password);
00258        if (secStatus != SECSuccess) {
00259               errWarn("SSL_SetPKCS11PinArg");
00260               return secStatus;
00261        }
00262 
00263        secStatus = SSL_SetURL(sslSocket, hostName);
00264        if (secStatus != SECSuccess) {
00265               errWarn("SSL_SetURL");
00266               return secStatus;
00267        }
00268 
00269        /* Prepare and setup network connection. */
00270        prStatus = PR_GetHostByName(hostName, buffer, sizeof(buffer), &hostEntry);
00271        if (prStatus != PR_SUCCESS) {
00272               errWarn("PR_GetHostByName");
00273               return SECFailure;
00274        }
00275 
00276        hostenum = PR_EnumerateHostEnt(0, &hostEntry, port, addr);
00277        if (hostenum == -1) {
00278               errWarn("PR_EnumerateHostEnt");
00279               return SECFailure;
00280        }
00281 
00282        ip = PR_ntohl(addr->inet.ip);
00283        fprintf(stderr,
00284               "Connecting to host %s (addr %d.%d.%d.%d) on port %d\n",
00285                      hostName, BYTE(3,ip), BYTE(2,ip), BYTE(1,ip), 
00286                      BYTE(0,ip), PR_ntohs(addr->inet.port)); 
00287 
00288        prStatus = PR_Connect(sslSocket, addr, PR_INTERVAL_NO_TIMEOUT);
00289        if (prStatus != PR_SUCCESS) {
00290               errWarn("PR_Connect");
00291               return SECFailure;
00292        }
00293 
00294        /* Established SSL connection, ready to send data. */
00295 #if 0
00296        secStatus = SSL_ForceHandshake(sslSocket);
00297        if (secStatus != SECSuccess) {
00298               errWarn("SSL_ForceHandshake");
00299               return secStatus;
00300        }
00301 #endif
00302 
00303        secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_FALSE);
00304        if (secStatus != SECSuccess) {
00305               errWarn("SSL_ResetHandshake");
00306               prStatus = PR_Close(sslSocket);
00307               if (prStatus != PR_SUCCESS) {
00308                      errWarn("PR_Close");
00309               }
00310               return secStatus;
00311        }
00312 
00313        secStatus = handle_connection(sslSocket, connection);
00314        if (secStatus != SECSuccess) {
00315               /* error already printed out in handle_connection */
00316               /* errWarn("handle_connection"); */
00317               prStatus = PR_Close(sslSocket);
00318               if (prStatus != PR_SUCCESS) {
00319                      errWarn("PR_Close");
00320               }
00321               return secStatus;
00322        }
00323 
00324        PR_Close(sslSocket);
00325        return SECSuccess;
00326 }
00327 
00328 void
00329 client_main(unsigned short      port, 
00330             int                      connections, 
00331             const char *        hostName)
00332 {
00333        int                  i;
00334        SECStatus     secStatus;
00335        PRStatus    prStatus;
00336        PRInt32     rv;
00337        PRNetAddr     addr;
00338        PRHostEnt   hostEntry;
00339        char        buffer[256];
00340 
00341        /* Setup network connection. */
00342        prStatus = PR_GetHostByName(hostName, buffer, 256, &hostEntry);
00343        if (prStatus != PR_SUCCESS) {
00344               exitErr("PR_GetHostByName");
00345        }
00346 
00347        rv = PR_EnumerateHostEnt(0, &hostEntry, port, &addr);
00348        if (rv < 0) {
00349               exitErr("PR_EnumerateHostEnt");
00350        }
00351 
00352        secStatus = launch_thread(&threadMGR, do_connects, &addr, 1);
00353        if (secStatus != SECSuccess) {
00354               exitErr("launch_thread");
00355        }
00356 
00357        if (connections > 1) {
00358               /* wait for the first connection to terminate, then launch the rest. */
00359               reap_threads(&threadMGR);
00360               /* Start up the connections */
00361               for (i = 2; i <= connections; ++i) {
00362                      secStatus = launch_thread(&threadMGR, do_connects, &addr, i);
00363                      if (secStatus != SECSuccess) {
00364                             errWarn("launch_thread");
00365                      }
00366               }
00367        }
00368 
00369        reap_threads(&threadMGR);
00370        destroy_thread_data(&threadMGR);
00371 }
00372 
00373 #define HEXCHAR_TO_INT(c, i) \
00374     if (((c) >= '0') && ((c) <= '9')) { \
00375        i = (c) - '0'; \
00376     } else if (((c) >= 'a') && ((c) <= 'f')) { \
00377        i = (c) - 'a' + 10; \
00378     } else if (((c) >= 'A') && ((c) <= 'F')) { \
00379        i = (c) - 'A' + 10; \
00380     } else { \
00381        Usage(progName); \
00382     }
00383 
00384 int
00385 main(int argc, char **argv)
00386 {
00387        char *               certDir = NULL;
00388        char *               progName     = NULL;
00389        int                  connections  = 1;
00390        char *               cipherString = NULL;
00391        SECStatus            secStatus;
00392        PLOptState *         optstate;
00393        PLOptStatus          status;
00394 
00395        /* Call the NSPR initialization routines */
00396        PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
00397 
00398        progName = PORT_Strdup(argv[0]);
00399 
00400        hostName = NULL;
00401        optstate = PL_CreateOptState(argc, argv, "C:c:d:n:p:w:");
00402        while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
00403               switch(optstate->option) {
00404               case 'C' : cipherString = PL_strdup(optstate->value); break;
00405               case 'c' : connections = PORT_Atoi(optstate->value);  break;
00406               case 'd' : certDir = PL_strdup(optstate->value);      break;
00407               case 'p' : port = PORT_Atoi(optstate->value);         break;
00408               case 'w' : password = PL_strdup(optstate->value);      break;
00409               case '\0': hostName = PL_strdup(optstate->value);     break;
00410               default  : Usage(progName);
00411               }
00412        }
00413 
00414        if (port == 0) {
00415               port = 443;
00416        }
00417 
00418        if (port == 0 || hostName == NULL)
00419               Usage(progName);
00420 
00421        /* Set our password function callback. */
00422        PK11_SetPasswordFunc(myPasswd);
00423 
00424        /* Initialize the NSS libraries. */
00425        if (certDir) {
00426            secStatus = NSS_Init(certDir);
00427        } else {
00428            secStatus = NSS_NoDB_Init(NULL);
00429 
00430            /* load the builtins */
00431            SECMOD_AddNewModule("Builtins",
00432                             DLL_PREFIX"nssckbi."DLL_SUFFIX, 0, 0);
00433        }
00434        if (secStatus != SECSuccess) {
00435               exitErr("NSS_Init");
00436        }
00437        SECU_RegisterDynamicOids();
00438 
00439        /* All cipher suites except RSA_NULL_MD5 are enabled by 
00440         * Domestic Policy. */
00441        NSS_SetDomesticPolicy();
00442        SSL_CipherPrefSetDefault(SSL_RSA_WITH_NULL_MD5, PR_TRUE);
00443 
00444        /* all the SSL2 and SSL3 cipher suites are enabled by default. */
00445        if (cipherString) {
00446            int ndx;
00447 
00448            /* disable all the ciphers, then enable the ones we want. */
00449            disableAllSSLCiphers();
00450 
00451            while (0 != (ndx = *cipherString++)) {
00452               int  cipher;
00453 
00454               if (ndx == ':') {
00455                   int ctmp;
00456 
00457                   cipher = 0;
00458                   HEXCHAR_TO_INT(*cipherString, ctmp)
00459                   cipher |= (ctmp << 12);
00460                   cipherString++;
00461                   HEXCHAR_TO_INT(*cipherString, ctmp)
00462                   cipher |= (ctmp << 8);
00463                   cipherString++;
00464                   HEXCHAR_TO_INT(*cipherString, ctmp)
00465                   cipher |= (ctmp << 4);
00466                   cipherString++;
00467                   HEXCHAR_TO_INT(*cipherString, ctmp)
00468                   cipher |= ctmp;
00469                   cipherString++;
00470               } else {
00471                   const int *cptr;
00472                   if (! isalpha(ndx))
00473                      Usage(progName);
00474                   cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites;
00475                   for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; )
00476                      /* do nothing */;
00477               }
00478               if (cipher > 0) {
00479                   SSL_CipherPrefSetDefault(cipher, PR_TRUE);
00480               } else {
00481                   Usage(progName);
00482               }
00483            }
00484        }
00485 
00486        client_main(port, connections, hostName);
00487 
00488         if (NSS_Shutdown() != SECSuccess) {
00489             exit(1);
00490         }
00491 
00492        PR_Cleanup();
00493        PORT_Free(progName);
00494        return 0;
00495 }
00496