Wed, 31 Dec 2014 07:16:47 +0100
Revert simplistic fix pending revisit of Mozilla integration attempt.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | /* |
michael@0 | 6 | ** |
michael@0 | 7 | ** Sample client side test program that uses SSL and NSS |
michael@0 | 8 | ** |
michael@0 | 9 | */ |
michael@0 | 10 | |
michael@0 | 11 | #include "secutil.h" |
michael@0 | 12 | #include "basicutil.h" |
michael@0 | 13 | |
michael@0 | 14 | #if defined(XP_UNIX) |
michael@0 | 15 | #include <unistd.h> |
michael@0 | 16 | #else |
michael@0 | 17 | #include <ctype.h> /* for isalpha() */ |
michael@0 | 18 | #endif |
michael@0 | 19 | |
michael@0 | 20 | #include <stdio.h> |
michael@0 | 21 | #include <string.h> |
michael@0 | 22 | #include <stdlib.h> |
michael@0 | 23 | #include <errno.h> |
michael@0 | 24 | #include <fcntl.h> |
michael@0 | 25 | #include <stdarg.h> |
michael@0 | 26 | |
michael@0 | 27 | #include "nspr.h" |
michael@0 | 28 | #include "prio.h" |
michael@0 | 29 | #include "prnetdb.h" |
michael@0 | 30 | #include "nss.h" |
michael@0 | 31 | #include "ocsp.h" |
michael@0 | 32 | #include "ssl.h" |
michael@0 | 33 | #include "sslproto.h" |
michael@0 | 34 | #include "pk11func.h" |
michael@0 | 35 | #include "plgetopt.h" |
michael@0 | 36 | #include "plstr.h" |
michael@0 | 37 | |
michael@0 | 38 | #if defined(WIN32) |
michael@0 | 39 | #include <fcntl.h> |
michael@0 | 40 | #include <io.h> |
michael@0 | 41 | #endif |
michael@0 | 42 | |
michael@0 | 43 | #define PRINTF if (verbose) printf |
michael@0 | 44 | #define FPRINTF if (verbose) fprintf |
michael@0 | 45 | |
michael@0 | 46 | #define MAX_WAIT_FOR_SERVER 600 |
michael@0 | 47 | #define WAIT_INTERVAL 100 |
michael@0 | 48 | |
michael@0 | 49 | #define EXIT_CODE_HANDSHAKE_FAILED 254 |
michael@0 | 50 | |
michael@0 | 51 | #define EXIT_CODE_SIDECHANNELTEST_GOOD 0 |
michael@0 | 52 | #define EXIT_CODE_SIDECHANNELTEST_BADCERT 1 |
michael@0 | 53 | #define EXIT_CODE_SIDECHANNELTEST_NODATA 2 |
michael@0 | 54 | #define EXIT_CODE_SIDECHANNELTEST_REVOKED 3 |
michael@0 | 55 | |
michael@0 | 56 | PRIntervalTime maxInterval = PR_INTERVAL_NO_TIMEOUT; |
michael@0 | 57 | |
michael@0 | 58 | int ssl2CipherSuites[] = { |
michael@0 | 59 | SSL_EN_RC4_128_WITH_MD5, /* A */ |
michael@0 | 60 | SSL_EN_RC4_128_EXPORT40_WITH_MD5, /* B */ |
michael@0 | 61 | SSL_EN_RC2_128_CBC_WITH_MD5, /* C */ |
michael@0 | 62 | SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */ |
michael@0 | 63 | SSL_EN_DES_64_CBC_WITH_MD5, /* E */ |
michael@0 | 64 | SSL_EN_DES_192_EDE3_CBC_WITH_MD5, /* F */ |
michael@0 | 65 | 0 |
michael@0 | 66 | }; |
michael@0 | 67 | |
michael@0 | 68 | int ssl3CipherSuites[] = { |
michael@0 | 69 | -1, /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA* a */ |
michael@0 | 70 | -1, /* SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, * b */ |
michael@0 | 71 | TLS_RSA_WITH_RC4_128_MD5, /* c */ |
michael@0 | 72 | TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* d */ |
michael@0 | 73 | TLS_RSA_WITH_DES_CBC_SHA, /* e */ |
michael@0 | 74 | TLS_RSA_EXPORT_WITH_RC4_40_MD5, /* f */ |
michael@0 | 75 | TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* g */ |
michael@0 | 76 | -1, /* SSL_FORTEZZA_DMS_WITH_NULL_SHA, * h */ |
michael@0 | 77 | TLS_RSA_WITH_NULL_MD5, /* i */ |
michael@0 | 78 | SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, /* j */ |
michael@0 | 79 | SSL_RSA_FIPS_WITH_DES_CBC_SHA, /* k */ |
michael@0 | 80 | TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, /* l */ |
michael@0 | 81 | TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, /* m */ |
michael@0 | 82 | TLS_RSA_WITH_RC4_128_SHA, /* n */ |
michael@0 | 83 | TLS_DHE_DSS_WITH_RC4_128_SHA, /* o */ |
michael@0 | 84 | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, /* p */ |
michael@0 | 85 | TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, /* q */ |
michael@0 | 86 | TLS_DHE_RSA_WITH_DES_CBC_SHA, /* r */ |
michael@0 | 87 | TLS_DHE_DSS_WITH_DES_CBC_SHA, /* s */ |
michael@0 | 88 | TLS_DHE_DSS_WITH_AES_128_CBC_SHA, /* t */ |
michael@0 | 89 | TLS_DHE_RSA_WITH_AES_128_CBC_SHA, /* u */ |
michael@0 | 90 | TLS_RSA_WITH_AES_128_CBC_SHA, /* v */ |
michael@0 | 91 | TLS_DHE_DSS_WITH_AES_256_CBC_SHA, /* w */ |
michael@0 | 92 | TLS_DHE_RSA_WITH_AES_256_CBC_SHA, /* x */ |
michael@0 | 93 | TLS_RSA_WITH_AES_256_CBC_SHA, /* y */ |
michael@0 | 94 | TLS_RSA_WITH_NULL_SHA, /* z */ |
michael@0 | 95 | 0 |
michael@0 | 96 | }; |
michael@0 | 97 | |
michael@0 | 98 | unsigned long __cmp_umuls; |
michael@0 | 99 | PRBool verbose; |
michael@0 | 100 | int renegotiationsToDo = 0; |
michael@0 | 101 | int renegotiationsDone = 0; |
michael@0 | 102 | |
michael@0 | 103 | static char *progName; |
michael@0 | 104 | |
michael@0 | 105 | secuPWData pwdata = { PW_NONE, 0 }; |
michael@0 | 106 | |
michael@0 | 107 | void printSecurityInfo(PRFileDesc *fd) |
michael@0 | 108 | { |
michael@0 | 109 | CERTCertificate * cert; |
michael@0 | 110 | const SECItemArray *csa; |
michael@0 | 111 | SSL3Statistics * ssl3stats = SSL_GetStatistics(); |
michael@0 | 112 | SECStatus result; |
michael@0 | 113 | SSLChannelInfo channel; |
michael@0 | 114 | SSLCipherSuiteInfo suite; |
michael@0 | 115 | |
michael@0 | 116 | result = SSL_GetChannelInfo(fd, &channel, sizeof channel); |
michael@0 | 117 | if (result == SECSuccess && |
michael@0 | 118 | channel.length == sizeof channel && |
michael@0 | 119 | channel.cipherSuite) { |
michael@0 | 120 | result = SSL_GetCipherSuiteInfo(channel.cipherSuite, |
michael@0 | 121 | &suite, sizeof suite); |
michael@0 | 122 | if (result == SECSuccess) { |
michael@0 | 123 | FPRINTF(stderr, |
michael@0 | 124 | "tstclnt: SSL version %d.%d using %d-bit %s with %d-bit %s MAC\n", |
michael@0 | 125 | channel.protocolVersion >> 8, channel.protocolVersion & 0xff, |
michael@0 | 126 | suite.effectiveKeyBits, suite.symCipherName, |
michael@0 | 127 | suite.macBits, suite.macAlgorithmName); |
michael@0 | 128 | FPRINTF(stderr, |
michael@0 | 129 | "tstclnt: Server Auth: %d-bit %s, Key Exchange: %d-bit %s\n" |
michael@0 | 130 | " Compression: %s\n", |
michael@0 | 131 | channel.authKeyBits, suite.authAlgorithmName, |
michael@0 | 132 | channel.keaKeyBits, suite.keaTypeName, |
michael@0 | 133 | channel.compressionMethodName); |
michael@0 | 134 | } |
michael@0 | 135 | } |
michael@0 | 136 | cert = SSL_RevealCert(fd); |
michael@0 | 137 | if (cert) { |
michael@0 | 138 | char * ip = CERT_NameToAscii(&cert->issuer); |
michael@0 | 139 | char * sp = CERT_NameToAscii(&cert->subject); |
michael@0 | 140 | if (sp) { |
michael@0 | 141 | fprintf(stderr, "subject DN: %s\n", sp); |
michael@0 | 142 | PORT_Free(sp); |
michael@0 | 143 | } |
michael@0 | 144 | if (ip) { |
michael@0 | 145 | fprintf(stderr, "issuer DN: %s\n", ip); |
michael@0 | 146 | PORT_Free(ip); |
michael@0 | 147 | } |
michael@0 | 148 | CERT_DestroyCertificate(cert); |
michael@0 | 149 | cert = NULL; |
michael@0 | 150 | } |
michael@0 | 151 | fprintf(stderr, |
michael@0 | 152 | "%ld cache hits; %ld cache misses, %ld cache not reusable\n" |
michael@0 | 153 | "%ld stateless resumes\n", |
michael@0 | 154 | ssl3stats->hsh_sid_cache_hits, ssl3stats->hsh_sid_cache_misses, |
michael@0 | 155 | ssl3stats->hsh_sid_cache_not_ok, ssl3stats->hsh_sid_stateless_resumes); |
michael@0 | 156 | |
michael@0 | 157 | csa = SSL_PeerStapledOCSPResponses(fd); |
michael@0 | 158 | if (csa) { |
michael@0 | 159 | fprintf(stderr, "Received %d Cert Status items (OCSP stapled data)\n", |
michael@0 | 160 | csa->len); |
michael@0 | 161 | } |
michael@0 | 162 | } |
michael@0 | 163 | |
michael@0 | 164 | void |
michael@0 | 165 | handshakeCallback(PRFileDesc *fd, void *client_data) |
michael@0 | 166 | { |
michael@0 | 167 | const char *secondHandshakeName = (char *)client_data; |
michael@0 | 168 | if (secondHandshakeName) { |
michael@0 | 169 | SSL_SetURL(fd, secondHandshakeName); |
michael@0 | 170 | } |
michael@0 | 171 | printSecurityInfo(fd); |
michael@0 | 172 | if (renegotiationsDone < renegotiationsToDo) { |
michael@0 | 173 | SSL_ReHandshake(fd, (renegotiationsToDo < 2)); |
michael@0 | 174 | ++renegotiationsDone; |
michael@0 | 175 | } |
michael@0 | 176 | } |
michael@0 | 177 | |
michael@0 | 178 | static void PrintUsageHeader(const char *progName) |
michael@0 | 179 | { |
michael@0 | 180 | fprintf(stderr, |
michael@0 | 181 | "Usage: %s -h host [-a 1st_hs_name ] [-a 2nd_hs_name ] [-p port]\n" |
michael@0 | 182 | "[-d certdir] [-n nickname] [-Bafosvx] [-c ciphers] [-Y]\n" |
michael@0 | 183 | "[-V [min-version]:[max-version]] [-T]\n" |
michael@0 | 184 | "[-r N] [-w passwd] [-W pwfile] [-q [-t seconds]]\n", |
michael@0 | 185 | progName); |
michael@0 | 186 | } |
michael@0 | 187 | |
michael@0 | 188 | static void PrintParameterUsage(void) |
michael@0 | 189 | { |
michael@0 | 190 | fprintf(stderr, "%-20s Send different SNI name. 1st_hs_name - at first\n" |
michael@0 | 191 | "%-20s handshake, 2nd_hs_name - at second handshake.\n" |
michael@0 | 192 | "%-20s Default is host from the -h argument.\n", "-a name", |
michael@0 | 193 | "", ""); |
michael@0 | 194 | fprintf(stderr, "%-20s Hostname to connect with\n", "-h host"); |
michael@0 | 195 | fprintf(stderr, "%-20s Port number for SSL server\n", "-p port"); |
michael@0 | 196 | fprintf(stderr, |
michael@0 | 197 | "%-20s Directory with cert database (default is ~/.netscape)\n", |
michael@0 | 198 | "-d certdir"); |
michael@0 | 199 | fprintf(stderr, "%-20s Nickname of key and cert for client auth\n", |
michael@0 | 200 | "-n nickname"); |
michael@0 | 201 | fprintf(stderr, |
michael@0 | 202 | "%-20s Bypass PKCS11 layer for SSL encryption and MACing.\n", "-B"); |
michael@0 | 203 | fprintf(stderr, |
michael@0 | 204 | "%-20s Restricts the set of enabled SSL/TLS protocols versions.\n" |
michael@0 | 205 | "%-20s All versions are enabled by default.\n" |
michael@0 | 206 | "%-20s Possible values for min/max: ssl2 ssl3 tls1.0 tls1.1 tls1.2\n" |
michael@0 | 207 | "%-20s Example: \"-V ssl3:\" enables SSL 3 and newer.\n", |
michael@0 | 208 | "-V [min]:[max]", "", "", ""); |
michael@0 | 209 | fprintf(stderr, "%-20s Prints only payload data. Skips HTTP header.\n", "-S"); |
michael@0 | 210 | fprintf(stderr, "%-20s Client speaks first. \n", "-f"); |
michael@0 | 211 | fprintf(stderr, "%-20s Use synchronous certificate validation " |
michael@0 | 212 | "(required for SSL2)\n", "-O"); |
michael@0 | 213 | fprintf(stderr, "%-20s Override bad server cert. Make it OK.\n", "-o"); |
michael@0 | 214 | fprintf(stderr, "%-20s Disable SSL socket locking.\n", "-s"); |
michael@0 | 215 | fprintf(stderr, "%-20s Verbose progress reporting.\n", "-v"); |
michael@0 | 216 | fprintf(stderr, "%-20s Use export policy.\n", "-x"); |
michael@0 | 217 | fprintf(stderr, "%-20s Ping the server and then exit.\n", "-q"); |
michael@0 | 218 | fprintf(stderr, "%-20s Timeout for server ping (default: no timeout).\n", "-t seconds"); |
michael@0 | 219 | fprintf(stderr, "%-20s Renegotiate N times (resuming session if N>1).\n", "-r N"); |
michael@0 | 220 | fprintf(stderr, "%-20s Enable the session ticket extension.\n", "-u"); |
michael@0 | 221 | fprintf(stderr, "%-20s Enable compression.\n", "-z"); |
michael@0 | 222 | fprintf(stderr, "%-20s Enable false start.\n", "-g"); |
michael@0 | 223 | fprintf(stderr, "%-20s Enable the cert_status extension (OCSP stapling).\n", "-T"); |
michael@0 | 224 | fprintf(stderr, "%-20s Require fresh revocation info from side channel.\n" |
michael@0 | 225 | "%-20s -F once means: require for server cert only\n" |
michael@0 | 226 | "%-20s -F twice means: require for intermediates, too\n" |
michael@0 | 227 | "%-20s (Connect, handshake with server, disable dynamic download\n" |
michael@0 | 228 | "%-20s of OCSP/CRL, verify cert using CERT_PKIXVerifyCert.)\n" |
michael@0 | 229 | "%-20s Exit code:\n" |
michael@0 | 230 | "%-20s 0: have fresh and valid revocation data, status good\n" |
michael@0 | 231 | "%-20s 1: cert failed to verify, prior to revocation checking\n" |
michael@0 | 232 | "%-20s 2: missing, old or invalid revocation data\n" |
michael@0 | 233 | "%-20s 3: have fresh and valid revocation data, status revoked\n", |
michael@0 | 234 | "-F", "", "", "", "", "", "", "", "", ""); |
michael@0 | 235 | fprintf(stderr, "%-20s Test -F allows 0=any (default), 1=only OCSP, 2=only CRL\n", "-M"); |
michael@0 | 236 | fprintf(stderr, "%-20s Restrict ciphers\n", "-c ciphers"); |
michael@0 | 237 | fprintf(stderr, "%-20s Print cipher values allowed for parameter -c and exit\n", "-Y"); |
michael@0 | 238 | fprintf(stderr, "%-20s Enforce using an IPv4 destination address\n", "-4"); |
michael@0 | 239 | fprintf(stderr, "%-20s Enforce using an IPv6 destination address\n", "-6"); |
michael@0 | 240 | fprintf(stderr, "%-20s (Options -4 and -6 cannot be combined.)\n", ""); |
michael@0 | 241 | } |
michael@0 | 242 | |
michael@0 | 243 | static void Usage(const char *progName) |
michael@0 | 244 | { |
michael@0 | 245 | PrintUsageHeader(progName); |
michael@0 | 246 | PrintParameterUsage(); |
michael@0 | 247 | exit(1); |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | static void PrintCipherUsage(const char *progName) |
michael@0 | 251 | { |
michael@0 | 252 | PrintUsageHeader(progName); |
michael@0 | 253 | fprintf(stderr, "%-20s Letter(s) chosen from the following list\n", |
michael@0 | 254 | "-c ciphers"); |
michael@0 | 255 | fprintf(stderr, |
michael@0 | 256 | "A SSL2 RC4 128 WITH MD5\n" |
michael@0 | 257 | "B SSL2 RC4 128 EXPORT40 WITH MD5\n" |
michael@0 | 258 | "C SSL2 RC2 128 CBC WITH MD5\n" |
michael@0 | 259 | "D SSL2 RC2 128 CBC EXPORT40 WITH MD5\n" |
michael@0 | 260 | "E SSL2 DES 64 CBC WITH MD5\n" |
michael@0 | 261 | "F SSL2 DES 192 EDE3 CBC WITH MD5\n" |
michael@0 | 262 | "\n" |
michael@0 | 263 | "c SSL3 RSA WITH RC4 128 MD5\n" |
michael@0 | 264 | "d SSL3 RSA WITH 3DES EDE CBC SHA\n" |
michael@0 | 265 | "e SSL3 RSA WITH DES CBC SHA\n" |
michael@0 | 266 | "f SSL3 RSA EXPORT WITH RC4 40 MD5\n" |
michael@0 | 267 | "g SSL3 RSA EXPORT WITH RC2 CBC 40 MD5\n" |
michael@0 | 268 | "i SSL3 RSA WITH NULL MD5\n" |
michael@0 | 269 | "j SSL3 RSA FIPS WITH 3DES EDE CBC SHA\n" |
michael@0 | 270 | "k SSL3 RSA FIPS WITH DES CBC SHA\n" |
michael@0 | 271 | "l SSL3 RSA EXPORT WITH DES CBC SHA\t(new)\n" |
michael@0 | 272 | "m SSL3 RSA EXPORT WITH RC4 56 SHA\t(new)\n" |
michael@0 | 273 | "n SSL3 RSA WITH RC4 128 SHA\n" |
michael@0 | 274 | "o SSL3 DHE DSS WITH RC4 128 SHA\n" |
michael@0 | 275 | "p SSL3 DHE RSA WITH 3DES EDE CBC SHA\n" |
michael@0 | 276 | "q SSL3 DHE DSS WITH 3DES EDE CBC SHA\n" |
michael@0 | 277 | "r SSL3 DHE RSA WITH DES CBC SHA\n" |
michael@0 | 278 | "s SSL3 DHE DSS WITH DES CBC SHA\n" |
michael@0 | 279 | "t SSL3 DHE DSS WITH AES 128 CBC SHA\n" |
michael@0 | 280 | "u SSL3 DHE RSA WITH AES 128 CBC SHA\n" |
michael@0 | 281 | "v SSL3 RSA WITH AES 128 CBC SHA\n" |
michael@0 | 282 | "w SSL3 DHE DSS WITH AES 256 CBC SHA\n" |
michael@0 | 283 | "x SSL3 DHE RSA WITH AES 256 CBC SHA\n" |
michael@0 | 284 | "y SSL3 RSA WITH AES 256 CBC SHA\n" |
michael@0 | 285 | "z SSL3 RSA WITH NULL SHA\n" |
michael@0 | 286 | "\n" |
michael@0 | 287 | ":WXYZ Use cipher with hex code { 0xWX , 0xYZ } in TLS\n" |
michael@0 | 288 | ); |
michael@0 | 289 | exit(1); |
michael@0 | 290 | } |
michael@0 | 291 | |
michael@0 | 292 | void |
michael@0 | 293 | milliPause(PRUint32 milli) |
michael@0 | 294 | { |
michael@0 | 295 | PRIntervalTime ticks = PR_MillisecondsToInterval(milli); |
michael@0 | 296 | PR_Sleep(ticks); |
michael@0 | 297 | } |
michael@0 | 298 | |
michael@0 | 299 | void |
michael@0 | 300 | disableAllSSLCiphers(void) |
michael@0 | 301 | { |
michael@0 | 302 | const PRUint16 *cipherSuites = SSL_GetImplementedCiphers(); |
michael@0 | 303 | int i = SSL_GetNumImplementedCiphers(); |
michael@0 | 304 | SECStatus rv; |
michael@0 | 305 | |
michael@0 | 306 | /* disable all the SSL3 cipher suites */ |
michael@0 | 307 | while (--i >= 0) { |
michael@0 | 308 | PRUint16 suite = cipherSuites[i]; |
michael@0 | 309 | rv = SSL_CipherPrefSetDefault(suite, PR_FALSE); |
michael@0 | 310 | if (rv != SECSuccess) { |
michael@0 | 311 | PRErrorCode err = PR_GetError(); |
michael@0 | 312 | fprintf(stderr, |
michael@0 | 313 | "SSL_CipherPrefSet didn't like value 0x%04x (i = %d): %s\n", |
michael@0 | 314 | suite, i, SECU_Strerror(err)); |
michael@0 | 315 | exit(2); |
michael@0 | 316 | } |
michael@0 | 317 | } |
michael@0 | 318 | } |
michael@0 | 319 | |
michael@0 | 320 | typedef struct |
michael@0 | 321 | { |
michael@0 | 322 | PRBool shouldPause; /* PR_TRUE if we should use asynchronous peer cert |
michael@0 | 323 | * authentication */ |
michael@0 | 324 | PRBool isPaused; /* PR_TRUE if libssl is waiting for us to validate the |
michael@0 | 325 | * peer's certificate and restart the handshake. */ |
michael@0 | 326 | void * dbHandle; /* Certificate database handle to use while |
michael@0 | 327 | * authenticating the peer's certificate. */ |
michael@0 | 328 | PRBool testFreshStatusFromSideChannel; |
michael@0 | 329 | PRErrorCode sideChannelRevocationTestResultCode; |
michael@0 | 330 | PRBool requireDataForIntermediates; |
michael@0 | 331 | PRBool allowOCSPSideChannelData; |
michael@0 | 332 | PRBool allowCRLSideChannelData; |
michael@0 | 333 | } ServerCertAuth; |
michael@0 | 334 | |
michael@0 | 335 | |
michael@0 | 336 | /* |
michael@0 | 337 | * Callback is called when incoming certificate is not valid. |
michael@0 | 338 | * Returns SECSuccess to accept the cert anyway, SECFailure to reject. |
michael@0 | 339 | */ |
michael@0 | 340 | static SECStatus |
michael@0 | 341 | ownBadCertHandler(void * arg, PRFileDesc * socket) |
michael@0 | 342 | { |
michael@0 | 343 | PRErrorCode err = PR_GetError(); |
michael@0 | 344 | /* can log invalid cert here */ |
michael@0 | 345 | fprintf(stderr, "Bad server certificate: %d, %s\n", err, |
michael@0 | 346 | SECU_Strerror(err)); |
michael@0 | 347 | return SECSuccess; /* override, say it's OK. */ |
michael@0 | 348 | } |
michael@0 | 349 | |
michael@0 | 350 | |
michael@0 | 351 | |
michael@0 | 352 | #define EXIT_CODE_SIDECHANNELTEST_GOOD 0 |
michael@0 | 353 | #define EXIT_CODE_SIDECHANNELTEST_BADCERT 1 |
michael@0 | 354 | #define EXIT_CODE_SIDECHANNELTEST_NODATA 2 |
michael@0 | 355 | #define EXIT_CODE_SIDECHANNELTEST_REVOKED 3 |
michael@0 | 356 | |
michael@0 | 357 | static void |
michael@0 | 358 | verifyFromSideChannel(CERTCertificate *cert, ServerCertAuth *sca) |
michael@0 | 359 | { |
michael@0 | 360 | PRUint64 revDoNotUse = |
michael@0 | 361 | CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD; |
michael@0 | 362 | |
michael@0 | 363 | PRUint64 revUseLocalOnlyAndSoftFail = |
michael@0 | 364 | CERT_REV_M_TEST_USING_THIS_METHOD |
michael@0 | 365 | | CERT_REV_M_FORBID_NETWORK_FETCHING |
michael@0 | 366 | | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE |
michael@0 | 367 | | CERT_REV_M_IGNORE_MISSING_FRESH_INFO |
michael@0 | 368 | | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO; |
michael@0 | 369 | |
michael@0 | 370 | PRUint64 revUseLocalOnlyAndHardFail = |
michael@0 | 371 | CERT_REV_M_TEST_USING_THIS_METHOD |
michael@0 | 372 | | CERT_REV_M_FORBID_NETWORK_FETCHING |
michael@0 | 373 | | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE |
michael@0 | 374 | | CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO |
michael@0 | 375 | | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO; |
michael@0 | 376 | |
michael@0 | 377 | PRUint64 methodFlagsDoNotUse[2]; |
michael@0 | 378 | PRUint64 methodFlagsCheckSoftFail[2]; |
michael@0 | 379 | PRUint64 methodFlagsCheckHardFail[2]; |
michael@0 | 380 | CERTRevocationTests revTestsDoNotCheck; |
michael@0 | 381 | CERTRevocationTests revTestsOverallSoftFail; |
michael@0 | 382 | CERTRevocationTests revTestsOverallHardFail; |
michael@0 | 383 | CERTRevocationFlags rev; |
michael@0 | 384 | CERTValInParam cvin[2]; |
michael@0 | 385 | CERTValOutParam cvout[1]; |
michael@0 | 386 | SECStatus rv; |
michael@0 | 387 | |
michael@0 | 388 | methodFlagsDoNotUse[cert_revocation_method_crl] = revDoNotUse; |
michael@0 | 389 | methodFlagsDoNotUse[cert_revocation_method_ocsp] = revDoNotUse; |
michael@0 | 390 | |
michael@0 | 391 | methodFlagsCheckSoftFail[cert_revocation_method_crl] = |
michael@0 | 392 | sca->allowCRLSideChannelData ? revUseLocalOnlyAndSoftFail : revDoNotUse; |
michael@0 | 393 | methodFlagsCheckSoftFail[cert_revocation_method_ocsp] = |
michael@0 | 394 | sca->allowOCSPSideChannelData ? revUseLocalOnlyAndSoftFail : revDoNotUse; |
michael@0 | 395 | |
michael@0 | 396 | methodFlagsCheckHardFail[cert_revocation_method_crl] = |
michael@0 | 397 | sca->allowCRLSideChannelData ? revUseLocalOnlyAndHardFail : revDoNotUse; |
michael@0 | 398 | methodFlagsCheckHardFail[cert_revocation_method_ocsp] = |
michael@0 | 399 | sca->allowOCSPSideChannelData ? revUseLocalOnlyAndHardFail : revDoNotUse; |
michael@0 | 400 | |
michael@0 | 401 | revTestsDoNotCheck.cert_rev_flags_per_method = methodFlagsDoNotUse; |
michael@0 | 402 | revTestsDoNotCheck.number_of_defined_methods = 2; |
michael@0 | 403 | revTestsDoNotCheck.number_of_preferred_methods = 0; |
michael@0 | 404 | revTestsDoNotCheck.cert_rev_method_independent_flags = |
michael@0 | 405 | CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST |
michael@0 | 406 | | CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT; |
michael@0 | 407 | |
michael@0 | 408 | revTestsOverallSoftFail.cert_rev_flags_per_method = 0; /* must define later */ |
michael@0 | 409 | revTestsOverallSoftFail.number_of_defined_methods = 2; |
michael@0 | 410 | revTestsOverallSoftFail.number_of_preferred_methods = 0; |
michael@0 | 411 | revTestsOverallSoftFail.cert_rev_method_independent_flags = |
michael@0 | 412 | CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST |
michael@0 | 413 | | CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT; |
michael@0 | 414 | |
michael@0 | 415 | revTestsOverallHardFail.cert_rev_flags_per_method = 0; /* must define later */ |
michael@0 | 416 | revTestsOverallHardFail.number_of_defined_methods = 2; |
michael@0 | 417 | revTestsOverallHardFail.number_of_preferred_methods = 0; |
michael@0 | 418 | revTestsOverallHardFail.cert_rev_method_independent_flags = |
michael@0 | 419 | CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST |
michael@0 | 420 | | CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE; |
michael@0 | 421 | |
michael@0 | 422 | rev.chainTests = revTestsDoNotCheck; |
michael@0 | 423 | rev.leafTests = revTestsDoNotCheck; |
michael@0 | 424 | |
michael@0 | 425 | cvin[0].type = cert_pi_revocationFlags; |
michael@0 | 426 | cvin[0].value.pointer.revocation = &rev; |
michael@0 | 427 | cvin[1].type = cert_pi_end; |
michael@0 | 428 | |
michael@0 | 429 | cvout[0].type = cert_po_end; |
michael@0 | 430 | |
michael@0 | 431 | /* Strategy: |
michael@0 | 432 | * |
michael@0 | 433 | * Verify with revocation checking disabled. |
michael@0 | 434 | * On failure return 1. |
michael@0 | 435 | * |
michael@0 | 436 | * if result if "good", then continue testing. |
michael@0 | 437 | * |
michael@0 | 438 | * Verify with CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO. |
michael@0 | 439 | * If result is good, return 0. |
michael@0 | 440 | * |
michael@0 | 441 | * On failure continue testing, find out why it failed. |
michael@0 | 442 | * |
michael@0 | 443 | * Verify with CERT_REV_M_IGNORE_MISSING_FRESH_INFO |
michael@0 | 444 | * |
michael@0 | 445 | * If result is "good", then our previous test failed, |
michael@0 | 446 | * because we don't have fresh revocation info, return 2. |
michael@0 | 447 | * |
michael@0 | 448 | * If result is still bad, we do have revocation info, |
michael@0 | 449 | * and it says "revoked" or something equivalent, return 3. |
michael@0 | 450 | */ |
michael@0 | 451 | |
michael@0 | 452 | /* revocation checking disabled */ |
michael@0 | 453 | rv = CERT_PKIXVerifyCert(cert, certificateUsageSSLServer, |
michael@0 | 454 | cvin, cvout, NULL); |
michael@0 | 455 | if (rv != SECSuccess) { |
michael@0 | 456 | sca->sideChannelRevocationTestResultCode = |
michael@0 | 457 | EXIT_CODE_SIDECHANNELTEST_BADCERT; |
michael@0 | 458 | return; |
michael@0 | 459 | } |
michael@0 | 460 | |
michael@0 | 461 | /* revocation checking, hard fail */ |
michael@0 | 462 | if (sca->allowOCSPSideChannelData && sca->allowCRLSideChannelData) { |
michael@0 | 463 | /* any method is allowed. use soft fail on individual checks, |
michael@0 | 464 | * but use hard fail on the overall check |
michael@0 | 465 | */ |
michael@0 | 466 | revTestsOverallHardFail.cert_rev_flags_per_method = methodFlagsCheckSoftFail; |
michael@0 | 467 | } |
michael@0 | 468 | else { |
michael@0 | 469 | /* only one method is allowed. use hard fail on the individual checks. |
michael@0 | 470 | * hard/soft fail is irrelevant on overall flags. |
michael@0 | 471 | */ |
michael@0 | 472 | revTestsOverallHardFail.cert_rev_flags_per_method = methodFlagsCheckHardFail; |
michael@0 | 473 | } |
michael@0 | 474 | rev.leafTests = revTestsOverallHardFail; |
michael@0 | 475 | rev.chainTests = |
michael@0 | 476 | sca->requireDataForIntermediates ? revTestsOverallHardFail : revTestsDoNotCheck; |
michael@0 | 477 | rv = CERT_PKIXVerifyCert(cert, certificateUsageSSLServer, |
michael@0 | 478 | cvin, cvout, NULL); |
michael@0 | 479 | if (rv == SECSuccess) { |
michael@0 | 480 | sca->sideChannelRevocationTestResultCode = |
michael@0 | 481 | EXIT_CODE_SIDECHANNELTEST_GOOD; |
michael@0 | 482 | return; |
michael@0 | 483 | } |
michael@0 | 484 | |
michael@0 | 485 | /* revocation checking, soft fail */ |
michael@0 | 486 | revTestsOverallSoftFail.cert_rev_flags_per_method = methodFlagsCheckSoftFail; |
michael@0 | 487 | rev.leafTests = revTestsOverallSoftFail; |
michael@0 | 488 | rev.chainTests = |
michael@0 | 489 | sca->requireDataForIntermediates ? revTestsOverallSoftFail : revTestsDoNotCheck; |
michael@0 | 490 | rv = CERT_PKIXVerifyCert(cert, certificateUsageSSLServer, |
michael@0 | 491 | cvin, cvout, NULL); |
michael@0 | 492 | if (rv == SECSuccess) { |
michael@0 | 493 | sca->sideChannelRevocationTestResultCode = |
michael@0 | 494 | EXIT_CODE_SIDECHANNELTEST_NODATA; |
michael@0 | 495 | return; |
michael@0 | 496 | } |
michael@0 | 497 | |
michael@0 | 498 | sca->sideChannelRevocationTestResultCode = |
michael@0 | 499 | EXIT_CODE_SIDECHANNELTEST_REVOKED; |
michael@0 | 500 | } |
michael@0 | 501 | |
michael@0 | 502 | static SECStatus |
michael@0 | 503 | ownAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, |
michael@0 | 504 | PRBool isServer) |
michael@0 | 505 | { |
michael@0 | 506 | ServerCertAuth * serverCertAuth = (ServerCertAuth *) arg; |
michael@0 | 507 | |
michael@0 | 508 | if (!serverCertAuth->shouldPause) { |
michael@0 | 509 | CERTCertificate *cert; |
michael@0 | 510 | int i; |
michael@0 | 511 | const SECItemArray *csa; |
michael@0 | 512 | |
michael@0 | 513 | if (!serverCertAuth->testFreshStatusFromSideChannel) { |
michael@0 | 514 | return SSL_AuthCertificate(serverCertAuth->dbHandle, |
michael@0 | 515 | fd, checkSig, isServer); |
michael@0 | 516 | } |
michael@0 | 517 | |
michael@0 | 518 | /* No verification attempt must have happened before now, |
michael@0 | 519 | * to ensure revocation data has been actively retrieved yet, |
michael@0 | 520 | * or our test will produce incorrect results. |
michael@0 | 521 | */ |
michael@0 | 522 | |
michael@0 | 523 | cert = SSL_RevealCert(fd); |
michael@0 | 524 | if (!cert) { |
michael@0 | 525 | exit(254); |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | csa = SSL_PeerStapledOCSPResponses(fd); |
michael@0 | 529 | if (csa) { |
michael@0 | 530 | for (i = 0; i < csa->len; ++i) { |
michael@0 | 531 | PORT_SetError(0); |
michael@0 | 532 | if (CERT_CacheOCSPResponseFromSideChannel( |
michael@0 | 533 | serverCertAuth->dbHandle, cert, PR_Now(), |
michael@0 | 534 | &csa->items[i], arg) != SECSuccess) { |
michael@0 | 535 | PRErrorCode error = PR_GetError(); |
michael@0 | 536 | PORT_Assert(error != 0); |
michael@0 | 537 | } |
michael@0 | 538 | } |
michael@0 | 539 | } |
michael@0 | 540 | |
michael@0 | 541 | verifyFromSideChannel(cert, serverCertAuth); |
michael@0 | 542 | CERT_DestroyCertificate(cert); |
michael@0 | 543 | /* return success to ensure our caller will continue and we will |
michael@0 | 544 | * reach the code that handles |
michael@0 | 545 | * serverCertAuth->sideChannelRevocationTestResultCode |
michael@0 | 546 | */ |
michael@0 | 547 | return SECSuccess; |
michael@0 | 548 | } |
michael@0 | 549 | |
michael@0 | 550 | FPRINTF(stderr, "%s: using asynchronous certificate validation\n", |
michael@0 | 551 | progName); |
michael@0 | 552 | |
michael@0 | 553 | PORT_Assert(!serverCertAuth->isPaused); |
michael@0 | 554 | serverCertAuth->isPaused = PR_TRUE; |
michael@0 | 555 | return SECWouldBlock; |
michael@0 | 556 | } |
michael@0 | 557 | |
michael@0 | 558 | SECStatus |
michael@0 | 559 | own_GetClientAuthData(void * arg, |
michael@0 | 560 | PRFileDesc * socket, |
michael@0 | 561 | struct CERTDistNamesStr * caNames, |
michael@0 | 562 | struct CERTCertificateStr ** pRetCert, |
michael@0 | 563 | struct SECKEYPrivateKeyStr **pRetKey) |
michael@0 | 564 | { |
michael@0 | 565 | if (verbose > 1) { |
michael@0 | 566 | SECStatus rv; |
michael@0 | 567 | fprintf(stderr, "Server requested Client Authentication\n"); |
michael@0 | 568 | if (caNames && caNames->nnames > 0) { |
michael@0 | 569 | PLArenaPool *arena = caNames->arena; |
michael@0 | 570 | if (!arena) |
michael@0 | 571 | arena = PORT_NewArena(2048); |
michael@0 | 572 | if (arena) { |
michael@0 | 573 | int i; |
michael@0 | 574 | for (i = 0; i < caNames->nnames; ++i) { |
michael@0 | 575 | char *nameString; |
michael@0 | 576 | CERTName dn; |
michael@0 | 577 | rv = SEC_QuickDERDecodeItem(arena, |
michael@0 | 578 | &dn, |
michael@0 | 579 | SEC_ASN1_GET(CERT_NameTemplate), |
michael@0 | 580 | caNames->names + i); |
michael@0 | 581 | if (rv != SECSuccess) |
michael@0 | 582 | continue; |
michael@0 | 583 | nameString = CERT_NameToAscii(&dn); |
michael@0 | 584 | if (!nameString) |
michael@0 | 585 | continue; |
michael@0 | 586 | fprintf(stderr, "CA[%d]: %s\n", i + 1, nameString); |
michael@0 | 587 | PORT_Free(nameString); |
michael@0 | 588 | } |
michael@0 | 589 | if (!caNames->arena) { |
michael@0 | 590 | PORT_FreeArena(arena, PR_FALSE); |
michael@0 | 591 | } |
michael@0 | 592 | } |
michael@0 | 593 | } |
michael@0 | 594 | rv = NSS_GetClientAuthData(arg, socket, caNames, pRetCert, pRetKey); |
michael@0 | 595 | if (rv == SECSuccess && *pRetCert) { |
michael@0 | 596 | char *nameString = CERT_NameToAscii(&((*pRetCert)->subject)); |
michael@0 | 597 | if (nameString) { |
michael@0 | 598 | fprintf(stderr, "sent cert: %s\n", nameString); |
michael@0 | 599 | PORT_Free(nameString); |
michael@0 | 600 | } |
michael@0 | 601 | } else { |
michael@0 | 602 | fprintf(stderr, "send no cert\n"); |
michael@0 | 603 | } |
michael@0 | 604 | return rv; |
michael@0 | 605 | } |
michael@0 | 606 | return NSS_GetClientAuthData(arg, socket, caNames, pRetCert, pRetKey); |
michael@0 | 607 | } |
michael@0 | 608 | |
michael@0 | 609 | #if defined(WIN32) || defined(OS2) |
michael@0 | 610 | void |
michael@0 | 611 | thread_main(void * arg) |
michael@0 | 612 | { |
michael@0 | 613 | PRFileDesc * ps = (PRFileDesc *)arg; |
michael@0 | 614 | PRFileDesc * std_in = PR_GetSpecialFD(PR_StandardInput); |
michael@0 | 615 | int wc, rc; |
michael@0 | 616 | char buf[256]; |
michael@0 | 617 | |
michael@0 | 618 | #ifdef WIN32 |
michael@0 | 619 | { |
michael@0 | 620 | /* Put stdin into O_BINARY mode |
michael@0 | 621 | ** or else incoming \r\n's will become \n's. |
michael@0 | 622 | */ |
michael@0 | 623 | int smrv = _setmode(_fileno(stdin), _O_BINARY); |
michael@0 | 624 | if (smrv == -1) { |
michael@0 | 625 | fprintf(stderr, |
michael@0 | 626 | "%s: Cannot change stdin to binary mode. Use -i option instead.\n", |
michael@0 | 627 | progName); |
michael@0 | 628 | /* plow ahead anyway */ |
michael@0 | 629 | } |
michael@0 | 630 | } |
michael@0 | 631 | #endif |
michael@0 | 632 | |
michael@0 | 633 | do { |
michael@0 | 634 | rc = PR_Read(std_in, buf, sizeof buf); |
michael@0 | 635 | if (rc <= 0) |
michael@0 | 636 | break; |
michael@0 | 637 | wc = PR_Send(ps, buf, rc, 0, maxInterval); |
michael@0 | 638 | } while (wc == rc); |
michael@0 | 639 | PR_Close(ps); |
michael@0 | 640 | } |
michael@0 | 641 | |
michael@0 | 642 | #endif |
michael@0 | 643 | |
michael@0 | 644 | static void |
michael@0 | 645 | printHostNameAndAddr(const char * host, const PRNetAddr * addr) |
michael@0 | 646 | { |
michael@0 | 647 | PRUint16 port = PR_NetAddrInetPort(addr); |
michael@0 | 648 | char addrBuf[80]; |
michael@0 | 649 | PRStatus st = PR_NetAddrToString(addr, addrBuf, sizeof addrBuf); |
michael@0 | 650 | |
michael@0 | 651 | if (st == PR_SUCCESS) { |
michael@0 | 652 | port = PR_ntohs(port); |
michael@0 | 653 | FPRINTF(stderr, "%s: connecting to %s:%hu (address=%s)\n", |
michael@0 | 654 | progName, host, port, addrBuf); |
michael@0 | 655 | } |
michael@0 | 656 | } |
michael@0 | 657 | |
michael@0 | 658 | /* |
michael@0 | 659 | * Prints output according to skipProtoHeader flag. If skipProtoHeader |
michael@0 | 660 | * is not set, prints without any changes, otherwise looking |
michael@0 | 661 | * for \n\r\n(empty line sequence: HTTP header separator) and |
michael@0 | 662 | * prints everything after it. |
michael@0 | 663 | */ |
michael@0 | 664 | static void |
michael@0 | 665 | separateReqHeader(const PRFileDesc* outFd, const char* buf, const int nb, |
michael@0 | 666 | PRBool *wrStarted, int *ptrnMatched) { |
michael@0 | 667 | |
michael@0 | 668 | /* it is sufficient to look for only "\n\r\n". Hopping that |
michael@0 | 669 | * HTTP response format satisfies the standard */ |
michael@0 | 670 | char *ptrnStr = "\n\r\n"; |
michael@0 | 671 | char *resPtr; |
michael@0 | 672 | |
michael@0 | 673 | if (nb == 0) { |
michael@0 | 674 | return; |
michael@0 | 675 | } |
michael@0 | 676 | |
michael@0 | 677 | if (*ptrnMatched > 0) { |
michael@0 | 678 | /* Get here only if previous separateReqHeader call found |
michael@0 | 679 | * only a fragment of "\n\r\n" in previous buffer. */ |
michael@0 | 680 | PORT_Assert(*ptrnMatched < 3); |
michael@0 | 681 | |
michael@0 | 682 | /* the size of fragment of "\n\r\n" what we want to find in this |
michael@0 | 683 | * buffer is equal to *ptrnMatched */ |
michael@0 | 684 | if (*ptrnMatched <= nb) { |
michael@0 | 685 | /* move the pointer to the beginning of the fragment */ |
michael@0 | 686 | int strSize = *ptrnMatched; |
michael@0 | 687 | char *tmpPtrn = ptrnStr + (3 - strSize); |
michael@0 | 688 | if (PL_strncmp(buf, tmpPtrn, strSize) == 0) { |
michael@0 | 689 | /* print the rest of the buffer(without the fragment) */ |
michael@0 | 690 | PR_Write((void*)outFd, buf + strSize, nb - strSize); |
michael@0 | 691 | *wrStarted = PR_TRUE; |
michael@0 | 692 | return; |
michael@0 | 693 | } |
michael@0 | 694 | } else { |
michael@0 | 695 | /* we are here only when nb == 1 && *ptrnMatched == 2 */ |
michael@0 | 696 | if (*buf == '\r') { |
michael@0 | 697 | *ptrnMatched = 1; |
michael@0 | 698 | } else { |
michael@0 | 699 | *ptrnMatched = 0; |
michael@0 | 700 | } |
michael@0 | 701 | return; |
michael@0 | 702 | } |
michael@0 | 703 | } |
michael@0 | 704 | resPtr = PL_strnstr(buf, ptrnStr, nb); |
michael@0 | 705 | if (resPtr != NULL) { |
michael@0 | 706 | /* if "\n\r\n" was found in the buffer, calculate offset |
michael@0 | 707 | * and print the rest of the buffer */ |
michael@0 | 708 | int newBn = nb - (resPtr - buf + 3); /* 3 is the length of "\n\r\n" */ |
michael@0 | 709 | |
michael@0 | 710 | PR_Write((void*)outFd, resPtr + 3, newBn); |
michael@0 | 711 | *wrStarted = PR_TRUE; |
michael@0 | 712 | return; |
michael@0 | 713 | } else { |
michael@0 | 714 | /* try to find a fragment of "\n\r\n" at the end of the buffer. |
michael@0 | 715 | * if found, set *ptrnMatched to the number of chars left to find |
michael@0 | 716 | * in the next buffer.*/ |
michael@0 | 717 | int i; |
michael@0 | 718 | for(i = 1 ;i < 3;i++) { |
michael@0 | 719 | char *bufPrt; |
michael@0 | 720 | int strSize = 3 - i; |
michael@0 | 721 | |
michael@0 | 722 | if (strSize > nb) { |
michael@0 | 723 | continue; |
michael@0 | 724 | } |
michael@0 | 725 | bufPrt = (char*)(buf + nb - strSize); |
michael@0 | 726 | |
michael@0 | 727 | if (PL_strncmp(bufPrt, ptrnStr, strSize) == 0) { |
michael@0 | 728 | *ptrnMatched = i; |
michael@0 | 729 | return; |
michael@0 | 730 | } |
michael@0 | 731 | } |
michael@0 | 732 | } |
michael@0 | 733 | } |
michael@0 | 734 | |
michael@0 | 735 | #define SSOCK_FD 0 |
michael@0 | 736 | #define STDIN_FD 1 |
michael@0 | 737 | |
michael@0 | 738 | #define HEXCHAR_TO_INT(c, i) \ |
michael@0 | 739 | if (((c) >= '0') && ((c) <= '9')) { \ |
michael@0 | 740 | i = (c) - '0'; \ |
michael@0 | 741 | } else if (((c) >= 'a') && ((c) <= 'f')) { \ |
michael@0 | 742 | i = (c) - 'a' + 10; \ |
michael@0 | 743 | } else if (((c) >= 'A') && ((c) <= 'F')) { \ |
michael@0 | 744 | i = (c) - 'A' + 10; \ |
michael@0 | 745 | } else { \ |
michael@0 | 746 | Usage(progName); \ |
michael@0 | 747 | } |
michael@0 | 748 | |
michael@0 | 749 | static SECStatus |
michael@0 | 750 | restartHandshakeAfterServerCertIfNeeded(PRFileDesc * fd, |
michael@0 | 751 | ServerCertAuth * serverCertAuth, |
michael@0 | 752 | PRBool override) |
michael@0 | 753 | { |
michael@0 | 754 | SECStatus rv; |
michael@0 | 755 | PRErrorCode error; |
michael@0 | 756 | |
michael@0 | 757 | if (!serverCertAuth->isPaused) |
michael@0 | 758 | return SECSuccess; |
michael@0 | 759 | |
michael@0 | 760 | FPRINTF(stderr, "%s: handshake was paused by auth certificate hook\n", |
michael@0 | 761 | progName); |
michael@0 | 762 | |
michael@0 | 763 | serverCertAuth->isPaused = PR_FALSE; |
michael@0 | 764 | rv = SSL_AuthCertificate(serverCertAuth->dbHandle, fd, PR_TRUE, PR_FALSE); |
michael@0 | 765 | if (rv != SECSuccess) { |
michael@0 | 766 | error = PR_GetError(); |
michael@0 | 767 | if (error == 0) { |
michael@0 | 768 | PR_NOT_REACHED("SSL_AuthCertificate return SECFailure without " |
michael@0 | 769 | "setting error code."); |
michael@0 | 770 | error = PR_INVALID_STATE_ERROR; |
michael@0 | 771 | } else if (override) { |
michael@0 | 772 | rv = ownBadCertHandler(NULL, fd); |
michael@0 | 773 | } |
michael@0 | 774 | } |
michael@0 | 775 | if (rv == SECSuccess) { |
michael@0 | 776 | error = 0; |
michael@0 | 777 | } |
michael@0 | 778 | |
michael@0 | 779 | if (SSL_AuthCertificateComplete(fd, error) != SECSuccess) { |
michael@0 | 780 | rv = SECFailure; |
michael@0 | 781 | } |
michael@0 | 782 | |
michael@0 | 783 | return rv; |
michael@0 | 784 | } |
michael@0 | 785 | |
michael@0 | 786 | int main(int argc, char **argv) |
michael@0 | 787 | { |
michael@0 | 788 | PRFileDesc * s; |
michael@0 | 789 | PRFileDesc * std_out; |
michael@0 | 790 | char * host = NULL; |
michael@0 | 791 | char * certDir = NULL; |
michael@0 | 792 | char * nickname = NULL; |
michael@0 | 793 | char * cipherString = NULL; |
michael@0 | 794 | char * tmp; |
michael@0 | 795 | int multiplier = 0; |
michael@0 | 796 | SECStatus rv; |
michael@0 | 797 | PRStatus status; |
michael@0 | 798 | PRInt32 filesReady; |
michael@0 | 799 | int npds; |
michael@0 | 800 | int override = 0; |
michael@0 | 801 | SSLVersionRange enabledVersions; |
michael@0 | 802 | PRBool enableSSL2 = PR_TRUE; |
michael@0 | 803 | int bypassPKCS11 = 0; |
michael@0 | 804 | int disableLocking = 0; |
michael@0 | 805 | int useExportPolicy = 0; |
michael@0 | 806 | int enableSessionTickets = 0; |
michael@0 | 807 | int enableCompression = 0; |
michael@0 | 808 | int enableFalseStart = 0; |
michael@0 | 809 | int enableCertStatus = 0; |
michael@0 | 810 | PRSocketOptionData opt; |
michael@0 | 811 | PRNetAddr addr; |
michael@0 | 812 | PRPollDesc pollset[2]; |
michael@0 | 813 | PRBool allowIPv4 = PR_TRUE; |
michael@0 | 814 | PRBool allowIPv6 = PR_TRUE; |
michael@0 | 815 | PRBool pingServerFirst = PR_FALSE; |
michael@0 | 816 | int pingTimeoutSeconds = -1; |
michael@0 | 817 | PRBool clientSpeaksFirst = PR_FALSE; |
michael@0 | 818 | PRBool wrStarted = PR_FALSE; |
michael@0 | 819 | PRBool skipProtoHeader = PR_FALSE; |
michael@0 | 820 | ServerCertAuth serverCertAuth; |
michael@0 | 821 | int headerSeparatorPtrnId = 0; |
michael@0 | 822 | int error = 0; |
michael@0 | 823 | PRUint16 portno = 443; |
michael@0 | 824 | char * hs1SniHostName = NULL; |
michael@0 | 825 | char * hs2SniHostName = NULL; |
michael@0 | 826 | PLOptState *optstate; |
michael@0 | 827 | PLOptStatus optstatus; |
michael@0 | 828 | PRStatus prStatus; |
michael@0 | 829 | |
michael@0 | 830 | serverCertAuth.shouldPause = PR_TRUE; |
michael@0 | 831 | serverCertAuth.isPaused = PR_FALSE; |
michael@0 | 832 | serverCertAuth.dbHandle = NULL; |
michael@0 | 833 | serverCertAuth.testFreshStatusFromSideChannel = PR_FALSE; |
michael@0 | 834 | serverCertAuth.sideChannelRevocationTestResultCode = EXIT_CODE_HANDSHAKE_FAILED; |
michael@0 | 835 | serverCertAuth.requireDataForIntermediates = PR_FALSE; |
michael@0 | 836 | serverCertAuth.allowOCSPSideChannelData = PR_TRUE; |
michael@0 | 837 | serverCertAuth.allowCRLSideChannelData = PR_TRUE; |
michael@0 | 838 | |
michael@0 | 839 | progName = strrchr(argv[0], '/'); |
michael@0 | 840 | if (!progName) |
michael@0 | 841 | progName = strrchr(argv[0], '\\'); |
michael@0 | 842 | progName = progName ? progName+1 : argv[0]; |
michael@0 | 843 | |
michael@0 | 844 | tmp = PR_GetEnv("NSS_DEBUG_TIMEOUT"); |
michael@0 | 845 | if (tmp && tmp[0]) { |
michael@0 | 846 | int sec = PORT_Atoi(tmp); |
michael@0 | 847 | if (sec > 0) { |
michael@0 | 848 | maxInterval = PR_SecondsToInterval(sec); |
michael@0 | 849 | } |
michael@0 | 850 | } |
michael@0 | 851 | |
michael@0 | 852 | SSL_VersionRangeGetSupported(ssl_variant_stream, &enabledVersions); |
michael@0 | 853 | |
michael@0 | 854 | optstate = PL_CreateOptState(argc, argv, |
michael@0 | 855 | "46BFM:OSTV:W:Ya:c:d:fgh:m:n:op:qr:st:uvw:xz"); |
michael@0 | 856 | while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) { |
michael@0 | 857 | switch (optstate->option) { |
michael@0 | 858 | case '?': |
michael@0 | 859 | default : Usage(progName); break; |
michael@0 | 860 | |
michael@0 | 861 | case '4': allowIPv6 = PR_FALSE; if (!allowIPv4) Usage(progName); break; |
michael@0 | 862 | case '6': allowIPv4 = PR_FALSE; if (!allowIPv6) Usage(progName); break; |
michael@0 | 863 | |
michael@0 | 864 | case 'B': bypassPKCS11 = 1; break; |
michael@0 | 865 | |
michael@0 | 866 | case 'F': if (serverCertAuth.testFreshStatusFromSideChannel) { |
michael@0 | 867 | /* parameter given twice or more */ |
michael@0 | 868 | serverCertAuth.requireDataForIntermediates = PR_TRUE; |
michael@0 | 869 | } |
michael@0 | 870 | serverCertAuth.testFreshStatusFromSideChannel = PR_TRUE; |
michael@0 | 871 | break; |
michael@0 | 872 | |
michael@0 | 873 | case 'I': /* reserved for OCSP multi-stapling */ break; |
michael@0 | 874 | |
michael@0 | 875 | case 'O': serverCertAuth.shouldPause = PR_FALSE; break; |
michael@0 | 876 | |
michael@0 | 877 | case 'M': switch (atoi(optstate->value)) { |
michael@0 | 878 | case 1: |
michael@0 | 879 | serverCertAuth.allowOCSPSideChannelData = PR_TRUE; |
michael@0 | 880 | serverCertAuth.allowCRLSideChannelData = PR_FALSE; |
michael@0 | 881 | break; |
michael@0 | 882 | case 2: |
michael@0 | 883 | serverCertAuth.allowOCSPSideChannelData = PR_FALSE; |
michael@0 | 884 | serverCertAuth.allowCRLSideChannelData = PR_TRUE; |
michael@0 | 885 | break; |
michael@0 | 886 | case 0: |
michael@0 | 887 | default: |
michael@0 | 888 | serverCertAuth.allowOCSPSideChannelData = PR_TRUE; |
michael@0 | 889 | serverCertAuth.allowCRLSideChannelData = PR_TRUE; |
michael@0 | 890 | break; |
michael@0 | 891 | }; |
michael@0 | 892 | break; |
michael@0 | 893 | |
michael@0 | 894 | case 'S': skipProtoHeader = PR_TRUE; break; |
michael@0 | 895 | |
michael@0 | 896 | case 'T': enableCertStatus = 1; break; |
michael@0 | 897 | |
michael@0 | 898 | case 'V': if (SECU_ParseSSLVersionRangeString(optstate->value, |
michael@0 | 899 | enabledVersions, enableSSL2, |
michael@0 | 900 | &enabledVersions, &enableSSL2) != SECSuccess) { |
michael@0 | 901 | Usage(progName); |
michael@0 | 902 | } |
michael@0 | 903 | break; |
michael@0 | 904 | |
michael@0 | 905 | case 'Y': PrintCipherUsage(progName); exit(0); break; |
michael@0 | 906 | |
michael@0 | 907 | case 'a': if (!hs1SniHostName) { |
michael@0 | 908 | hs1SniHostName = PORT_Strdup(optstate->value); |
michael@0 | 909 | } else if (!hs2SniHostName) { |
michael@0 | 910 | hs2SniHostName = PORT_Strdup(optstate->value); |
michael@0 | 911 | } else { |
michael@0 | 912 | Usage(progName); |
michael@0 | 913 | } |
michael@0 | 914 | break; |
michael@0 | 915 | |
michael@0 | 916 | case 'c': cipherString = PORT_Strdup(optstate->value); break; |
michael@0 | 917 | |
michael@0 | 918 | case 'g': enableFalseStart = 1; break; |
michael@0 | 919 | |
michael@0 | 920 | case 'd': certDir = PORT_Strdup(optstate->value); break; |
michael@0 | 921 | |
michael@0 | 922 | case 'f': clientSpeaksFirst = PR_TRUE; break; |
michael@0 | 923 | |
michael@0 | 924 | case 'h': host = PORT_Strdup(optstate->value); break; |
michael@0 | 925 | |
michael@0 | 926 | case 'm': |
michael@0 | 927 | multiplier = atoi(optstate->value); |
michael@0 | 928 | if (multiplier < 0) |
michael@0 | 929 | multiplier = 0; |
michael@0 | 930 | break; |
michael@0 | 931 | |
michael@0 | 932 | case 'n': nickname = PORT_Strdup(optstate->value); break; |
michael@0 | 933 | |
michael@0 | 934 | case 'o': override = 1; break; |
michael@0 | 935 | |
michael@0 | 936 | case 'p': portno = (PRUint16)atoi(optstate->value); break; |
michael@0 | 937 | |
michael@0 | 938 | case 'q': pingServerFirst = PR_TRUE; break; |
michael@0 | 939 | |
michael@0 | 940 | case 's': disableLocking = 1; break; |
michael@0 | 941 | |
michael@0 | 942 | case 't': pingTimeoutSeconds = atoi(optstate->value); break; |
michael@0 | 943 | |
michael@0 | 944 | case 'u': enableSessionTickets = PR_TRUE; break; |
michael@0 | 945 | |
michael@0 | 946 | case 'v': verbose++; break; |
michael@0 | 947 | |
michael@0 | 948 | case 'r': renegotiationsToDo = atoi(optstate->value); break; |
michael@0 | 949 | |
michael@0 | 950 | case 'w': |
michael@0 | 951 | pwdata.source = PW_PLAINTEXT; |
michael@0 | 952 | pwdata.data = PORT_Strdup(optstate->value); |
michael@0 | 953 | break; |
michael@0 | 954 | |
michael@0 | 955 | case 'W': |
michael@0 | 956 | pwdata.source = PW_FROMFILE; |
michael@0 | 957 | pwdata.data = PORT_Strdup(optstate->value); |
michael@0 | 958 | break; |
michael@0 | 959 | |
michael@0 | 960 | case 'x': useExportPolicy = 1; break; |
michael@0 | 961 | |
michael@0 | 962 | case 'z': enableCompression = 1; break; |
michael@0 | 963 | } |
michael@0 | 964 | } |
michael@0 | 965 | |
michael@0 | 966 | PL_DestroyOptState(optstate); |
michael@0 | 967 | |
michael@0 | 968 | if (optstatus == PL_OPT_BAD) |
michael@0 | 969 | Usage(progName); |
michael@0 | 970 | |
michael@0 | 971 | if (!host || !portno) |
michael@0 | 972 | Usage(progName); |
michael@0 | 973 | |
michael@0 | 974 | if (serverCertAuth.testFreshStatusFromSideChannel |
michael@0 | 975 | && serverCertAuth.shouldPause) { |
michael@0 | 976 | fprintf(stderr, "%s: -F requires the use of -O\n", progName); |
michael@0 | 977 | exit(1); |
michael@0 | 978 | } |
michael@0 | 979 | |
michael@0 | 980 | PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); |
michael@0 | 981 | |
michael@0 | 982 | PK11_SetPasswordFunc(SECU_GetModulePassword); |
michael@0 | 983 | |
michael@0 | 984 | status = PR_StringToNetAddr(host, &addr); |
michael@0 | 985 | if (status == PR_SUCCESS) { |
michael@0 | 986 | addr.inet.port = PR_htons(portno); |
michael@0 | 987 | } else { |
michael@0 | 988 | /* Lookup host */ |
michael@0 | 989 | PRAddrInfo *addrInfo; |
michael@0 | 990 | void *enumPtr = NULL; |
michael@0 | 991 | |
michael@0 | 992 | addrInfo = PR_GetAddrInfoByName(host, PR_AF_UNSPEC, |
michael@0 | 993 | PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME); |
michael@0 | 994 | if (!addrInfo) { |
michael@0 | 995 | SECU_PrintError(progName, "error looking up host"); |
michael@0 | 996 | return 1; |
michael@0 | 997 | } |
michael@0 | 998 | for (;;) { |
michael@0 | 999 | enumPtr = PR_EnumerateAddrInfo(enumPtr, addrInfo, portno, &addr); |
michael@0 | 1000 | if (enumPtr == NULL) |
michael@0 | 1001 | break; |
michael@0 | 1002 | if (addr.raw.family == PR_AF_INET && allowIPv4) |
michael@0 | 1003 | break; |
michael@0 | 1004 | if (addr.raw.family == PR_AF_INET6 && allowIPv6) |
michael@0 | 1005 | break; |
michael@0 | 1006 | } |
michael@0 | 1007 | PR_FreeAddrInfo(addrInfo); |
michael@0 | 1008 | if (enumPtr == NULL) { |
michael@0 | 1009 | SECU_PrintError(progName, "error looking up host address"); |
michael@0 | 1010 | return 1; |
michael@0 | 1011 | } |
michael@0 | 1012 | } |
michael@0 | 1013 | |
michael@0 | 1014 | printHostNameAndAddr(host, &addr); |
michael@0 | 1015 | |
michael@0 | 1016 | if (pingServerFirst) { |
michael@0 | 1017 | int iter = 0; |
michael@0 | 1018 | PRErrorCode err; |
michael@0 | 1019 | int max_attempts = MAX_WAIT_FOR_SERVER; |
michael@0 | 1020 | if (pingTimeoutSeconds >= 0) { |
michael@0 | 1021 | /* If caller requested a timeout, let's try just twice. */ |
michael@0 | 1022 | max_attempts = 2; |
michael@0 | 1023 | } |
michael@0 | 1024 | do { |
michael@0 | 1025 | PRIntervalTime timeoutInterval = PR_INTERVAL_NO_TIMEOUT; |
michael@0 | 1026 | s = PR_OpenTCPSocket(addr.raw.family); |
michael@0 | 1027 | if (s == NULL) { |
michael@0 | 1028 | SECU_PrintError(progName, "Failed to create a TCP socket"); |
michael@0 | 1029 | } |
michael@0 | 1030 | opt.option = PR_SockOpt_Nonblocking; |
michael@0 | 1031 | opt.value.non_blocking = PR_FALSE; |
michael@0 | 1032 | prStatus = PR_SetSocketOption(s, &opt); |
michael@0 | 1033 | if (prStatus != PR_SUCCESS) { |
michael@0 | 1034 | PR_Close(s); |
michael@0 | 1035 | SECU_PrintError(progName, |
michael@0 | 1036 | "Failed to set blocking socket option"); |
michael@0 | 1037 | return 1; |
michael@0 | 1038 | } |
michael@0 | 1039 | if (pingTimeoutSeconds >= 0) { |
michael@0 | 1040 | timeoutInterval = PR_SecondsToInterval(pingTimeoutSeconds); |
michael@0 | 1041 | } |
michael@0 | 1042 | prStatus = PR_Connect(s, &addr, timeoutInterval); |
michael@0 | 1043 | if (prStatus == PR_SUCCESS) { |
michael@0 | 1044 | PR_Shutdown(s, PR_SHUTDOWN_BOTH); |
michael@0 | 1045 | PR_Close(s); |
michael@0 | 1046 | PR_Cleanup(); |
michael@0 | 1047 | return 0; |
michael@0 | 1048 | } |
michael@0 | 1049 | err = PR_GetError(); |
michael@0 | 1050 | if ((err != PR_CONNECT_REFUSED_ERROR) && |
michael@0 | 1051 | (err != PR_CONNECT_RESET_ERROR)) { |
michael@0 | 1052 | SECU_PrintError(progName, "TCP Connection failed"); |
michael@0 | 1053 | return 1; |
michael@0 | 1054 | } |
michael@0 | 1055 | PR_Close(s); |
michael@0 | 1056 | PR_Sleep(PR_MillisecondsToInterval(WAIT_INTERVAL)); |
michael@0 | 1057 | } while (++iter < max_attempts); |
michael@0 | 1058 | SECU_PrintError(progName, |
michael@0 | 1059 | "Client timed out while waiting for connection to server"); |
michael@0 | 1060 | return 1; |
michael@0 | 1061 | } |
michael@0 | 1062 | |
michael@0 | 1063 | /* open the cert DB, the key DB, and the secmod DB. */ |
michael@0 | 1064 | if (!certDir) { |
michael@0 | 1065 | certDir = SECU_DefaultSSLDir(); /* Look in $SSL_DIR */ |
michael@0 | 1066 | certDir = SECU_ConfigDirectory(certDir); |
michael@0 | 1067 | } else { |
michael@0 | 1068 | char *certDirTmp = certDir; |
michael@0 | 1069 | certDir = SECU_ConfigDirectory(certDirTmp); |
michael@0 | 1070 | PORT_Free(certDirTmp); |
michael@0 | 1071 | } |
michael@0 | 1072 | rv = NSS_Init(certDir); |
michael@0 | 1073 | if (rv != SECSuccess) { |
michael@0 | 1074 | SECU_PrintError(progName, "unable to open cert database"); |
michael@0 | 1075 | return 1; |
michael@0 | 1076 | } |
michael@0 | 1077 | |
michael@0 | 1078 | /* set the policy bits true for all the cipher suites. */ |
michael@0 | 1079 | if (useExportPolicy) |
michael@0 | 1080 | NSS_SetExportPolicy(); |
michael@0 | 1081 | else |
michael@0 | 1082 | NSS_SetDomesticPolicy(); |
michael@0 | 1083 | |
michael@0 | 1084 | /* all the SSL2 and SSL3 cipher suites are enabled by default. */ |
michael@0 | 1085 | if (cipherString) { |
michael@0 | 1086 | /* disable all the ciphers, then enable the ones we want. */ |
michael@0 | 1087 | disableAllSSLCiphers(); |
michael@0 | 1088 | } |
michael@0 | 1089 | |
michael@0 | 1090 | /* Create socket */ |
michael@0 | 1091 | s = PR_OpenTCPSocket(addr.raw.family); |
michael@0 | 1092 | if (s == NULL) { |
michael@0 | 1093 | SECU_PrintError(progName, "error creating socket"); |
michael@0 | 1094 | return 1; |
michael@0 | 1095 | } |
michael@0 | 1096 | |
michael@0 | 1097 | opt.option = PR_SockOpt_Nonblocking; |
michael@0 | 1098 | opt.value.non_blocking = PR_TRUE; /* default */ |
michael@0 | 1099 | if (serverCertAuth.testFreshStatusFromSideChannel) { |
michael@0 | 1100 | opt.value.non_blocking = PR_FALSE; |
michael@0 | 1101 | } |
michael@0 | 1102 | PR_SetSocketOption(s, &opt); |
michael@0 | 1103 | /*PR_SetSocketOption(PR_GetSpecialFD(PR_StandardInput), &opt);*/ |
michael@0 | 1104 | |
michael@0 | 1105 | s = SSL_ImportFD(NULL, s); |
michael@0 | 1106 | if (s == NULL) { |
michael@0 | 1107 | SECU_PrintError(progName, "error importing socket"); |
michael@0 | 1108 | return 1; |
michael@0 | 1109 | } |
michael@0 | 1110 | |
michael@0 | 1111 | rv = SSL_OptionSet(s, SSL_SECURITY, 1); |
michael@0 | 1112 | if (rv != SECSuccess) { |
michael@0 | 1113 | SECU_PrintError(progName, "error enabling socket"); |
michael@0 | 1114 | return 1; |
michael@0 | 1115 | } |
michael@0 | 1116 | |
michael@0 | 1117 | rv = SSL_OptionSet(s, SSL_HANDSHAKE_AS_CLIENT, 1); |
michael@0 | 1118 | if (rv != SECSuccess) { |
michael@0 | 1119 | SECU_PrintError(progName, "error enabling client handshake"); |
michael@0 | 1120 | return 1; |
michael@0 | 1121 | } |
michael@0 | 1122 | |
michael@0 | 1123 | /* all the SSL2 and SSL3 cipher suites are enabled by default. */ |
michael@0 | 1124 | if (cipherString) { |
michael@0 | 1125 | char *cstringSaved = cipherString; |
michael@0 | 1126 | int ndx; |
michael@0 | 1127 | |
michael@0 | 1128 | while (0 != (ndx = *cipherString++)) { |
michael@0 | 1129 | int cipher; |
michael@0 | 1130 | |
michael@0 | 1131 | if (ndx == ':') { |
michael@0 | 1132 | int ctmp; |
michael@0 | 1133 | |
michael@0 | 1134 | cipher = 0; |
michael@0 | 1135 | HEXCHAR_TO_INT(*cipherString, ctmp) |
michael@0 | 1136 | cipher |= (ctmp << 12); |
michael@0 | 1137 | cipherString++; |
michael@0 | 1138 | HEXCHAR_TO_INT(*cipherString, ctmp) |
michael@0 | 1139 | cipher |= (ctmp << 8); |
michael@0 | 1140 | cipherString++; |
michael@0 | 1141 | HEXCHAR_TO_INT(*cipherString, ctmp) |
michael@0 | 1142 | cipher |= (ctmp << 4); |
michael@0 | 1143 | cipherString++; |
michael@0 | 1144 | HEXCHAR_TO_INT(*cipherString, ctmp) |
michael@0 | 1145 | cipher |= ctmp; |
michael@0 | 1146 | cipherString++; |
michael@0 | 1147 | } else { |
michael@0 | 1148 | const int *cptr; |
michael@0 | 1149 | |
michael@0 | 1150 | if (! isalpha(ndx)) |
michael@0 | 1151 | Usage(progName); |
michael@0 | 1152 | cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites; |
michael@0 | 1153 | for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; ) |
michael@0 | 1154 | /* do nothing */; |
michael@0 | 1155 | } |
michael@0 | 1156 | if (cipher > 0) { |
michael@0 | 1157 | SECStatus status; |
michael@0 | 1158 | status = SSL_CipherPrefSet(s, cipher, SSL_ALLOWED); |
michael@0 | 1159 | if (status != SECSuccess) |
michael@0 | 1160 | SECU_PrintError(progName, "SSL_CipherPrefSet()"); |
michael@0 | 1161 | } else { |
michael@0 | 1162 | Usage(progName); |
michael@0 | 1163 | } |
michael@0 | 1164 | } |
michael@0 | 1165 | PORT_Free(cstringSaved); |
michael@0 | 1166 | } |
michael@0 | 1167 | |
michael@0 | 1168 | rv = SSL_VersionRangeSet(s, &enabledVersions); |
michael@0 | 1169 | if (rv != SECSuccess) { |
michael@0 | 1170 | SECU_PrintError(progName, "error setting SSL/TLS version range "); |
michael@0 | 1171 | return 1; |
michael@0 | 1172 | } |
michael@0 | 1173 | |
michael@0 | 1174 | rv = SSL_OptionSet(s, SSL_ENABLE_SSL2, enableSSL2); |
michael@0 | 1175 | if (rv != SECSuccess) { |
michael@0 | 1176 | SECU_PrintError(progName, "error enabling SSLv2 "); |
michael@0 | 1177 | return 1; |
michael@0 | 1178 | } |
michael@0 | 1179 | |
michael@0 | 1180 | rv = SSL_OptionSet(s, SSL_V2_COMPATIBLE_HELLO, enableSSL2); |
michael@0 | 1181 | if (rv != SECSuccess) { |
michael@0 | 1182 | SECU_PrintError(progName, "error enabling SSLv2 compatible hellos "); |
michael@0 | 1183 | return 1; |
michael@0 | 1184 | } |
michael@0 | 1185 | |
michael@0 | 1186 | /* enable PKCS11 bypass */ |
michael@0 | 1187 | rv = SSL_OptionSet(s, SSL_BYPASS_PKCS11, bypassPKCS11); |
michael@0 | 1188 | if (rv != SECSuccess) { |
michael@0 | 1189 | SECU_PrintError(progName, "error enabling PKCS11 bypass"); |
michael@0 | 1190 | return 1; |
michael@0 | 1191 | } |
michael@0 | 1192 | |
michael@0 | 1193 | /* disable SSL socket locking */ |
michael@0 | 1194 | rv = SSL_OptionSet(s, SSL_NO_LOCKS, disableLocking); |
michael@0 | 1195 | if (rv != SECSuccess) { |
michael@0 | 1196 | SECU_PrintError(progName, "error disabling SSL socket locking"); |
michael@0 | 1197 | return 1; |
michael@0 | 1198 | } |
michael@0 | 1199 | |
michael@0 | 1200 | /* enable Session Ticket extension. */ |
michael@0 | 1201 | rv = SSL_OptionSet(s, SSL_ENABLE_SESSION_TICKETS, enableSessionTickets); |
michael@0 | 1202 | if (rv != SECSuccess) { |
michael@0 | 1203 | SECU_PrintError(progName, "error enabling Session Ticket extension"); |
michael@0 | 1204 | return 1; |
michael@0 | 1205 | } |
michael@0 | 1206 | |
michael@0 | 1207 | /* enable compression. */ |
michael@0 | 1208 | rv = SSL_OptionSet(s, SSL_ENABLE_DEFLATE, enableCompression); |
michael@0 | 1209 | if (rv != SECSuccess) { |
michael@0 | 1210 | SECU_PrintError(progName, "error enabling compression"); |
michael@0 | 1211 | return 1; |
michael@0 | 1212 | } |
michael@0 | 1213 | |
michael@0 | 1214 | /* enable false start. */ |
michael@0 | 1215 | rv = SSL_OptionSet(s, SSL_ENABLE_FALSE_START, enableFalseStart); |
michael@0 | 1216 | if (rv != SECSuccess) { |
michael@0 | 1217 | SECU_PrintError(progName, "error enabling false start"); |
michael@0 | 1218 | return 1; |
michael@0 | 1219 | } |
michael@0 | 1220 | |
michael@0 | 1221 | /* enable cert status (OCSP stapling). */ |
michael@0 | 1222 | rv = SSL_OptionSet(s, SSL_ENABLE_OCSP_STAPLING, enableCertStatus); |
michael@0 | 1223 | if (rv != SECSuccess) { |
michael@0 | 1224 | SECU_PrintError(progName, "error enabling cert status (OCSP stapling)"); |
michael@0 | 1225 | return 1; |
michael@0 | 1226 | } |
michael@0 | 1227 | |
michael@0 | 1228 | SSL_SetPKCS11PinArg(s, &pwdata); |
michael@0 | 1229 | |
michael@0 | 1230 | serverCertAuth.dbHandle = CERT_GetDefaultCertDB(); |
michael@0 | 1231 | |
michael@0 | 1232 | SSL_AuthCertificateHook(s, ownAuthCertificate, &serverCertAuth); |
michael@0 | 1233 | if (override) { |
michael@0 | 1234 | SSL_BadCertHook(s, ownBadCertHandler, NULL); |
michael@0 | 1235 | } |
michael@0 | 1236 | SSL_GetClientAuthDataHook(s, own_GetClientAuthData, (void *)nickname); |
michael@0 | 1237 | SSL_HandshakeCallback(s, handshakeCallback, hs2SniHostName); |
michael@0 | 1238 | if (hs1SniHostName) { |
michael@0 | 1239 | SSL_SetURL(s, hs1SniHostName); |
michael@0 | 1240 | } else { |
michael@0 | 1241 | SSL_SetURL(s, host); |
michael@0 | 1242 | } |
michael@0 | 1243 | |
michael@0 | 1244 | /* Try to connect to the server */ |
michael@0 | 1245 | status = PR_Connect(s, &addr, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 1246 | if (status != PR_SUCCESS) { |
michael@0 | 1247 | if (PR_GetError() == PR_IN_PROGRESS_ERROR) { |
michael@0 | 1248 | if (verbose) |
michael@0 | 1249 | SECU_PrintError(progName, "connect"); |
michael@0 | 1250 | milliPause(50 * multiplier); |
michael@0 | 1251 | pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; |
michael@0 | 1252 | pollset[SSOCK_FD].out_flags = 0; |
michael@0 | 1253 | pollset[SSOCK_FD].fd = s; |
michael@0 | 1254 | while(1) { |
michael@0 | 1255 | FPRINTF(stderr, |
michael@0 | 1256 | "%s: about to call PR_Poll for connect completion!\n", |
michael@0 | 1257 | progName); |
michael@0 | 1258 | filesReady = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 1259 | if (filesReady < 0) { |
michael@0 | 1260 | SECU_PrintError(progName, "unable to connect (poll)"); |
michael@0 | 1261 | return 1; |
michael@0 | 1262 | } |
michael@0 | 1263 | FPRINTF(stderr, |
michael@0 | 1264 | "%s: PR_Poll returned 0x%02x for socket out_flags.\n", |
michael@0 | 1265 | progName, pollset[SSOCK_FD].out_flags); |
michael@0 | 1266 | if (filesReady == 0) { /* shouldn't happen! */ |
michael@0 | 1267 | FPRINTF(stderr, "%s: PR_Poll returned zero!\n", progName); |
michael@0 | 1268 | return 1; |
michael@0 | 1269 | } |
michael@0 | 1270 | status = PR_GetConnectStatus(pollset); |
michael@0 | 1271 | if (status == PR_SUCCESS) { |
michael@0 | 1272 | break; |
michael@0 | 1273 | } |
michael@0 | 1274 | if (PR_GetError() != PR_IN_PROGRESS_ERROR) { |
michael@0 | 1275 | SECU_PrintError(progName, "unable to connect (poll)"); |
michael@0 | 1276 | return 1; |
michael@0 | 1277 | } |
michael@0 | 1278 | SECU_PrintError(progName, "poll"); |
michael@0 | 1279 | milliPause(50 * multiplier); |
michael@0 | 1280 | } |
michael@0 | 1281 | } else { |
michael@0 | 1282 | SECU_PrintError(progName, "unable to connect"); |
michael@0 | 1283 | return 1; |
michael@0 | 1284 | } |
michael@0 | 1285 | } |
michael@0 | 1286 | |
michael@0 | 1287 | pollset[SSOCK_FD].fd = s; |
michael@0 | 1288 | pollset[SSOCK_FD].in_flags = PR_POLL_EXCEPT | |
michael@0 | 1289 | (clientSpeaksFirst ? 0 : PR_POLL_READ); |
michael@0 | 1290 | pollset[STDIN_FD].fd = PR_GetSpecialFD(PR_StandardInput); |
michael@0 | 1291 | pollset[STDIN_FD].in_flags = PR_POLL_READ; |
michael@0 | 1292 | npds = 2; |
michael@0 | 1293 | std_out = PR_GetSpecialFD(PR_StandardOutput); |
michael@0 | 1294 | |
michael@0 | 1295 | #if defined(WIN32) || defined(OS2) |
michael@0 | 1296 | /* PR_Poll cannot be used with stdin on Windows or OS/2. (sigh). |
michael@0 | 1297 | ** But use of PR_Poll and non-blocking sockets is a major feature |
michael@0 | 1298 | ** of this program. So, we simulate a pollable stdin with a |
michael@0 | 1299 | ** TCP socket pair and a thread that reads stdin and writes to |
michael@0 | 1300 | ** that socket pair. |
michael@0 | 1301 | */ |
michael@0 | 1302 | { |
michael@0 | 1303 | PRFileDesc * fds[2]; |
michael@0 | 1304 | PRThread * thread; |
michael@0 | 1305 | |
michael@0 | 1306 | int nspr_rv = PR_NewTCPSocketPair(fds); |
michael@0 | 1307 | if (nspr_rv != PR_SUCCESS) { |
michael@0 | 1308 | SECU_PrintError(progName, "PR_NewTCPSocketPair failed"); |
michael@0 | 1309 | error = 1; |
michael@0 | 1310 | goto done; |
michael@0 | 1311 | } |
michael@0 | 1312 | pollset[STDIN_FD].fd = fds[1]; |
michael@0 | 1313 | |
michael@0 | 1314 | thread = PR_CreateThread(PR_USER_THREAD, thread_main, fds[0], |
michael@0 | 1315 | PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, |
michael@0 | 1316 | PR_UNJOINABLE_THREAD, 0); |
michael@0 | 1317 | if (!thread) { |
michael@0 | 1318 | SECU_PrintError(progName, "PR_CreateThread failed"); |
michael@0 | 1319 | error = 1; |
michael@0 | 1320 | goto done; |
michael@0 | 1321 | } |
michael@0 | 1322 | } |
michael@0 | 1323 | #endif |
michael@0 | 1324 | |
michael@0 | 1325 | if (serverCertAuth.testFreshStatusFromSideChannel) { |
michael@0 | 1326 | SSL_ForceHandshake(s); |
michael@0 | 1327 | error = serverCertAuth.sideChannelRevocationTestResultCode; |
michael@0 | 1328 | goto done; |
michael@0 | 1329 | } |
michael@0 | 1330 | |
michael@0 | 1331 | /* |
michael@0 | 1332 | ** Select on stdin and on the socket. Write data from stdin to |
michael@0 | 1333 | ** socket, read data from socket and write to stdout. |
michael@0 | 1334 | */ |
michael@0 | 1335 | FPRINTF(stderr, "%s: ready...\n", progName); |
michael@0 | 1336 | |
michael@0 | 1337 | while (pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) { |
michael@0 | 1338 | char buf[4000]; /* buffer for stdin */ |
michael@0 | 1339 | int nb; /* num bytes read from stdin. */ |
michael@0 | 1340 | |
michael@0 | 1341 | rv = restartHandshakeAfterServerCertIfNeeded(s, &serverCertAuth, |
michael@0 | 1342 | override); |
michael@0 | 1343 | if (rv != SECSuccess) { |
michael@0 | 1344 | error = EXIT_CODE_HANDSHAKE_FAILED; |
michael@0 | 1345 | SECU_PrintError(progName, "authentication of server cert failed"); |
michael@0 | 1346 | goto done; |
michael@0 | 1347 | } |
michael@0 | 1348 | |
michael@0 | 1349 | pollset[SSOCK_FD].out_flags = 0; |
michael@0 | 1350 | pollset[STDIN_FD].out_flags = 0; |
michael@0 | 1351 | |
michael@0 | 1352 | FPRINTF(stderr, "%s: about to call PR_Poll !\n", progName); |
michael@0 | 1353 | filesReady = PR_Poll(pollset, npds, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 1354 | if (filesReady < 0) { |
michael@0 | 1355 | SECU_PrintError(progName, "select failed"); |
michael@0 | 1356 | error = 1; |
michael@0 | 1357 | goto done; |
michael@0 | 1358 | } |
michael@0 | 1359 | if (filesReady == 0) { /* shouldn't happen! */ |
michael@0 | 1360 | FPRINTF(stderr, "%s: PR_Poll returned zero!\n", progName); |
michael@0 | 1361 | return 1; |
michael@0 | 1362 | } |
michael@0 | 1363 | FPRINTF(stderr, "%s: PR_Poll returned!\n", progName); |
michael@0 | 1364 | if (pollset[STDIN_FD].in_flags) { |
michael@0 | 1365 | FPRINTF(stderr, |
michael@0 | 1366 | "%s: PR_Poll returned 0x%02x for stdin out_flags.\n", |
michael@0 | 1367 | progName, pollset[STDIN_FD].out_flags); |
michael@0 | 1368 | } |
michael@0 | 1369 | if (pollset[SSOCK_FD].in_flags) { |
michael@0 | 1370 | FPRINTF(stderr, |
michael@0 | 1371 | "%s: PR_Poll returned 0x%02x for socket out_flags.\n", |
michael@0 | 1372 | progName, pollset[SSOCK_FD].out_flags); |
michael@0 | 1373 | } |
michael@0 | 1374 | if (pollset[STDIN_FD].out_flags & PR_POLL_READ) { |
michael@0 | 1375 | /* Read from stdin and write to socket */ |
michael@0 | 1376 | nb = PR_Read(pollset[STDIN_FD].fd, buf, sizeof(buf)); |
michael@0 | 1377 | FPRINTF(stderr, "%s: stdin read %d bytes\n", progName, nb); |
michael@0 | 1378 | if (nb < 0) { |
michael@0 | 1379 | if (PR_GetError() != PR_WOULD_BLOCK_ERROR) { |
michael@0 | 1380 | SECU_PrintError(progName, "read from stdin failed"); |
michael@0 | 1381 | error = 1; |
michael@0 | 1382 | break; |
michael@0 | 1383 | } |
michael@0 | 1384 | } else if (nb == 0) { |
michael@0 | 1385 | /* EOF on stdin, stop polling stdin for read. */ |
michael@0 | 1386 | pollset[STDIN_FD].in_flags = 0; |
michael@0 | 1387 | } else { |
michael@0 | 1388 | char * bufp = buf; |
michael@0 | 1389 | FPRINTF(stderr, "%s: Writing %d bytes to server\n", |
michael@0 | 1390 | progName, nb); |
michael@0 | 1391 | do { |
michael@0 | 1392 | PRInt32 cc = PR_Send(s, bufp, nb, 0, maxInterval); |
michael@0 | 1393 | if (cc < 0) { |
michael@0 | 1394 | PRErrorCode err = PR_GetError(); |
michael@0 | 1395 | if (err != PR_WOULD_BLOCK_ERROR) { |
michael@0 | 1396 | SECU_PrintError(progName, |
michael@0 | 1397 | "write to SSL socket failed"); |
michael@0 | 1398 | error = 254; |
michael@0 | 1399 | goto done; |
michael@0 | 1400 | } |
michael@0 | 1401 | cc = 0; |
michael@0 | 1402 | } |
michael@0 | 1403 | bufp += cc; |
michael@0 | 1404 | nb -= cc; |
michael@0 | 1405 | if (nb <= 0) |
michael@0 | 1406 | break; |
michael@0 | 1407 | |
michael@0 | 1408 | rv = restartHandshakeAfterServerCertIfNeeded(s, |
michael@0 | 1409 | &serverCertAuth, override); |
michael@0 | 1410 | if (rv != SECSuccess) { |
michael@0 | 1411 | error = EXIT_CODE_HANDSHAKE_FAILED; |
michael@0 | 1412 | SECU_PrintError(progName, "authentication of server cert failed"); |
michael@0 | 1413 | goto done; |
michael@0 | 1414 | } |
michael@0 | 1415 | |
michael@0 | 1416 | pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; |
michael@0 | 1417 | pollset[SSOCK_FD].out_flags = 0; |
michael@0 | 1418 | FPRINTF(stderr, |
michael@0 | 1419 | "%s: about to call PR_Poll on writable socket !\n", |
michael@0 | 1420 | progName); |
michael@0 | 1421 | cc = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 1422 | FPRINTF(stderr, |
michael@0 | 1423 | "%s: PR_Poll returned with writable socket !\n", |
michael@0 | 1424 | progName); |
michael@0 | 1425 | } while (1); |
michael@0 | 1426 | pollset[SSOCK_FD].in_flags = PR_POLL_READ; |
michael@0 | 1427 | } |
michael@0 | 1428 | } |
michael@0 | 1429 | |
michael@0 | 1430 | if (pollset[SSOCK_FD].in_flags) { |
michael@0 | 1431 | FPRINTF(stderr, |
michael@0 | 1432 | "%s: PR_Poll returned 0x%02x for socket out_flags.\n", |
michael@0 | 1433 | progName, pollset[SSOCK_FD].out_flags); |
michael@0 | 1434 | } |
michael@0 | 1435 | if ( (pollset[SSOCK_FD].out_flags & PR_POLL_READ) |
michael@0 | 1436 | || (pollset[SSOCK_FD].out_flags & PR_POLL_ERR) |
michael@0 | 1437 | #ifdef PR_POLL_HUP |
michael@0 | 1438 | || (pollset[SSOCK_FD].out_flags & PR_POLL_HUP) |
michael@0 | 1439 | #endif |
michael@0 | 1440 | ) { |
michael@0 | 1441 | /* Read from socket and write to stdout */ |
michael@0 | 1442 | nb = PR_Recv(pollset[SSOCK_FD].fd, buf, sizeof buf, 0, maxInterval); |
michael@0 | 1443 | FPRINTF(stderr, "%s: Read from server %d bytes\n", progName, nb); |
michael@0 | 1444 | if (nb < 0) { |
michael@0 | 1445 | if (PR_GetError() != PR_WOULD_BLOCK_ERROR) { |
michael@0 | 1446 | SECU_PrintError(progName, "read from socket failed"); |
michael@0 | 1447 | error = 1; |
michael@0 | 1448 | goto done; |
michael@0 | 1449 | } |
michael@0 | 1450 | } else if (nb == 0) { |
michael@0 | 1451 | /* EOF from socket... stop polling socket for read */ |
michael@0 | 1452 | pollset[SSOCK_FD].in_flags = 0; |
michael@0 | 1453 | } else { |
michael@0 | 1454 | if (skipProtoHeader != PR_TRUE || wrStarted == PR_TRUE) { |
michael@0 | 1455 | PR_Write(std_out, buf, nb); |
michael@0 | 1456 | } else { |
michael@0 | 1457 | separateReqHeader(std_out, buf, nb, &wrStarted, |
michael@0 | 1458 | &headerSeparatorPtrnId); |
michael@0 | 1459 | } |
michael@0 | 1460 | if (verbose) |
michael@0 | 1461 | fputs("\n\n", stderr); |
michael@0 | 1462 | } |
michael@0 | 1463 | } |
michael@0 | 1464 | milliPause(50 * multiplier); |
michael@0 | 1465 | } |
michael@0 | 1466 | |
michael@0 | 1467 | done: |
michael@0 | 1468 | if (hs1SniHostName) { |
michael@0 | 1469 | PORT_Free(hs1SniHostName); |
michael@0 | 1470 | } |
michael@0 | 1471 | if (hs2SniHostName) { |
michael@0 | 1472 | PORT_Free(hs2SniHostName); |
michael@0 | 1473 | } |
michael@0 | 1474 | if (nickname) { |
michael@0 | 1475 | PORT_Free(nickname); |
michael@0 | 1476 | } |
michael@0 | 1477 | if (pwdata.data) { |
michael@0 | 1478 | PORT_Free(pwdata.data); |
michael@0 | 1479 | } |
michael@0 | 1480 | PORT_Free(host); |
michael@0 | 1481 | |
michael@0 | 1482 | PR_Close(s); |
michael@0 | 1483 | SSL_ClearSessionCache(); |
michael@0 | 1484 | if (NSS_Shutdown() != SECSuccess) { |
michael@0 | 1485 | exit(1); |
michael@0 | 1486 | } |
michael@0 | 1487 | |
michael@0 | 1488 | FPRINTF(stderr, "tstclnt: exiting with return code %d\n", error); |
michael@0 | 1489 | PR_Cleanup(); |
michael@0 | 1490 | return error; |
michael@0 | 1491 | } |