michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: ** michael@0: ** Sample client side test program that uses SSL and NSS michael@0: ** michael@0: */ michael@0: michael@0: #include "secutil.h" michael@0: #include "basicutil.h" michael@0: michael@0: #if defined(XP_UNIX) michael@0: #include michael@0: #else michael@0: #include /* for isalpha() */ michael@0: #endif michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "nspr.h" michael@0: #include "prio.h" michael@0: #include "prnetdb.h" michael@0: #include "nss.h" michael@0: #include "ocsp.h" michael@0: #include "ssl.h" michael@0: #include "sslproto.h" michael@0: #include "pk11func.h" michael@0: #include "plgetopt.h" michael@0: #include "plstr.h" michael@0: michael@0: #if defined(WIN32) michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: #define PRINTF if (verbose) printf michael@0: #define FPRINTF if (verbose) fprintf michael@0: michael@0: #define MAX_WAIT_FOR_SERVER 600 michael@0: #define WAIT_INTERVAL 100 michael@0: michael@0: #define EXIT_CODE_HANDSHAKE_FAILED 254 michael@0: michael@0: #define EXIT_CODE_SIDECHANNELTEST_GOOD 0 michael@0: #define EXIT_CODE_SIDECHANNELTEST_BADCERT 1 michael@0: #define EXIT_CODE_SIDECHANNELTEST_NODATA 2 michael@0: #define EXIT_CODE_SIDECHANNELTEST_REVOKED 3 michael@0: michael@0: PRIntervalTime maxInterval = PR_INTERVAL_NO_TIMEOUT; michael@0: michael@0: int ssl2CipherSuites[] = { michael@0: SSL_EN_RC4_128_WITH_MD5, /* A */ michael@0: SSL_EN_RC4_128_EXPORT40_WITH_MD5, /* B */ michael@0: SSL_EN_RC2_128_CBC_WITH_MD5, /* C */ michael@0: SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */ michael@0: SSL_EN_DES_64_CBC_WITH_MD5, /* E */ michael@0: SSL_EN_DES_192_EDE3_CBC_WITH_MD5, /* F */ michael@0: 0 michael@0: }; michael@0: michael@0: int ssl3CipherSuites[] = { michael@0: -1, /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA* a */ michael@0: -1, /* SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, * b */ michael@0: TLS_RSA_WITH_RC4_128_MD5, /* c */ michael@0: TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* d */ michael@0: TLS_RSA_WITH_DES_CBC_SHA, /* e */ michael@0: TLS_RSA_EXPORT_WITH_RC4_40_MD5, /* f */ michael@0: TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* g */ michael@0: -1, /* SSL_FORTEZZA_DMS_WITH_NULL_SHA, * h */ michael@0: TLS_RSA_WITH_NULL_MD5, /* i */ michael@0: SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, /* j */ michael@0: SSL_RSA_FIPS_WITH_DES_CBC_SHA, /* k */ michael@0: TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, /* l */ michael@0: TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, /* m */ michael@0: TLS_RSA_WITH_RC4_128_SHA, /* n */ michael@0: TLS_DHE_DSS_WITH_RC4_128_SHA, /* o */ michael@0: TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, /* p */ michael@0: TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, /* q */ michael@0: TLS_DHE_RSA_WITH_DES_CBC_SHA, /* r */ michael@0: TLS_DHE_DSS_WITH_DES_CBC_SHA, /* s */ michael@0: TLS_DHE_DSS_WITH_AES_128_CBC_SHA, /* t */ michael@0: TLS_DHE_RSA_WITH_AES_128_CBC_SHA, /* u */ michael@0: TLS_RSA_WITH_AES_128_CBC_SHA, /* v */ michael@0: TLS_DHE_DSS_WITH_AES_256_CBC_SHA, /* w */ michael@0: TLS_DHE_RSA_WITH_AES_256_CBC_SHA, /* x */ michael@0: TLS_RSA_WITH_AES_256_CBC_SHA, /* y */ michael@0: TLS_RSA_WITH_NULL_SHA, /* z */ michael@0: 0 michael@0: }; michael@0: michael@0: unsigned long __cmp_umuls; michael@0: PRBool verbose; michael@0: int renegotiationsToDo = 0; michael@0: int renegotiationsDone = 0; michael@0: michael@0: static char *progName; michael@0: michael@0: secuPWData pwdata = { PW_NONE, 0 }; michael@0: michael@0: void printSecurityInfo(PRFileDesc *fd) michael@0: { michael@0: CERTCertificate * cert; michael@0: const SECItemArray *csa; michael@0: SSL3Statistics * ssl3stats = SSL_GetStatistics(); michael@0: SECStatus result; michael@0: SSLChannelInfo channel; michael@0: SSLCipherSuiteInfo suite; michael@0: michael@0: result = SSL_GetChannelInfo(fd, &channel, sizeof channel); michael@0: if (result == SECSuccess && michael@0: channel.length == sizeof channel && michael@0: channel.cipherSuite) { michael@0: result = SSL_GetCipherSuiteInfo(channel.cipherSuite, michael@0: &suite, sizeof suite); michael@0: if (result == SECSuccess) { michael@0: FPRINTF(stderr, michael@0: "tstclnt: SSL version %d.%d using %d-bit %s with %d-bit %s MAC\n", michael@0: channel.protocolVersion >> 8, channel.protocolVersion & 0xff, michael@0: suite.effectiveKeyBits, suite.symCipherName, michael@0: suite.macBits, suite.macAlgorithmName); michael@0: FPRINTF(stderr, michael@0: "tstclnt: Server Auth: %d-bit %s, Key Exchange: %d-bit %s\n" michael@0: " Compression: %s\n", michael@0: channel.authKeyBits, suite.authAlgorithmName, michael@0: channel.keaKeyBits, suite.keaTypeName, michael@0: channel.compressionMethodName); michael@0: } michael@0: } michael@0: cert = SSL_RevealCert(fd); michael@0: if (cert) { michael@0: char * ip = CERT_NameToAscii(&cert->issuer); michael@0: char * sp = CERT_NameToAscii(&cert->subject); michael@0: if (sp) { michael@0: fprintf(stderr, "subject DN: %s\n", sp); michael@0: PORT_Free(sp); michael@0: } michael@0: if (ip) { michael@0: fprintf(stderr, "issuer DN: %s\n", ip); michael@0: PORT_Free(ip); michael@0: } michael@0: CERT_DestroyCertificate(cert); michael@0: cert = NULL; michael@0: } michael@0: fprintf(stderr, michael@0: "%ld cache hits; %ld cache misses, %ld cache not reusable\n" michael@0: "%ld stateless resumes\n", michael@0: ssl3stats->hsh_sid_cache_hits, ssl3stats->hsh_sid_cache_misses, michael@0: ssl3stats->hsh_sid_cache_not_ok, ssl3stats->hsh_sid_stateless_resumes); michael@0: michael@0: csa = SSL_PeerStapledOCSPResponses(fd); michael@0: if (csa) { michael@0: fprintf(stderr, "Received %d Cert Status items (OCSP stapled data)\n", michael@0: csa->len); michael@0: } michael@0: } michael@0: michael@0: void michael@0: handshakeCallback(PRFileDesc *fd, void *client_data) michael@0: { michael@0: const char *secondHandshakeName = (char *)client_data; michael@0: if (secondHandshakeName) { michael@0: SSL_SetURL(fd, secondHandshakeName); michael@0: } michael@0: printSecurityInfo(fd); michael@0: if (renegotiationsDone < renegotiationsToDo) { michael@0: SSL_ReHandshake(fd, (renegotiationsToDo < 2)); michael@0: ++renegotiationsDone; michael@0: } michael@0: } michael@0: michael@0: static void PrintUsageHeader(const char *progName) michael@0: { michael@0: fprintf(stderr, michael@0: "Usage: %s -h host [-a 1st_hs_name ] [-a 2nd_hs_name ] [-p port]\n" michael@0: "[-d certdir] [-n nickname] [-Bafosvx] [-c ciphers] [-Y]\n" michael@0: "[-V [min-version]:[max-version]] [-T]\n" michael@0: "[-r N] [-w passwd] [-W pwfile] [-q [-t seconds]]\n", michael@0: progName); michael@0: } michael@0: michael@0: static void PrintParameterUsage(void) michael@0: { michael@0: fprintf(stderr, "%-20s Send different SNI name. 1st_hs_name - at first\n" michael@0: "%-20s handshake, 2nd_hs_name - at second handshake.\n" michael@0: "%-20s Default is host from the -h argument.\n", "-a name", michael@0: "", ""); michael@0: fprintf(stderr, "%-20s Hostname to connect with\n", "-h host"); michael@0: fprintf(stderr, "%-20s Port number for SSL server\n", "-p port"); michael@0: fprintf(stderr, michael@0: "%-20s Directory with cert database (default is ~/.netscape)\n", michael@0: "-d certdir"); michael@0: fprintf(stderr, "%-20s Nickname of key and cert for client auth\n", michael@0: "-n nickname"); michael@0: fprintf(stderr, michael@0: "%-20s Bypass PKCS11 layer for SSL encryption and MACing.\n", "-B"); michael@0: fprintf(stderr, michael@0: "%-20s Restricts the set of enabled SSL/TLS protocols versions.\n" michael@0: "%-20s All versions are enabled by default.\n" michael@0: "%-20s Possible values for min/max: ssl2 ssl3 tls1.0 tls1.1 tls1.2\n" michael@0: "%-20s Example: \"-V ssl3:\" enables SSL 3 and newer.\n", michael@0: "-V [min]:[max]", "", "", ""); michael@0: fprintf(stderr, "%-20s Prints only payload data. Skips HTTP header.\n", "-S"); michael@0: fprintf(stderr, "%-20s Client speaks first. \n", "-f"); michael@0: fprintf(stderr, "%-20s Use synchronous certificate validation " michael@0: "(required for SSL2)\n", "-O"); michael@0: fprintf(stderr, "%-20s Override bad server cert. Make it OK.\n", "-o"); michael@0: fprintf(stderr, "%-20s Disable SSL socket locking.\n", "-s"); michael@0: fprintf(stderr, "%-20s Verbose progress reporting.\n", "-v"); michael@0: fprintf(stderr, "%-20s Use export policy.\n", "-x"); michael@0: fprintf(stderr, "%-20s Ping the server and then exit.\n", "-q"); michael@0: fprintf(stderr, "%-20s Timeout for server ping (default: no timeout).\n", "-t seconds"); michael@0: fprintf(stderr, "%-20s Renegotiate N times (resuming session if N>1).\n", "-r N"); michael@0: fprintf(stderr, "%-20s Enable the session ticket extension.\n", "-u"); michael@0: fprintf(stderr, "%-20s Enable compression.\n", "-z"); michael@0: fprintf(stderr, "%-20s Enable false start.\n", "-g"); michael@0: fprintf(stderr, "%-20s Enable the cert_status extension (OCSP stapling).\n", "-T"); michael@0: fprintf(stderr, "%-20s Require fresh revocation info from side channel.\n" michael@0: "%-20s -F once means: require for server cert only\n" michael@0: "%-20s -F twice means: require for intermediates, too\n" michael@0: "%-20s (Connect, handshake with server, disable dynamic download\n" michael@0: "%-20s of OCSP/CRL, verify cert using CERT_PKIXVerifyCert.)\n" michael@0: "%-20s Exit code:\n" michael@0: "%-20s 0: have fresh and valid revocation data, status good\n" michael@0: "%-20s 1: cert failed to verify, prior to revocation checking\n" michael@0: "%-20s 2: missing, old or invalid revocation data\n" michael@0: "%-20s 3: have fresh and valid revocation data, status revoked\n", michael@0: "-F", "", "", "", "", "", "", "", "", ""); michael@0: fprintf(stderr, "%-20s Test -F allows 0=any (default), 1=only OCSP, 2=only CRL\n", "-M"); michael@0: fprintf(stderr, "%-20s Restrict ciphers\n", "-c ciphers"); michael@0: fprintf(stderr, "%-20s Print cipher values allowed for parameter -c and exit\n", "-Y"); michael@0: fprintf(stderr, "%-20s Enforce using an IPv4 destination address\n", "-4"); michael@0: fprintf(stderr, "%-20s Enforce using an IPv6 destination address\n", "-6"); michael@0: fprintf(stderr, "%-20s (Options -4 and -6 cannot be combined.)\n", ""); michael@0: } michael@0: michael@0: static void Usage(const char *progName) michael@0: { michael@0: PrintUsageHeader(progName); michael@0: PrintParameterUsage(); michael@0: exit(1); michael@0: } michael@0: michael@0: static void PrintCipherUsage(const char *progName) michael@0: { michael@0: PrintUsageHeader(progName); michael@0: fprintf(stderr, "%-20s Letter(s) chosen from the following list\n", michael@0: "-c ciphers"); michael@0: fprintf(stderr, michael@0: "A SSL2 RC4 128 WITH MD5\n" michael@0: "B SSL2 RC4 128 EXPORT40 WITH MD5\n" michael@0: "C SSL2 RC2 128 CBC WITH MD5\n" michael@0: "D SSL2 RC2 128 CBC EXPORT40 WITH MD5\n" michael@0: "E SSL2 DES 64 CBC WITH MD5\n" michael@0: "F SSL2 DES 192 EDE3 CBC WITH MD5\n" michael@0: "\n" michael@0: "c SSL3 RSA WITH RC4 128 MD5\n" michael@0: "d SSL3 RSA WITH 3DES EDE CBC SHA\n" michael@0: "e SSL3 RSA WITH DES CBC SHA\n" michael@0: "f SSL3 RSA EXPORT WITH RC4 40 MD5\n" michael@0: "g SSL3 RSA EXPORT WITH RC2 CBC 40 MD5\n" michael@0: "i SSL3 RSA WITH NULL MD5\n" michael@0: "j SSL3 RSA FIPS WITH 3DES EDE CBC SHA\n" michael@0: "k SSL3 RSA FIPS WITH DES CBC SHA\n" michael@0: "l SSL3 RSA EXPORT WITH DES CBC SHA\t(new)\n" michael@0: "m SSL3 RSA EXPORT WITH RC4 56 SHA\t(new)\n" michael@0: "n SSL3 RSA WITH RC4 128 SHA\n" michael@0: "o SSL3 DHE DSS WITH RC4 128 SHA\n" michael@0: "p SSL3 DHE RSA WITH 3DES EDE CBC SHA\n" michael@0: "q SSL3 DHE DSS WITH 3DES EDE CBC SHA\n" michael@0: "r SSL3 DHE RSA WITH DES CBC SHA\n" michael@0: "s SSL3 DHE DSS WITH DES CBC SHA\n" michael@0: "t SSL3 DHE DSS WITH AES 128 CBC SHA\n" michael@0: "u SSL3 DHE RSA WITH AES 128 CBC SHA\n" michael@0: "v SSL3 RSA WITH AES 128 CBC SHA\n" michael@0: "w SSL3 DHE DSS WITH AES 256 CBC SHA\n" michael@0: "x SSL3 DHE RSA WITH AES 256 CBC SHA\n" michael@0: "y SSL3 RSA WITH AES 256 CBC SHA\n" michael@0: "z SSL3 RSA WITH NULL SHA\n" michael@0: "\n" michael@0: ":WXYZ Use cipher with hex code { 0xWX , 0xYZ } in TLS\n" michael@0: ); michael@0: exit(1); michael@0: } michael@0: michael@0: void michael@0: milliPause(PRUint32 milli) michael@0: { michael@0: PRIntervalTime ticks = PR_MillisecondsToInterval(milli); michael@0: PR_Sleep(ticks); michael@0: } michael@0: michael@0: void michael@0: disableAllSSLCiphers(void) michael@0: { michael@0: const PRUint16 *cipherSuites = SSL_GetImplementedCiphers(); michael@0: int i = SSL_GetNumImplementedCiphers(); michael@0: SECStatus rv; michael@0: michael@0: /* disable all the SSL3 cipher suites */ michael@0: while (--i >= 0) { michael@0: PRUint16 suite = cipherSuites[i]; michael@0: rv = SSL_CipherPrefSetDefault(suite, PR_FALSE); michael@0: if (rv != SECSuccess) { michael@0: PRErrorCode err = PR_GetError(); michael@0: fprintf(stderr, michael@0: "SSL_CipherPrefSet didn't like value 0x%04x (i = %d): %s\n", michael@0: suite, i, SECU_Strerror(err)); michael@0: exit(2); michael@0: } michael@0: } michael@0: } michael@0: michael@0: typedef struct michael@0: { michael@0: PRBool shouldPause; /* PR_TRUE if we should use asynchronous peer cert michael@0: * authentication */ michael@0: PRBool isPaused; /* PR_TRUE if libssl is waiting for us to validate the michael@0: * peer's certificate and restart the handshake. */ michael@0: void * dbHandle; /* Certificate database handle to use while michael@0: * authenticating the peer's certificate. */ michael@0: PRBool testFreshStatusFromSideChannel; michael@0: PRErrorCode sideChannelRevocationTestResultCode; michael@0: PRBool requireDataForIntermediates; michael@0: PRBool allowOCSPSideChannelData; michael@0: PRBool allowCRLSideChannelData; michael@0: } ServerCertAuth; michael@0: michael@0: michael@0: /* michael@0: * Callback is called when incoming certificate is not valid. michael@0: * Returns SECSuccess to accept the cert anyway, SECFailure to reject. michael@0: */ michael@0: static SECStatus michael@0: ownBadCertHandler(void * arg, PRFileDesc * socket) michael@0: { michael@0: PRErrorCode err = PR_GetError(); michael@0: /* can log invalid cert here */ michael@0: fprintf(stderr, "Bad server certificate: %d, %s\n", err, michael@0: SECU_Strerror(err)); michael@0: return SECSuccess; /* override, say it's OK. */ michael@0: } michael@0: michael@0: michael@0: michael@0: #define EXIT_CODE_SIDECHANNELTEST_GOOD 0 michael@0: #define EXIT_CODE_SIDECHANNELTEST_BADCERT 1 michael@0: #define EXIT_CODE_SIDECHANNELTEST_NODATA 2 michael@0: #define EXIT_CODE_SIDECHANNELTEST_REVOKED 3 michael@0: michael@0: static void michael@0: verifyFromSideChannel(CERTCertificate *cert, ServerCertAuth *sca) michael@0: { michael@0: PRUint64 revDoNotUse = michael@0: CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD; michael@0: michael@0: PRUint64 revUseLocalOnlyAndSoftFail = michael@0: CERT_REV_M_TEST_USING_THIS_METHOD michael@0: | CERT_REV_M_FORBID_NETWORK_FETCHING michael@0: | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE michael@0: | CERT_REV_M_IGNORE_MISSING_FRESH_INFO michael@0: | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO; michael@0: michael@0: PRUint64 revUseLocalOnlyAndHardFail = michael@0: CERT_REV_M_TEST_USING_THIS_METHOD michael@0: | CERT_REV_M_FORBID_NETWORK_FETCHING michael@0: | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE michael@0: | CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO michael@0: | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO; michael@0: michael@0: PRUint64 methodFlagsDoNotUse[2]; michael@0: PRUint64 methodFlagsCheckSoftFail[2]; michael@0: PRUint64 methodFlagsCheckHardFail[2]; michael@0: CERTRevocationTests revTestsDoNotCheck; michael@0: CERTRevocationTests revTestsOverallSoftFail; michael@0: CERTRevocationTests revTestsOverallHardFail; michael@0: CERTRevocationFlags rev; michael@0: CERTValInParam cvin[2]; michael@0: CERTValOutParam cvout[1]; michael@0: SECStatus rv; michael@0: michael@0: methodFlagsDoNotUse[cert_revocation_method_crl] = revDoNotUse; michael@0: methodFlagsDoNotUse[cert_revocation_method_ocsp] = revDoNotUse; michael@0: michael@0: methodFlagsCheckSoftFail[cert_revocation_method_crl] = michael@0: sca->allowCRLSideChannelData ? revUseLocalOnlyAndSoftFail : revDoNotUse; michael@0: methodFlagsCheckSoftFail[cert_revocation_method_ocsp] = michael@0: sca->allowOCSPSideChannelData ? revUseLocalOnlyAndSoftFail : revDoNotUse; michael@0: michael@0: methodFlagsCheckHardFail[cert_revocation_method_crl] = michael@0: sca->allowCRLSideChannelData ? revUseLocalOnlyAndHardFail : revDoNotUse; michael@0: methodFlagsCheckHardFail[cert_revocation_method_ocsp] = michael@0: sca->allowOCSPSideChannelData ? revUseLocalOnlyAndHardFail : revDoNotUse; michael@0: michael@0: revTestsDoNotCheck.cert_rev_flags_per_method = methodFlagsDoNotUse; michael@0: revTestsDoNotCheck.number_of_defined_methods = 2; michael@0: revTestsDoNotCheck.number_of_preferred_methods = 0; michael@0: revTestsDoNotCheck.cert_rev_method_independent_flags = michael@0: CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST michael@0: | CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT; michael@0: michael@0: revTestsOverallSoftFail.cert_rev_flags_per_method = 0; /* must define later */ michael@0: revTestsOverallSoftFail.number_of_defined_methods = 2; michael@0: revTestsOverallSoftFail.number_of_preferred_methods = 0; michael@0: revTestsOverallSoftFail.cert_rev_method_independent_flags = michael@0: CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST michael@0: | CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT; michael@0: michael@0: revTestsOverallHardFail.cert_rev_flags_per_method = 0; /* must define later */ michael@0: revTestsOverallHardFail.number_of_defined_methods = 2; michael@0: revTestsOverallHardFail.number_of_preferred_methods = 0; michael@0: revTestsOverallHardFail.cert_rev_method_independent_flags = michael@0: CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST michael@0: | CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE; michael@0: michael@0: rev.chainTests = revTestsDoNotCheck; michael@0: rev.leafTests = revTestsDoNotCheck; michael@0: michael@0: cvin[0].type = cert_pi_revocationFlags; michael@0: cvin[0].value.pointer.revocation = &rev; michael@0: cvin[1].type = cert_pi_end; michael@0: michael@0: cvout[0].type = cert_po_end; michael@0: michael@0: /* Strategy: michael@0: * michael@0: * Verify with revocation checking disabled. michael@0: * On failure return 1. michael@0: * michael@0: * if result if "good", then continue testing. michael@0: * michael@0: * Verify with CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO. michael@0: * If result is good, return 0. michael@0: * michael@0: * On failure continue testing, find out why it failed. michael@0: * michael@0: * Verify with CERT_REV_M_IGNORE_MISSING_FRESH_INFO michael@0: * michael@0: * If result is "good", then our previous test failed, michael@0: * because we don't have fresh revocation info, return 2. michael@0: * michael@0: * If result is still bad, we do have revocation info, michael@0: * and it says "revoked" or something equivalent, return 3. michael@0: */ michael@0: michael@0: /* revocation checking disabled */ michael@0: rv = CERT_PKIXVerifyCert(cert, certificateUsageSSLServer, michael@0: cvin, cvout, NULL); michael@0: if (rv != SECSuccess) { michael@0: sca->sideChannelRevocationTestResultCode = michael@0: EXIT_CODE_SIDECHANNELTEST_BADCERT; michael@0: return; michael@0: } michael@0: michael@0: /* revocation checking, hard fail */ michael@0: if (sca->allowOCSPSideChannelData && sca->allowCRLSideChannelData) { michael@0: /* any method is allowed. use soft fail on individual checks, michael@0: * but use hard fail on the overall check michael@0: */ michael@0: revTestsOverallHardFail.cert_rev_flags_per_method = methodFlagsCheckSoftFail; michael@0: } michael@0: else { michael@0: /* only one method is allowed. use hard fail on the individual checks. michael@0: * hard/soft fail is irrelevant on overall flags. michael@0: */ michael@0: revTestsOverallHardFail.cert_rev_flags_per_method = methodFlagsCheckHardFail; michael@0: } michael@0: rev.leafTests = revTestsOverallHardFail; michael@0: rev.chainTests = michael@0: sca->requireDataForIntermediates ? revTestsOverallHardFail : revTestsDoNotCheck; michael@0: rv = CERT_PKIXVerifyCert(cert, certificateUsageSSLServer, michael@0: cvin, cvout, NULL); michael@0: if (rv == SECSuccess) { michael@0: sca->sideChannelRevocationTestResultCode = michael@0: EXIT_CODE_SIDECHANNELTEST_GOOD; michael@0: return; michael@0: } michael@0: michael@0: /* revocation checking, soft fail */ michael@0: revTestsOverallSoftFail.cert_rev_flags_per_method = methodFlagsCheckSoftFail; michael@0: rev.leafTests = revTestsOverallSoftFail; michael@0: rev.chainTests = michael@0: sca->requireDataForIntermediates ? revTestsOverallSoftFail : revTestsDoNotCheck; michael@0: rv = CERT_PKIXVerifyCert(cert, certificateUsageSSLServer, michael@0: cvin, cvout, NULL); michael@0: if (rv == SECSuccess) { michael@0: sca->sideChannelRevocationTestResultCode = michael@0: EXIT_CODE_SIDECHANNELTEST_NODATA; michael@0: return; michael@0: } michael@0: michael@0: sca->sideChannelRevocationTestResultCode = michael@0: EXIT_CODE_SIDECHANNELTEST_REVOKED; michael@0: } michael@0: michael@0: static SECStatus michael@0: ownAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, michael@0: PRBool isServer) michael@0: { michael@0: ServerCertAuth * serverCertAuth = (ServerCertAuth *) arg; michael@0: michael@0: if (!serverCertAuth->shouldPause) { michael@0: CERTCertificate *cert; michael@0: int i; michael@0: const SECItemArray *csa; michael@0: michael@0: if (!serverCertAuth->testFreshStatusFromSideChannel) { michael@0: return SSL_AuthCertificate(serverCertAuth->dbHandle, michael@0: fd, checkSig, isServer); michael@0: } michael@0: michael@0: /* No verification attempt must have happened before now, michael@0: * to ensure revocation data has been actively retrieved yet, michael@0: * or our test will produce incorrect results. michael@0: */ michael@0: michael@0: cert = SSL_RevealCert(fd); michael@0: if (!cert) { michael@0: exit(254); michael@0: } michael@0: michael@0: csa = SSL_PeerStapledOCSPResponses(fd); michael@0: if (csa) { michael@0: for (i = 0; i < csa->len; ++i) { michael@0: PORT_SetError(0); michael@0: if (CERT_CacheOCSPResponseFromSideChannel( michael@0: serverCertAuth->dbHandle, cert, PR_Now(), michael@0: &csa->items[i], arg) != SECSuccess) { michael@0: PRErrorCode error = PR_GetError(); michael@0: PORT_Assert(error != 0); michael@0: } michael@0: } michael@0: } michael@0: michael@0: verifyFromSideChannel(cert, serverCertAuth); michael@0: CERT_DestroyCertificate(cert); michael@0: /* return success to ensure our caller will continue and we will michael@0: * reach the code that handles michael@0: * serverCertAuth->sideChannelRevocationTestResultCode michael@0: */ michael@0: return SECSuccess; michael@0: } michael@0: michael@0: FPRINTF(stderr, "%s: using asynchronous certificate validation\n", michael@0: progName); michael@0: michael@0: PORT_Assert(!serverCertAuth->isPaused); michael@0: serverCertAuth->isPaused = PR_TRUE; michael@0: return SECWouldBlock; michael@0: } michael@0: michael@0: SECStatus michael@0: own_GetClientAuthData(void * arg, michael@0: PRFileDesc * socket, michael@0: struct CERTDistNamesStr * caNames, michael@0: struct CERTCertificateStr ** pRetCert, michael@0: struct SECKEYPrivateKeyStr **pRetKey) michael@0: { michael@0: if (verbose > 1) { michael@0: SECStatus rv; michael@0: fprintf(stderr, "Server requested Client Authentication\n"); michael@0: if (caNames && caNames->nnames > 0) { michael@0: PLArenaPool *arena = caNames->arena; michael@0: if (!arena) michael@0: arena = PORT_NewArena(2048); michael@0: if (arena) { michael@0: int i; michael@0: for (i = 0; i < caNames->nnames; ++i) { michael@0: char *nameString; michael@0: CERTName dn; michael@0: rv = SEC_QuickDERDecodeItem(arena, michael@0: &dn, michael@0: SEC_ASN1_GET(CERT_NameTemplate), michael@0: caNames->names + i); michael@0: if (rv != SECSuccess) michael@0: continue; michael@0: nameString = CERT_NameToAscii(&dn); michael@0: if (!nameString) michael@0: continue; michael@0: fprintf(stderr, "CA[%d]: %s\n", i + 1, nameString); michael@0: PORT_Free(nameString); michael@0: } michael@0: if (!caNames->arena) { michael@0: PORT_FreeArena(arena, PR_FALSE); michael@0: } michael@0: } michael@0: } michael@0: rv = NSS_GetClientAuthData(arg, socket, caNames, pRetCert, pRetKey); michael@0: if (rv == SECSuccess && *pRetCert) { michael@0: char *nameString = CERT_NameToAscii(&((*pRetCert)->subject)); michael@0: if (nameString) { michael@0: fprintf(stderr, "sent cert: %s\n", nameString); michael@0: PORT_Free(nameString); michael@0: } michael@0: } else { michael@0: fprintf(stderr, "send no cert\n"); michael@0: } michael@0: return rv; michael@0: } michael@0: return NSS_GetClientAuthData(arg, socket, caNames, pRetCert, pRetKey); michael@0: } michael@0: michael@0: #if defined(WIN32) || defined(OS2) michael@0: void michael@0: thread_main(void * arg) michael@0: { michael@0: PRFileDesc * ps = (PRFileDesc *)arg; michael@0: PRFileDesc * std_in = PR_GetSpecialFD(PR_StandardInput); michael@0: int wc, rc; michael@0: char buf[256]; michael@0: michael@0: #ifdef WIN32 michael@0: { michael@0: /* Put stdin into O_BINARY mode michael@0: ** or else incoming \r\n's will become \n's. michael@0: */ michael@0: int smrv = _setmode(_fileno(stdin), _O_BINARY); michael@0: if (smrv == -1) { michael@0: fprintf(stderr, michael@0: "%s: Cannot change stdin to binary mode. Use -i option instead.\n", michael@0: progName); michael@0: /* plow ahead anyway */ michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: do { michael@0: rc = PR_Read(std_in, buf, sizeof buf); michael@0: if (rc <= 0) michael@0: break; michael@0: wc = PR_Send(ps, buf, rc, 0, maxInterval); michael@0: } while (wc == rc); michael@0: PR_Close(ps); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: static void michael@0: printHostNameAndAddr(const char * host, const PRNetAddr * addr) michael@0: { michael@0: PRUint16 port = PR_NetAddrInetPort(addr); michael@0: char addrBuf[80]; michael@0: PRStatus st = PR_NetAddrToString(addr, addrBuf, sizeof addrBuf); michael@0: michael@0: if (st == PR_SUCCESS) { michael@0: port = PR_ntohs(port); michael@0: FPRINTF(stderr, "%s: connecting to %s:%hu (address=%s)\n", michael@0: progName, host, port, addrBuf); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Prints output according to skipProtoHeader flag. If skipProtoHeader michael@0: * is not set, prints without any changes, otherwise looking michael@0: * for \n\r\n(empty line sequence: HTTP header separator) and michael@0: * prints everything after it. michael@0: */ michael@0: static void michael@0: separateReqHeader(const PRFileDesc* outFd, const char* buf, const int nb, michael@0: PRBool *wrStarted, int *ptrnMatched) { michael@0: michael@0: /* it is sufficient to look for only "\n\r\n". Hopping that michael@0: * HTTP response format satisfies the standard */ michael@0: char *ptrnStr = "\n\r\n"; michael@0: char *resPtr; michael@0: michael@0: if (nb == 0) { michael@0: return; michael@0: } michael@0: michael@0: if (*ptrnMatched > 0) { michael@0: /* Get here only if previous separateReqHeader call found michael@0: * only a fragment of "\n\r\n" in previous buffer. */ michael@0: PORT_Assert(*ptrnMatched < 3); michael@0: michael@0: /* the size of fragment of "\n\r\n" what we want to find in this michael@0: * buffer is equal to *ptrnMatched */ michael@0: if (*ptrnMatched <= nb) { michael@0: /* move the pointer to the beginning of the fragment */ michael@0: int strSize = *ptrnMatched; michael@0: char *tmpPtrn = ptrnStr + (3 - strSize); michael@0: if (PL_strncmp(buf, tmpPtrn, strSize) == 0) { michael@0: /* print the rest of the buffer(without the fragment) */ michael@0: PR_Write((void*)outFd, buf + strSize, nb - strSize); michael@0: *wrStarted = PR_TRUE; michael@0: return; michael@0: } michael@0: } else { michael@0: /* we are here only when nb == 1 && *ptrnMatched == 2 */ michael@0: if (*buf == '\r') { michael@0: *ptrnMatched = 1; michael@0: } else { michael@0: *ptrnMatched = 0; michael@0: } michael@0: return; michael@0: } michael@0: } michael@0: resPtr = PL_strnstr(buf, ptrnStr, nb); michael@0: if (resPtr != NULL) { michael@0: /* if "\n\r\n" was found in the buffer, calculate offset michael@0: * and print the rest of the buffer */ michael@0: int newBn = nb - (resPtr - buf + 3); /* 3 is the length of "\n\r\n" */ michael@0: michael@0: PR_Write((void*)outFd, resPtr + 3, newBn); michael@0: *wrStarted = PR_TRUE; michael@0: return; michael@0: } else { michael@0: /* try to find a fragment of "\n\r\n" at the end of the buffer. michael@0: * if found, set *ptrnMatched to the number of chars left to find michael@0: * in the next buffer.*/ michael@0: int i; michael@0: for(i = 1 ;i < 3;i++) { michael@0: char *bufPrt; michael@0: int strSize = 3 - i; michael@0: michael@0: if (strSize > nb) { michael@0: continue; michael@0: } michael@0: bufPrt = (char*)(buf + nb - strSize); michael@0: michael@0: if (PL_strncmp(bufPrt, ptrnStr, strSize) == 0) { michael@0: *ptrnMatched = i; michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: #define SSOCK_FD 0 michael@0: #define STDIN_FD 1 michael@0: michael@0: #define HEXCHAR_TO_INT(c, i) \ michael@0: if (((c) >= '0') && ((c) <= '9')) { \ michael@0: i = (c) - '0'; \ michael@0: } else if (((c) >= 'a') && ((c) <= 'f')) { \ michael@0: i = (c) - 'a' + 10; \ michael@0: } else if (((c) >= 'A') && ((c) <= 'F')) { \ michael@0: i = (c) - 'A' + 10; \ michael@0: } else { \ michael@0: Usage(progName); \ michael@0: } michael@0: michael@0: static SECStatus michael@0: restartHandshakeAfterServerCertIfNeeded(PRFileDesc * fd, michael@0: ServerCertAuth * serverCertAuth, michael@0: PRBool override) michael@0: { michael@0: SECStatus rv; michael@0: PRErrorCode error; michael@0: michael@0: if (!serverCertAuth->isPaused) michael@0: return SECSuccess; michael@0: michael@0: FPRINTF(stderr, "%s: handshake was paused by auth certificate hook\n", michael@0: progName); michael@0: michael@0: serverCertAuth->isPaused = PR_FALSE; michael@0: rv = SSL_AuthCertificate(serverCertAuth->dbHandle, fd, PR_TRUE, PR_FALSE); michael@0: if (rv != SECSuccess) { michael@0: error = PR_GetError(); michael@0: if (error == 0) { michael@0: PR_NOT_REACHED("SSL_AuthCertificate return SECFailure without " michael@0: "setting error code."); michael@0: error = PR_INVALID_STATE_ERROR; michael@0: } else if (override) { michael@0: rv = ownBadCertHandler(NULL, fd); michael@0: } michael@0: } michael@0: if (rv == SECSuccess) { michael@0: error = 0; michael@0: } michael@0: michael@0: if (SSL_AuthCertificateComplete(fd, error) != SECSuccess) { michael@0: rv = SECFailure; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: PRFileDesc * s; michael@0: PRFileDesc * std_out; michael@0: char * host = NULL; michael@0: char * certDir = NULL; michael@0: char * nickname = NULL; michael@0: char * cipherString = NULL; michael@0: char * tmp; michael@0: int multiplier = 0; michael@0: SECStatus rv; michael@0: PRStatus status; michael@0: PRInt32 filesReady; michael@0: int npds; michael@0: int override = 0; michael@0: SSLVersionRange enabledVersions; michael@0: PRBool enableSSL2 = PR_TRUE; michael@0: int bypassPKCS11 = 0; michael@0: int disableLocking = 0; michael@0: int useExportPolicy = 0; michael@0: int enableSessionTickets = 0; michael@0: int enableCompression = 0; michael@0: int enableFalseStart = 0; michael@0: int enableCertStatus = 0; michael@0: PRSocketOptionData opt; michael@0: PRNetAddr addr; michael@0: PRPollDesc pollset[2]; michael@0: PRBool allowIPv4 = PR_TRUE; michael@0: PRBool allowIPv6 = PR_TRUE; michael@0: PRBool pingServerFirst = PR_FALSE; michael@0: int pingTimeoutSeconds = -1; michael@0: PRBool clientSpeaksFirst = PR_FALSE; michael@0: PRBool wrStarted = PR_FALSE; michael@0: PRBool skipProtoHeader = PR_FALSE; michael@0: ServerCertAuth serverCertAuth; michael@0: int headerSeparatorPtrnId = 0; michael@0: int error = 0; michael@0: PRUint16 portno = 443; michael@0: char * hs1SniHostName = NULL; michael@0: char * hs2SniHostName = NULL; michael@0: PLOptState *optstate; michael@0: PLOptStatus optstatus; michael@0: PRStatus prStatus; michael@0: michael@0: serverCertAuth.shouldPause = PR_TRUE; michael@0: serverCertAuth.isPaused = PR_FALSE; michael@0: serverCertAuth.dbHandle = NULL; michael@0: serverCertAuth.testFreshStatusFromSideChannel = PR_FALSE; michael@0: serverCertAuth.sideChannelRevocationTestResultCode = EXIT_CODE_HANDSHAKE_FAILED; michael@0: serverCertAuth.requireDataForIntermediates = PR_FALSE; michael@0: serverCertAuth.allowOCSPSideChannelData = PR_TRUE; michael@0: serverCertAuth.allowCRLSideChannelData = PR_TRUE; michael@0: michael@0: progName = strrchr(argv[0], '/'); michael@0: if (!progName) michael@0: progName = strrchr(argv[0], '\\'); michael@0: progName = progName ? progName+1 : argv[0]; michael@0: michael@0: tmp = PR_GetEnv("NSS_DEBUG_TIMEOUT"); michael@0: if (tmp && tmp[0]) { michael@0: int sec = PORT_Atoi(tmp); michael@0: if (sec > 0) { michael@0: maxInterval = PR_SecondsToInterval(sec); michael@0: } michael@0: } michael@0: michael@0: SSL_VersionRangeGetSupported(ssl_variant_stream, &enabledVersions); michael@0: michael@0: optstate = PL_CreateOptState(argc, argv, michael@0: "46BFM:OSTV:W:Ya:c:d:fgh:m:n:op:qr:st:uvw:xz"); michael@0: while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) { michael@0: switch (optstate->option) { michael@0: case '?': michael@0: default : Usage(progName); break; michael@0: michael@0: case '4': allowIPv6 = PR_FALSE; if (!allowIPv4) Usage(progName); break; michael@0: case '6': allowIPv4 = PR_FALSE; if (!allowIPv6) Usage(progName); break; michael@0: michael@0: case 'B': bypassPKCS11 = 1; break; michael@0: michael@0: case 'F': if (serverCertAuth.testFreshStatusFromSideChannel) { michael@0: /* parameter given twice or more */ michael@0: serverCertAuth.requireDataForIntermediates = PR_TRUE; michael@0: } michael@0: serverCertAuth.testFreshStatusFromSideChannel = PR_TRUE; michael@0: break; michael@0: michael@0: case 'I': /* reserved for OCSP multi-stapling */ break; michael@0: michael@0: case 'O': serverCertAuth.shouldPause = PR_FALSE; break; michael@0: michael@0: case 'M': switch (atoi(optstate->value)) { michael@0: case 1: michael@0: serverCertAuth.allowOCSPSideChannelData = PR_TRUE; michael@0: serverCertAuth.allowCRLSideChannelData = PR_FALSE; michael@0: break; michael@0: case 2: michael@0: serverCertAuth.allowOCSPSideChannelData = PR_FALSE; michael@0: serverCertAuth.allowCRLSideChannelData = PR_TRUE; michael@0: break; michael@0: case 0: michael@0: default: michael@0: serverCertAuth.allowOCSPSideChannelData = PR_TRUE; michael@0: serverCertAuth.allowCRLSideChannelData = PR_TRUE; michael@0: break; michael@0: }; michael@0: break; michael@0: michael@0: case 'S': skipProtoHeader = PR_TRUE; break; michael@0: michael@0: case 'T': enableCertStatus = 1; break; michael@0: michael@0: case 'V': if (SECU_ParseSSLVersionRangeString(optstate->value, michael@0: enabledVersions, enableSSL2, michael@0: &enabledVersions, &enableSSL2) != SECSuccess) { michael@0: Usage(progName); michael@0: } michael@0: break; michael@0: michael@0: case 'Y': PrintCipherUsage(progName); exit(0); break; michael@0: michael@0: case 'a': if (!hs1SniHostName) { michael@0: hs1SniHostName = PORT_Strdup(optstate->value); michael@0: } else if (!hs2SniHostName) { michael@0: hs2SniHostName = PORT_Strdup(optstate->value); michael@0: } else { michael@0: Usage(progName); michael@0: } michael@0: break; michael@0: michael@0: case 'c': cipherString = PORT_Strdup(optstate->value); break; michael@0: michael@0: case 'g': enableFalseStart = 1; break; michael@0: michael@0: case 'd': certDir = PORT_Strdup(optstate->value); break; michael@0: michael@0: case 'f': clientSpeaksFirst = PR_TRUE; break; michael@0: michael@0: case 'h': host = PORT_Strdup(optstate->value); break; michael@0: michael@0: case 'm': michael@0: multiplier = atoi(optstate->value); michael@0: if (multiplier < 0) michael@0: multiplier = 0; michael@0: break; michael@0: michael@0: case 'n': nickname = PORT_Strdup(optstate->value); break; michael@0: michael@0: case 'o': override = 1; break; michael@0: michael@0: case 'p': portno = (PRUint16)atoi(optstate->value); break; michael@0: michael@0: case 'q': pingServerFirst = PR_TRUE; break; michael@0: michael@0: case 's': disableLocking = 1; break; michael@0: michael@0: case 't': pingTimeoutSeconds = atoi(optstate->value); break; michael@0: michael@0: case 'u': enableSessionTickets = PR_TRUE; break; michael@0: michael@0: case 'v': verbose++; break; michael@0: michael@0: case 'r': renegotiationsToDo = atoi(optstate->value); break; michael@0: michael@0: case 'w': michael@0: pwdata.source = PW_PLAINTEXT; michael@0: pwdata.data = PORT_Strdup(optstate->value); michael@0: break; michael@0: michael@0: case 'W': michael@0: pwdata.source = PW_FROMFILE; michael@0: pwdata.data = PORT_Strdup(optstate->value); michael@0: break; michael@0: michael@0: case 'x': useExportPolicy = 1; break; michael@0: michael@0: case 'z': enableCompression = 1; break; michael@0: } michael@0: } michael@0: michael@0: PL_DestroyOptState(optstate); michael@0: michael@0: if (optstatus == PL_OPT_BAD) michael@0: Usage(progName); michael@0: michael@0: if (!host || !portno) michael@0: Usage(progName); michael@0: michael@0: if (serverCertAuth.testFreshStatusFromSideChannel michael@0: && serverCertAuth.shouldPause) { michael@0: fprintf(stderr, "%s: -F requires the use of -O\n", progName); michael@0: exit(1); michael@0: } michael@0: michael@0: PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); michael@0: michael@0: PK11_SetPasswordFunc(SECU_GetModulePassword); michael@0: michael@0: status = PR_StringToNetAddr(host, &addr); michael@0: if (status == PR_SUCCESS) { michael@0: addr.inet.port = PR_htons(portno); michael@0: } else { michael@0: /* Lookup host */ michael@0: PRAddrInfo *addrInfo; michael@0: void *enumPtr = NULL; michael@0: michael@0: addrInfo = PR_GetAddrInfoByName(host, PR_AF_UNSPEC, michael@0: PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME); michael@0: if (!addrInfo) { michael@0: SECU_PrintError(progName, "error looking up host"); michael@0: return 1; michael@0: } michael@0: for (;;) { michael@0: enumPtr = PR_EnumerateAddrInfo(enumPtr, addrInfo, portno, &addr); michael@0: if (enumPtr == NULL) michael@0: break; michael@0: if (addr.raw.family == PR_AF_INET && allowIPv4) michael@0: break; michael@0: if (addr.raw.family == PR_AF_INET6 && allowIPv6) michael@0: break; michael@0: } michael@0: PR_FreeAddrInfo(addrInfo); michael@0: if (enumPtr == NULL) { michael@0: SECU_PrintError(progName, "error looking up host address"); michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: printHostNameAndAddr(host, &addr); michael@0: michael@0: if (pingServerFirst) { michael@0: int iter = 0; michael@0: PRErrorCode err; michael@0: int max_attempts = MAX_WAIT_FOR_SERVER; michael@0: if (pingTimeoutSeconds >= 0) { michael@0: /* If caller requested a timeout, let's try just twice. */ michael@0: max_attempts = 2; michael@0: } michael@0: do { michael@0: PRIntervalTime timeoutInterval = PR_INTERVAL_NO_TIMEOUT; michael@0: s = PR_OpenTCPSocket(addr.raw.family); michael@0: if (s == NULL) { michael@0: SECU_PrintError(progName, "Failed to create a TCP socket"); michael@0: } michael@0: opt.option = PR_SockOpt_Nonblocking; michael@0: opt.value.non_blocking = PR_FALSE; michael@0: prStatus = PR_SetSocketOption(s, &opt); michael@0: if (prStatus != PR_SUCCESS) { michael@0: PR_Close(s); michael@0: SECU_PrintError(progName, michael@0: "Failed to set blocking socket option"); michael@0: return 1; michael@0: } michael@0: if (pingTimeoutSeconds >= 0) { michael@0: timeoutInterval = PR_SecondsToInterval(pingTimeoutSeconds); michael@0: } michael@0: prStatus = PR_Connect(s, &addr, timeoutInterval); michael@0: if (prStatus == PR_SUCCESS) { michael@0: PR_Shutdown(s, PR_SHUTDOWN_BOTH); michael@0: PR_Close(s); michael@0: PR_Cleanup(); michael@0: return 0; michael@0: } michael@0: err = PR_GetError(); michael@0: if ((err != PR_CONNECT_REFUSED_ERROR) && michael@0: (err != PR_CONNECT_RESET_ERROR)) { michael@0: SECU_PrintError(progName, "TCP Connection failed"); michael@0: return 1; michael@0: } michael@0: PR_Close(s); michael@0: PR_Sleep(PR_MillisecondsToInterval(WAIT_INTERVAL)); michael@0: } while (++iter < max_attempts); michael@0: SECU_PrintError(progName, michael@0: "Client timed out while waiting for connection to server"); michael@0: return 1; michael@0: } michael@0: michael@0: /* open the cert DB, the key DB, and the secmod DB. */ michael@0: if (!certDir) { michael@0: certDir = SECU_DefaultSSLDir(); /* Look in $SSL_DIR */ michael@0: certDir = SECU_ConfigDirectory(certDir); michael@0: } else { michael@0: char *certDirTmp = certDir; michael@0: certDir = SECU_ConfigDirectory(certDirTmp); michael@0: PORT_Free(certDirTmp); michael@0: } michael@0: rv = NSS_Init(certDir); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "unable to open cert database"); michael@0: return 1; michael@0: } michael@0: michael@0: /* set the policy bits true for all the cipher suites. */ michael@0: if (useExportPolicy) michael@0: NSS_SetExportPolicy(); michael@0: else michael@0: NSS_SetDomesticPolicy(); michael@0: michael@0: /* all the SSL2 and SSL3 cipher suites are enabled by default. */ michael@0: if (cipherString) { michael@0: /* disable all the ciphers, then enable the ones we want. */ michael@0: disableAllSSLCiphers(); michael@0: } michael@0: michael@0: /* Create socket */ michael@0: s = PR_OpenTCPSocket(addr.raw.family); michael@0: if (s == NULL) { michael@0: SECU_PrintError(progName, "error creating socket"); michael@0: return 1; michael@0: } michael@0: michael@0: opt.option = PR_SockOpt_Nonblocking; michael@0: opt.value.non_blocking = PR_TRUE; /* default */ michael@0: if (serverCertAuth.testFreshStatusFromSideChannel) { michael@0: opt.value.non_blocking = PR_FALSE; michael@0: } michael@0: PR_SetSocketOption(s, &opt); michael@0: /*PR_SetSocketOption(PR_GetSpecialFD(PR_StandardInput), &opt);*/ michael@0: michael@0: s = SSL_ImportFD(NULL, s); michael@0: if (s == NULL) { michael@0: SECU_PrintError(progName, "error importing socket"); michael@0: return 1; michael@0: } michael@0: michael@0: rv = SSL_OptionSet(s, SSL_SECURITY, 1); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "error enabling socket"); michael@0: return 1; michael@0: } michael@0: michael@0: rv = SSL_OptionSet(s, SSL_HANDSHAKE_AS_CLIENT, 1); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "error enabling client handshake"); michael@0: return 1; michael@0: } michael@0: michael@0: /* all the SSL2 and SSL3 cipher suites are enabled by default. */ michael@0: if (cipherString) { michael@0: char *cstringSaved = cipherString; michael@0: int ndx; michael@0: michael@0: while (0 != (ndx = *cipherString++)) { michael@0: int cipher; michael@0: michael@0: if (ndx == ':') { michael@0: int ctmp; michael@0: michael@0: cipher = 0; michael@0: HEXCHAR_TO_INT(*cipherString, ctmp) michael@0: cipher |= (ctmp << 12); michael@0: cipherString++; michael@0: HEXCHAR_TO_INT(*cipherString, ctmp) michael@0: cipher |= (ctmp << 8); michael@0: cipherString++; michael@0: HEXCHAR_TO_INT(*cipherString, ctmp) michael@0: cipher |= (ctmp << 4); michael@0: cipherString++; michael@0: HEXCHAR_TO_INT(*cipherString, ctmp) michael@0: cipher |= ctmp; michael@0: cipherString++; michael@0: } else { michael@0: const int *cptr; michael@0: michael@0: if (! isalpha(ndx)) michael@0: Usage(progName); michael@0: cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites; michael@0: for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; ) michael@0: /* do nothing */; michael@0: } michael@0: if (cipher > 0) { michael@0: SECStatus status; michael@0: status = SSL_CipherPrefSet(s, cipher, SSL_ALLOWED); michael@0: if (status != SECSuccess) michael@0: SECU_PrintError(progName, "SSL_CipherPrefSet()"); michael@0: } else { michael@0: Usage(progName); michael@0: } michael@0: } michael@0: PORT_Free(cstringSaved); michael@0: } michael@0: michael@0: rv = SSL_VersionRangeSet(s, &enabledVersions); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "error setting SSL/TLS version range "); michael@0: return 1; michael@0: } michael@0: michael@0: rv = SSL_OptionSet(s, SSL_ENABLE_SSL2, enableSSL2); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "error enabling SSLv2 "); michael@0: return 1; michael@0: } michael@0: michael@0: rv = SSL_OptionSet(s, SSL_V2_COMPATIBLE_HELLO, enableSSL2); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "error enabling SSLv2 compatible hellos "); michael@0: return 1; michael@0: } michael@0: michael@0: /* enable PKCS11 bypass */ michael@0: rv = SSL_OptionSet(s, SSL_BYPASS_PKCS11, bypassPKCS11); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "error enabling PKCS11 bypass"); michael@0: return 1; michael@0: } michael@0: michael@0: /* disable SSL socket locking */ michael@0: rv = SSL_OptionSet(s, SSL_NO_LOCKS, disableLocking); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "error disabling SSL socket locking"); michael@0: return 1; michael@0: } michael@0: michael@0: /* enable Session Ticket extension. */ michael@0: rv = SSL_OptionSet(s, SSL_ENABLE_SESSION_TICKETS, enableSessionTickets); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "error enabling Session Ticket extension"); michael@0: return 1; michael@0: } michael@0: michael@0: /* enable compression. */ michael@0: rv = SSL_OptionSet(s, SSL_ENABLE_DEFLATE, enableCompression); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "error enabling compression"); michael@0: return 1; michael@0: } michael@0: michael@0: /* enable false start. */ michael@0: rv = SSL_OptionSet(s, SSL_ENABLE_FALSE_START, enableFalseStart); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "error enabling false start"); michael@0: return 1; michael@0: } michael@0: michael@0: /* enable cert status (OCSP stapling). */ michael@0: rv = SSL_OptionSet(s, SSL_ENABLE_OCSP_STAPLING, enableCertStatus); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "error enabling cert status (OCSP stapling)"); michael@0: return 1; michael@0: } michael@0: michael@0: SSL_SetPKCS11PinArg(s, &pwdata); michael@0: michael@0: serverCertAuth.dbHandle = CERT_GetDefaultCertDB(); michael@0: michael@0: SSL_AuthCertificateHook(s, ownAuthCertificate, &serverCertAuth); michael@0: if (override) { michael@0: SSL_BadCertHook(s, ownBadCertHandler, NULL); michael@0: } michael@0: SSL_GetClientAuthDataHook(s, own_GetClientAuthData, (void *)nickname); michael@0: SSL_HandshakeCallback(s, handshakeCallback, hs2SniHostName); michael@0: if (hs1SniHostName) { michael@0: SSL_SetURL(s, hs1SniHostName); michael@0: } else { michael@0: SSL_SetURL(s, host); michael@0: } michael@0: michael@0: /* Try to connect to the server */ michael@0: status = PR_Connect(s, &addr, PR_INTERVAL_NO_TIMEOUT); michael@0: if (status != PR_SUCCESS) { michael@0: if (PR_GetError() == PR_IN_PROGRESS_ERROR) { michael@0: if (verbose) michael@0: SECU_PrintError(progName, "connect"); michael@0: milliPause(50 * multiplier); michael@0: pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; michael@0: pollset[SSOCK_FD].out_flags = 0; michael@0: pollset[SSOCK_FD].fd = s; michael@0: while(1) { michael@0: FPRINTF(stderr, michael@0: "%s: about to call PR_Poll for connect completion!\n", michael@0: progName); michael@0: filesReady = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT); michael@0: if (filesReady < 0) { michael@0: SECU_PrintError(progName, "unable to connect (poll)"); michael@0: return 1; michael@0: } michael@0: FPRINTF(stderr, michael@0: "%s: PR_Poll returned 0x%02x for socket out_flags.\n", michael@0: progName, pollset[SSOCK_FD].out_flags); michael@0: if (filesReady == 0) { /* shouldn't happen! */ michael@0: FPRINTF(stderr, "%s: PR_Poll returned zero!\n", progName); michael@0: return 1; michael@0: } michael@0: status = PR_GetConnectStatus(pollset); michael@0: if (status == PR_SUCCESS) { michael@0: break; michael@0: } michael@0: if (PR_GetError() != PR_IN_PROGRESS_ERROR) { michael@0: SECU_PrintError(progName, "unable to connect (poll)"); michael@0: return 1; michael@0: } michael@0: SECU_PrintError(progName, "poll"); michael@0: milliPause(50 * multiplier); michael@0: } michael@0: } else { michael@0: SECU_PrintError(progName, "unable to connect"); michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: pollset[SSOCK_FD].fd = s; michael@0: pollset[SSOCK_FD].in_flags = PR_POLL_EXCEPT | michael@0: (clientSpeaksFirst ? 0 : PR_POLL_READ); michael@0: pollset[STDIN_FD].fd = PR_GetSpecialFD(PR_StandardInput); michael@0: pollset[STDIN_FD].in_flags = PR_POLL_READ; michael@0: npds = 2; michael@0: std_out = PR_GetSpecialFD(PR_StandardOutput); michael@0: michael@0: #if defined(WIN32) || defined(OS2) michael@0: /* PR_Poll cannot be used with stdin on Windows or OS/2. (sigh). michael@0: ** But use of PR_Poll and non-blocking sockets is a major feature michael@0: ** of this program. So, we simulate a pollable stdin with a michael@0: ** TCP socket pair and a thread that reads stdin and writes to michael@0: ** that socket pair. michael@0: */ michael@0: { michael@0: PRFileDesc * fds[2]; michael@0: PRThread * thread; michael@0: michael@0: int nspr_rv = PR_NewTCPSocketPair(fds); michael@0: if (nspr_rv != PR_SUCCESS) { michael@0: SECU_PrintError(progName, "PR_NewTCPSocketPair failed"); michael@0: error = 1; michael@0: goto done; michael@0: } michael@0: pollset[STDIN_FD].fd = fds[1]; michael@0: michael@0: thread = PR_CreateThread(PR_USER_THREAD, thread_main, fds[0], michael@0: PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, michael@0: PR_UNJOINABLE_THREAD, 0); michael@0: if (!thread) { michael@0: SECU_PrintError(progName, "PR_CreateThread failed"); michael@0: error = 1; michael@0: goto done; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: if (serverCertAuth.testFreshStatusFromSideChannel) { michael@0: SSL_ForceHandshake(s); michael@0: error = serverCertAuth.sideChannelRevocationTestResultCode; michael@0: goto done; michael@0: } michael@0: michael@0: /* michael@0: ** Select on stdin and on the socket. Write data from stdin to michael@0: ** socket, read data from socket and write to stdout. michael@0: */ michael@0: FPRINTF(stderr, "%s: ready...\n", progName); michael@0: michael@0: while (pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) { michael@0: char buf[4000]; /* buffer for stdin */ michael@0: int nb; /* num bytes read from stdin. */ michael@0: michael@0: rv = restartHandshakeAfterServerCertIfNeeded(s, &serverCertAuth, michael@0: override); michael@0: if (rv != SECSuccess) { michael@0: error = EXIT_CODE_HANDSHAKE_FAILED; michael@0: SECU_PrintError(progName, "authentication of server cert failed"); michael@0: goto done; michael@0: } michael@0: michael@0: pollset[SSOCK_FD].out_flags = 0; michael@0: pollset[STDIN_FD].out_flags = 0; michael@0: michael@0: FPRINTF(stderr, "%s: about to call PR_Poll !\n", progName); michael@0: filesReady = PR_Poll(pollset, npds, PR_INTERVAL_NO_TIMEOUT); michael@0: if (filesReady < 0) { michael@0: SECU_PrintError(progName, "select failed"); michael@0: error = 1; michael@0: goto done; michael@0: } michael@0: if (filesReady == 0) { /* shouldn't happen! */ michael@0: FPRINTF(stderr, "%s: PR_Poll returned zero!\n", progName); michael@0: return 1; michael@0: } michael@0: FPRINTF(stderr, "%s: PR_Poll returned!\n", progName); michael@0: if (pollset[STDIN_FD].in_flags) { michael@0: FPRINTF(stderr, michael@0: "%s: PR_Poll returned 0x%02x for stdin out_flags.\n", michael@0: progName, pollset[STDIN_FD].out_flags); michael@0: } michael@0: if (pollset[SSOCK_FD].in_flags) { michael@0: FPRINTF(stderr, michael@0: "%s: PR_Poll returned 0x%02x for socket out_flags.\n", michael@0: progName, pollset[SSOCK_FD].out_flags); michael@0: } michael@0: if (pollset[STDIN_FD].out_flags & PR_POLL_READ) { michael@0: /* Read from stdin and write to socket */ michael@0: nb = PR_Read(pollset[STDIN_FD].fd, buf, sizeof(buf)); michael@0: FPRINTF(stderr, "%s: stdin read %d bytes\n", progName, nb); michael@0: if (nb < 0) { michael@0: if (PR_GetError() != PR_WOULD_BLOCK_ERROR) { michael@0: SECU_PrintError(progName, "read from stdin failed"); michael@0: error = 1; michael@0: break; michael@0: } michael@0: } else if (nb == 0) { michael@0: /* EOF on stdin, stop polling stdin for read. */ michael@0: pollset[STDIN_FD].in_flags = 0; michael@0: } else { michael@0: char * bufp = buf; michael@0: FPRINTF(stderr, "%s: Writing %d bytes to server\n", michael@0: progName, nb); michael@0: do { michael@0: PRInt32 cc = PR_Send(s, bufp, nb, 0, maxInterval); michael@0: if (cc < 0) { michael@0: PRErrorCode err = PR_GetError(); michael@0: if (err != PR_WOULD_BLOCK_ERROR) { michael@0: SECU_PrintError(progName, michael@0: "write to SSL socket failed"); michael@0: error = 254; michael@0: goto done; michael@0: } michael@0: cc = 0; michael@0: } michael@0: bufp += cc; michael@0: nb -= cc; michael@0: if (nb <= 0) michael@0: break; michael@0: michael@0: rv = restartHandshakeAfterServerCertIfNeeded(s, michael@0: &serverCertAuth, override); michael@0: if (rv != SECSuccess) { michael@0: error = EXIT_CODE_HANDSHAKE_FAILED; michael@0: SECU_PrintError(progName, "authentication of server cert failed"); michael@0: goto done; michael@0: } michael@0: michael@0: pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; michael@0: pollset[SSOCK_FD].out_flags = 0; michael@0: FPRINTF(stderr, michael@0: "%s: about to call PR_Poll on writable socket !\n", michael@0: progName); michael@0: cc = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT); michael@0: FPRINTF(stderr, michael@0: "%s: PR_Poll returned with writable socket !\n", michael@0: progName); michael@0: } while (1); michael@0: pollset[SSOCK_FD].in_flags = PR_POLL_READ; michael@0: } michael@0: } michael@0: michael@0: if (pollset[SSOCK_FD].in_flags) { michael@0: FPRINTF(stderr, michael@0: "%s: PR_Poll returned 0x%02x for socket out_flags.\n", michael@0: progName, pollset[SSOCK_FD].out_flags); michael@0: } michael@0: if ( (pollset[SSOCK_FD].out_flags & PR_POLL_READ) michael@0: || (pollset[SSOCK_FD].out_flags & PR_POLL_ERR) michael@0: #ifdef PR_POLL_HUP michael@0: || (pollset[SSOCK_FD].out_flags & PR_POLL_HUP) michael@0: #endif michael@0: ) { michael@0: /* Read from socket and write to stdout */ michael@0: nb = PR_Recv(pollset[SSOCK_FD].fd, buf, sizeof buf, 0, maxInterval); michael@0: FPRINTF(stderr, "%s: Read from server %d bytes\n", progName, nb); michael@0: if (nb < 0) { michael@0: if (PR_GetError() != PR_WOULD_BLOCK_ERROR) { michael@0: SECU_PrintError(progName, "read from socket failed"); michael@0: error = 1; michael@0: goto done; michael@0: } michael@0: } else if (nb == 0) { michael@0: /* EOF from socket... stop polling socket for read */ michael@0: pollset[SSOCK_FD].in_flags = 0; michael@0: } else { michael@0: if (skipProtoHeader != PR_TRUE || wrStarted == PR_TRUE) { michael@0: PR_Write(std_out, buf, nb); michael@0: } else { michael@0: separateReqHeader(std_out, buf, nb, &wrStarted, michael@0: &headerSeparatorPtrnId); michael@0: } michael@0: if (verbose) michael@0: fputs("\n\n", stderr); michael@0: } michael@0: } michael@0: milliPause(50 * multiplier); michael@0: } michael@0: michael@0: done: michael@0: if (hs1SniHostName) { michael@0: PORT_Free(hs1SniHostName); michael@0: } michael@0: if (hs2SniHostName) { michael@0: PORT_Free(hs2SniHostName); michael@0: } michael@0: if (nickname) { michael@0: PORT_Free(nickname); michael@0: } michael@0: if (pwdata.data) { michael@0: PORT_Free(pwdata.data); michael@0: } michael@0: PORT_Free(host); michael@0: michael@0: PR_Close(s); michael@0: SSL_ClearSessionCache(); michael@0: if (NSS_Shutdown() != SECSuccess) { michael@0: exit(1); michael@0: } michael@0: michael@0: FPRINTF(stderr, "tstclnt: exiting with return code %d\n", error); michael@0: PR_Cleanup(); michael@0: return error; michael@0: }