X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=apps%2Fs_client.c;h=ce7366f8fe6c830b362d35cd29efd5cbdf7ce76b;hp=36da3b6aa7090a96b9b1691b75dd4ffc9905f556;hb=1fb6b0bf3e895c6b30f9c95a23284f93e4fa19b0;hpb=add8d0e9e0bb80728f4b89d15573bf2e70596ceb diff --git a/apps/s_client.c b/apps/s_client.c index 36da3b6aa7..ce7366f8fe 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. * Copyright 2005 Nokia. All rights reserved. * * Licensed under the OpenSSL license (the "License"). You may not use @@ -30,6 +30,7 @@ typedef unsigned int u_int; #endif #include "apps.h" +#include "progs.h" #include #include #include @@ -196,19 +197,13 @@ static int psk_use_session_cb(SSL *s, const EVP_MD *md, return 0; } - if (key_len == EVP_MD_size(EVP_sha256())) - cipher = SSL_CIPHER_find(s, tls13_aes128gcmsha256_id); - else if(key_len == EVP_MD_size(EVP_sha384())) - cipher = SSL_CIPHER_find(s, tls13_aes256gcmsha384_id); - + /* We default to SHA-256 */ + cipher = SSL_CIPHER_find(s, tls13_aes128gcmsha256_id); if (cipher == NULL) { - /* Doesn't look like a suitable TLSv1.3 key. Ignore it */ - OPENSSL_free(key); - *id = NULL; - *idlen = 0; - *sess = NULL; + BIO_printf(bio_err, "Error finding suitable ciphersuite\n"); return 0; } + usesess = SSL_SESSION_new(); if (usesess == NULL || !SSL_SESSION_set1_master_key(usesess, key, key_len) @@ -372,8 +367,6 @@ static char *ssl_give_srp_client_pwd_cb(SSL *s, void *arg) #endif -static char *srtp_profiles = NULL; - #ifndef OPENSSL_NO_NEXTPROTONEG /* This the context that we pass to next_proto_cb */ typedef struct tlsextnextprotoctx_st { @@ -417,10 +410,11 @@ static int serverinfo_cli_parse_cb(SSL *s, unsigned int ext_type, unsigned char ext_buf[4 + 65536]; /* Reconstruct the type/len fields prior to extension data */ - ext_buf[0] = ext_type >> 8; - ext_buf[1] = ext_type & 0xFF; - ext_buf[2] = inlen >> 8; - ext_buf[3] = inlen & 0xFF; + inlen &= 0xffff; /* for formal memcmpy correctness */ + ext_buf[0] = (unsigned char)(ext_type >> 8); + ext_buf[1] = (unsigned char)(ext_type); + ext_buf[2] = (unsigned char)(inlen >> 8); + ext_buf[3] = (unsigned char)(inlen); memcpy(ext_buf + 4, in, inlen); BIO_snprintf(pem_name, sizeof(pem_name), "SERVERINFO FOR EXTENSION %d", @@ -568,7 +562,7 @@ static int tlsa_import_rrset(SSL *con, STACK_OF(OPENSSL_STRING) *rrset) typedef enum OPTION_choice { OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, - OPT_4, OPT_6, OPT_HOST, OPT_PORT, OPT_CONNECT, OPT_UNIX, + OPT_4, OPT_6, OPT_HOST, OPT_PORT, OPT_CONNECT, OPT_BIND, OPT_UNIX, OPT_XMPPHOST, OPT_VERIFY, OPT_NAMEOPT, OPT_CERT, OPT_CRL, OPT_CRL_DOWNLOAD, OPT_SESS_OUT, OPT_SESS_IN, OPT_CERTFORM, OPT_CRLFORM, OPT_VERIFY_RET_ERROR, OPT_VERIFY_QUIET, @@ -589,9 +583,9 @@ typedef enum OPTION_choice { 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_NOSERVERNAME, OPT_ASYNC, - OPT_USE_SRTP, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, OPT_SMTPHOST, - OPT_MAX_SEND_FRAG, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES, OPT_READ_BUF, - OPT_KEYLOG_FILE, OPT_EARLY_DATA, OPT_REQCAFILE, + OPT_USE_SRTP, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, OPT_PROTOHOST, + OPT_MAXFRAGLEN, OPT_MAX_SEND_FRAG, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES, + OPT_READ_BUF, OPT_KEYLOG_FILE, OPT_EARLY_DATA, OPT_REQCAFILE, OPT_V_ENUM, OPT_X_ENUM, OPT_S_ENUM, @@ -600,6 +594,7 @@ typedef enum OPTION_choice { OPT_CT, OPT_NOCT, OPT_CTLOG_FILE, #endif OPT_DANE_TLSA_RRDATA, OPT_DANE_EE_NO_NAME, + OPT_FORCE_PHA, OPT_R_ENUM } OPTION_CHOICE; @@ -609,6 +604,7 @@ const OPTIONS s_client_options[] = { {"port", OPT_PORT, 'p', "Use -connect instead"}, {"connect", OPT_CONNECT, 's', "TCP/IP where to connect (default is :" PORT ")"}, + {"bind", OPT_BIND, 's', "bind local address for connection"}, {"proxy", OPT_PROXY, 's', "Connect to via specified proxy to the real server"}, #ifdef AF_UNIX @@ -655,19 +651,23 @@ const OPTIONS s_client_options[] = { {"starttls", OPT_STARTTLS, 's', "Use the appropriate STARTTLS command before starting TLS"}, {"xmpphost", OPT_XMPPHOST, 's', - "Host to use with \"-starttls xmpp[-server]\""}, + "Alias of -name option for \"-starttls xmpp[-server]\""}, OPT_R_OPTIONS, {"sess_out", OPT_SESS_OUT, '>', "File to write SSL session to"}, {"sess_in", OPT_SESS_IN, '<', "File to read SSL session from"}, +#ifndef OPENSSL_NO_SRTP {"use_srtp", OPT_USE_SRTP, 's', "Offer SRTP key management with a colon-separated profile list"}, +#endif {"keymatexport", OPT_KEYMATEXPORT, 's', "Export keying material using label"}, {"keymatexportlen", OPT_KEYMATEXPORTLEN, 'p', "Export len bytes of keying material (default 20)"}, + {"maxfraglen", OPT_MAXFRAGLEN, 'p', + "Enable Maximum Fragment Length Negotiation (len values: 512, 1024, 2048 and 4096)"}, {"fallback_scsv", OPT_FALLBACKSCSV, '-', "Send the fallback SCSV"}, - {"name", OPT_SMTPHOST, 's', - "Hostname to use for \"-starttls lmtp\" or \"-starttls smtp\""}, + {"name", OPT_PROTOHOST, 's', + "Hostname to use for \"-starttls lmtp\", \"-starttls smtp\" or \"-starttls xmpp[-server]\""}, {"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"}, @@ -784,6 +784,7 @@ const OPTIONS s_client_options[] = { #endif {"keylogfile", OPT_KEYLOG_FILE, '>', "Write TLS secrets to file"}, {"early_data", OPT_EARLY_DATA, '<', "File to send as early data"}, + {"force_pha", OPT_FORCE_PHA, '-', "Force-enable post-handshake-authentication"}, {NULL, OPT_EOF, 0x00, NULL} }; @@ -878,15 +879,15 @@ int s_client_main(int argc, char **argv) const SSL_METHOD *meth = TLS_client_method(); const char *CApath = NULL, *CAfile = NULL; char *cbuf = NULL, *sbuf = NULL; - char *mbuf = NULL, *proxystr = NULL, *connectstr = NULL; + char *mbuf = NULL, *proxystr = NULL, *connectstr = NULL, *bindstr = NULL; char *cert_file = NULL, *key_file = NULL, *chain_file = NULL; char *chCApath = NULL, *chCAfile = NULL, *host = NULL; char *port = OPENSSL_strdup(PORT); + char *bindhost = NULL, *bindport = NULL; char *passarg = NULL, *pass = NULL, *vfyCApath = NULL, *vfyCAfile = NULL; char *ReqCAfile = NULL; char *sess_in = NULL, *crl_file = NULL, *p; - char *xmpphost = NULL; - const char *ehlo = "mail.example.com"; + const char *protohost = NULL; struct timeval timeout, *timeoutp; fd_set readfds, writefds; int noCApath = 0, noCAfile = 0; @@ -917,7 +918,7 @@ int s_client_main(int argc, char **argv) #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) struct timeval tv; #endif - char *servername = NULL; + const char *servername = NULL; int noservername = 0; const char *alpn_in = NULL; tlsextctx tlsextcbp = { NULL, 0 }; @@ -933,6 +934,7 @@ int s_client_main(int argc, char **argv) int srp_lateuser = 0; SRP_ARG srp_arg = { NULL, NULL, 0, 0, 0, 1024 }; #endif + char *srtp_profiles = NULL; #ifndef OPENSSL_NO_CT char *ctlog_file = NULL; int ct_validation = 0; @@ -943,6 +945,7 @@ int s_client_main(int argc, char **argv) unsigned int split_send_fragment = 0, max_pipelines = 0; enum { use_inet, use_unix, use_unknown } connect_type = use_unknown; int count4or6 = 0; + uint8_t maxfraglen = 0; int c_nbio = 0, c_msg = 0, c_ign_eof = 0, c_brief = 0; int c_tlsextdebug = 0; #ifndef OPENSSL_NO_OCSP @@ -954,6 +957,7 @@ int s_client_main(int argc, char **argv) int isdtls = 0; #endif char *psksessf = NULL; + int force_pha = 0; FD_ZERO(&readfds); FD_ZERO(&writefds); @@ -1046,6 +1050,9 @@ int s_client_main(int argc, char **argv) connect_type = use_inet; freeandcopy(&connectstr, opt_arg()); break; + case OPT_BIND: + freeandcopy(&bindstr, opt_arg()); + break; case OPT_PROXY: proxystr = opt_arg(); starttls_proto = PROTO_CONNECT; @@ -1058,10 +1065,9 @@ int s_client_main(int argc, char **argv) break; #endif case OPT_XMPPHOST: - xmpphost = opt_arg(); - break; - case OPT_SMTPHOST: - ehlo = opt_arg(); + /* fall through, since this is an alias */ + case OPT_PROTOHOST: + protohost = opt_arg(); break; case OPT_VERIFY: verify = SSL_VERIFY_PEER; @@ -1426,6 +1432,28 @@ int s_client_main(int argc, char **argv) case OPT_ASYNC: async = 1; break; + case OPT_MAXFRAGLEN: + len = atoi(opt_arg()); + switch (len) { + case 512: + maxfraglen = TLSEXT_max_fragment_length_512; + break; + case 1024: + maxfraglen = TLSEXT_max_fragment_length_1024; + break; + case 2048: + maxfraglen = TLSEXT_max_fragment_length_2048; + break; + case 4096: + maxfraglen = TLSEXT_max_fragment_length_4096; + break; + default: + BIO_printf(bio_err, + "%s: Max Fragment Len %u is out of permitted values", + prog, len); + goto opthelp; + } + break; case OPT_MAX_SEND_FRAG: max_send_fragment = atoi(opt_arg()); break; @@ -1444,6 +1472,9 @@ int s_client_main(int argc, char **argv) case OPT_EARLY_DATA: early_data_file = opt_arg(); break; + case OPT_FORCE_PHA: + force_pha = 1; + break; } } if (count4or6 >= 2) { @@ -1523,6 +1554,18 @@ int s_client_main(int argc, char **argv) } } + if (bindstr != NULL) { + int res; + res = BIO_parse_hostserv(bindstr, &bindhost, &bindport, + BIO_PARSE_PRIO_HOST); + if (!res) { + BIO_printf(bio_err, + "%s: -bind argument parameter malformed or ambiguous\n", + prog); + goto end; + } + } + #ifdef AF_UNIX if (socket_family == AF_UNIX && socket_type != SOCK_STREAM) { BIO_printf(bio_err, @@ -1630,6 +1673,9 @@ int s_client_main(int argc, char **argv) if (sdebug) ssl_ctx_security_debug(ctx, sdebug); + if (!config_ctx(cctx, ssl_args, ctx)) + goto end; + if (ssl_config != NULL) { if (SSL_CTX_config(ctx, ssl_config) == 0) { BIO_printf(bio_err, "Error using configuration \"%s\"\n", @@ -1639,9 +1685,11 @@ int s_client_main(int argc, char **argv) } } - if (SSL_CTX_set_min_proto_version(ctx, min_version) == 0) + if (min_version != 0 + && SSL_CTX_set_min_proto_version(ctx, min_version) == 0) goto end; - if (SSL_CTX_set_max_proto_version(ctx, max_version) == 0) + if (max_version != 0 + && SSL_CTX_set_max_proto_version(ctx, max_version) == 0) goto end; if (vpmtouched && !SSL_CTX_set1_param(ctx, vpm)) { @@ -1679,8 +1727,13 @@ int s_client_main(int argc, char **argv) SSL_CTX_set_default_read_buffer_len(ctx, read_buf_len); } - if (!config_ctx(cctx, ssl_args, ctx)) + if (maxfraglen > 0 + && !SSL_CTX_set_tlsext_max_fragment_length(ctx, maxfraglen)) { + BIO_printf(bio_err, + "%s: Max Fragment Length code %u is out of permitted values" + "\n", prog, maxfraglen); goto end; + } if (!ssl_load_stores(ctx, vfyCApath, vfyCAfile, chCApath, chCAfile, crls, crl_download)) { @@ -1868,6 +1921,12 @@ int s_client_main(int argc, char **argv) goto end; con = SSL_new(ctx); + if (con == NULL) + goto end; + + if (force_pha) + SSL_force_post_handshake_auth(con); + if (sess_in != NULL) { SSL_SESSION *sess; BIO *stmp = BIO_new_file(sess_in, "r"); @@ -1888,6 +1947,7 @@ int s_client_main(int argc, char **argv) ERR_print_errors(bio_err); goto end; } + SSL_SESSION_free(sess); } @@ -1930,8 +1990,8 @@ int s_client_main(int argc, char **argv) } re_start: - if (init_client(&s, host, port, socket_family, socket_type, protocol) - == 0) { + if (init_client(&s, host, port, bindhost, bindport, socket_family, + socket_type, protocol) == 0) { BIO_printf(bio_err, "connect:errno=%d\n", get_last_socket_error()); BIO_closesocket(s); goto end; @@ -2079,10 +2139,12 @@ int s_client_main(int argc, char **argv) do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); } while (mbuf_len > 3 && mbuf[3] == '-'); + if (protohost == NULL) + protohost = "mail.example.com"; if (starttls_proto == (int)PROTO_LMTP) - BIO_printf(fbio, "LHLO %s\r\n", ehlo); + BIO_printf(fbio, "LHLO %s\r\n", protohost); else - BIO_printf(fbio, "EHLO %s\r\n", ehlo); + BIO_printf(fbio, "EHLO %s\r\n", protohost); (void)BIO_flush(fbio); /* * Wait for multi-line response to end LHLO LMTP or EHLO SMTP @@ -2168,7 +2230,7 @@ int s_client_main(int argc, char **argv) "xmlns:stream='http://etherx.jabber.org/streams' " "xmlns='jabber:%s' to='%s' version='1.0'>", starttls_proto == PROTO_XMPP ? "client" : "server", - xmpphost ? xmpphost : host); + protohost ? protohost : host); seen = BIO_read(sbio, mbuf, BUFSIZZ); if (seen < 0) { BIO_printf(bio_err, "BIO_read failed\n"); @@ -2627,6 +2689,7 @@ int s_client_main(int argc, char **argv) default: BIO_printf(bio_err, "Error writing early data\n"); BIO_free(edfile); + ERR_print_errors(bio_err); goto shut; } } @@ -2986,7 +3049,7 @@ int s_client_main(int argc, char **argv) if (in_init) print_stuff(bio_c_out, con, full_log); do_ssl_shutdown(con); -#if defined(OPENSSL_SYS_WINDOWS) + /* * Give the socket time to send its last data before we close it. * No amount of setting SO_LINGER etc on the socket seems to persuade @@ -2994,8 +3057,23 @@ int s_client_main(int argc, char **argv) * for a short time seems to do it (units in ms) * TODO: Find a better way to do this */ +#if defined(OPENSSL_SYS_WINDOWS) Sleep(50); +#elif defined(OPENSSL_SYS_CYGWIN) + usleep(50000); #endif + + /* + * If we ended with an alert being sent, but still with data in the + * network buffer to be read, then calling BIO_closesocket() will + * result in a TCP-RST being sent. On some platforms (notably + * Windows) then this will result in the peer immediately abandoning + * the connection including any buffered alert data before it has + * had a chance to be read. Shutting down the sending side first, + * and then closing the socket sends TCP-FIN first followed by + * TCP-RST. This seems to allow the peer to read the alert data. + */ + shutdown(SSL_get_fd(con), 1); /* SHUT_WR */ BIO_closesocket(SSL_get_fd(con)); end: if (con != NULL) { @@ -3018,6 +3096,7 @@ int s_client_main(int argc, char **argv) OPENSSL_free(srp_arg.srppassin); #endif OPENSSL_free(connectstr); + OPENSSL_free(bindstr); OPENSSL_free(host); OPENSSL_free(port); X509_VERIFY_PARAM_free(vpm); @@ -3033,7 +3112,7 @@ int s_client_main(int argc, char **argv) bio_c_out = NULL; BIO_free(bio_c_msg); bio_c_msg = NULL; - return (ret); + return ret; } static void print_stuff(BIO *bio, SSL *s, int full)