X-Git-Url: https://git.openssl.org/?p=openssl.git;a=blobdiff_plain;f=apps%2Fs_client.c;h=8e1a5dda0c3e7830005c46df92fc272cffb95ffb;hp=e79cf7e496e3161124f441699c6bf24091780a0c;hb=d2add501f070290a8de8c356d1c3c1a155cf1225;hpb=748e85308ef4f3e672975b3604ea2d76424fa404 diff --git a/apps/s_client.c b/apps/s_client.c index e79cf7e496..8e1a5dda0c 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -83,30 +83,20 @@ typedef unsigned int u_int; #define BUFSIZZ 1024*8 #define S_CLIENT_IRC_READ_TIMEOUT 8 -extern int verify_depth; -extern int verify_error; -extern int verify_return_error; -extern int verify_quiet; - static char *prog; -static int c_nbio = 0; -static int c_tlsextdebug = 0; -static int c_status_req = 0; static int c_debug = 0; -static int c_msg = 0; static int c_showcerts = 0; static char *keymatexportlabel = NULL; static int keymatexportlen = 20; static BIO *bio_c_out = NULL; -static BIO *bio_c_msg = NULL; static int c_quiet = 0; -static int c_ign_eof = 0; -static int c_brief = 0; +static char *sess_out = NULL; static void print_stuff(BIO *berr, SSL *con, int full); #ifndef OPENSSL_NO_OCSP static int ocsp_resp_cb(SSL *s, void *arg); #endif +static int ldap_ExtendedResponse_parse(const char *buf, long rem); static int saved_errno; @@ -144,13 +134,9 @@ 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"; -/* - * char *psk_key=NULL; by default PSK is not used - */ static unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, @@ -188,7 +174,7 @@ static unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity, psk_key); return 0; } - if (key_len > max_psk_len) { + if (max_psk_len > INT_MAX || key_len > (long)max_psk_len) { BIO_printf(bio_err, "psk buffer of callback is too small (%d) for key (%ld)\n", max_psk_len, key_len); @@ -404,7 +390,7 @@ static ossl_ssize_t hexdecode(const char **inptr, void *result) { unsigned char **out = (unsigned char **)result; const char *in = *inptr; - unsigned char *ret = app_malloc(strlen(in)/2, "hexdecode"); + unsigned char *ret = app_malloc(strlen(in) / 2, "hexdecode"); unsigned char *cp = ret; uint8_t byte; int nibble = 0; @@ -536,7 +522,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_XMPPHOST, OPT_VERIFY, + 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, @@ -552,14 +538,16 @@ typedef enum OPTION_choice { OPT_SRP_MOREGROUPS, #endif OPT_SSL3, OPT_SSL_CONFIG, - OPT_TLS1_2, OPT_TLS1_1, OPT_TLS1, OPT_DTLS, OPT_DTLS1, + OPT_TLS1_3, OPT_TLS1_2, OPT_TLS1_1, OPT_TLS1, OPT_DTLS, OPT_DTLS1, OPT_DTLS1_2, OPT_TIMEOUT, OPT_MTU, OPT_KEYFORM, OPT_PASS, - OPT_CERT_CHAIN, OPT_CAPATH, OPT_NOCAPATH, OPT_CHAINCAPATH, OPT_VERIFYCAPATH, + 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_USE_SRTP, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, OPT_SMTPHOST, OPT_ASYNC, 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, @@ -567,10 +555,10 @@ typedef enum OPTION_choice { #ifndef OPENSSL_NO_CT OPT_CT, OPT_NOCT, OPT_CTLOG_FILE, #endif - OPT_DANE_TLSA_RRDATA + OPT_DANE_TLSA_RRDATA, OPT_DANE_EE_NO_NAME } OPTION_CHOICE; -OPTIONS s_client_options[] = { +const OPTIONS s_client_options[] = { {"help", OPT_HELP, '-', "Display this summary"}, {"host", OPT_HOST, 's', "Use -connect instead"}, {"port", OPT_PORT, 'p', "Use -connect instead"}, @@ -579,7 +567,7 @@ OPTIONS s_client_options[] = { {"proxy", OPT_PROXY, 's', "Connect to via specified proxy to the real server"}, #ifdef AF_UNIX - {"unix", OPT_UNIX, 's', "Connect over unix domain sockets"}, + {"unix", OPT_UNIX, 's', "Connect over the specified Unix-domain socket"}, #endif {"4", OPT_4, '-', "Use IPv4 only"}, #ifdef AF_INET6 @@ -589,8 +577,9 @@ OPTIONS s_client_options[] = { {"cert", OPT_CERT, '<', "Certificate file to use, PEM format assumed"}, {"certform", OPT_CERTFORM, 'F', "Certificate format (PEM or DER) PEM default"}, - {"key", OPT_KEY, '<', "Private key file to use, if not in -cert file"}, - {"keyform", OPT_KEYFORM, 'F', "Key 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"}, + {"keyform", OPT_KEYFORM, 'E', "Key format (PEM, DER or engine) PEM default"}, {"pass", OPT_PASS, 's', "Private key file pass phrase source"}, {"CApath", OPT_CAPATH, '/', "PEM format directory of CA's"}, {"CAfile", OPT_CAFILE, '<', "PEM format file of CA's"}, @@ -598,9 +587,13 @@ OPTIONS s_client_options[] = { "Do not load the default certificates file"}, {"no-CApath", OPT_NOCAPATH, '-', "Do not load certificates from the default certificates directory"}, + {"requestCAfile", OPT_REQCAFILE, '<', + "PEM format file of CA names sent to server"}, {"dane_tlsa_domain", OPT_DANE_TLSA_DOMAIN, 's', "DANE TLSA base domain"}, {"dane_tlsa_rrdata", OPT_DANE_TLSA_RRDATA, 's', "DANE TLSA rrdata presentation form"}, + {"dane_ee_no_namechecks", OPT_DANE_EE_NO_NAME, '-', + "Disable name checks when matching DANE-EE(3) TLSA records"}, {"reconnect", OPT_RECONNECT, '-', "Drop and re-make the connection with the same Session-ID"}, {"showcerts", OPT_SHOWCERTS, '-', "Show all certificates in the chain"}, @@ -629,7 +622,8 @@ OPTIONS s_client_options[] = { {"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 smtp\""}, + {"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"}, @@ -690,6 +684,9 @@ OPTIONS s_client_options[] = { #ifndef OPENSSL_NO_TLS1_2 {"tls1_2", OPT_TLS1_2, '-', "Just use TLSv1.2"}, #endif +#ifndef OPENSSL_NO_TLS1_3 + {"tls1_3", OPT_TLS1_3, '-', "Just use TLSv1.3"}, +#endif #ifndef OPENSSL_NO_DTLS {"dtls", OPT_DTLS, '-', "Use any version of DTLS"}, {"timeout", OPT_TIMEOUT, '-', @@ -736,6 +733,8 @@ OPTIONS s_client_options[] = { {"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} }; @@ -749,7 +748,12 @@ typedef enum PROTOCOL_choice { PROTO_XMPP, PROTO_XMPP_SERVER, PROTO_CONNECT, - PROTO_IRC + PROTO_IRC, + PROTO_POSTGRES, + PROTO_LMTP, + PROTO_NNTP, + PROTO_SIEVE, + PROTO_LDAP } PROTOCOL_CHOICE; static const OPT_PAIR services[] = { @@ -761,6 +765,11 @@ static const OPT_PAIR services[] = { {"xmpp-server", PROTO_XMPP_SERVER}, {"telnet", PROTO_TELNET}, {"irc", PROTO_IRC}, + {"postgres", PROTO_POSTGRES}, + {"lmtp", PROTO_LMTP}, + {"nntp", PROTO_NNTP}, + {"sieve", PROTO_SIEVE}, + {"ldap", PROTO_LDAP}, {NULL, 0} }; @@ -768,6 +777,10 @@ static const OPT_PAIR services[] = { (o == OPT_4 || o == OPT_6 || o == OPT_HOST || o == OPT_PORT || o == OPT_CONNECT) #define IS_UNIX_FLAG(o) (o == OPT_UNIX) +#define IS_PROT_FLAG(o) \ + (o == OPT_SSL3 || o == OPT_TLS1 || o == OPT_TLS1_1 || o == OPT_TLS1_2 \ + || o == OPT_TLS1_3 || o == OPT_DTLS || o == OPT_DTLS1 || o == OPT_DTLS1_2) + /* Free |*dest| and optionally set it to a copy of |source|. */ static void freeandcopy(char **dest, const char *source) { @@ -777,6 +790,24 @@ static void freeandcopy(char **dest, const char *source) *dest = OPENSSL_strdup(source); } +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); + } + + /* + * We always return a "fail" response so that the session gets freed again + * because we haven't used the reference. + */ + return 0; +} + int s_client_main(int argc, char **argv) { BIO *sbio; @@ -791,16 +822,19 @@ int s_client_main(int argc, char **argv) STACK_OF(OPENSSL_STRING) *ssl_args = NULL; char *dane_tlsa_domain = NULL; STACK_OF(OPENSSL_STRING) *dane_tlsa_rrset = NULL; + int dane_ee_no_name = 0; STACK_OF(X509_CRL) *crls = NULL; const SSL_METHOD *meth = TLS_client_method(); - char *CApath = NULL, *CAfile = NULL, *cbuf = NULL, *sbuf = NULL; + const char *CApath = NULL, *CAfile = NULL; + char *cbuf = NULL, *sbuf = NULL; char *mbuf = NULL, *proxystr = NULL, *connectstr = NULL; char *cert_file = NULL, *key_file = NULL, *chain_file = NULL; char *chCApath = NULL, *chCAfile = NULL, *host = NULL; char *port = OPENSSL_strdup(PORT); char *inrand = NULL; char *passarg = NULL, *pass = NULL, *vfyCApath = NULL, *vfyCAfile = NULL; - char *sess_in = NULL, *sess_out = NULL, *crl_file = NULL, *p; + char *ReqCAfile = NULL; + char *sess_in = NULL, *crl_file = NULL, *p; char *xmpphost = NULL; const char *ehlo = "mail.example.com"; struct timeval timeout, *timeoutp; @@ -816,7 +850,9 @@ 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; +#if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS) int at_eof = 0; +#endif int read_buf_len = 0; int fallback_scsv = 0; long randamt = 0; @@ -851,12 +887,19 @@ int s_client_main(int argc, char **argv) char *ctlog_file = NULL; int ct_validation = 0; #endif - int min_version = 0, max_version = 0; + int min_version = 0, max_version = 0, prot_opt = 0, no_prot_opt = 0; int async = 0; unsigned int split_send_fragment = 0; unsigned int max_pipelines = 0; enum { use_inet, use_unix, use_unknown } connect_type = use_unknown; int count4or6 = 0; + int c_nbio = 0, c_msg = 0, c_ign_eof = 0, c_brief = 0; + int c_tlsextdebug = 0; +#ifndef OPENSSL_NO_OCSP + int c_status_req = 0; +#endif + BIO *bio_c_msg = NULL; + const char *keylog_file = NULL, *early_data_file = NULL; FD_ZERO(&readfds); FD_ZERO(&writefds); @@ -870,17 +913,10 @@ int s_client_main(int argc, char **argv) prog = opt_progname(argv[0]); c_quiet = 0; - c_ign_eof = 0; c_debug = 0; - c_msg = 0; c_showcerts = 0; c_nbio = 0; - verify_depth = 0; - verify_error = X509_V_OK; vpm = X509_VERIFY_PARAM_new(); - cbuf = app_malloc(BUFSIZZ, "cbuf"); - sbuf = app_malloc(BUFSIZZ, "sbuf"); - mbuf = app_malloc(BUFSIZZ, "mbuf"); cctx = SSL_CONF_CTX_new(); if (vpm == NULL || cctx == NULL) { @@ -888,6 +924,10 @@ int s_client_main(int argc, char **argv) goto end; } + cbuf = app_malloc(BUFSIZZ, "cbuf"); + sbuf = app_malloc(BUFSIZZ, "sbuf"); + mbuf = app_malloc(BUFSIZZ, "mbuf"); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT | SSL_CONF_FLAG_CMDLINE); prog = opt_init(argc, argv, s_client_options); @@ -895,16 +935,29 @@ int s_client_main(int argc, char **argv) /* Check for intermixing flags. */ if (connect_type == use_unix && IS_INET_FLAG(o)) { BIO_printf(bio_err, - "%s: Intermixed protocol flags (unix and internet domains)\n", - prog); + "%s: Intermixed protocol flags (unix and internet domains)\n", + prog); goto end; } if (connect_type == use_inet && IS_UNIX_FLAG(o)) { BIO_printf(bio_err, - "%s: Intermixed protocol flags (internet and unix domains)\n", - prog); + "%s: Intermixed protocol flags (internet and unix domains)\n", + prog); + goto end; + } + + if (IS_PROT_FLAG(o) && ++prot_opt > 1) { + BIO_printf(bio_err, "Cannot supply multiple protocol flags\n"); + goto end; + } + if (IS_NO_PROT_FLAG(o)) + no_prot_opt++; + if (prot_opt == 1 && no_prot_opt) { + BIO_printf(bio_err, + "Cannot supply both a protocol flag and '-no_'\n"); goto end; } + switch (o) { case OPT_EOF: case OPT_ERR: @@ -958,13 +1011,17 @@ int s_client_main(int argc, char **argv) break; case OPT_VERIFY: verify = SSL_VERIFY_PEER; - verify_depth = atoi(opt_arg()); + verify_args.depth = atoi(opt_arg()); if (!c_quiet) - BIO_printf(bio_err, "verify depth is %d\n", verify_depth); + BIO_printf(bio_err, "verify depth is %d\n", verify_args.depth); break; case OPT_CERT: cert_file = opt_arg(); break; + case OPT_NAMEOPT: + if (!set_nameopt(opt_arg())) + goto end; + break; case OPT_CRL: crl_file = opt_arg(); break; @@ -986,13 +1043,13 @@ int s_client_main(int argc, char **argv) goto opthelp; break; case OPT_VERIFY_RET_ERROR: - verify_return_error = 1; + verify_args.return_error = 1; break; case OPT_VERIFY_QUIET: - verify_quiet = 1; + verify_args.quiet = 1; break; case OPT_BRIEF: - c_brief = verify_quiet = c_quiet = 1; + c_brief = verify_args.quiet = c_quiet = 1; break; case OPT_S_CASES: if (ssl_args == NULL) @@ -1056,7 +1113,9 @@ int s_client_main(int argc, char **argv) c_tlsextdebug = 1; break; case OPT_STATUS: +#ifndef OPENSSL_NO_OCSP c_status_req = 1; +#endif break; case OPT_WDEBUG: #ifdef WATT32 @@ -1138,6 +1197,10 @@ int s_client_main(int argc, char **argv) min_version = SSL3_VERSION; max_version = SSL3_VERSION; break; + case OPT_TLS1_3: + min_version = TLS1_3_VERSION; + max_version = TLS1_3_VERSION; + break; case OPT_TLS1_2: min_version = TLS1_2_VERSION; max_version = TLS1_2_VERSION; @@ -1186,7 +1249,7 @@ int s_client_main(int argc, char **argv) fallback_scsv = 1; break; case OPT_KEYFORM: - if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &key_format)) + if (!opt_format(opt_arg(), OPT_FMT_PDE, &key_format)) goto opthelp; break; case OPT_PASS: @@ -1216,6 +1279,9 @@ int s_client_main(int argc, char **argv) case OPT_BUILD_CHAIN: build_chain = 1; break; + case OPT_REQCAFILE: + ReqCAfile = opt_arg(); + break; case OPT_CAFILE: CAfile = opt_arg(); break; @@ -1251,6 +1317,9 @@ int s_client_main(int argc, char **argv) goto end; } break; + case OPT_DANE_EE_NO_NAME: + dane_ee_no_name = 1; + break; case OPT_NEXTPROTONEG: #ifndef OPENSSL_NO_NEXTPROTONEG next_proto_neg_in = opt_arg(); @@ -1306,6 +1375,12 @@ int s_client_main(int argc, char **argv) case OPT_READ_BUF: read_buf_len = atoi(opt_arg()); break; + case OPT_KEYLOG_FILE: + keylog_file = opt_arg(); + break; + case OPT_EARLY_DATA: + early_data_file = opt_arg(); + break; } } if (count4or6 >= 2) { @@ -1329,8 +1404,8 @@ int s_client_main(int argc, char **argv) if (tmp_port != port) OPENSSL_free(tmp_port); if (!res) { - BIO_printf(bio_err, "%s: -proxy argument malformed or ambiguous\n", - prog); + BIO_printf(bio_err, + "%s: -proxy argument malformed or ambiguous\n", prog); goto end; } } else { @@ -1469,8 +1544,8 @@ int s_client_main(int argc, char **argv) if (SSL_CTX_config(ctx, ssl_config) == 0) { BIO_printf(bio_err, "Error using configuration \"%s\"\n", ssl_config); - ERR_print_errors(bio_err); - goto end; + ERR_print_errors(bio_err); + goto end; } } @@ -1508,6 +1583,16 @@ int s_client_main(int argc, char **argv) ERR_print_errors(bio_err); goto end; } + if (ReqCAfile != NULL) { + STACK_OF(X509_NAME) *nm = sk_X509_NAME_new_null(); + if (nm == NULL || !SSL_add_file_cert_subjects_to_stack(nm, ReqCAfile)) { + sk_X509_NAME_pop_free(nm, X509_NAME_free); + BIO_printf(bio_err, "Error loading CA names\n"); + ERR_print_errors(bio_err); + goto end; + } + SSL_CTX_set0_CA_list(ctx, nm); + } #ifndef OPENSSL_NO_ENGINE if (ssl_client_engine) { if (!SSL_CTX_set_client_cert_engine(ctx, ssl_client_engine)) { @@ -1523,8 +1608,7 @@ int s_client_main(int argc, char **argv) #ifndef OPENSSL_NO_PSK if (psk_key != NULL) { if (c_debug) - BIO_printf(bio_c_out, - "PSK key given, setting client callback\n"); + BIO_printf(bio_c_out, "PSK key given, setting client callback\n"); SSL_CTX_set_psk_client_callback(ctx, psk_client_cb); } #endif @@ -1556,7 +1640,7 @@ int s_client_main(int argc, char **argv) } /* Returns 0 on success! */ if (SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len) != 0) { - BIO_printf(bio_err, "Error setting ALPN\n"); + BIO_printf(bio_err, "Error setting ALPN\n"); goto end; } OPENSSL_free(alpn); @@ -1568,8 +1652,8 @@ int s_client_main(int argc, char **argv) NULL, NULL, NULL, serverinfo_cli_parse_cb, NULL)) { BIO_printf(bio_err, - "Warning: Unable to add custom extension %u, skipping\n", - serverinfo_types[i]); + "Warning: Unable to add custom extension %u, skipping\n", + serverinfo_types[i]); } } @@ -1637,12 +1721,27 @@ int s_client_main(int argc, char **argv) if (dane_tlsa_domain != NULL) { if (SSL_CTX_dane_enable(ctx) <= 0) { BIO_printf(bio_err, - "%s: Error enabling DANE TLSA authentication.\n", prog); + "%s: Error enabling DANE TLSA authentication.\n", + prog); ERR_print_errors(bio_err); goto end; } } + /* + * In TLSv1.3 NewSessionTicket messages arrive after the handshake and can + * 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) { + 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 (sess_in) { SSL_SESSION *sess; @@ -1687,7 +1786,7 @@ int s_client_main(int argc, char **argv) } if (dane_tlsa_rrset == NULL) { BIO_printf(bio_err, "%s: DANE TLSA authentication requires at " - "least one -dane_tlsa_rrset option.\n", prog); + "least one -dane_tlsa_rrdata option.\n", prog); goto end; } if (tlsa_import_rrset(con, dane_tlsa_rrset) <= 0) { @@ -1695,6 +1794,8 @@ int s_client_main(int argc, char **argv) "records.\n", prog); goto end; } + if (dane_ee_no_name) + SSL_dane_set_flags(con, DANE_FLAG_NO_DANE_EE_NAMECHECKS); } else if (dane_tlsa_rrset != NULL) { BIO_printf(bio_err, "%s: DANE TLSA authentication requires the " "-dane_tlsa_domain option.\n", prog); @@ -1702,8 +1803,7 @@ int s_client_main(int argc, char **argv) } re_start: - if (init_client(&s, host, port, socket_family, socket_type) == 0) - { + if (init_client(&s, host, port, socket_family, socket_type) == 0) { BIO_printf(bio_err, "connect:errno=%d\n", get_last_socket_error()); BIO_closesocket(s); goto end; @@ -1719,18 +1819,25 @@ int s_client_main(int argc, char **argv) } #ifndef OPENSSL_NO_DTLS if (socket_type == SOCK_DGRAM) { - struct sockaddr peer; - int peerlen = sizeof peer; + union BIO_sock_info_u peer_info; sbio = BIO_new_dgram(s, BIO_NOCLOSE); - if (getsockname(s, &peer, (void *)&peerlen) < 0) { + if ((peer_info.addr = BIO_ADDR_new()) == NULL) { + BIO_printf(bio_err, "memory allocation failure\n"); + BIO_closesocket(s); + goto end; + } + if (!BIO_sock_info(s, 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); goto end; } - (void)BIO_ctrl_set_connected(sbio, &peer); + (void)BIO_ctrl_set_connected(sbio, peer_info.addr); + BIO_ADDR_free(peer_info.addr); + peer_info.addr = NULL; if (enable_timeouts) { timeout.tv_sec = 0; @@ -1799,7 +1906,10 @@ int s_client_main(int argc, char **argv) SSL_set_connect_state(con); /* ok, lets connect */ - width = SSL_get_fd(con) + 1; + if (fileno_stdin() > SSL_get_fd(con)) + width = fileno_stdin() + 1; + else + width = SSL_get_fd(con) + 1; read_tty = 1; write_tty = 0; @@ -1815,6 +1925,7 @@ int s_client_main(int argc, char **argv) switch ((PROTOCOL_CHOICE) starttls_proto) { case PROTO_OFF: break; + case PROTO_LMTP: case PROTO_SMTP: { /* @@ -1827,27 +1938,32 @@ int s_client_main(int argc, char **argv) */ int foundit = 0; BIO *fbio = BIO_new(BIO_f_buffer()); + BIO_push(fbio, sbio); - /* wait for multi-line response to end from SMTP */ + /* Wait for multi-line response to end from LMTP or SMTP */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); - } - while (mbuf_len > 3 && mbuf[3] == '-'); - BIO_printf(fbio, "EHLO %s\r\n", ehlo); + } while (mbuf_len > 3 && mbuf[3] == '-'); + if (starttls_proto == (int)PROTO_LMTP) + BIO_printf(fbio, "LHLO %s\r\n", ehlo); + else + BIO_printf(fbio, "EHLO %s\r\n", ehlo); (void)BIO_flush(fbio); - /* wait for multi-line response to end EHLO SMTP response */ + /* + * Wait for multi-line response to end LHLO LMTP or EHLO SMTP + * response. + */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); if (strstr(mbuf, "STARTTLS")) foundit = 1; - } - while (mbuf_len > 3 && mbuf[3] == '-'); + } while (mbuf_len > 3 && mbuf[3] == '-'); (void)BIO_flush(fbio); BIO_pop(fbio); BIO_free(fbio); if (!foundit) BIO_printf(bio_err, - "didn't find starttls in server response," + "Didn't find STARTTLS in server response," " trying anyway...\n"); BIO_printf(sbio, "STARTTLS\r\n"); BIO_read(sbio, sbuf, BUFSIZZ); @@ -1868,6 +1984,7 @@ int s_client_main(int argc, char **argv) { int foundit = 0; BIO *fbio = BIO_new(BIO_f_buffer()); + BIO_push(fbio, sbio); BIO_gets(fbio, mbuf, BUFSIZZ); /* STARTTLS command requires CAPABILITY... */ @@ -1885,7 +2002,7 @@ int s_client_main(int argc, char **argv) BIO_free(fbio); if (!foundit) BIO_printf(bio_err, - "didn't find STARTTLS in server response," + "Didn't find STARTTLS in server response," " trying anyway...\n"); BIO_printf(sbio, ". STARTTLS\r\n"); BIO_read(sbio, sbuf, BUFSIZZ); @@ -1894,6 +2011,7 @@ int s_client_main(int argc, char **argv) case PROTO_FTP: { BIO *fbio = BIO_new(BIO_f_buffer()); + BIO_push(fbio, sbio); /* wait for multi-line response to end from FTP */ do { @@ -1917,7 +2035,11 @@ int s_client_main(int argc, char **argv) starttls_proto == PROTO_XMPP ? "client" : "server", xmpphost ? xmpphost : host); seen = BIO_read(sbio, mbuf, BUFSIZZ); - mbuf[seen] = 0; + if (seen < 0) { + BIO_printf(bio_err, "BIO_read failed\n"); + goto end; + } + mbuf[seen] = '\0'; while (!strstr (mbuf, ""); seen = BIO_read(sbio, sbuf, BUFSIZZ); - sbuf[seen] = 0; + if (seen < 0) { + BIO_printf(bio_err, "BIO_read failed\n"); + goto shut; + } + sbuf[seen] = '\0'; if (!strstr(sbuf, " 3 && foundit == 0); + /* + * 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[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) { - BIO_printf(bio_err, "%s: HTTP CONNECT failed\n", prog); + if (foundit != success) { goto shut; } } @@ -2054,6 +2200,213 @@ int s_client_main(int argc, char **argv) goto shut; } } + break; + case PROTO_POSTGRES: + { + static const unsigned char ssl_request[] = { + /* Length SSLRequest */ + 0, 0, 0, 8, 4, 210, 22, 47 + }; + int bytes; + + /* Send SSLRequest packet */ + BIO_write(sbio, ssl_request, 8); + (void)BIO_flush(sbio); + + /* Reply will be a single S if SSL is enabled */ + bytes = BIO_read(sbio, sbuf, BUFSIZZ); + if (bytes != 1 || sbuf[0] != 'S') + goto shut; + } + break; + case PROTO_NNTP: + { + int foundit = 0; + BIO *fbio = BIO_new(BIO_f_buffer()); + + BIO_push(fbio, sbio); + BIO_gets(fbio, mbuf, BUFSIZZ); + /* 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] != '.'); + (void)BIO_flush(fbio); + BIO_pop(fbio); + BIO_free(fbio); + if (!foundit) + BIO_printf(bio_err, + "Didn't find STARTTLS in server response," + " trying anyway...\n"); + BIO_printf(sbio, "STARTTLS\r\n"); + mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ); + if (mbuf_len < 0) { + BIO_printf(bio_err, "BIO_read failed\n"); + goto end; + } + mbuf[mbuf_len] = '\0'; + if (strstr(mbuf, "382") == NULL) { + BIO_printf(bio_err, "STARTTLS failed: %s", mbuf); + goto shut; + } + } + break; + case PROTO_SIEVE: + { + int foundit = 0; + BIO *fbio = BIO_new(BIO_f_buffer()); + + BIO_push(fbio, sbio); + /* wait for multi-line response to end from Sieve */ + do { + mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); + /* + * According to RFC 5804 § 1.7, capability + * is case-insensitive, make it uppercase + */ + if (mbuf_len > 1 && mbuf[0] == '"') { + make_uppercase(mbuf); + if (strncmp(mbuf, "\"STARTTLS\"", 10) == 0) + foundit = 1; + } + } while (mbuf_len > 1 && mbuf[0] == '"'); + (void)BIO_flush(fbio); + BIO_pop(fbio); + BIO_free(fbio); + if (!foundit) + BIO_printf(bio_err, + "Didn't find STARTTLS in server response," + " trying anyway...\n"); + BIO_printf(sbio, "STARTTLS\r\n"); + mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ); + if (mbuf_len < 0) { + BIO_printf(bio_err, "BIO_read failed\n"); + goto end; + } + mbuf[mbuf_len] = '\0'; + if (mbuf_len < 2) { + BIO_printf(bio_err, "STARTTLS failed: %s", mbuf); + goto shut; + } + /* + * According to RFC 5804 § 2.2, response codes are case- + * insensitive, make it uppercase but preserve the response. + */ + strncpy(sbuf, mbuf, 2); + make_uppercase(sbuf); + if (strncmp(sbuf, "OK", 2) != 0) { + BIO_printf(bio_err, "STARTTLS not supported: %s", mbuf); + goto shut; + } + } + break; + case PROTO_LDAP: + { + /* StartTLS Operation according to RFC 4511 */ + static char ldap_tls_genconf[] = "asn1=SEQUENCE:LDAPMessage\n" + "[LDAPMessage]\n" + "messageID=INTEGER:1\n" + "extendedReq=EXPLICIT:23A,IMPLICIT:0C," + "FORMAT:ASCII,OCT:1.3.6.1.4.1.1466.20037\n"; + long errline = -1; + char *genstr = NULL; + int result = -1; + ASN1_TYPE *atyp = NULL; + BIO *ldapbio = BIO_new(BIO_s_mem()); + CONF *cnf = NCONF_new(NULL); + + if (cnf == NULL) { + BIO_free(ldapbio); + goto end; + } + BIO_puts(ldapbio, ldap_tls_genconf); + if (NCONF_load_bio(cnf, ldapbio, &errline) <= 0) { + BIO_free(ldapbio); + NCONF_free(cnf); + if (errline <= 0) { + BIO_printf(bio_err, "NCONF_load_bio failed\n"); + goto end; + } else { + BIO_printf(bio_err, "Error on line %ld\n", errline); + goto end; + } + } + BIO_free(ldapbio); + genstr = NCONF_get_string(cnf, "default", "asn1"); + if (genstr == NULL) { + NCONF_free(cnf); + BIO_printf(bio_err, "NCONF_get_string failed\n"); + goto end; + } + atyp = ASN1_generate_nconf(genstr, cnf); + if (atyp == NULL) { + NCONF_free(cnf); + BIO_printf(bio_err, "ASN1_generate_nconf failed\n"); + goto end; + } + NCONF_free(cnf); + + /* Send SSLRequest packet */ + BIO_write(sbio, atyp->value.sequence->data, + atyp->value.sequence->length); + (void)BIO_flush(sbio); + ASN1_TYPE_free(atyp); + + mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ); + if (mbuf_len < 0) { + BIO_printf(bio_err, "BIO_read failed\n"); + goto end; + } + result = ldap_ExtendedResponse_parse(mbuf, mbuf_len); + if (result < 0) { + BIO_printf(bio_err, "ldap_ExtendedResponse_parse failed\n"); + goto shut; + } else if (result > 0) { + BIO_printf(bio_err, "STARTTLS failed, LDAP Result Code: %i\n", + result); + goto shut; + } + mbuf_len = 0; + } + break; + } + + if (early_data_file != NULL + && SSL_get0_session(con) != NULL + && SSL_SESSION_get_max_early_data(SSL_get0_session(con)) > 0) { + BIO *edfile = BIO_new_file(early_data_file, "r"); + size_t readbytes, writtenbytes; + int finish = 0; + + if (edfile == NULL) { + BIO_printf(bio_err, "Cannot open early data file\n"); + goto shut; + } + + while (!finish) { + if (!BIO_read_ex(edfile, cbuf, BUFSIZZ, &readbytes)) + finish = 1; + + while (!SSL_write_early_data(con, cbuf, readbytes, &writtenbytes)) { + switch (SSL_get_error(con, 0)) { + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_ASYNC: + case SSL_ERROR_WANT_READ: + /* Just keep trying - busy waiting */ + continue; + default: + BIO_printf(bio_err, "Error writing early data\n"); + BIO_free(edfile); + goto shut; + } + } + } + + BIO_free(edfile); } for (;;) { @@ -2066,7 +2419,8 @@ int s_client_main(int argc, char **argv) else timeoutp = NULL; - if (SSL_in_init(con) && !SSL_total_renegotiations(con)) { + if (!SSL_is_init_finished(con) && SSL_total_renegotiations(con) == 0 + && SSL_get_key_update_type(con) == SSL_KEY_UPDATE_NONE) { in_init = 1; tty_on = 0; } else { @@ -2080,15 +2434,6 @@ int s_client_main(int argc, char **argv) tlsextcbp.ack ? "" : "not "); } - if (sess_out) { - BIO *stmp = BIO_new_file(sess_out, "w"); - if (stmp) { - PEM_write_bio_SSL_SESSION(stmp, SSL_get_session(con)); - BIO_free(stmp); - } else - BIO_printf(bio_err, "Error writing session file %s\n", - sess_out); - } if (c_brief) { BIO_puts(bio_err, "CONNECTION ESTABLISHED\n"); print_ssl_summary(con); @@ -2128,9 +2473,11 @@ int s_client_main(int argc, char **argv) * set the flag so we exit. */ if (read_tty && !at_eof) - openssl_fdset(fileno(stdin), &readfds); + openssl_fdset(fileno_stdin(), &readfds); +#if !defined(OPENSSL_SYS_VMS) if (write_tty) - openssl_fdset(fileno(stdout), &writefds); + openssl_fdset(fileno_stdout(), &writefds); +#endif } if (read_ssl) openssl_fdset(SSL_get_fd(con), &readfds); @@ -2181,7 +2528,6 @@ int s_client_main(int argc, char **argv) BIO_printf(bio_err, "bad select %d\n", get_last_socket_error()); goto shut; - /* goto end; */ } } @@ -2256,11 +2602,11 @@ int s_client_main(int argc, char **argv) goto shut; } } -#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) +#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_VMS) /* Assume Windows/DOS/BeOS can always write */ else if (!ssl_pending && write_tty) #else - else if (!ssl_pending && FD_ISSET(fileno(stdout), &writefds)) + else if (!ssl_pending && FD_ISSET(fileno_stdout(), &writefds)) #endif { #ifdef CHARSET_EBCDIC @@ -2272,10 +2618,9 @@ int s_client_main(int argc, char **argv) BIO_printf(bio_c_out, "DONE\n"); ret = 0; goto shut; - /* goto end; */ } - sbuf_len -= i;; + sbuf_len -= i; sbuf_off += i; if (sbuf_len <= 0) { read_ssl = 1; @@ -2342,14 +2687,13 @@ int s_client_main(int argc, char **argv) case SSL_ERROR_SSL: ERR_print_errors(bio_err); goto shut; - /* break; */ } } /* OPENSSL_SYS_MSDOS includes OPENSSL_SYS_WINDOWS */ #if defined(OPENSSL_SYS_MSDOS) else if (has_stdin_waiting()) #else - else if (FD_ISSET(fileno(stdin), &readfds)) + else if (FD_ISSET(fileno_stdin(), &readfds)) #endif { if (crlf) { @@ -2372,9 +2716,10 @@ int s_client_main(int argc, char **argv) assert(lf_num == 0); } else i = raw_read_stdin(cbuf, BUFSIZZ); - +#if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS) if (i == 0) at_eof = 1; +#endif if ((!c_ign_eof) && ((i <= 0) || (cbuf[0] == 'Q' && cmdletters))) { BIO_printf(bio_err, "DONE\n"); @@ -2387,6 +2732,15 @@ int s_client_main(int argc, char **argv) SSL_renegotiate(con); cbuf_len = 0; } + + 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"); @@ -2433,6 +2787,7 @@ int s_client_main(int argc, char **argv) OPENSSL_free(next_proto.data); #endif SSL_CTX_free(ctx); + set_keylog_file(NULL, NULL); X509_free(cert); sk_X509_CRL_pop_free(crls, X509_CRL_free); EVP_PKEY_free(key); @@ -2441,6 +2796,7 @@ int s_client_main(int argc, char **argv) #ifndef OPENSSL_NO_SRP OPENSSL_free(srp_arg.srppassin); #endif + OPENSSL_free(connectstr); OPENSSL_free(host); OPENSSL_free(port); X509_VERIFY_PARAM_free(vpm); @@ -2451,6 +2807,7 @@ int s_client_main(int argc, char **argv) OPENSSL_clear_free(cbuf, BUFSIZZ); OPENSSL_clear_free(sbuf, BUFSIZZ); OPENSSL_clear_free(mbuf, BUFSIZZ); + release_engine(e); BIO_free(bio_c_out); bio_c_out = NULL; BIO_free(bio_c_msg); @@ -2560,7 +2917,8 @@ static void print_stuff(BIO *bio, SSL *s, int full) #endif BIO_printf(bio, - "---\nSSL handshake has read %"PRIu64" bytes and written %"PRIu64" bytes\n", + "---\nSSL handshake has read %ju bytes " + "and written %ju bytes\n", BIO_number_read(SSL_get_rbio(s)), BIO_number_written(SSL_get_wbio(s))); } @@ -2591,11 +2949,15 @@ static void print_stuff(BIO *bio, SSL *s, int full) { /* Print out local port of connection: useful for debugging */ int sock; - struct sockaddr_in ladd; - socklen_t ladd_size = sizeof(ladd); + union BIO_sock_info_u info; + sock = SSL_get_fd(s); - getsockname(sock, (struct sockaddr *)&ladd, &ladd_size); - BIO_printf(bio_c_out, "LOCAL PORT is %u\n", ntohs(ladd.sin_port)); + if ((info.addr = BIO_ADDR_new()) != NULL + && BIO_sock_info(sock, BIO_SOCK_INFO_ADDRESS, &info)) { + BIO_printf(bio_c_out, "LOCAL PORT is %u\n", + ntohs(BIO_ADDR_rawport(info.addr))); + } + BIO_ADDR_free(info.addr); } #endif @@ -2632,9 +2994,25 @@ static void print_stuff(BIO *bio, SSL *s, int full) } #endif + if (SSL_version(s) == TLS1_3_VERSION) { + switch (SSL_get_early_data_status(s)) { + case SSL_EARLY_DATA_NOT_SENT: + BIO_printf(bio, "Early data was not sent\n"); + break; + + case SSL_EARLY_DATA_REJECTED: + BIO_printf(bio, "Early data was rejected\n"); + break; + + case SSL_EARLY_DATA_ACCEPTED: + BIO_printf(bio, "Early data was accepted\n"); + break; + + } + } + SSL_SESSION_print(bio, SSL_get_session(s)); - if ((SSL_get_session(s) != NULL) && - (keymatexportlabel != NULL)) { + if (SSL_get_session(s) != NULL && keymatexportlabel != NULL) { BIO_printf(bio, "Keying material exporter:\n"); BIO_printf(bio, " Label: '%s'\n", keymatexportlabel); BIO_printf(bio, " Length: %i bytes\n", keymatexportlen); @@ -2685,4 +3063,88 @@ static int ocsp_resp_cb(SSL *s, void *arg) } # endif -#endif /* OPENSSL_NO_SOCK */ +static int ldap_ExtendedResponse_parse(const char *buf, long rem) +{ + const unsigned char *cur, *end; + long len; + int tag, xclass, inf, ret = -1; + + cur = (const unsigned char *)buf; + end = cur + rem; + + /* + * From RFC 4511: + * + * LDAPMessage ::= SEQUENCE { + * messageID MessageID, + * protocolOp CHOICE { + * ... + * extendedResp ExtendedResponse, + * ... }, + * controls [0] Controls OPTIONAL } + * + * ExtendedResponse ::= [APPLICATION 24] SEQUENCE { + * COMPONENTS OF LDAPResult, + * responseName [10] LDAPOID OPTIONAL, + * responseValue [11] OCTET STRING OPTIONAL } + * + * LDAPResult ::= SEQUENCE { + * resultCode ENUMERATED { + * success (0), + * ... + * other (80), + * ... }, + * matchedDN LDAPDN, + * diagnosticMessage LDAPString, + * referral [3] Referral OPTIONAL } + */ + + /* pull SEQUENCE */ + inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem); + if (inf != V_ASN1_CONSTRUCTED || tag != V_ASN1_SEQUENCE || + (rem = end - cur, len > rem)) { + BIO_printf(bio_err, "Unexpected LDAP response\n"); + goto end; + } + + rem = len; /* ensure that we don't overstep the SEQUENCE */ + + /* pull MessageID */ + inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem); + if (inf != V_ASN1_UNIVERSAL || tag != V_ASN1_INTEGER || + (rem = end - cur, len > rem)) { + BIO_printf(bio_err, "No MessageID\n"); + goto end; + } + + cur += len; /* shall we check for MessageId match or just skip? */ + + /* pull [APPLICATION 24] */ + rem = end - cur; + inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem); + if (inf != V_ASN1_CONSTRUCTED || xclass != V_ASN1_APPLICATION || + tag != 24) { + BIO_printf(bio_err, "Not ExtendedResponse\n"); + goto end; + } + + /* pull resultCode */ + rem = end - cur; + inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem); + if (inf != V_ASN1_UNIVERSAL || tag != V_ASN1_ENUMERATED || len == 0 || + (rem = end - cur, len > rem)) { + BIO_printf(bio_err, "Not LDAPResult\n"); + goto end; + } + + /* len should always be one, but just in case... */ + for (ret = 0, inf = 0; inf < len; inf++) { + ret <<= 8; + ret |= cur[inf]; + } + /* There is more data, but we don't care... */ + end: + return ret; +} + +#endif /* OPENSSL_NO_SOCK */