X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=apps%2Fs_client.c;h=c338b0c5250a5b07f43383f1e3c0c6d5927e8cea;hp=d6797f50662ef4ed1466a73938343968fb7a084b;hb=6bea2a72a8bfe94bb7298374d1977b2ca580c415;hpb=ab69ac00f3c7a04151662813794ac82bc591a89b diff --git a/apps/s_client.c b/apps/s_client.c index d6797f5066..c338b0c525 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -165,12 +165,16 @@ typedef unsigned int u_int; #ifndef OPENSSL_NO_SRP # include #endif +#ifndef OPENSSL_NO_CT +# include +#endif #include "s_apps.h" #include "timeouts.h" -#if (defined(OPENSSL_SYS_VMS) && __VMS_VER < 70000000) -/* FIONBIO used as a switch to enable ioctl, and that isn't in VMS < 7.0 */ -# undef FIONBIO +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) +# include +# endif #endif #undef BUFSIZZ @@ -184,6 +188,8 @@ extern int verify_quiet; static char *prog; static int async = 0; +static unsigned int split_send_fragment = 0; +static unsigned int max_pipelines = 0; static int c_nbio = 0; static int c_tlsextdebug = 0; static int c_status_req = 0; @@ -439,7 +445,7 @@ static char *srtp_profiles = NULL; /* This the context that we pass to next_proto_cb */ typedef struct tlsextnextprotoctx_st { unsigned char *data; - unsigned short len; + size_t len; int status; } tlsextnextprotoctx; @@ -509,9 +515,9 @@ static ossl_ssize_t hexdecode(const char **inptr, void *result) for (byte = 0; *in; ++in) { char c; - if (isspace(*in)) + if (isspace(_UC(*in))) continue; - c = tolower(*in); + c = tolower(_UC(*in)); if ('0' <= c && c <= '9') { byte |= c - '0'; } else if ('a' <= c && c <= 'f') { @@ -553,11 +559,11 @@ static ossl_ssize_t checked_uint8(const char **inptr, void *out) e = restore_errno(); if (((v == LONG_MIN || v == LONG_MAX) && e == ERANGE) || - endp == in || !isspace(*endp) || + endp == in || !isspace(_UC(*endp)) || v != (*result = (uint8_t) v)) { return -1; } - for (in = endp; isspace(*in); ++in) + for (in = endp; isspace(_UC(*in)); ++in) continue; *inptr = in; @@ -648,13 +654,16 @@ typedef enum OPTION_choice { OPT_CERT_CHAIN, OPT_CAPATH, OPT_NOCAPATH, OPT_CHAINCAPATH, OPT_VERIFYCAPATH, OPT_KEY, OPT_RECONNECT, OPT_BUILD_CHAIN, OPT_CAFILE, OPT_NOCAFILE, OPT_CHAINCAFILE, OPT_VERIFYCAFILE, OPT_NEXTPROTONEG, OPT_ALPN, - OPT_SERVERINFO, OPT_STARTTLS, OPT_SERVERNAME, OPT_JPAKE, + OPT_SERVERINFO, OPT_STARTTLS, OPT_SERVERNAME, OPT_USE_SRTP, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, OPT_SMTPHOST, - OPT_ASYNC, + OPT_ASYNC, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES, OPT_READ_BUF, OPT_V_ENUM, OPT_X_ENUM, OPT_S_ENUM, OPT_FALLBACKSCSV, OPT_NOCMDS, OPT_PROXY, OPT_DANE_TLSA_DOMAIN, +#ifndef OPENSSL_NO_CT + OPT_NOCT, OPT_REQUESTCT, OPT_REQUIRECT, OPT_CTLOG_FILE, +#endif OPT_DANE_TLSA_RRDATA } OPTION_CHOICE; @@ -692,7 +701,8 @@ OPTIONS s_client_options[] = { {"showcerts", OPT_SHOWCERTS, '-', "Show all certificates in the chain"}, {"debug", OPT_DEBUG, '-', "Extra output"}, {"msg", OPT_MSG, '-', "Show protocol messages"}, - {"msgfile", OPT_MSGFILE, '>'}, + {"msgfile", OPT_MSGFILE, '>', + "File to send output of -msg or -trace, instead of stdout"}, {"nbio_test", OPT_NBIO_TEST, '-', "More ssl protocol testing"}, {"state", OPT_STATE, '-', "Print the ssl states"}, {"crlf", OPT_CRLF, '-', "Convert LF from terminal into CRLF"}, @@ -715,21 +725,31 @@ OPTIONS s_client_options[] = { "Export len bytes of keying material (default 20)"}, {"fallback_scsv", OPT_FALLBACKSCSV, '-', "Send the fallback SCSV"}, {"name", OPT_SMTPHOST, 's', "Hostname to use for \"-starttls smtp\""}, - {"CRL", OPT_CRL, '<'}, - {"crl_download", OPT_CRL_DOWNLOAD, '-'}, - {"CRLform", OPT_CRLFORM, 'F'}, - {"verify_return_error", OPT_VERIFY_RET_ERROR, '-'}, - {"verify_quiet", OPT_VERIFY_QUIET, '-'}, - {"brief", OPT_BRIEF, '-'}, - {"prexit", OPT_PREXIT, '-'}, - {"security_debug", OPT_SECURITY_DEBUG, '-'}, - {"security_debug_verbose", OPT_SECURITY_DEBUG_VERBOSE, '-'}, - {"cert_chain", OPT_CERT_CHAIN, '<'}, - {"chainCApath", OPT_CHAINCAPATH, '/'}, - {"verifyCApath", OPT_VERIFYCAPATH, '/'}, - {"build_chain", OPT_BUILD_CHAIN, '-'}, - {"chainCAfile", OPT_CHAINCAFILE, '<'}, - {"verifyCAfile", OPT_VERIFYCAFILE, '<'}, + {"CRL", OPT_CRL, '<', "CRL file to use"}, + {"crl_download", OPT_CRL_DOWNLOAD, '-', "Download CRL from distribution points"}, + {"CRLform", OPT_CRLFORM, 'F', "CRL format (PEM or DER) PEM is default"}, + {"verify_return_error", OPT_VERIFY_RET_ERROR, '-', + "Close connection on verification error"}, + {"verify_quiet", OPT_VERIFY_QUIET, '-', "Restrict verify output to errors"}, + {"brief", OPT_BRIEF, '-', + "Restrict output to brief summary of connection parameters"}, + {"prexit", OPT_PREXIT, '-', + "Print session information when the program exits"}, + {"security_debug", OPT_SECURITY_DEBUG, '-', + "Enable security debug messages"}, + {"security_debug_verbose", OPT_SECURITY_DEBUG_VERBOSE, '-', + "Output more security debug output"}, + {"cert_chain", OPT_CERT_CHAIN, '<', + "Certificate chain file (in PEM format)"}, + {"chainCApath", OPT_CHAINCAPATH, '/', + "Use dir as certificate store path to build CA certificate chain"}, + {"verifyCApath", OPT_VERIFYCAPATH, '/', + "Use dir as certificate store path to verify CA certificate"}, + {"build_chain", OPT_BUILD_CHAIN, '-', "Build certificate chain"}, + {"chainCAfile", OPT_CHAINCAFILE, '<', + "CA file for certificate chain (PEM format)"}, + {"verifyCAfile", OPT_VERIFYCAFILE, '<', + "CA file for certificate verification (PEM format)"}, {"nocommands", OPT_NOCMDS, '-', "Do not use interactive command letters"}, {"servername", OPT_SERVERNAME, 's', "Set TLS extension servername in ClientHello"}, @@ -741,7 +761,13 @@ OPTIONS s_client_options[] = { {"alpn", OPT_ALPN, 's', "Enable ALPN extension, considering named protocols supported (comma-separated list)"}, {"async", OPT_ASYNC, '-', "Support asynchronous operation"}, - {"ssl_config", OPT_SSL_CONFIG, 's'}, + {"ssl_config", OPT_SSL_CONFIG, 's', "Use specified configuration file"}, + {"split_send_frag", OPT_SPLIT_SEND_FRAG, 'n', + "Size used to split data for encrypt pipelines"}, + {"max_pipelines", OPT_MAX_PIPELINES, 'n', + "Maximum number of encrypt/decrypt pipelines to be used"}, + {"read_buf", OPT_READ_BUF, 'n', + "Default read buffer size to be used for connections"}, OPT_S_OPTIONS, OPT_V_OPTIONS, OPT_X_OPTIONS, @@ -758,31 +784,27 @@ OPTIONS s_client_options[] = { {"tls1_2", OPT_TLS1_2, '-', "Just use TLSv1.2"}, #endif #ifndef OPENSSL_NO_DTLS - {"dtls", OPT_DTLS, '-'}, - {"timeout", OPT_TIMEOUT, '-'}, + {"dtls", OPT_DTLS, '-', "Use any version of DTLS"}, + {"timeout", OPT_TIMEOUT, '-', + "Enable send/receive timeout on DTLS connections"}, {"mtu", OPT_MTU, 'p', "Set the link layer MTU"}, #endif #ifndef OPENSSL_NO_DTLS1 {"dtls1", OPT_DTLS1, '-', "Just use DTLSv1"}, #endif #ifndef OPENSSL_NO_DTLS1_2 - {"dtls1_2", OPT_DTLS1_2, '-'}, + {"dtls1_2", OPT_DTLS1_2, '-', "Just use DTLSv1.2"}, #endif #ifndef OPENSSL_NO_SSL_TRACE - {"trace", OPT_TRACE, '-'}, + {"trace", OPT_TRACE, '-', "Show trace output of protocol messages"}, #endif #ifdef WATT32 {"wdebug", OPT_WDEBUG, '-', "WATT-32 tcp debugging"}, #endif -#ifdef FIONBIO {"nbio", OPT_NBIO, '-', "Use non-blocking IO"}, -#endif #ifndef OPENSSL_NO_PSK {"psk_identity", OPT_PSK_IDENTITY, 's', "PSK identity"}, {"psk", OPT_PSK, 's', "PSK in hex (without 0x)"}, -# ifndef OPENSSL_NO_JPAKE - {"jpake", OPT_JPAKE, 's', "JPAKE secret to use"}, -# endif #endif #ifndef OPENSSL_NO_SRP {"srpuser", OPT_SRPUSER, 's', "SRP authentification for 'user'"}, @@ -799,7 +821,14 @@ OPTIONS s_client_options[] = { #endif #ifndef OPENSSL_NO_ENGINE {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, - {"ssl_client_engine", OPT_SSL_CLIENT_ENGINE, 's'}, + {"ssl_client_engine", OPT_SSL_CLIENT_ENGINE, 's', + "Specify engine to be used for client certificate operations"}, +#endif +#ifndef OPENSSL_NO_CT + {"noct", OPT_NOCT, '-', "Do not request or parse SCTs (default)"}, + {"requestct", OPT_REQUESTCT, '-', "Request SCTs (enables OCSP stapling)"}, + {"requirect", OPT_REQUIRECT, '-', "Require at least 1 SCT (enables OCSP stapling)"}, + {"ctlogfile", OPT_CTLOG_FILE, '<', "CT log list CONF file"}, #endif {NULL} }; @@ -853,7 +882,7 @@ int s_client_main(int argc, char **argv) char *inrand = NULL; char *passarg = NULL, *pass = NULL, *vfyCApath = NULL, *vfyCAfile = NULL; char *sess_in = NULL, *sess_out = NULL, *crl_file = NULL, *p; - char *jpake_secret = NULL, *xmpphost = NULL; + char *xmpphost = NULL; const char *ehlo = "mail.example.com"; struct sockaddr peer; struct timeval timeout, *timeoutp; @@ -869,6 +898,7 @@ int s_client_main(int argc, char **argv) int socket_family = AF_UNSPEC, socket_type = SOCK_STREAM; int starttls_proto = PROTO_OFF, crl_format = FORMAT_PEM, crl_download = 0; int write_tty, read_tty, write_ssl, read_ssl, tty_on, ssl_pending; + int read_buf_len = 0; int fallback_scsv = 0; long socket_mtu = 0, randamt = 0; OPTION_CHOICE o; @@ -893,6 +923,20 @@ int s_client_main(int argc, char **argv) char *srppass = NULL; int srp_lateuser = 0; SRP_ARG srp_arg = { NULL, NULL, 0, 0, 0, 1024 }; +#endif +#ifndef OPENSSL_NO_CT + char *ctlog_file = NULL; + ct_validation_cb ct_validation = NULL; +#endif + + FD_ZERO(&readfds); + FD_ZERO(&writefds); +/* Known false-positive of MemorySanitizer. */ +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) + __msan_unpoison(&readfds, sizeof(readfds)); + __msan_unpoison(&writefds, sizeof(writefds)); +# endif #endif prog = opt_progname(argv[0]); @@ -1141,7 +1185,7 @@ int s_client_main(int argc, char **argv) break; case OPT_PSK: for (p = psk_key = opt_arg(); *p; p++) { - if (isxdigit(*p)) + if (isxdigit(_UC(*p))) continue; BIO_printf(bio_err, "Not a hex number '%s'\n", psk_key); goto end; @@ -1274,6 +1318,20 @@ int s_client_main(int argc, char **argv) case OPT_NOCAFILE: noCAfile = 1; break; +#ifndef OPENSSL_NO_CT + case OPT_NOCT: + ct_validation = NULL; + break; + case OPT_REQUESTCT: + ct_validation = CT_verify_no_bad_scts; + break; + case OPT_REQUIRECT: + ct_validation = CT_verify_at_least_one_good_sct; + break; + case OPT_CTLOG_FILE: + ctlog_file = opt_arg(); + break; +#endif case OPT_CHAINCAFILE: chCAfile = opt_arg(); break; @@ -1316,11 +1374,6 @@ int s_client_main(int argc, char **argv) case OPT_SERVERNAME: servername = opt_arg(); break; - case OPT_JPAKE: -#ifndef OPENSSL_NO_JPAKE - jpake_secret = opt_arg(); -#endif - break; case OPT_USE_SRTP: srtp_profiles = opt_arg(); break; @@ -1333,10 +1386,27 @@ int s_client_main(int argc, char **argv) case OPT_ASYNC: async = 1; break; + case OPT_SPLIT_SEND_FRAG: + split_send_fragment = atoi(opt_arg()); + if (split_send_fragment == 0) { + /* + * Not allowed - set to a deliberately bad value so we get an + * error message below + */ + split_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH + 1; + } + break; + case OPT_MAX_PIPELINES: + max_pipelines = atoi(opt_arg()); + break; + case OPT_READ_BUF: + read_buf_len = atoi(opt_arg()); + break; } } argc = opt_num_rest(); - argv = opt_rest(); + if (argc != 0) + goto opthelp; if (proxystr) { int res; @@ -1378,15 +1448,16 @@ int s_client_main(int argc, char **argv) "Can't use unix sockets and datagrams together\n"); goto end; } -#if !defined(OPENSSL_NO_JPAKE) && !defined(OPENSSL_NO_PSK) - if (jpake_secret) { - if (psk_key) { - BIO_printf(bio_err, "Can't use JPAKE and PSK together\n"); - goto end; - } - psk_identity = "JPAKE"; + + if (split_send_fragment > SSL3_RT_MAX_PLAIN_LENGTH) { + BIO_printf(bio_err, "Bad split send fragment size\n"); + goto end; + } + + if (max_pipelines > SSL_MAX_PIPELINES) { + BIO_printf(bio_err, "Bad max pipelines value\n"); + goto end; } -#endif #if !defined(OPENSSL_NO_NEXTPROTONEG) next_proto.status = -1; @@ -1419,8 +1490,7 @@ int s_client_main(int argc, char **argv) } if (cert_file) { - cert = load_cert(cert_file, cert_format, - NULL, e, "client certificate file"); + cert = load_cert(cert_file, cert_format, "client certificate file"); if (cert == NULL) { ERR_print_errors(bio_err); goto end; @@ -1428,7 +1498,7 @@ int s_client_main(int argc, char **argv) } if (chain_file) { - if (!load_certs(chain_file, &chain, FORMAT_PEM, NULL, e, + if (!load_certs(chain_file, &chain, FORMAT_PEM, NULL, "client certificate chain")) goto end; } @@ -1504,10 +1574,19 @@ int s_client_main(int argc, char **argv) if (async) { SSL_CTX_set_mode(ctx, SSL_MODE_ASYNC); - ASYNC_init(1, 0, 0); + } + if (split_send_fragment > 0) { + SSL_CTX_set_split_send_fragment(ctx, split_send_fragment); + } + if (max_pipelines > 0) { + SSL_CTX_set_max_pipelines(ctx, max_pipelines); + } + + if (read_buf_len > 0) { + SSL_CTX_set_default_read_buffer_len(ctx, read_buf_len); } - if (!config_ctx(cctx, ssl_args, ctx, jpake_secret == NULL)) + if (!config_ctx(cctx, ssl_args, ctx)) goto end; if (!ssl_load_stores(ctx, vfyCApath, vfyCAfile, chCApath, chCAfile, @@ -1529,10 +1608,10 @@ int s_client_main(int argc, char **argv) #endif #ifndef OPENSSL_NO_PSK - if (psk_key != NULL || jpake_secret) { + if (psk_key != NULL) { if (c_debug) BIO_printf(bio_c_out, - "PSK key given or JPAKE in use, setting client callback\n"); + "PSK key given, setting client callback\n"); SSL_CTX_set_psk_client_callback(ctx, psk_client_cb); } #endif @@ -1555,7 +1634,7 @@ int s_client_main(int argc, char **argv) SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto); #endif if (alpn_in) { - unsigned short alpn_len; + size_t alpn_len; unsigned char *alpn = next_protos_parse(&alpn_len, alpn_in); if (alpn == NULL) { @@ -1584,6 +1663,28 @@ int s_client_main(int argc, char **argv) if (state) SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback); +#ifndef OPENSSL_NO_CT + if (!SSL_CTX_set_ct_validation_callback(ctx, ct_validation, NULL)) { + ERR_print_errors(bio_err); + goto end; + } + + if (!ctx_set_ctlog_list_file(ctx, ctlog_file)) { + if (ct_validation != NULL) { + ERR_print_errors(bio_err); + goto end; + } + + /* + * If CT validation is not enabled, the log list isn't needed so don't + * show errors or abort. We try to load it regardless because then we + * can show the names of the logs any SCTs came from (SCTs may be seen + * even with validation disabled). + */ + ERR_clear_error(); + } +#endif + SSL_CTX_set_verify(ctx, verify, verify_callback); if (!ctx_set_verify_locations(ctx, CAfile, CApath, noCAfile, noCApath)) { @@ -1689,28 +1790,25 @@ int s_client_main(int argc, char **argv) if (init_client(&s, host, port, socket_family, socket_type) == 0) { BIO_printf(bio_err, "connect:errno=%d\n", get_last_socket_error()); - SHUTDOWN(s); + BIO_closesocket(s); goto end; } BIO_printf(bio_c_out, "CONNECTED(%08X)\n", s); -#ifdef FIONBIO if (c_nbio) { - unsigned long l = 1; - BIO_printf(bio_c_out, "turning on non blocking io\n"); - if (BIO_socket_ioctl(s, FIONBIO, &l) < 0) { + if (!BIO_socket_nbio(s, 1)) { ERR_print_errors(bio_err); goto end; } + BIO_printf(bio_c_out, "Turned on non blocking io\n"); } -#endif if (socket_type == SOCK_DGRAM) { sbio = BIO_new_dgram(s, BIO_NOCLOSE); if (getsockname(s, &peer, (void *)&peerlen) < 0) { BIO_printf(bio_err, "getsockname:errno=%d\n", get_last_socket_error()); - SHUTDOWN(s); + BIO_closesocket(s); goto end; } @@ -1775,10 +1873,6 @@ int s_client_main(int argc, char **argv) SSL_CTX_set_tlsext_status_cb(ctx, ocsp_resp_cb); SSL_CTX_set_tlsext_status_arg(ctx, bio_c_out); } -#ifndef OPENSSL_NO_JPAKE - if (jpake_secret) - jpake_client_auth(bio_c_out, sbio, jpake_secret); -#endif SSL_set_bio(con, sbio, sbio); SSL_set_connect_state(con); @@ -2096,13 +2190,13 @@ int s_client_main(int argc, char **argv) "drop connection and then reconnect\n"); do_ssl_shutdown(con); SSL_set_connect_state(con); - SHUTDOWN(SSL_get_fd(con)); + BIO_closesocket(SSL_get_fd(con)); goto re_start; } } } - ssl_pending = read_ssl && SSL_pending(con); + ssl_pending = read_ssl && SSL_has_pending(con); if (!ssl_pending) { #if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS) && !defined(OPENSSL_SYS_NETWARE) @@ -2413,16 +2507,13 @@ int s_client_main(int argc, char **argv) if (in_init) print_stuff(bio_c_out, con, full_log); do_ssl_shutdown(con); - SHUTDOWN(SSL_get_fd(con)); + BIO_closesocket(SSL_get_fd(con)); end: if (con != NULL) { if (prexit != 0) print_stuff(bio_c_out, con, 1); SSL_free(con); } - if (async) { - ASYNC_cleanup(1); - } #if !defined(OPENSSL_NO_NEXTPROTONEG) OPENSSL_free(next_proto.data); #endif @@ -2461,13 +2552,13 @@ static void print_stuff(BIO *bio, SSL *s, int full) const SSL_CIPHER *c; X509_NAME *xn; int i; - int mdpth; - EVP_PKEY *mspki; - const char *peername; #ifndef OPENSSL_NO_COMP const COMP_METHOD *comp, *expansion; #endif unsigned char *exportedkeymat; +#ifndef OPENSSL_NO_CT + const STACK_OF(SCT) *scts; +#endif if (full) { int got_a_chain = 0; @@ -2520,24 +2611,30 @@ static void print_stuff(BIO *bio, SSL *s, int full) ssl_print_sigalgs(bio, s); ssl_print_tmp_key(bio, s); +#ifndef OPENSSL_NO_CT + scts = SSL_get0_peer_scts(s); + BIO_printf(bio, "---\nSCTs present (%i)\n", + scts != NULL ? sk_SCT_num(scts) : 0); + + if (SSL_get_ct_validation_callback(s) == NULL) { + BIO_printf(bio, "Warning: CT validation is disabled, so not all " + "SCTs may be displayed. Re-run with \"-requestct\".\n"); + } + + if (scts != NULL && sk_SCT_num(scts) > 0) { + BIO_printf(bio, "---\n"); + SCT_LIST_print(scts, bio, 0, "\n---\n"); + BIO_printf(bio, "\n"); + } +#endif + BIO_printf(bio, "---\nSSL handshake has read %"PRIu64" bytes and written %"PRIu64" bytes\n", BIO_number_read(SSL_get_rbio(s)), BIO_number_written(SSL_get_wbio(s))); } - if ((mdpth = SSL_get0_dane_authority(s, NULL, &mspki)) >= 0) { - uint8_t usage, selector, mtype; - mdpth = SSL_get0_dane_tlsa(s, &usage, &selector, &mtype, NULL, NULL); - BIO_printf(bio, "DANE TLSA %d %d %d %s at depth %d\n", - usage, selector, mtype, - (mspki != NULL) ? "TA public key verified certificate" : - mdpth ? "matched TA certificate" : "matched EE certificate", - mdpth); - } - if (SSL_get_verify_result(s) == X509_V_OK && - (peername = SSL_get0_peername(s)) != NULL) - BIO_printf(bio, "Verified peername: %s\n", peername); - BIO_printf(bio, (SSL_cache_hit(s) ? "---\nReused, " : "---\nNew, ")); + print_verify_detail(s, bio); + BIO_printf(bio, (SSL_session_reused(s) ? "---\nReused, " : "---\nNew, ")); c = SSL_get_current_cipher(s); BIO_printf(bio, "%s, Cipher is %s\n", SSL_CIPHER_get_version(c), SSL_CIPHER_get_name(c));