diff options
Diffstat (limited to 'security/nss/cmd/vfyserv/vfyserv.c')
-rw-r--r-- | security/nss/cmd/vfyserv/vfyserv.c | 594 |
1 files changed, 594 insertions, 0 deletions
diff --git a/security/nss/cmd/vfyserv/vfyserv.c b/security/nss/cmd/vfyserv/vfyserv.c new file mode 100644 index 000000000..84ee69eef --- /dev/null +++ b/security/nss/cmd/vfyserv/vfyserv.c @@ -0,0 +1,594 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/**************************************************************************** + * SSL client program that tests a server for proper operation of SSL2, * + * SSL3, and TLS. Test propder certificate installation. * + * * + * This code was modified from the SSLSample code also kept in the NSS * + * directory. * + ****************************************************************************/ + +#include <stdio.h> +#include <string.h> + +#if defined(XP_UNIX) +#include <unistd.h> +#endif + +#include "prerror.h" + +#include "pk11func.h" +#include "secmod.h" +#include "secitem.h" + + +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> + +#include "nspr.h" +#include "plgetopt.h" +#include "prio.h" +#include "prnetdb.h" +#include "nss.h" +#include "secutil.h" +#include "ocsp.h" + +#include "vfyserv.h" + +#define RD_BUF_SIZE (60 * 1024) + +extern int ssl2CipherSuites[]; +extern int ssl3CipherSuites[]; + +GlobalThreadMgr threadMGR; +char *certNickname = NULL; +char *hostName = NULL; +secuPWData pwdata = { PW_NONE, 0 }; +unsigned short port = 0; +PRBool dumpChain; + +static void +Usage(const char *progName) +{ + PRFileDesc *pr_stderr; + + pr_stderr = PR_STDERR; + + PR_fprintf(pr_stderr, "Usage:\n" + " %s [-c ] [-o] [-p port] [-d dbdir] [-w password] [-f pwfile]\n" + " \t\t[-C cipher(s)] [-l <url> -t <nickname> ] hostname", + progName); + PR_fprintf (pr_stderr, "\nWhere:\n"); + PR_fprintf (pr_stderr, + " %-13s dump server cert chain into files\n", + "-c"); + PR_fprintf (pr_stderr, + " %-13s perform server cert OCSP check\n", + "-o"); + PR_fprintf (pr_stderr, + " %-13s server port to be used\n", + "-p"); + PR_fprintf (pr_stderr, + " %-13s use security databases in \"dbdir\"\n", + "-d dbdir"); + PR_fprintf (pr_stderr, + " %-13s key database password\n", + "-w password"); + PR_fprintf (pr_stderr, + " %-13s token password file\n", + "-f pwfile"); + PR_fprintf (pr_stderr, + " %-13s communication cipher list\n", + "-C cipher(s)"); + PR_fprintf (pr_stderr, + " %-13s OCSP responder location. This location is used to\n" + " %-13s check status of a server certificate. If not \n" + " %-13s specified, location will be taken from the AIA\n" + " %-13s server certificate extension.\n", + "-l url", "", "", ""); + PR_fprintf (pr_stderr, + " %-13s OCSP Trusted Responder Cert nickname\n\n", + "-t nickname"); + + exit(1); +} + +PRFileDesc * +setupSSLSocket(PRNetAddr *addr) +{ + PRFileDesc *tcpSocket; + PRFileDesc *sslSocket; + PRSocketOptionData socketOption; + PRStatus prStatus; + SECStatus secStatus; + + + tcpSocket = PR_NewTCPSocket(); + if (tcpSocket == NULL) { + errWarn("PR_NewTCPSocket"); + } + + /* Make the socket blocking. */ + socketOption.option = PR_SockOpt_Nonblocking; + socketOption.value.non_blocking = PR_FALSE; + + prStatus = PR_SetSocketOption(tcpSocket, &socketOption); + if (prStatus != PR_SUCCESS) { + errWarn("PR_SetSocketOption"); + goto loser; + } + + + /* Import the socket into the SSL layer. */ + sslSocket = SSL_ImportFD(NULL, tcpSocket); + if (!sslSocket) { + errWarn("SSL_ImportFD"); + goto loser; + } + + /* Set configuration options. */ + secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE); + if (secStatus != SECSuccess) { + errWarn("SSL_OptionSet:SSL_SECURITY"); + goto loser; + } + + secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); + if (secStatus != SECSuccess) { + errWarn("SSL_OptionSet:SSL_HANDSHAKE_AS_CLIENT"); + goto loser; + } + + /* Set SSL callback routines. */ + secStatus = SSL_GetClientAuthDataHook(sslSocket, + (SSLGetClientAuthData)myGetClientAuthData, + (void *)certNickname); + if (secStatus != SECSuccess) { + errWarn("SSL_GetClientAuthDataHook"); + goto loser; + } + + secStatus = SSL_AuthCertificateHook(sslSocket, + (SSLAuthCertificate)myAuthCertificate, + (void *)CERT_GetDefaultCertDB()); + if (secStatus != SECSuccess) { + errWarn("SSL_AuthCertificateHook"); + goto loser; + } + + secStatus = SSL_BadCertHook(sslSocket, + (SSLBadCertHandler)myBadCertHandler, NULL); + if (secStatus != SECSuccess) { + errWarn("SSL_BadCertHook"); + goto loser; + } + + secStatus = SSL_HandshakeCallback(sslSocket, + myHandshakeCallback, + NULL); + if (secStatus != SECSuccess) { + errWarn("SSL_HandshakeCallback"); + goto loser; + } + + return sslSocket; + +loser: + + PR_Close(tcpSocket); + return NULL; +} + + +const char requestString[] = {"GET /testfile HTTP/1.0\r\n\r\n" }; + +SECStatus +handle_connection(PRFileDesc *sslSocket, int connection) +{ + int countRead = 0; + PRInt32 numBytes; + char *readBuffer; + + readBuffer = PORT_Alloc(RD_BUF_SIZE); + if (!readBuffer) { + exitErr("PORT_Alloc"); + } + + /* compose the http request here. */ + + numBytes = PR_Write(sslSocket, requestString, strlen(requestString)); + if (numBytes <= 0) { + errWarn("PR_Write"); + PR_Free(readBuffer); + readBuffer = NULL; + return SECFailure; + } + + /* read until EOF */ + while (PR_TRUE) { + numBytes = PR_Read(sslSocket, readBuffer, RD_BUF_SIZE); + if (numBytes == 0) { + break; /* EOF */ + } + if (numBytes < 0) { + errWarn("PR_Read"); + break; + } + countRead += numBytes; + } + + printSecurityInfo(stderr, sslSocket); + + PR_Free(readBuffer); + readBuffer = NULL; + + /* Caller closes the socket. */ + + fprintf(stderr, + "***** Connection %d read %d bytes total.\n", + connection, countRead); + + return SECSuccess; /* success */ +} + +#define BYTE(n,i) (((i)>>((n)*8))&0xff) + +/* one copy of this function is launched in a separate thread for each +** connection to be made. +*/ +SECStatus +do_connects(void *a, int connection) +{ + PRNetAddr *addr = (PRNetAddr *)a; + PRFileDesc *sslSocket; + PRHostEnt hostEntry; + char buffer[PR_NETDB_BUF_SIZE]; + PRStatus prStatus; + PRIntn hostenum; + PRInt32 ip; + SECStatus secStatus; + + /* Set up SSL secure socket. */ + sslSocket = setupSSLSocket(addr); + if (sslSocket == NULL) { + errWarn("setupSSLSocket"); + return SECFailure; + } + + secStatus = SSL_SetPKCS11PinArg(sslSocket, &pwdata); + if (secStatus != SECSuccess) { + errWarn("SSL_SetPKCS11PinArg"); + return secStatus; + } + + secStatus = SSL_SetURL(sslSocket, hostName); + if (secStatus != SECSuccess) { + errWarn("SSL_SetURL"); + return secStatus; + } + + /* Prepare and setup network connection. */ + prStatus = PR_GetHostByName(hostName, buffer, sizeof(buffer), &hostEntry); + if (prStatus != PR_SUCCESS) { + errWarn("PR_GetHostByName"); + return SECFailure; + } + + hostenum = PR_EnumerateHostEnt(0, &hostEntry, port, addr); + if (hostenum == -1) { + errWarn("PR_EnumerateHostEnt"); + return SECFailure; + } + + ip = PR_ntohl(addr->inet.ip); + fprintf(stderr, + "Connecting to host %s (addr %d.%d.%d.%d) on port %d\n", + hostName, BYTE(3,ip), BYTE(2,ip), BYTE(1,ip), + BYTE(0,ip), PR_ntohs(addr->inet.port)); + + prStatus = PR_Connect(sslSocket, addr, PR_INTERVAL_NO_TIMEOUT); + if (prStatus != PR_SUCCESS) { + errWarn("PR_Connect"); + return SECFailure; + } + + /* Established SSL connection, ready to send data. */ +#if 0 + secStatus = SSL_ForceHandshake(sslSocket); + if (secStatus != SECSuccess) { + errWarn("SSL_ForceHandshake"); + return secStatus; + } +#endif + + secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_FALSE); + if (secStatus != SECSuccess) { + errWarn("SSL_ResetHandshake"); + prStatus = PR_Close(sslSocket); + if (prStatus != PR_SUCCESS) { + errWarn("PR_Close"); + } + return secStatus; + } + + secStatus = handle_connection(sslSocket, connection); + if (secStatus != SECSuccess) { + /* error already printed out in handle_connection */ + /* errWarn("handle_connection"); */ + prStatus = PR_Close(sslSocket); + if (prStatus != PR_SUCCESS) { + errWarn("PR_Close"); + } + return secStatus; + } + + PR_Close(sslSocket); + return SECSuccess; +} + +void +client_main(unsigned short port, + int connections, + const char * hostName) +{ + int i; + SECStatus secStatus; + PRStatus prStatus; + PRInt32 rv; + PRNetAddr addr; + PRHostEnt hostEntry; + char buffer[PR_NETDB_BUF_SIZE]; + + /* Setup network connection. */ + prStatus = PR_GetHostByName(hostName, buffer, sizeof(buffer), &hostEntry); + if (prStatus != PR_SUCCESS) { + exitErr("PR_GetHostByName"); + } + + rv = PR_EnumerateHostEnt(0, &hostEntry, port, &addr); + if (rv < 0) { + exitErr("PR_EnumerateHostEnt"); + } + + secStatus = launch_thread(&threadMGR, do_connects, &addr, 1); + if (secStatus != SECSuccess) { + exitErr("launch_thread"); + } + + if (connections > 1) { + /* wait for the first connection to terminate, then launch the rest. */ + reap_threads(&threadMGR); + /* Start up the connections */ + for (i = 2; i <= connections; ++i) { + secStatus = launch_thread(&threadMGR, do_connects, &addr, i); + if (secStatus != SECSuccess) { + errWarn("launch_thread"); + } + } + } + + reap_threads(&threadMGR); + destroy_thread_data(&threadMGR); +} + +#define HEXCHAR_TO_INT(c, i) \ + if (((c) >= '0') && ((c) <= '9')) { \ + i = (c) - '0'; \ + } else if (((c) >= 'a') && ((c) <= 'f')) { \ + i = (c) - 'a' + 10; \ + } else if (((c) >= 'A') && ((c) <= 'F')) { \ + i = (c) - 'A' + 10; \ + } else { \ + Usage(progName); \ + } + +int +main(int argc, char **argv) +{ + char * certDir = NULL; + char * progName = NULL; + int connections = 1; + char * cipherString = NULL; + char * respUrl = NULL; + char * respCertName = NULL; + SECStatus secStatus; + PLOptState * optstate; + PLOptStatus status; + PRBool doOcspCheck = PR_FALSE; + + /* Call the NSPR initialization routines */ + PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + + progName = PORT_Strdup(argv[0]); + + hostName = NULL; + optstate = PL_CreateOptState(argc, argv, "C:cd:f:l:n:p:ot:w:"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch(optstate->option) { + case 'C' : cipherString = PL_strdup(optstate->value); break; + case 'c' : dumpChain = PR_TRUE; break; + case 'd' : certDir = PL_strdup(optstate->value); break; + case 'l' : respUrl = PL_strdup(optstate->value); break; + case 'p' : port = PORT_Atoi(optstate->value); break; + case 'o' : doOcspCheck = PR_TRUE; break; + case 't' : respCertName = PL_strdup(optstate->value); break; + case 'w': + pwdata.source = PW_PLAINTEXT; + pwdata.data = PORT_Strdup(optstate->value); + break; + + case 'f': + pwdata.source = PW_FROMFILE; + pwdata.data = PORT_Strdup(optstate->value); + break; + case '\0': hostName = PL_strdup(optstate->value); break; + default : Usage(progName); + } + } + + if (port == 0) { + port = 443; + } + + if (port == 0 || hostName == NULL) + Usage(progName); + + if (doOcspCheck && + ((respCertName != NULL && respUrl == NULL) || + (respUrl != NULL && respCertName == NULL))) { + SECU_PrintError (progName, "options -l <url> and -t " + "<responder> must be used together"); + Usage(progName); + } + + PK11_SetPasswordFunc(SECU_GetModulePassword); + + /* Initialize the NSS libraries. */ + if (certDir) { + secStatus = NSS_Init(certDir); + } else { + secStatus = NSS_NoDB_Init(NULL); + + /* load the builtins */ + SECMOD_AddNewModule("Builtins", + DLL_PREFIX"nssckbi."DLL_SUFFIX, 0, 0); + } + if (secStatus != SECSuccess) { + exitErr("NSS_Init"); + } + SECU_RegisterDynamicOids(); + + if (doOcspCheck == PR_TRUE) { + SECStatus rv; + CERTCertDBHandle *handle = CERT_GetDefaultCertDB(); + if (handle == NULL) { + SECU_PrintError (progName, "problem getting certdb handle"); + goto cleanup; + } + + rv = CERT_EnableOCSPChecking (handle); + if (rv != SECSuccess) { + SECU_PrintError (progName, "error enabling OCSP checking"); + goto cleanup; + } + + if (respUrl != NULL) { + rv = CERT_SetOCSPDefaultResponder (handle, respUrl, + respCertName); + if (rv != SECSuccess) { + SECU_PrintError (progName, + "error setting default responder"); + goto cleanup; + } + + rv = CERT_EnableOCSPDefaultResponder (handle); + if (rv != SECSuccess) { + SECU_PrintError (progName, + "error enabling default responder"); + goto cleanup; + } + } + } + + /* All cipher suites except RSA_NULL_MD5 are enabled by + * Domestic Policy. */ + NSS_SetDomesticPolicy(); + SSL_CipherPrefSetDefault(SSL_RSA_WITH_NULL_MD5, PR_TRUE); + + /* all the SSL2 and SSL3 cipher suites are enabled by default. */ + if (cipherString) { + int ndx; + + /* disable all the ciphers, then enable the ones we want. */ + disableAllSSLCiphers(); + + while (0 != (ndx = *cipherString++)) { + int cipher; + + if (ndx == ':') { + int ctmp; + + cipher = 0; + HEXCHAR_TO_INT(*cipherString, ctmp) + cipher |= (ctmp << 12); + cipherString++; + HEXCHAR_TO_INT(*cipherString, ctmp) + cipher |= (ctmp << 8); + cipherString++; + HEXCHAR_TO_INT(*cipherString, ctmp) + cipher |= (ctmp << 4); + cipherString++; + HEXCHAR_TO_INT(*cipherString, ctmp) + cipher |= ctmp; + cipherString++; + } else { + const int *cptr; + if (! isalpha(ndx)) + Usage(progName); + cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites; + for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; ) + /* do nothing */; + } + if (cipher > 0) { + SSL_CipherPrefSetDefault(cipher, PR_TRUE); + } else { + Usage(progName); + } + } + } + + client_main(port, connections, hostName); + +cleanup: + if (doOcspCheck) { + CERTCertDBHandle *handle = CERT_GetDefaultCertDB(); + CERT_DisableOCSPDefaultResponder(handle); + CERT_DisableOCSPChecking (handle); + } + + if (NSS_Shutdown() != SECSuccess) { + exit(1); + } + + PR_Cleanup(); + PORT_Free(progName); + return 0; +} + |