diff options
Diffstat (limited to 'security/nss/cmd/tstclnt')
-rw-r--r-- | security/nss/cmd/tstclnt/Makefile | 78 | ||||
-rw-r--r-- | security/nss/cmd/tstclnt/manifest.mn | 54 | ||||
-rw-r--r-- | security/nss/cmd/tstclnt/tstclnt.c | 1065 |
3 files changed, 1197 insertions, 0 deletions
diff --git a/security/nss/cmd/tstclnt/Makefile b/security/nss/cmd/tstclnt/Makefile new file mode 100644 index 000000000..297114522 --- /dev/null +++ b/security/nss/cmd/tstclnt/Makefile @@ -0,0 +1,78 @@ +#! gmake +# +# ***** 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): +# +# 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 ***** + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +include ../platlibs.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + +#include ../platlibs.mk + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + +include ../platrules.mk + diff --git a/security/nss/cmd/tstclnt/manifest.mn b/security/nss/cmd/tstclnt/manifest.mn new file mode 100644 index 000000000..fa3f5fe16 --- /dev/null +++ b/security/nss/cmd/tstclnt/manifest.mn @@ -0,0 +1,54 @@ +# +# ***** 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): +# +# 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 ***** + +CORE_DEPTH = ../../.. + +# MODULE public and private header directories are implicitly REQUIRED. +MODULE = nss + +# This next line is used by .mk files +# and gets translated into $LINCS in manifest.mnw +# The MODULE is always implicitly required. +# Listing it here in REQUIRES makes it appear twice in the cc command line. +REQUIRES = seccmd dbm + +# DIRS = + +CSRCS = tstclnt.c + +PROGRAM = tstclnt + diff --git a/security/nss/cmd/tstclnt/tstclnt.c b/security/nss/cmd/tstclnt/tstclnt.c new file mode 100644 index 000000000..06c154108 --- /dev/null +++ b/security/nss/cmd/tstclnt/tstclnt.c @@ -0,0 +1,1065 @@ +/* ***** 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): + * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories + * 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 ***** */ + +/* +** +** Sample client side test program that uses SSL and NSS +** +*/ + +#include "secutil.h" + +#if defined(XP_UNIX) +#include <unistd.h> +#else +#include <ctype.h> /* for isalpha() */ +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> + +#include "nspr.h" +#include "prio.h" +#include "prnetdb.h" +#include "nss.h" +#include "ssl.h" +#include "sslproto.h" +#include "pk11func.h" +#include "plgetopt.h" +#include "plstr.h" + +#if defined(WIN32) +#include <fcntl.h> +#include <io.h> +#endif + +#define PRINTF if (verbose) printf +#define FPRINTF if (verbose) fprintf + +#define MAX_WAIT_FOR_SERVER 600 +#define WAIT_INTERVAL 100 + +PRIntervalTime maxInterval = PR_INTERVAL_NO_TIMEOUT; + +int ssl2CipherSuites[] = { + SSL_EN_RC4_128_WITH_MD5, /* A */ + SSL_EN_RC4_128_EXPORT40_WITH_MD5, /* B */ + SSL_EN_RC2_128_CBC_WITH_MD5, /* C */ + SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */ + SSL_EN_DES_64_CBC_WITH_MD5, /* E */ + SSL_EN_DES_192_EDE3_CBC_WITH_MD5, /* F */ + 0 +}; + +int ssl3CipherSuites[] = { + -1, /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA* a */ + -1, /* SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, * b */ + SSL_RSA_WITH_RC4_128_MD5, /* c */ + SSL_RSA_WITH_3DES_EDE_CBC_SHA, /* d */ + SSL_RSA_WITH_DES_CBC_SHA, /* e */ + SSL_RSA_EXPORT_WITH_RC4_40_MD5, /* f */ + SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* g */ + -1, /* SSL_FORTEZZA_DMS_WITH_NULL_SHA, * h */ + SSL_RSA_WITH_NULL_MD5, /* i */ + SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, /* j */ + SSL_RSA_FIPS_WITH_DES_CBC_SHA, /* k */ + TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, /* l */ + TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, /* m */ + SSL_RSA_WITH_RC4_128_SHA, /* n */ + TLS_DHE_DSS_WITH_RC4_128_SHA, /* o */ + SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, /* p */ + SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, /* q */ + SSL_DHE_RSA_WITH_DES_CBC_SHA, /* r */ + SSL_DHE_DSS_WITH_DES_CBC_SHA, /* s */ + TLS_DHE_DSS_WITH_AES_128_CBC_SHA, /* t */ + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, /* u */ + TLS_RSA_WITH_AES_128_CBC_SHA, /* v */ + TLS_DHE_DSS_WITH_AES_256_CBC_SHA, /* w */ + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, /* x */ + TLS_RSA_WITH_AES_256_CBC_SHA, /* y */ + SSL_RSA_WITH_NULL_SHA, /* z */ + 0 +}; + +unsigned long __cmp_umuls; +PRBool verbose; +int renegotiate = 0; + +static char *progName; + +secuPWData pwdata = { PW_NONE, 0 }; + +void printSecurityInfo(PRFileDesc *fd) +{ + CERTCertificate * cert; + SSL3Statistics * ssl3stats = SSL_GetStatistics(); + SECStatus result; + SSLChannelInfo channel; + SSLCipherSuiteInfo suite; + + result = SSL_GetChannelInfo(fd, &channel, sizeof channel); + if (result == SECSuccess && + channel.length == sizeof channel && + channel.cipherSuite) { + result = SSL_GetCipherSuiteInfo(channel.cipherSuite, + &suite, sizeof suite); + if (result == SECSuccess) { + FPRINTF(stderr, + "tstclnt: SSL version %d.%d using %d-bit %s with %d-bit %s MAC\n", + channel.protocolVersion >> 8, channel.protocolVersion & 0xff, + suite.effectiveKeyBits, suite.symCipherName, + suite.macBits, suite.macAlgorithmName); + FPRINTF(stderr, + "tstclnt: Server Auth: %d-bit %s, Key Exchange: %d-bit %s\n", + channel.authKeyBits, suite.authAlgorithmName, + channel.keaKeyBits, suite.keaTypeName); + } + } + cert = SSL_RevealCert(fd); + if (cert) { + char * ip = CERT_NameToAscii(&cert->issuer); + char * sp = CERT_NameToAscii(&cert->subject); + if (sp) { + fprintf(stderr, "subject DN: %s\n", sp); + PORT_Free(sp); + } + if (ip) { + fprintf(stderr, "issuer DN: %s\n", ip); + PORT_Free(ip); + } + CERT_DestroyCertificate(cert); + cert = NULL; + } + fprintf(stderr, + "%ld cache hits; %ld cache misses, %ld cache not reusable\n" + "%ld stateless resumes\n", + ssl3stats->hsh_sid_cache_hits, ssl3stats->hsh_sid_cache_misses, + ssl3stats->hsh_sid_cache_not_ok, ssl3stats->hsh_sid_stateless_resumes); +} + +void +handshakeCallback(PRFileDesc *fd, void *client_data) +{ + printSecurityInfo(fd); + if (renegotiate > 0) { + renegotiate--; + SSL_ReHandshake(fd, PR_FALSE); + } +} + +static void Usage(const char *progName) +{ + fprintf(stderr, +"Usage: %s -h host [-p port] [-d certdir] [-n nickname] [-23BTfosvxr] \n" +" [-c ciphers] [-w passwd] [-W pwfile] [-q]\n", progName); + fprintf(stderr, "%-20s Hostname to connect with\n", "-h host"); + fprintf(stderr, "%-20s Port number for SSL server\n", "-p port"); + fprintf(stderr, + "%-20s Directory with cert database (default is ~/.netscape)\n", + "-d certdir"); + fprintf(stderr, "%-20s Nickname of key and cert for client auth\n", + "-n nickname"); + fprintf(stderr, + "%-20s Bypass PKCS11 layer for SSL encryption and MACing.\n", "-B"); + fprintf(stderr, "%-20s Disable SSL v2.\n", "-2"); + fprintf(stderr, "%-20s Disable SSL v3.\n", "-3"); + fprintf(stderr, "%-20s Disable TLS (SSL v3.1).\n", "-T"); + fprintf(stderr, "%-20s Prints only payload data. Skips HTTP header.\n", "-S"); + fprintf(stderr, "%-20s Client speaks first. \n", "-f"); + fprintf(stderr, "%-20s Override bad server cert. Make it OK.\n", "-o"); + fprintf(stderr, "%-20s Disable SSL socket locking.\n", "-s"); + fprintf(stderr, "%-20s Verbose progress reporting.\n", "-v"); + fprintf(stderr, "%-20s Use export policy.\n", "-x"); + fprintf(stderr, "%-20s Ping the server and then exit.\n", "-q"); + fprintf(stderr, "%-20s Renegotiate with session resumption.\n", "-r"); + fprintf(stderr, "%-20s Enable the session ticket extension.\n", "-u"); + fprintf(stderr, "%-20s Letter(s) chosen from the following list\n", + "-c ciphers"); + fprintf(stderr, +"A SSL2 RC4 128 WITH MD5\n" +"B SSL2 RC4 128 EXPORT40 WITH MD5\n" +"C SSL2 RC2 128 CBC WITH MD5\n" +"D SSL2 RC2 128 CBC EXPORT40 WITH MD5\n" +"E SSL2 DES 64 CBC WITH MD5\n" +"F SSL2 DES 192 EDE3 CBC WITH MD5\n" +"\n" +"c SSL3 RSA WITH RC4 128 MD5\n" +"d SSL3 RSA WITH 3DES EDE CBC SHA\n" +"e SSL3 RSA WITH DES CBC SHA\n" +"f SSL3 RSA EXPORT WITH RC4 40 MD5\n" +"g SSL3 RSA EXPORT WITH RC2 CBC 40 MD5\n" +"i SSL3 RSA WITH NULL MD5\n" +"j SSL3 RSA FIPS WITH 3DES EDE CBC SHA\n" +"k SSL3 RSA FIPS WITH DES CBC SHA\n" +"l SSL3 RSA EXPORT WITH DES CBC SHA\t(new)\n" +"m SSL3 RSA EXPORT WITH RC4 56 SHA\t(new)\n" +"n SSL3 RSA WITH RC4 128 SHA\n" +"o SSL3 DHE DSS WITH RC4 128 SHA\n" +"p SSL3 DHE RSA WITH 3DES EDE CBC SHA\n" +"q SSL3 DHE DSS WITH 3DES EDE CBC SHA\n" +"r SSL3 DHE RSA WITH DES CBC SHA\n" +"s SSL3 DHE DSS WITH DES CBC SHA\n" +"t SSL3 DHE DSS WITH AES 128 CBC SHA\n" +"u SSL3 DHE RSA WITH AES 128 CBC SHA\n" +"v SSL3 RSA WITH AES 128 CBC SHA\n" +"w SSL3 DHE DSS WITH AES 256 CBC SHA\n" +"x SSL3 DHE RSA WITH AES 256 CBC SHA\n" +"y SSL3 RSA WITH AES 256 CBC SHA\n" +"z SSL3 RSA WITH NULL SHA\n" +"\n" +":WXYZ Use cipher with hex code { 0xWX , 0xYZ } in TLS\n" + ); + exit(1); +} + +void +milliPause(PRUint32 milli) +{ + PRIntervalTime ticks = PR_MillisecondsToInterval(milli); + PR_Sleep(ticks); +} + +void +disableAllSSLCiphers(void) +{ + const PRUint16 *cipherSuites = SSL_ImplementedCiphers; + int i = SSL_NumImplementedCiphers; + SECStatus rv; + + /* disable all the SSL3 cipher suites */ + while (--i >= 0) { + PRUint16 suite = cipherSuites[i]; + rv = SSL_CipherPrefSetDefault(suite, PR_FALSE); + if (rv != SECSuccess) { + PRErrorCode err = PR_GetError(); + fprintf(stderr, + "SSL_CipherPrefSet didn't like value 0x%04x (i = %d): %s\n", + suite, i, SECU_Strerror(err)); + exit(2); + } + } +} + +/* + * Callback is called when incoming certificate is not valid. + * Returns SECSuccess to accept the cert anyway, SECFailure to reject. + */ +static SECStatus +ownBadCertHandler(void * arg, PRFileDesc * socket) +{ + PRErrorCode err = PR_GetError(); + /* can log invalid cert here */ + fprintf(stderr, "Bad server certificate: %d, %s\n", err, + SECU_Strerror(err)); + return SECSuccess; /* override, say it's OK. */ +} + +SECStatus +own_GetClientAuthData(void * arg, + PRFileDesc * socket, + struct CERTDistNamesStr * caNames, + struct CERTCertificateStr ** pRetCert, + struct SECKEYPrivateKeyStr **pRetKey) +{ + if (verbose > 1) { + SECStatus rv; + fprintf(stderr, "Server requested Client Authentication\n"); + if (caNames && caNames->nnames > 0) { + PLArenaPool *arena = caNames->arena; + if (!arena) + arena = PORT_NewArena(2048); + if (arena) { + int i; + for (i = 0; i < caNames->nnames; ++i) { + char *nameString; + CERTName dn; + rv = SEC_QuickDERDecodeItem(arena, + &dn, + SEC_ASN1_GET(CERT_NameTemplate), + caNames->names + i); + if (rv != SECSuccess) + continue; + nameString = CERT_NameToAscii(&dn); + if (!nameString) + continue; + fprintf(stderr, "CA[%d]: %s\n", i + 1, nameString); + PORT_Free(nameString); + } + if (!caNames->arena) { + PORT_FreeArena(arena, PR_FALSE); + } + } + } + rv = NSS_GetClientAuthData(arg, socket, caNames, pRetCert, pRetKey); + if (rv == SECSuccess && *pRetCert) { + char *nameString = CERT_NameToAscii(&((*pRetCert)->subject)); + if (nameString) { + fprintf(stderr, "sent cert: %s\n", nameString); + PORT_Free(nameString); + } + } else { + fprintf(stderr, "send no cert\n"); + } + return rv; + } + return NSS_GetClientAuthData(arg, socket, caNames, pRetCert, pRetKey); +} + +#if defined(WIN32) || defined(OS2) +void +thread_main(void * arg) +{ + PRFileDesc * ps = (PRFileDesc *)arg; + PRFileDesc * std_in = PR_GetSpecialFD(PR_StandardInput); + int wc, rc; + char buf[256]; + +#ifdef WIN32 + { + /* Put stdin into O_BINARY mode + ** or else incoming \r\n's will become \n's. + */ + int smrv = _setmode(_fileno(stdin), _O_BINARY); + if (smrv == -1) { + fprintf(stderr, + "%s: Cannot change stdin to binary mode. Use -i option instead.\n", + progName); + /* plow ahead anyway */ + } + } +#endif + + do { + rc = PR_Read(std_in, buf, sizeof buf); + if (rc <= 0) + break; + wc = PR_Send(ps, buf, rc, 0, maxInterval); + } while (wc == rc); + PR_Close(ps); +} + +#endif + +static void +printHostNameAndAddr(const char * host, const PRNetAddr * addr) +{ + PRUint16 port = PR_NetAddrInetPort(addr); + char addrBuf[80]; + PRStatus st = PR_NetAddrToString(addr, addrBuf, sizeof addrBuf); + + if (st == PR_SUCCESS) { + port = PR_ntohs(port); + FPRINTF(stderr, "%s: connecting to %s:%hu (address=%s)\n", + progName, host, port, addrBuf); + } +} + +/* + * Prints output according to skipProtoHeader flag. If skipProtoHeader + * is not set, prints without any changes, otherwise looking + * for \n\r\n(empty line sequence: HTTP header separator) and + * prints everything after it. + */ +static void +separateReqHeader(const PRFileDesc* outFd, const char* buf, const int nb, + PRBool *wrStarted, int *ptrnMatched) { + + /* it is sufficient to look for only "\n\r\n". Hopping that + * HTTP response format satisfies the standard */ + char *ptrnStr = "\n\r\n"; + char *resPtr; + + if (nb == 0) { + return; + } + + if (*ptrnMatched > 0) { + /* Get here only if previous separateReqHeader call found + * only a fragment of "\n\r\n" in previous buffer. */ + PORT_Assert(*ptrnMatched < 3); + + /* the size of fragment of "\n\r\n" what we want to find in this + * buffer is equal to *ptrnMatched */ + if (*ptrnMatched <= nb) { + /* move the pointer to the beginning of the fragment */ + int strSize = *ptrnMatched; + char *tmpPtrn = ptrnStr + (3 - strSize); + if (PL_strncmp(buf, tmpPtrn, strSize) == 0) { + /* print the rest of the buffer(without the fragment) */ + PR_Write((void*)outFd, buf + strSize, nb - strSize); + *wrStarted = PR_TRUE; + return; + } + } else { + /* we are here only when nb == 1 && *ptrnMatched == 2 */ + if (*buf == '\r') { + *ptrnMatched = 1; + } else { + *ptrnMatched = 0; + } + return; + } + } + resPtr = PL_strnstr(buf, ptrnStr, nb); + if (resPtr != NULL) { + /* if "\n\r\n" was found in the buffer, calculate offset + * and print the rest of the buffer */ + int newBn = nb - (resPtr - buf + 3); /* 3 is the length of "\n\r\n" */ + + PR_Write((void*)outFd, resPtr + 3, newBn); + *wrStarted = PR_TRUE; + return; + } else { + /* try to find a fragment of "\n\r\n" at the end of the buffer. + * if found, set *ptrnMatched to the number of chars left to find + * in the next buffer.*/ + int i; + for(i = 1 ;i < 3;i++) { + char *bufPrt; + int strSize = 3 - i; + + if (strSize > nb) { + continue; + } + bufPrt = (char*)(buf + nb - strSize); + + if (PL_strncmp(bufPrt, ptrnStr, strSize) == 0) { + *ptrnMatched = i; + return; + } + } + } +} + +#define SSOCK_FD 0 +#define STDIN_FD 1 + +#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) +{ + PRFileDesc * s; + PRFileDesc * std_out; + CERTCertDBHandle * handle; + char * host = NULL; + char * certDir = NULL; + char * nickname = NULL; + char * cipherString = NULL; + char * tmp; + int multiplier = 0; + SECStatus rv; + PRStatus status; + PRInt32 filesReady; + int npds; + int override = 0; + int disableSSL2 = 0; + int disableSSL3 = 0; + int disableTLS = 0; + int bypassPKCS11 = 0; + int disableLocking = 0; + int useExportPolicy = 0; + int enableSessionTickets = 0; + PRSocketOptionData opt; + PRNetAddr addr; + PRPollDesc pollset[2]; + PRBool pingServerFirst = PR_FALSE; + PRBool clientSpeaksFirst = PR_FALSE; + PRBool wrStarted = PR_FALSE; + PRBool skipProtoHeader = PR_FALSE; + int headerSeparatorPtrnId = 0; + int error = 0; + PRUint16 portno = 443; + PLOptState *optstate; + PLOptStatus optstatus; + PRStatus prStatus; + + progName = strrchr(argv[0], '/'); + if (!progName) + progName = strrchr(argv[0], '\\'); + progName = progName ? progName+1 : argv[0]; + + tmp = PR_GetEnv("NSS_DEBUG_TIMEOUT"); + if (tmp && tmp[0]) { + int sec = PORT_Atoi(tmp); + if (sec > 0) { + maxInterval = PR_SecondsToInterval(sec); + } + } + + optstate = PL_CreateOptState(argc, argv, "23BTSfc:h:p:d:m:n:oqr:suvw:xW:"); + while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch (optstate->option) { + case '?': + default : Usage(progName); break; + + case '2': disableSSL2 = 1; break; + + case '3': disableSSL3 = 1; break; + + case 'B': bypassPKCS11 = 1; break; + + case 'T': disableTLS = 1; break; + + case 'S': skipProtoHeader = PR_TRUE; break; + + case 'c': cipherString = PORT_Strdup(optstate->value); break; + + case 'h': host = PORT_Strdup(optstate->value); break; + + case 'f': clientSpeaksFirst = PR_TRUE; break; + + case 'd': certDir = PORT_Strdup(optstate->value); break; + + case 'm': + multiplier = atoi(optstate->value); + if (multiplier < 0) + multiplier = 0; + break; + + case 'n': nickname = PORT_Strdup(optstate->value); break; + + case 'o': override = 1; break; + + case 'p': portno = (PRUint16)atoi(optstate->value); break; + + case 'q': pingServerFirst = PR_TRUE; break; + + case 's': disableLocking = 1; break; + + case 'u': enableSessionTickets = PR_TRUE; break; + + case 'v': verbose++; break; + + case 'r': renegotiate = atoi(optstate->value); break; + + case 'w': + pwdata.source = PW_PLAINTEXT; + pwdata.data = PORT_Strdup(optstate->value); + break; + + case 'W': + pwdata.source = PW_FROMFILE; + pwdata.data = PORT_Strdup(optstate->value); + break; + + case 'x': useExportPolicy = 1; break; + } + } + + PL_DestroyOptState(optstate); + + if (optstatus == PL_OPT_BAD) + Usage(progName); + + if (!host || !portno) + Usage(progName); + + PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + + PK11_SetPasswordFunc(SECU_GetModulePassword); + + /* open the cert DB, the key DB, and the secmod DB. */ + if (!certDir) { + certDir = SECU_DefaultSSLDir(); /* Look in $SSL_DIR */ + certDir = SECU_ConfigDirectory(certDir); + } else { + char *certDirTmp = certDir; + certDir = SECU_ConfigDirectory(certDirTmp); + PORT_Free(certDirTmp); + } + rv = NSS_Init(certDir); + if (rv != SECSuccess) { + SECU_PrintError(progName, "unable to open cert database"); +#if 0 + rv = CERT_OpenVolatileCertDB(handle); + CERT_SetDefaultCertDB(handle); +#else + return 1; +#endif + } + handle = CERT_GetDefaultCertDB(); + + /* set the policy bits true for all the cipher suites. */ + if (useExportPolicy) + NSS_SetExportPolicy(); + else + NSS_SetDomesticPolicy(); + + /* all the SSL2 and SSL3 cipher suites are enabled by default. */ + if (cipherString) { + /* disable all the ciphers, then enable the ones we want. */ + disableAllSSLCiphers(); + } + + status = PR_StringToNetAddr(host, &addr); + if (status == PR_SUCCESS) { + addr.inet.port = PR_htons(portno); + } else { + /* Lookup host */ + PRAddrInfo *addrInfo; + void *enumPtr = NULL; + + addrInfo = PR_GetAddrInfoByName(host, PR_AF_UNSPEC, + PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME); + if (!addrInfo) { + SECU_PrintError(progName, "error looking up host"); + return 1; + } + do { + enumPtr = PR_EnumerateAddrInfo(enumPtr, addrInfo, portno, &addr); + } while (enumPtr != NULL && + addr.raw.family != PR_AF_INET && + addr.raw.family != PR_AF_INET6); + PR_FreeAddrInfo(addrInfo); + if (enumPtr == NULL) { + SECU_PrintError(progName, "error looking up host address"); + return 1; + } + } + + printHostNameAndAddr(host, &addr); + + if (pingServerFirst) { + int iter = 0; + PRErrorCode err; + do { + s = PR_OpenTCPSocket(addr.raw.family); + if (s == NULL) { + SECU_PrintError(progName, "Failed to create a TCP socket"); + } + opt.option = PR_SockOpt_Nonblocking; + opt.value.non_blocking = PR_FALSE; + prStatus = PR_SetSocketOption(s, &opt); + if (prStatus != PR_SUCCESS) { + PR_Close(s); + SECU_PrintError(progName, + "Failed to set blocking socket option"); + return 1; + } + prStatus = PR_Connect(s, &addr, PR_INTERVAL_NO_TIMEOUT); + if (prStatus == PR_SUCCESS) { + PR_Shutdown(s, PR_SHUTDOWN_BOTH); + PR_Close(s); + if (NSS_Shutdown() != SECSuccess) { + exit(1); + } + PR_Cleanup(); + return 0; + } + err = PR_GetError(); + if ((err != PR_CONNECT_REFUSED_ERROR) && + (err != PR_CONNECT_RESET_ERROR)) { + SECU_PrintError(progName, "TCP Connection failed"); + return 1; + } + PR_Close(s); + PR_Sleep(PR_MillisecondsToInterval(WAIT_INTERVAL)); + } while (++iter < MAX_WAIT_FOR_SERVER); + SECU_PrintError(progName, + "Client timed out while waiting for connection to server"); + return 1; + } + + /* Create socket */ + s = PR_OpenTCPSocket(addr.raw.family); + if (s == NULL) { + SECU_PrintError(progName, "error creating socket"); + return 1; + } + + opt.option = PR_SockOpt_Nonblocking; + opt.value.non_blocking = PR_TRUE; + PR_SetSocketOption(s, &opt); + /*PR_SetSocketOption(PR_GetSpecialFD(PR_StandardInput), &opt);*/ + + s = SSL_ImportFD(NULL, s); + if (s == NULL) { + SECU_PrintError(progName, "error importing socket"); + return 1; + } + + rv = SSL_OptionSet(s, SSL_SECURITY, 1); + if (rv != SECSuccess) { + SECU_PrintError(progName, "error enabling socket"); + return 1; + } + + rv = SSL_OptionSet(s, SSL_HANDSHAKE_AS_CLIENT, 1); + if (rv != SECSuccess) { + SECU_PrintError(progName, "error enabling client handshake"); + return 1; + } + + /* all the SSL2 and SSL3 cipher suites are enabled by default. */ + if (cipherString) { + char *cstringSaved = cipherString; + int ndx; + + 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) { + SECStatus status; + status = SSL_CipherPrefSet(s, cipher, SSL_ALLOWED); + if (status != SECSuccess) + SECU_PrintError(progName, "SSL_CipherPrefSet()"); + } else { + Usage(progName); + } + } + PORT_Free(cstringSaved); + } + + rv = SSL_OptionSet(s, SSL_ENABLE_SSL2, !disableSSL2); + if (rv != SECSuccess) { + SECU_PrintError(progName, "error enabling SSLv2 "); + return 1; + } + + rv = SSL_OptionSet(s, SSL_ENABLE_SSL3, !disableSSL3); + if (rv != SECSuccess) { + SECU_PrintError(progName, "error enabling SSLv3 "); + return 1; + } + + rv = SSL_OptionSet(s, SSL_ENABLE_TLS, !disableTLS); + if (rv != SECSuccess) { + SECU_PrintError(progName, "error enabling TLS "); + return 1; + } + + /* disable ssl2 and ssl2-compatible client hellos. */ + rv = SSL_OptionSet(s, SSL_V2_COMPATIBLE_HELLO, !disableSSL2); + if (rv != SECSuccess) { + SECU_PrintError(progName, "error disabling v2 compatibility"); + return 1; + } + + /* enable PKCS11 bypass */ + rv = SSL_OptionSet(s, SSL_BYPASS_PKCS11, bypassPKCS11); + if (rv != SECSuccess) { + SECU_PrintError(progName, "error enabling PKCS11 bypass"); + return 1; + } + + /* disable SSL socket locking */ + rv = SSL_OptionSet(s, SSL_NO_LOCKS, disableLocking); + if (rv != SECSuccess) { + SECU_PrintError(progName, "error disabling SSL socket locking"); + return 1; + } + + /* enable Session Ticket extension. */ + rv = SSL_OptionSet(s, SSL_ENABLE_SESSION_TICKETS, enableSessionTickets); + if (rv != SECSuccess) { + SECU_PrintError(progName, "error enabling Session Ticket extension"); + return 1; + } + + SSL_SetPKCS11PinArg(s, &pwdata); + + SSL_AuthCertificateHook(s, SSL_AuthCertificate, (void *)handle); + if (override) { + SSL_BadCertHook(s, ownBadCertHandler, NULL); + } + SSL_GetClientAuthDataHook(s, own_GetClientAuthData, (void *)nickname); + SSL_HandshakeCallback(s, handshakeCallback, NULL); + SSL_SetURL(s, host); + + /* Try to connect to the server */ + status = PR_Connect(s, &addr, PR_INTERVAL_NO_TIMEOUT); + if (status != PR_SUCCESS) { + if (PR_GetError() == PR_IN_PROGRESS_ERROR) { + if (verbose) + SECU_PrintError(progName, "connect"); + milliPause(50 * multiplier); + pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; + pollset[SSOCK_FD].out_flags = 0; + pollset[SSOCK_FD].fd = s; + while(1) { + FPRINTF(stderr, + "%s: about to call PR_Poll for connect completion!\n", + progName); + filesReady = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT); + if (filesReady < 0) { + SECU_PrintError(progName, "unable to connect (poll)"); + return 1; + } + FPRINTF(stderr, + "%s: PR_Poll returned 0x%02x for socket out_flags.\n", + progName, pollset[SSOCK_FD].out_flags); + if (filesReady == 0) { /* shouldn't happen! */ + FPRINTF(stderr, "%s: PR_Poll returned zero!\n", progName); + return 1; + } + /* Must milliPause between PR_Poll and PR_GetConnectStatus, + * Or else winsock gets mighty confused. + * Sleep(0); + */ + milliPause(1); + status = PR_GetConnectStatus(pollset); + if (status == PR_SUCCESS) { + break; + } + if (PR_GetError() != PR_IN_PROGRESS_ERROR) { + SECU_PrintError(progName, "unable to connect (poll)"); + return 1; + } + SECU_PrintError(progName, "poll"); + milliPause(50 * multiplier); + } + } else { + SECU_PrintError(progName, "unable to connect"); + return 1; + } + } + + pollset[SSOCK_FD].fd = s; + pollset[SSOCK_FD].in_flags = PR_POLL_EXCEPT | + (clientSpeaksFirst ? 0 : PR_POLL_READ); + pollset[STDIN_FD].fd = PR_GetSpecialFD(PR_StandardInput); + pollset[STDIN_FD].in_flags = PR_POLL_READ; + npds = 2; + std_out = PR_GetSpecialFD(PR_StandardOutput); + +#if defined(WIN32) || defined(OS2) + /* PR_Poll cannot be used with stdin on Windows or OS/2. (sigh). + ** But use of PR_Poll and non-blocking sockets is a major feature + ** of this program. So, we simulate a pollable stdin with a + ** TCP socket pair and a thread that reads stdin and writes to + ** that socket pair. + */ + { + PRFileDesc * fds[2]; + PRThread * thread; + + int nspr_rv = PR_NewTCPSocketPair(fds); + if (nspr_rv != PR_SUCCESS) { + SECU_PrintError(progName, "PR_NewTCPSocketPair failed"); + error = 1; + goto done; + } + pollset[STDIN_FD].fd = fds[1]; + + thread = PR_CreateThread(PR_USER_THREAD, thread_main, fds[0], + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (!thread) { + SECU_PrintError(progName, "PR_CreateThread failed"); + error = 1; + goto done; + } + } +#endif + + /* + ** Select on stdin and on the socket. Write data from stdin to + ** socket, read data from socket and write to stdout. + */ + FPRINTF(stderr, "%s: ready...\n", progName); + + while (pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) { + char buf[4000]; /* buffer for stdin */ + int nb; /* num bytes read from stdin. */ + + pollset[SSOCK_FD].out_flags = 0; + pollset[STDIN_FD].out_flags = 0; + + FPRINTF(stderr, "%s: about to call PR_Poll !\n", progName); + filesReady = PR_Poll(pollset, npds, PR_INTERVAL_NO_TIMEOUT); + if (filesReady < 0) { + SECU_PrintError(progName, "select failed"); + error = 1; + goto done; + } + if (filesReady == 0) { /* shouldn't happen! */ + FPRINTF(stderr, "%s: PR_Poll returned zero!\n", progName); + return 1; + } + FPRINTF(stderr, "%s: PR_Poll returned!\n", progName); + if (pollset[STDIN_FD].in_flags) { + FPRINTF(stderr, + "%s: PR_Poll returned 0x%02x for stdin out_flags.\n", + progName, pollset[STDIN_FD].out_flags); + } + if (pollset[SSOCK_FD].in_flags) { + FPRINTF(stderr, + "%s: PR_Poll returned 0x%02x for socket out_flags.\n", + progName, pollset[SSOCK_FD].out_flags); + } + if (pollset[STDIN_FD].out_flags & PR_POLL_READ) { + /* Read from stdin and write to socket */ + nb = PR_Read(pollset[STDIN_FD].fd, buf, sizeof(buf)); + FPRINTF(stderr, "%s: stdin read %d bytes\n", progName, nb); + if (nb < 0) { + if (PR_GetError() != PR_WOULD_BLOCK_ERROR) { + SECU_PrintError(progName, "read from stdin failed"); + error = 1; + break; + } + } else if (nb == 0) { + /* EOF on stdin, stop polling stdin for read. */ + pollset[STDIN_FD].in_flags = 0; + } else { + char * bufp = buf; + FPRINTF(stderr, "%s: Writing %d bytes to server\n", + progName, nb); + do { + PRInt32 cc = PR_Send(s, bufp, nb, 0, maxInterval); + if (cc < 0) { + PRErrorCode err = PR_GetError(); + if (err != PR_WOULD_BLOCK_ERROR) { + SECU_PrintError(progName, + "write to SSL socket failed"); + error = 254; + goto done; + } + cc = 0; + } + bufp += cc; + nb -= cc; + if (nb <= 0) + break; + pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; + pollset[SSOCK_FD].out_flags = 0; + FPRINTF(stderr, + "%s: about to call PR_Poll on writable socket !\n", + progName); + cc = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT); + FPRINTF(stderr, + "%s: PR_Poll returned with writable socket !\n", + progName); + } while (1); + pollset[SSOCK_FD].in_flags = PR_POLL_READ; + } + } + + if (pollset[SSOCK_FD].in_flags) { + FPRINTF(stderr, + "%s: PR_Poll returned 0x%02x for socket out_flags.\n", + progName, pollset[SSOCK_FD].out_flags); + } + if ( (pollset[SSOCK_FD].out_flags & PR_POLL_READ) + || (pollset[SSOCK_FD].out_flags & PR_POLL_ERR) +#ifdef PR_POLL_HUP + || (pollset[SSOCK_FD].out_flags & PR_POLL_HUP) +#endif + ) { + /* Read from socket and write to stdout */ + nb = PR_Recv(pollset[SSOCK_FD].fd, buf, sizeof buf, 0, maxInterval); + FPRINTF(stderr, "%s: Read from server %d bytes\n", progName, nb); + if (nb < 0) { + if (PR_GetError() != PR_WOULD_BLOCK_ERROR) { + SECU_PrintError(progName, "read from socket failed"); + error = 1; + goto done; + } + } else if (nb == 0) { + /* EOF from socket... stop polling socket for read */ + pollset[SSOCK_FD].in_flags = 0; + } else { + if (skipProtoHeader != PR_TRUE || wrStarted == PR_TRUE) { + PR_Write(std_out, buf, nb); + } else { + separateReqHeader(std_out, buf, nb, &wrStarted, + &headerSeparatorPtrnId); + } + if (verbose) + fputs("\n\n", stderr); + } + } + milliPause(50 * multiplier); + } + + done: + if (nickname) { + PORT_Free(nickname); + } + if (pwdata.data) { + PORT_Free(pwdata.data); + } + PORT_Free(host); + + PR_Close(s); + SSL_ClearSessionCache(); + if (NSS_Shutdown() != SECSuccess) { + exit(1); + } + + FPRINTF(stderr, "tstclnt: exiting with return code %d\n", error); + PR_Cleanup(); + return error; +} |