X-Git-Url: https://git.openssl.org/gitweb/?a=blobdiff_plain;f=apps%2Fs_client.c;h=eb4dbdcaa26df518ee98397190a16cb1ae695af4;hb=576bcdb5bdc1311064a108098eedc4a0723615ba;hp=f84ae2b4b3345c94c4a4279de11dc367f933e982;hpb=5ffff5990caa42b8a2d55fc70e23edbe9397e4cb;p=openssl.git diff --git a/apps/s_client.c b/apps/s_client.c index f84ae2b4b3..eb4dbdcaa2 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -1,13 +1,14 @@ /* - * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * Copyright 2005 Nokia. All rights reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ +#include "e_os.h" #include #include #include @@ -28,8 +29,8 @@ typedef unsigned int u_int; #endif -#define USE_SOCKETS #include "apps.h" +#include "progs.h" #include #include #include @@ -37,6 +38,7 @@ typedef unsigned int u_int; #include #include #include +#include #include #ifndef OPENSSL_NO_SRP # include @@ -46,6 +48,7 @@ typedef unsigned int u_int; #endif #include "s_apps.h" #include "timeouts.h" +#include "internal/sockets.h" #if defined(__has_feature) # if __has_feature(memory_sanitizer) @@ -53,6 +56,12 @@ typedef unsigned int u_int; # endif #endif +DEFINE_STACK_OF(X509) +DEFINE_STACK_OF(X509_CRL) +DEFINE_STACK_OF(X509_NAME) +DEFINE_STACK_OF(SCT) +DEFINE_STACK_OF_STRING() + #undef BUFSIZZ #define BUFSIZZ 1024*8 #define S_CLIENT_IRC_READ_TIMEOUT 8 @@ -72,6 +81,7 @@ static void print_stuff(BIO *berr, SSL *con, int full); static int ocsp_resp_cb(SSL *s, void *arg); #endif static int ldap_ExtendedResponse_parse(const char *buf, long rem); +static int is_dNS_name(const char *host); static int saved_errno; @@ -109,10 +119,10 @@ static void do_ssl_shutdown(SSL *ssl) } while (ret < 0); } -#ifndef OPENSSL_NO_PSK /* Default PSK identity and key */ static char *psk_identity = "Client_identity"; +#ifndef OPENSSL_NO_PSK static unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, @@ -172,8 +182,8 @@ static unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity, } #endif -#define TLS13_AES_128_GCM_SHA256_BYTES ((const unsigned char *)"\x13\x01") -#define TLS13_AES_256_GCM_SHA384_BYTES ((const unsigned char *)"\x13\x02") +const unsigned char tls13_aes128gcmsha256_id[] = { 0x13, 0x01 }; +const unsigned char tls13_aes256gcmsha384_id[] = { 0x13, 0x02 }; static int psk_use_session_cb(SSL *s, const EVP_MD *md, const unsigned char **id, size_t *idlen, @@ -195,16 +205,14 @@ 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_AES_128_GCM_SHA256_BYTES); - else if(key_len == EVP_MD_size(EVP_sha384())) - cipher = SSL_CIPHER_find(s, TLS13_AES_256_GCM_SHA384_BYTES); - + /* 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 */ + BIO_printf(bio_err, "Error finding suitable ciphersuite\n"); OPENSSL_free(key); return 0; } + usesess = SSL_SESSION_new(); if (usesess == NULL || !SSL_SESSION_set1_master_key(usesess, key, key_len) @@ -217,17 +225,20 @@ static int psk_use_session_cb(SSL *s, const EVP_MD *md, } cipher = SSL_SESSION_get0_cipher(usesess); - if (cipher == NULL) goto err; - if (md != NULL && SSL_CIPHER_get_handshake_digest(cipher) != md) - goto err; - - *sess = usesess; - - *id = (unsigned char *)psk_identity; - *idlen = strlen(psk_identity); + if (md != NULL && SSL_CIPHER_get_handshake_digest(cipher) != md) { + /* PSK not usable, ignore it */ + *id = NULL; + *idlen = 0; + *sess = NULL; + SSL_SESSION_free(usesess); + } else { + *sess = usesess; + *id = (unsigned char *)psk_identity; + *idlen = strlen(psk_identity); + } return 1; @@ -266,8 +277,6 @@ typedef struct srp_arg_st { int strength; /* minimal size for N */ } SRP_ARG; -# define SRP_NUMBER_ITERATIONS_FOR_PRIME 64 - static int srp_Verify_N_and_g(const BIGNUM *N, const BIGNUM *g) { BN_CTX *bn_ctx = BN_CTX_new(); @@ -275,10 +284,10 @@ static int srp_Verify_N_and_g(const BIGNUM *N, const BIGNUM *g) BIGNUM *r = BN_new(); int ret = g != NULL && N != NULL && bn_ctx != NULL && BN_is_odd(N) && - BN_is_prime_ex(N, SRP_NUMBER_ITERATIONS_FOR_PRIME, bn_ctx, NULL) == 1 && + BN_check_prime(N, bn_ctx, NULL) == 1 && p != NULL && BN_rshift1(p, N) && /* p = (N-1)/2 */ - BN_is_prime_ex(p, SRP_NUMBER_ITERATIONS_FOR_PRIME, bn_ctx, NULL) == 1 && + BN_check_prime(p, bn_ctx, NULL) == 1 && r != NULL && /* verify g^((N-1)/2) == -1 (mod N) */ BN_mod_exp(r, g, p, N, bn_ctx) && @@ -365,8 +374,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 { @@ -410,10 +417,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", @@ -561,19 +569,16 @@ 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, OPT_BRIEF, OPT_PREXIT, OPT_CRLF, OPT_QUIET, OPT_NBIO, - OPT_SSL_CLIENT_ENGINE, OPT_RAND, OPT_IGN_EOF, OPT_NO_IGN_EOF, + OPT_SSL_CLIENT_ENGINE, OPT_IGN_EOF, OPT_NO_IGN_EOF, OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_STATUS, OPT_WDEBUG, OPT_MSG, OPT_MSGFILE, OPT_ENGINE, OPT_TRACE, OPT_SECURITY_DEBUG, OPT_SECURITY_DEBUG_VERBOSE, OPT_SHOWCERTS, OPT_NBIO_TEST, OPT_STATE, -#ifndef OPENSSL_NO_PSK - OPT_PSK_IDENTITY, OPT_PSK, -#endif - OPT_PSK_SESS, + OPT_PSK_IDENTITY, OPT_PSK, OPT_PSK_SESS, #ifndef OPENSSL_NO_SRP OPT_SRPUSER, OPT_SRPPASS, OPT_SRP_STRENGTH, OPT_SRP_LATEUSER, OPT_SRP_MOREGROUPS, @@ -581,31 +586,56 @@ typedef enum OPTION_choice { OPT_SSL3, OPT_SSL_CONFIG, OPT_TLS1_3, OPT_TLS1_2, OPT_TLS1_1, OPT_TLS1, OPT_DTLS, OPT_DTLS1, OPT_DTLS1_2, OPT_SCTP, OPT_TIMEOUT, OPT_MTU, OPT_KEYFORM, OPT_PASS, - 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_CERT_CHAIN, OPT_KEY, OPT_RECONNECT, OPT_BUILD_CHAIN, + OPT_NEXTPROTONEG, OPT_ALPN, + OPT_CAPATH, OPT_NOCAPATH, OPT_CHAINCAPATH, OPT_VERIFYCAPATH, + OPT_CAFILE, OPT_NOCAFILE, OPT_CHAINCAFILE, OPT_VERIFYCAFILE, + OPT_CASTORE, OPT_NOCASTORE, OPT_CHAINCASTORE, OPT_VERIFYCASTORE, 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, - OPT_FALLBACKSCSV, OPT_NOCMDS, OPT_PROXY, OPT_DANE_TLSA_DOMAIN, + OPT_FALLBACKSCSV, OPT_NOCMDS, OPT_PROXY, OPT_PROXY_USER, OPT_PROXY_PASS, + OPT_DANE_TLSA_DOMAIN, #ifndef OPENSSL_NO_CT OPT_CT, OPT_NOCT, OPT_CTLOG_FILE, #endif - OPT_DANE_TLSA_RRDATA, OPT_DANE_EE_NO_NAME + OPT_DANE_TLSA_RRDATA, OPT_DANE_EE_NO_NAME, + OPT_ENABLE_PHA, + OPT_SCTP_LABEL_BUG, + OPT_R_ENUM, OPT_PROV_ENUM } OPTION_CHOICE; const OPTIONS s_client_options[] = { + {OPT_HELP_STR, 1, '-', "Usage: %s [options] [host:port]\n"}, + + OPT_SECTION("General"), {"help", OPT_HELP, '-', "Display this summary"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"ssl_client_engine", OPT_SSL_CLIENT_ENGINE, 's', + "Specify engine to be used for client certificate operations"}, +#endif + {"ssl_config", OPT_SSL_CONFIG, 's', "Use specified section for SSL_CTX configuration"}, +#ifndef OPENSSL_NO_CT + {"ct", OPT_CT, '-', "Request and parse SCTs (also enables OCSP stapling)"}, + {"noct", OPT_NOCT, '-', "Do not request or parse SCTs (default)"}, + {"ctlogfile", OPT_CTLOG_FILE, '<', "CT log list CONF file"}, +#endif + + OPT_SECTION("Network"), {"host", OPT_HOST, 's', "Use -connect instead"}, {"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"}, + {"proxy_user", OPT_PROXY_USER, 's', "UserID for proxy authentication"}, + {"proxy_pass", OPT_PROXY_PASS, 's', "Proxy authentication password source"}, #ifdef AF_UNIX {"unix", OPT_UNIX, 's', "Connect over the specified Unix-domain socket"}, #endif @@ -613,20 +643,38 @@ const OPTIONS s_client_options[] = { #ifdef AF_INET6 {"6", OPT_6, '-', "Use IPv6 only"}, #endif - {"verify", OPT_VERIFY, 'p', "Turn on peer certificate verification"}, - {"cert", OPT_CERT, '<', "Certificate file to use, PEM format assumed"}, + {"maxfraglen", OPT_MAXFRAGLEN, 'p', + "Enable Maximum Fragment Length Negotiation (len values: 512, 1024, 2048 and 4096)"}, + {"max_send_frag", OPT_MAX_SEND_FRAG, 'p', "Maximum Size of send frames "}, + {"split_send_frag", OPT_SPLIT_SEND_FRAG, 'p', + "Size used to split data for encrypt pipelines"}, + {"max_pipelines", OPT_MAX_PIPELINES, 'p', + "Maximum number of encrypt/decrypt pipelines to be used"}, + {"read_buf", OPT_READ_BUF, 'p', + "Default read buffer size to be used for connections"}, + {"fallback_scsv", OPT_FALLBACKSCSV, '-', "Send the fallback SCSV"}, + + OPT_SECTION("Identity"), + {"cert", OPT_CERT, '<', "Client certificate file to use"}, {"certform", OPT_CERTFORM, 'F', - "Certificate format (PEM or DER) PEM default"}, - {"nameopt", OPT_NAMEOPT, 's', "Various certificate name options"}, - {"key", OPT_KEY, 's', "Private key file to use, if not in -cert file"}, + "Client certificate file format (PEM or DER) PEM default"}, + {"cert_chain", OPT_CERT_CHAIN, '<', + "Client certificate chain file (in PEM format)"}, + {"build_chain", OPT_BUILD_CHAIN, '-', "Build client certificate chain"}, + {"key", OPT_KEY, 's', "Private key file to use; default is: -cert file"}, {"keyform", OPT_KEYFORM, 'E', "Key format (PEM, DER or engine) PEM default"}, {"pass", OPT_PASS, 's', "Private key file pass phrase source"}, + {"verify", OPT_VERIFY, 'p', "Turn on peer certificate verification"}, + {"nameopt", OPT_NAMEOPT, 's', "Certificate subject/issuer name printing options"}, {"CApath", OPT_CAPATH, '/', "PEM format directory of CA's"}, {"CAfile", OPT_CAFILE, '<', "PEM format file of CA's"}, + {"CAstore", OPT_CASTORE, ':', "URI to store of CA's"}, {"no-CAfile", OPT_NOCAFILE, '-', "Do not load the default certificates file"}, {"no-CApath", OPT_NOCAPATH, '-', "Do not load certificates from the default certificates directory"}, + {"no-CAstore", OPT_NOCASTORE, '-', + "Do not load certificates from the default certificates store"}, {"requestCAfile", OPT_REQCAFILE, '<', "PEM format file of CA names to send to the server"}, {"dane_tlsa_domain", OPT_DANE_TLSA_DOMAIN, 's', "DANE TLSA base domain"}, @@ -634,15 +682,19 @@ const OPTIONS s_client_options[] = { "DANE TLSA rrdata presentation form"}, {"dane_ee_no_namechecks", OPT_DANE_EE_NO_NAME, '-', "Disable name checks when matching DANE-EE(3) TLSA records"}, + {"psk_identity", OPT_PSK_IDENTITY, 's', "PSK identity"}, + {"psk", OPT_PSK, 's', "PSK in hex (without 0x)"}, + {"psk_session", OPT_PSK_SESS, '<', "File to read PSK SSL session from"}, + {"name", OPT_PROTOHOST, 's', + "Hostname to use for \"-starttls lmtp\", \"-starttls smtp\" or \"-starttls xmpp[-server]\""}, + + OPT_SECTION("Session"), {"reconnect", OPT_RECONNECT, '-', "Drop and re-make the connection with the same Session-ID"}, - {"showcerts", OPT_SHOWCERTS, '-', "Show all certificates in the chain"}, - {"debug", OPT_DEBUG, '-', "Extra output"}, - {"msg", OPT_MSG, '-', "Show protocol messages"}, - {"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"}, + {"sess_out", OPT_SESS_OUT, '>', "File to write SSL session to"}, + {"sess_in", OPT_SESS_IN, '<', "File to read SSL session from"}, + + OPT_SECTION("Input/Output"), {"crlf", OPT_CRLF, '-', "Convert LF from terminal into CRLF"}, {"quiet", OPT_QUIET, '-', "No s_client output"}, {"ign_eof", OPT_IGN_EOF, '-', "Ignore input eof (default when -quiet)"}, @@ -650,45 +702,36 @@ 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]\""}, - {"rand", OPT_RAND, 's', - "Load the file(s) into the random number generator"}, - {"sess_out", OPT_SESS_OUT, '>', "File to write SSL session to"}, - {"sess_in", OPT_SESS_IN, '<', "File to read SSL session from"}, - {"use_srtp", OPT_USE_SRTP, 's', - "Offer SRTP key management with a colon-separated profile list"}, - {"keymatexport", OPT_KEYMATEXPORT, 's', - "Export keying material using label"}, - {"keymatexportlen", OPT_KEYMATEXPORTLEN, 'p', - "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 lmtp\" or \"-starttls smtp\""}, - {"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"}, + "Alias of -name option for \"-starttls xmpp[-server]\""}, {"brief", OPT_BRIEF, '-', "Restrict output to brief summary of connection parameters"}, {"prexit", OPT_PREXIT, '-', "Print session information when the program exits"}, + + OPT_SECTION("Debug"), + {"showcerts", OPT_SHOWCERTS, '-', + "Show all certificates sent by the server"}, + {"debug", OPT_DEBUG, '-', "Extra output"}, + {"msg", OPT_MSG, '-', "Show protocol messages"}, + {"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"}, + {"keymatexport", OPT_KEYMATEXPORT, 's', + "Export keying material using label"}, + {"keymatexportlen", OPT_KEYMATEXPORTLEN, 'p', + "Export len bytes of keying material (default 20)"}, {"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)"}, +#ifndef OPENSSL_NO_SSL_TRACE + {"trace", OPT_TRACE, '-', "Show trace output of protocol messages"}, +#endif +#ifdef WATT32 + {"wdebug", OPT_WDEBUG, '-', "WATT-32 tcp debugging"}, +#endif + {"keylogfile", OPT_KEYLOG_FILE, '>', "Write TLS secrets to file"}, {"nocommands", OPT_NOCMDS, '-', "Do not use interactive command letters"}, {"servername", OPT_SERVERNAME, 's', "Set TLS extension servername (SNI) in ClientHello (default)"}, @@ -704,17 +747,9 @@ const 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', "Use specified configuration file"}, - {"max_send_frag", OPT_MAX_SEND_FRAG, 'p', "Maximum Size of send frames "}, - {"split_send_frag", OPT_SPLIT_SEND_FRAG, 'p', - "Size used to split data for encrypt pipelines"}, - {"max_pipelines", OPT_MAX_PIPELINES, 'p', - "Maximum number of encrypt/decrypt pipelines to be used"}, - {"read_buf", OPT_READ_BUF, 'p', - "Default read buffer size to be used for connections"}, - OPT_S_OPTIONS, - OPT_V_OPTIONS, - OPT_X_OPTIONS, + {"nbio", OPT_NBIO, '-', "Use non-blocking IO"}, + + OPT_SECTION("Protocol and version"), #ifndef OPENSSL_NO_SSL3 {"ssl3", OPT_SSL3, '-', "Just use SSLv3"}, #endif @@ -744,19 +779,18 @@ const OPTIONS s_client_options[] = { #endif #ifndef OPENSSL_NO_SCTP {"sctp", OPT_SCTP, '-', "Use SCTP"}, + {"sctp_label_bug", OPT_SCTP_LABEL_BUG, '-', "Enable SCTP label length bug"}, #endif -#ifndef OPENSSL_NO_SSL_TRACE - {"trace", OPT_TRACE, '-', "Show trace output of protocol messages"}, -#endif -#ifdef WATT32 - {"wdebug", OPT_WDEBUG, '-', "WATT-32 tcp debugging"}, +#ifndef OPENSSL_NO_NEXTPROTONEG + {"nextprotoneg", OPT_NEXTPROTONEG, 's', + "Enable NPN extension, considering named protocols supported (comma-separated list)"}, #endif - {"nbio", OPT_NBIO, '-', "Use non-blocking IO"}, -#ifndef OPENSSL_NO_PSK - {"psk_identity", OPT_PSK_IDENTITY, 's', "PSK identity"}, - {"psk", OPT_PSK, 's', "PSK in hex (without 0x)"}, + {"early_data", OPT_EARLY_DATA, '<', "File to send as early data"}, + {"enable_pha", OPT_ENABLE_PHA, '-', "Enable post-handshake-authentication"}, +#ifndef OPENSSL_NO_SRTP + {"use_srtp", OPT_USE_SRTP, 's', + "Offer SRTP key management with a colon-separated profile list"}, #endif - {"psk_session", OPT_PSK_SESS, '<', "File to read PSK SSL session from"}, #ifndef OPENSSL_NO_SRP {"srpuser", OPT_SRPUSER, 's', "SRP authentication for 'user'"}, {"srppass", OPT_SRPPASS, 's', "Password for 'user'"}, @@ -766,23 +800,34 @@ const OPTIONS s_client_options[] = { "Tolerate other than the known g N values."}, {"srp_strength", OPT_SRP_STRENGTH, 'p', "Minimal length in bits for N"}, #endif -#ifndef OPENSSL_NO_NEXTPROTONEG - {"nextprotoneg", OPT_NEXTPROTONEG, 's', - "Enable NPN extension, considering named protocols supported (comma-separated list)"}, -#endif -#ifndef OPENSSL_NO_ENGINE - {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, - {"ssl_client_engine", OPT_SSL_CLIENT_ENGINE, 's', - "Specify engine to be used for client certificate operations"}, -#endif -#ifndef OPENSSL_NO_CT - {"ct", OPT_CT, '-', "Request and parse SCTs (also enables OCSP stapling)"}, - {"noct", OPT_NOCT, '-', "Do not request or parse SCTs (default)"}, - {"ctlogfile", OPT_CTLOG_FILE, '<', "CT log list CONF file"}, -#endif - {"keylogfile", OPT_KEYLOG_FILE, '>', "Write TLS secrets to file"}, - {"early_data", OPT_EARLY_DATA, '<', "File to send as early data"}, - {NULL, OPT_EOF, 0x00, NULL} + + OPT_R_OPTIONS, + OPT_S_OPTIONS, + OPT_V_OPTIONS, + {"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"}, + {"chainCAfile", OPT_CHAINCAFILE, '<', + "CA file for certificate chain (PEM format)"}, + {"chainCApath", OPT_CHAINCAPATH, '/', + "Use dir as certificate store path to build CA certificate chain"}, + {"chainCAstore", OPT_CHAINCASTORE, ':', + "CA store URI for certificate chain"}, + {"verifyCAfile", OPT_VERIFYCAFILE, '<', + "CA file for certificate verification (PEM format)"}, + {"verifyCApath", OPT_VERIFYCAPATH, '/', + "Use dir as certificate store path to verify CA certificate"}, + {"verifyCAstore", OPT_VERIFYCASTORE, ':', + "CA store URI for certificate verification"}, + OPT_X_OPTIONS, + OPT_PROV_OPTIONS, + + OPT_PARAMETERS(), + {"host:port", 0, 0, "Where to connect; same as -connect option"}, + {NULL} }; typedef enum PROTOCOL_choice { @@ -839,15 +884,29 @@ static void freeandcopy(char **dest, const char *source) *dest = OPENSSL_strdup(source); } -static int new_session_cb(SSL *S, SSL_SESSION *sess) +static int new_session_cb(SSL *s, SSL_SESSION *sess) { - BIO *stmp = BIO_new_file(sess_out, "w"); - if (stmp == NULL) { - BIO_printf(bio_err, "Error writing session file %s\n", sess_out); - } else { - PEM_write_bio_SSL_SESSION(stmp, sess); - BIO_free(stmp); + if (sess_out != NULL) { + BIO *stmp = BIO_new_file(sess_out, "w"); + + if (stmp == NULL) { + BIO_printf(bio_err, "Error writing session file %s\n", sess_out); + } else { + PEM_write_bio_SSL_SESSION(stmp, sess); + BIO_free(stmp); + } + } + + /* + * Session data gets dumped on connection for TLSv1.2 and below, and on + * arrival of the NewSessionTicket for TLSv1.3. + */ + if (SSL_version(s) == TLS1_3_VERSION) { + BIO_printf(bio_c_out, + "---\nPost-Handshake New Session Ticket arrived:\n"); + SSL_SESSION_print(bio_c_out, sess); + BIO_printf(bio_c_out, "---\n"); } /* @@ -874,27 +933,29 @@ int s_client_main(int argc, char **argv) int dane_ee_no_name = 0; STACK_OF(X509_CRL) *crls = NULL; 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; + const char *CApath = NULL, *CAfile = NULL, *CAstore = NULL; + char *cbuf = NULL, *sbuf = NULL, *mbuf = NULL; + char *proxystr = NULL, *proxyuser = NULL; + char *proxypassarg = NULL, *proxypass = NULL; + char *connectstr = NULL, *bindstr = NULL; char *cert_file = NULL, *key_file = NULL, *chain_file = NULL; - char *chCApath = NULL, *chCAfile = NULL, *host = NULL; + char *chCApath = NULL, *chCAfile = NULL, *chCAstore = NULL, *host = NULL; char *port = OPENSSL_strdup(PORT); - char *inrand = NULL; - char *passarg = NULL, *pass = NULL, *vfyCApath = NULL, *vfyCAfile = NULL; + char *bindhost = NULL, *bindport = NULL; + char *passarg = NULL, *pass = NULL; + char *vfyCApath = NULL, *vfyCAfile = NULL, *vfyCAstore = 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; + int noCApath = 0, noCAfile = 0, noCAstore = 0; int build_chain = 0, cbuf_len, cbuf_off, cert_format = FORMAT_PEM; int key_format = FORMAT_PEM, crlf = 0, full_log = 1, mbuf_len = 0; int prexit = 0; int sdebug = 0; int reconnect = 0, verify = SSL_VERIFY_NONE, vpmtouched = 0; - int ret = 1, in_init = 1, i, nbio_test = 0, s = -1, k, width, state = 0; + int ret = 1, in_init = 1, i, nbio_test = 0, sock = -1, k, width, state = 0; int sbuf_len, sbuf_off, cmdletters = 1; int socket_family = AF_UNSPEC, socket_type = SOCK_STREAM, protocol = 0; int starttls_proto = PROTO_OFF, crl_format = FORMAT_PEM, crl_download = 0; @@ -904,7 +965,6 @@ int s_client_main(int argc, char **argv) #endif int read_buf_len = 0; int fallback_scsv = 0; - long randamt = 0; OPTION_CHOICE o; #ifndef OPENSSL_NO_DTLS int enable_timeouts = 0; @@ -917,7 +977,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 +993,9 @@ int s_client_main(int argc, char **argv) int srp_lateuser = 0; SRP_ARG srp_arg = { NULL, NULL, 0, 0, 0, 1024 }; #endif +#ifndef OPENSSL_NO_SRTP + char *srtp_profiles = NULL; +#endif #ifndef OPENSSL_NO_CT char *ctlog_file = NULL; int ct_validation = 0; @@ -943,6 +1006,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 +1018,10 @@ int s_client_main(int argc, char **argv) int isdtls = 0; #endif char *psksessf = NULL; + int enable_pha = 0; +#ifndef OPENSSL_NO_SCTP + int sctp_label_bug = 0; +#endif FD_ZERO(&readfds); FD_ZERO(&writefds); @@ -1046,10 +1114,19 @@ 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; break; + case OPT_PROXY_USER: + proxyuser = opt_arg(); + break; + case OPT_PROXY_PASS: + proxypassarg = opt_arg(); + break; #ifdef AF_UNIX case OPT_UNIX: connect_type = use_unix; @@ -1058,10 +1135,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; @@ -1097,6 +1173,7 @@ int s_client_main(int argc, char **argv) goto opthelp; break; case OPT_VERIFY_RET_ERROR: + verify = SSL_VERIFY_PEER; verify_args.return_error = 1; break; case OPT_VERIFY_QUIET: @@ -1151,8 +1228,13 @@ int s_client_main(int argc, char **argv) } #endif break; - case OPT_RAND: - inrand = opt_arg(); + case OPT_R_CASES: + if (!opt_rand(o)) + goto end; + break; + case OPT_PROV_CASES: + if (!opt_provider(o)) + goto end; break; case OPT_IGN_EOF: c_ign_eof = 1; @@ -1202,7 +1284,6 @@ int s_client_main(int argc, char **argv) case OPT_STATE: state = 1; break; -#ifndef OPENSSL_NO_PSK case OPT_PSK_IDENTITY: psk_identity = opt_arg(); break; @@ -1214,7 +1295,6 @@ int s_client_main(int argc, char **argv) goto end; } break; -#endif case OPT_PSK_SESS: psksessf = opt_arg(); break; @@ -1298,6 +1378,11 @@ int s_client_main(int argc, char **argv) case OPT_SCTP: #ifndef OPENSSL_NO_SCTP protocol = IPPROTO_SCTP; +#endif + break; + case OPT_SCTP_LABEL_BUG: +#ifndef OPENSSL_NO_SCTP + sctp_label_bug = 1; #endif break; case OPT_TIMEOUT: @@ -1370,6 +1455,18 @@ int s_client_main(int argc, char **argv) case OPT_VERIFYCAFILE: vfyCAfile = opt_arg(); break; + case OPT_CASTORE: + CAstore = opt_arg(); + break; + case OPT_NOCASTORE: + noCAstore = 1; + break; + case OPT_CHAINCASTORE: + chCAstore = opt_arg(); + break; + case OPT_VERIFYCASTORE: + vfyCAstore = opt_arg(); + break; case OPT_DANE_TLSA_DOMAIN: dane_tlsa_domain = opt_arg(); break; @@ -1416,7 +1513,9 @@ int s_client_main(int argc, char **argv) noservername = 1; break; case OPT_USE_SRTP: +#ifndef OPENSSL_NO_SRTP srtp_profiles = opt_arg(); +#endif break; case OPT_KEYMATEXPORT: keymatexportlabel = opt_arg(); @@ -1427,6 +1526,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; @@ -1445,8 +1566,12 @@ int s_client_main(int argc, char **argv) case OPT_EARLY_DATA: early_data_file = opt_arg(); break; + case OPT_ENABLE_PHA: + enable_pha = 1; + break; } } + if (count4or6 >= 2) { BIO_printf(bio_err, "%s: Can't use both -4 and -6\n", prog); goto opthelp; @@ -1478,7 +1603,7 @@ int s_client_main(int argc, char **argv) goto opthelp; } connect_type = use_inet; - connectstr = *opt_rest(); + freeandcopy(&connectstr, *opt_rest()); } else if (argc != 0) { goto opthelp; } @@ -1524,11 +1649,25 @@ 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, "Can't use unix sockets and datagrams together\n"); goto end; } +#endif #ifndef OPENSSL_NO_SCTP if (protocol == IPPROTO_SCTP) { @@ -1555,7 +1694,17 @@ int s_client_main(int argc, char **argv) #endif if (!app_passwd(passarg, NULL, &pass, NULL)) { - BIO_printf(bio_err, "Error getting password\n"); + BIO_printf(bio_err, "Error getting private key password\n"); + goto end; + } + + if (!app_passwd(proxypassarg, NULL, &proxypass, NULL)) { + BIO_printf(bio_err, "Error getting proxy password\n"); + goto end; + } + + if (proxypass != NULL && proxyuser == NULL) { + BIO_printf(bio_err, "Error: Must specify proxy_user with proxy_pass\n"); goto end; } @@ -1565,18 +1714,14 @@ int s_client_main(int argc, char **argv) if (key_file != NULL) { key = load_key(key_file, key_format, 0, pass, e, "client certificate private key file"); - if (key == NULL) { - ERR_print_errors(bio_err); + if (key == NULL) goto end; - } } if (cert_file != NULL) { cert = load_cert(cert_file, cert_format, "client certificate file"); - if (cert == NULL) { - ERR_print_errors(bio_err); + if (cert == NULL) goto end; - } } if (chain_file != NULL) { @@ -1587,12 +1732,9 @@ int s_client_main(int argc, char **argv) if (crl_file != NULL) { X509_CRL *crl; - crl = load_crl(crl_file, crl_format); - if (crl == NULL) { - BIO_puts(bio_err, "Error loading CRL\n"); - ERR_print_errors(bio_err); + crl = load_crl(crl_file, crl_format, "CRL"); + if (crl == NULL) goto end; - } crls = sk_X509_CRL_new_null(); if (crls == NULL || !sk_X509_CRL_push(crls, crl)) { BIO_puts(bio_err, "Error adding CRL\n"); @@ -1605,16 +1747,6 @@ int s_client_main(int argc, char **argv) if (!load_excert(&exc)) goto end; - if (!app_RAND_load_file(NULL, 1) && inrand == NULL - && !RAND_status()) { - BIO_printf(bio_err, - "warning, not much extra random data, consider using the -rand option\n"); - } - if (inrand != NULL) { - randamt = app_RAND_load_files(inrand); - BIO_printf(bio_err, "%ld semi-random bytes loaded\n", randamt); - } - if (bio_c_out == NULL) { if (c_quiet && !c_debug) { bio_c_out = BIO_new(BIO_s_null()); @@ -1636,9 +1768,14 @@ int s_client_main(int argc, char **argv) goto end; } + SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY); + 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", @@ -1648,9 +1785,16 @@ int s_client_main(int argc, char **argv) } } - if (SSL_CTX_set_min_proto_version(ctx, min_version) == 0) +#ifndef OPENSSL_NO_SCTP + if (protocol == IPPROTO_SCTP && sctp_label_bug == 1) + SSL_CTX_set_mode(ctx, SSL_MODE_DTLS_SCTP_LABEL_LENGTH_BUG); +#endif + + 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)) { @@ -1688,10 +1832,17 @@ 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, + if (!ssl_load_stores(ctx, + vfyCApath, vfyCAfile, vfyCAstore, + chCApath, chCAfile, chCAstore, crls, crl_download)) { BIO_printf(bio_err, "Error loading store locations\n"); ERR_print_errors(bio_err); @@ -1820,7 +1971,8 @@ int s_client_main(int argc, char **argv) SSL_CTX_set_verify(ctx, verify, verify_callback); - if (!ctx_set_verify_locations(ctx, CAfile, CApath, noCAfile, noCApath)) { + if (!ctx_set_verify_locations(ctx, CAfile, noCAfile, CApath, noCApath, + CAstore, noCAstore)) { ERR_print_errors(bio_err); goto end; } @@ -1867,16 +2019,20 @@ int s_client_main(int argc, char **argv) * come at any time. Therefore we use a callback to write out the session * when we know about it. This approach works for < TLSv1.3 as well. */ - if (sess_out != NULL) { - SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT - | SSL_SESS_CACHE_NO_INTERNAL_STORE); - SSL_CTX_sess_set_new_cb(ctx, new_session_cb); - } + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT + | SSL_SESS_CACHE_NO_INTERNAL_STORE); + SSL_CTX_sess_set_new_cb(ctx, new_session_cb); if (set_keylog_file(ctx, keylog_file)) goto end; con = SSL_new(ctx); + if (con == NULL) + goto end; + + if (enable_pha) + SSL_set_post_handshake_auth(con, 1); + if (sess_in != NULL) { SSL_SESSION *sess; BIO *stmp = BIO_new_file(sess_in, "r"); @@ -1897,6 +2053,7 @@ int s_client_main(int argc, char **argv) ERR_print_errors(bio_err); goto end; } + SSL_SESSION_free(sess); } @@ -1904,9 +2061,11 @@ int s_client_main(int argc, char **argv) SSL_set_mode(con, SSL_MODE_SEND_FALLBACK_SCSV); if (!noservername && (servername != NULL || dane_tlsa_domain == NULL)) { - if (servername == NULL) - servername = (host == NULL) ? "localhost" : host; - if (!SSL_set_tlsext_host_name(con, servername)) { + if (servername == NULL) { + if(host == NULL || is_dNS_name(host)) + servername = (host == NULL) ? "localhost" : host; + } + if (servername != NULL && !SSL_set_tlsext_host_name(con, servername)) { BIO_printf(bio_err, "Unable to set TLS servername extension.\n"); ERR_print_errors(bio_err); goto end; @@ -1939,16 +2098,16 @@ 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(&sock, 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); + BIO_closesocket(sock); goto end; } - BIO_printf(bio_c_out, "CONNECTED(%08X)\n", s); + BIO_printf(bio_c_out, "CONNECTED(%08X)\n", sock); if (c_nbio) { - if (!BIO_socket_nbio(s, 1)) { + if (!BIO_socket_nbio(sock, 1)) { ERR_print_errors(bio_err); goto end; } @@ -1960,21 +2119,21 @@ int s_client_main(int argc, char **argv) #ifndef OPENSSL_NO_SCTP if (protocol == IPPROTO_SCTP) - sbio = BIO_new_dgram_sctp(s, BIO_NOCLOSE); + sbio = BIO_new_dgram_sctp(sock, BIO_NOCLOSE); else #endif - sbio = BIO_new_dgram(s, BIO_NOCLOSE); + sbio = BIO_new_dgram(sock, BIO_NOCLOSE); if ((peer_info.addr = BIO_ADDR_new()) == NULL) { BIO_printf(bio_err, "memory allocation failure\n"); - BIO_closesocket(s); + BIO_closesocket(sock); goto end; } - if (!BIO_sock_info(s, BIO_SOCK_INFO_ADDRESS, &peer_info)) { + if (!BIO_sock_info(sock, BIO_SOCK_INFO_ADDRESS, &peer_info)) { BIO_printf(bio_err, "getsockname:errno=%d\n", get_last_socket_error()); BIO_ADDR_free(peer_info.addr); - BIO_closesocket(s); + BIO_closesocket(sock); goto end; } @@ -2011,7 +2170,7 @@ int s_client_main(int argc, char **argv) } } else #endif /* OPENSSL_NO_DTLS */ - sbio = BIO_new_socket(s, BIO_NOCLOSE); + sbio = BIO_new_socket(sock, BIO_NOCLOSE); if (nbio_test) { BIO *test; @@ -2088,10 +2247,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 @@ -2161,7 +2322,7 @@ int s_client_main(int argc, char **argv) do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); } - while (mbuf_len > 3 && mbuf[3] == '-'); + while (mbuf_len > 3 && (!isdigit(mbuf[0]) || !isdigit(mbuf[1]) || !isdigit(mbuf[2]) || mbuf[3] != ' ')); (void)BIO_flush(fbio); BIO_pop(fbio); BIO_free(fbio); @@ -2177,7 +2338,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"); @@ -2240,57 +2401,9 @@ int s_client_main(int argc, char **argv) } break; case PROTO_CONNECT: - { - enum { - error_proto, /* Wrong protocol, not even HTTP */ - error_connect, /* CONNECT failed */ - success - } foundit = error_connect; - BIO *fbio = BIO_new(BIO_f_buffer()); - - BIO_push(fbio, sbio); - BIO_printf(fbio, "CONNECT %s HTTP/1.0\r\n\r\n", connectstr); - (void)BIO_flush(fbio); - /* - * The first line is the HTTP response. According to RFC 7230, - * it's formated exactly like this: - * - * HTTP/d.d ddd Reason text\r\n - */ - mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); - if (mbuf_len < (int)strlen("HTTP/1.0 200")) { - BIO_printf(bio_err, - "%s: HTTP CONNECT failed, insufficient response " - "from proxy (got %d octets)\n", prog, mbuf_len); - (void)BIO_flush(fbio); - BIO_pop(fbio); - BIO_free(fbio); - goto shut; - } - if (mbuf[8] != ' ') { - BIO_printf(bio_err, - "%s: HTTP CONNECT failed, incorrect response " - "from proxy\n", prog); - foundit = error_proto; - } else if (mbuf[9] != '2') { - BIO_printf(bio_err, "%s: HTTP CONNECT failed: %s ", prog, - &mbuf[9]); - } else { - foundit = success; - } - if (foundit != error_proto) { - /* Read past all following headers */ - do { - mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); - } while (mbuf_len > 2); - } - (void)BIO_flush(fbio); - BIO_pop(fbio); - BIO_free(fbio); - if (foundit != success) { - goto shut; - } - } + if (!OSSL_HTTP_proxy_connect(sbio, host, port, proxyuser, proxypass, + 0 /* no timeout */, bio_err, prog)) + goto shut; break; case PROTO_IRC: { @@ -2407,10 +2520,9 @@ int s_client_main(int argc, char **argv) } else if (packet[pos++] == '\0') { break; } - pos++; } - /* make sure we have more 15 bytes left in the packet */ + /* make sure we have at least 15 bytes left in the packet */ if (pos + 15 > bytes) { BIO_printf(bio_err, "MySQL server handshake packet is broken.\n"); @@ -2463,12 +2575,16 @@ int s_client_main(int argc, char **argv) /* STARTTLS command requires CAPABILITIES... */ BIO_printf(fbio, "CAPABILITIES\r\n"); (void)BIO_flush(fbio); - /* wait for multi-line CAPABILITIES response */ - do { - mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); - if (strstr(mbuf, "STARTTLS")) - foundit = 1; - } while (mbuf_len > 1 && mbuf[0] != '.'); + BIO_gets(fbio, mbuf, BUFSIZZ); + /* no point in trying to parse the CAPABILITIES response if there is none */ + if (strstr(mbuf, "101") != NULL) { + /* wait for multi-line CAPABILITIES response */ + do { + mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); + if (strstr(mbuf, "STARTTLS")) + foundit = 1; + } while (mbuf_len > 1 && mbuf[0] != '.'); + } (void)BIO_flush(fbio); BIO_pop(fbio); BIO_free(fbio); @@ -2610,8 +2726,10 @@ int s_client_main(int argc, char **argv) } if (early_data_file != NULL - && SSL_get0_session(con) != NULL - && SSL_SESSION_get_max_early_data(SSL_get0_session(con)) > 0) { + && ((SSL_get0_session(con) != NULL + && SSL_SESSION_get_max_early_data(SSL_get0_session(con)) > 0) + || (psksess != NULL + && SSL_SESSION_get_max_early_data(psksess) > 0))) { BIO *edfile = BIO_new_file(early_data_file, "r"); size_t readbytes, writtenbytes; int finish = 0; @@ -2635,6 +2753,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; } } @@ -2647,8 +2766,7 @@ int s_client_main(int argc, char **argv) FD_ZERO(&readfds); FD_ZERO(&writefds); - if ((SSL_version(con) == DTLS1_VERSION) && - DTLSv1_get_timeout(con, &timeout)) + if (SSL_is_dtls(con) && DTLSv1_get_timeout(con, &timeout)) timeoutp = &timeout; else timeoutp = NULL; @@ -2759,10 +2877,8 @@ int s_client_main(int argc, char **argv) } } - if ((SSL_version(con) == DTLS1_VERSION) - && DTLSv1_handle_timeout(con) > 0) { + if (SSL_is_dtls(con) && DTLSv1_handle_timeout(con) > 0) BIO_printf(bio_err, "TIMEOUT occurred\n"); - } if (!ssl_pending && FD_ISSET(SSL_get_fd(con), &writefds)) { k = SSL_write(con, &(cbuf[cbuf_off]), (unsigned int)cbuf_len); @@ -2959,24 +3075,14 @@ int s_client_main(int argc, char **argv) BIO_printf(bio_err, "RENEGOTIATING\n"); SSL_renegotiate(con); cbuf_len = 0; - } - - if (!c_ign_eof && (cbuf[0] == 'K' || cbuf[0] == 'k' ) + } else if (!c_ign_eof && (cbuf[0] == 'K' || cbuf[0] == 'k' ) && cmdletters) { BIO_printf(bio_err, "KEYUPDATE\n"); SSL_key_update(con, cbuf[0] == 'K' ? SSL_KEY_UPDATE_REQUESTED : SSL_KEY_UPDATE_NOT_REQUESTED); cbuf_len = 0; - } -#ifndef OPENSSL_NO_HEARTBEATS - else if ((!c_ign_eof) && (cbuf[0] == 'B' && cmdletters)) { - BIO_printf(bio_err, "HEARTBEATING\n"); - SSL_heartbeat(con); - cbuf_len = 0; - } -#endif - else { + } else { cbuf_len = i; cbuf_off = 0; #ifdef CHARSET_EBCDIC @@ -2994,16 +3100,31 @@ 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 - * Windows to send the data before closing the socket...but sleeping - * for a short time seems to do it (units in ms) - * TODO: Find a better way to do this + * 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. */ - Sleep(50); -#endif + shutdown(SSL_get_fd(con), 1); /* SHUT_WR */ + /* + * We just said we have nothing else to say, but it doesn't mean that + * the other side has nothing. It's even recommended to consume incoming + * data. [In testing context this ensures that alerts are passed on...] + */ + timeout.tv_sec = 0; + timeout.tv_usec = 500000; /* some extreme round-trip */ + do { + FD_ZERO(&readfds); + openssl_fdset(sock, &readfds); + } while (select(sock + 1, &readfds, NULL, NULL, &timeout) > 0 + && BIO_read(sbio, sbuf, BUFSIZZ) > 0); + BIO_closesocket(SSL_get_fd(con)); end: if (con != NULL) { @@ -3011,6 +3132,7 @@ int s_client_main(int argc, char **argv) print_stuff(bio_c_out, con, 1); SSL_free(con); } + SSL_SESSION_free(psksess); #if !defined(OPENSSL_NO_NEXTPROTONEG) OPENSSL_free(next_proto.data); #endif @@ -3025,6 +3147,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); @@ -3035,12 +3158,14 @@ int s_client_main(int argc, char **argv) OPENSSL_clear_free(cbuf, BUFSIZZ); OPENSSL_clear_free(sbuf, BUFSIZZ); OPENSSL_clear_free(mbuf, BUFSIZZ); + if (proxypass != NULL) + OPENSSL_clear_free(proxypass, strlen(proxypass)); release_engine(e); BIO_free(bio_c_out); 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) @@ -3048,7 +3173,8 @@ static void print_stuff(BIO *bio, SSL *s, int full) X509 *peer = NULL; STACK_OF(X509) *sk; const SSL_CIPHER *c; - int i; + int i, istls13 = (SSL_version(s) == TLS1_3_VERSION); + long verify_result; #ifndef OPENSSL_NO_COMP const COMP_METHOD *comp, *expansion; #endif @@ -3155,9 +3281,14 @@ static void print_stuff(BIO *bio, SSL *s, int full) BIO_printf(bio, "Expansion: %s\n", expansion ? SSL_COMP_get_name(expansion) : "NONE"); #endif +#ifndef OPENSSL_NO_KTLS + if (BIO_get_ktls_send(SSL_get_wbio(s))) + BIO_printf(bio_err, "Using Kernel TLS for sending\n"); + if (BIO_get_ktls_recv(SSL_get_rbio(s))) + BIO_printf(bio_err, "Using Kernel TLS for receiving\n"); +#endif -#ifdef SSL_DEBUG - { + if (OSSL_TRACE_ENABLED(TLS)) { /* Print out local port of connection: useful for debugging */ int sock; union BIO_sock_info_u info; @@ -3170,7 +3301,6 @@ static void print_stuff(BIO *bio, SSL *s, int full) } BIO_ADDR_free(info.addr); } -#endif #if !defined(OPENSSL_NO_NEXTPROTONEG) if (next_proto.status != -1) { @@ -3205,7 +3335,7 @@ static void print_stuff(BIO *bio, SSL *s, int full) } #endif - if (SSL_version(s) == TLS1_3_VERSION) { + if (istls13) { switch (SSL_get_early_data_status(s)) { case SSL_EARLY_DATA_NOT_SENT: BIO_printf(bio, "Early data was not sent\n"); @@ -3220,9 +3350,20 @@ static void print_stuff(BIO *bio, SSL *s, int full) break; } + + /* + * We also print the verify results when we dump session information, + * but in TLSv1.3 we may not get that right away (or at all) depending + * on when we get a NewSessionTicket. Therefore we print it now as well. + */ + verify_result = SSL_get_verify_result(s); + BIO_printf(bio, "Verify return code: %ld (%s)\n", verify_result, + X509_verify_cert_error_string(verify_result)); + } else { + /* In TLSv1.3 we do this on arrival of a NewSessionTicket */ + SSL_SESSION_print(bio, SSL_get_session(s)); } - SSL_SESSION_print(bio, SSL_get_session(s)); if (SSL_get_session(s) != NULL && keymatexportlabel != NULL) { BIO_printf(bio, "Keying material exporter:\n"); BIO_printf(bio, " Label: '%s'\n", keymatexportlabel); @@ -3358,4 +3499,69 @@ static int ldap_ExtendedResponse_parse(const char *buf, long rem) return ret; } +/* + * Host dNS Name verifier: used for checking that the hostname is in dNS format + * before setting it as SNI + */ +static int is_dNS_name(const char *host) +{ + const size_t MAX_LABEL_LENGTH = 63; + size_t i; + int isdnsname = 0; + size_t length = strlen(host); + size_t label_length = 0; + int all_numeric = 1; + + /* + * Deviation from strict DNS name syntax, also check names with '_' + * Check DNS name syntax, any '-' or '.' must be internal, + * and on either side of each '.' we can't have a '-' or '.'. + * + * If the name has just one label, we don't consider it a DNS name. + */ + for (i = 0; i < length && label_length < MAX_LABEL_LENGTH; ++i) { + char c = host[i]; + + if ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || c == '_') { + label_length += 1; + all_numeric = 0; + continue; + } + + if (c >= '0' && c <= '9') { + label_length += 1; + continue; + } + + /* Dot and hyphen cannot be first or last. */ + if (i > 0 && i < length - 1) { + if (c == '-') { + label_length += 1; + continue; + } + /* + * Next to a dot the preceding and following characters must not be + * another dot or a hyphen. Otherwise, record that the name is + * plausible, since it has two or more labels. + */ + if (c == '.' + && host[i + 1] != '.' + && host[i - 1] != '-' + && host[i + 1] != '-') { + label_length = 0; + isdnsname = 1; + continue; + } + } + isdnsname = 0; + break; + } + + /* dNS name must not be all numeric and labels must be shorter than 64 characters. */ + isdnsname &= !all_numeric && !(label_length == MAX_LABEL_LENGTH); + + return isdnsname; +} #endif /* OPENSSL_NO_SOCK */