Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
/****************************************************************************
* 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 ssl3CipherSuites[];
extern int numSSL3CipherSuites;
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(int connections)
{
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(TLS_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 = 0;
if (ndx == ':') {
int ctmp = 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 {
if (!isalpha((unsigned char)ndx))
Usage(progName);
ndx = tolower((unsigned char)ndx) - 'a';
if (ndx < numSSL3CipherSuites) {
cipher = ssl3CipherSuites[ndx];
}
}
if (cipher > 0) {
SECStatus rv = SSL_CipherPrefSetDefault(cipher, PR_TRUE);
if (rv != SECSuccess) {
SECU_PrintError(progName,
"error setting cipher default preference");
goto cleanup;
}
} else {
Usage(progName);
}
}
}
client_main(connections);
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;
}