X-Git-Url: https://git.openssl.org/?p=openssl.git;a=blobdiff_plain;f=apps%2Fs_client.c;h=872496c3a244dbc8eea279301125b226c70f0bce;hp=89cddb30f8d10c891abaff7d390149571a93c905;hb=09d62b336d9e2a11b330d45d4f0f3f37cbb0d674;hpb=f69d050ee344e931aea4102f09bb0134a4f4c12a diff --git a/apps/s_client.c b/apps/s_client.c index 89cddb30f8..872496c3a2 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -2,7 +2,7 @@ * 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 + * 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 @@ -74,6 +74,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 char *base64encode (const void *buf, size_t len); static int saved_errno; @@ -201,6 +202,7 @@ static int psk_use_session_cb(SSL *s, const EVP_MD *md, cipher = SSL_CIPHER_find(s, tls13_aes128gcmsha256_id); if (cipher == NULL) { BIO_printf(bio_err, "Error finding suitable ciphersuite\n"); + OPENSSL_free(key); return 0; } @@ -589,12 +591,14 @@ typedef enum OPTION_choice { 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_FORCE_PHA, + OPT_ENABLE_PHA, + OPT_SCTP_LABEL_BUG, OPT_R_ENUM } OPTION_CHOICE; @@ -607,6 +611,8 @@ const OPTIONS s_client_options[] = { {"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 @@ -637,7 +643,8 @@ const OPTIONS s_client_options[] = { "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"}, + {"showcerts", OPT_SHOWCERTS, '-', + "Show all certificates sent by the server"}, {"debug", OPT_DEBUG, '-', "Extra output"}, {"msg", OPT_MSG, '-', "Show protocol messages"}, {"msgfile", OPT_MSGFILE, '>', @@ -748,6 +755,7 @@ 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"}, @@ -784,7 +792,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"}, + {"enable_pha", OPT_ENABLE_PHA, '-', "Enable post-handshake-authentication"}, {NULL, OPT_EOF, 0x00, NULL} }; @@ -842,15 +850,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"); } /* @@ -878,8 +900,10 @@ int s_client_main(int argc, char **argv) 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, *bindstr = 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 *port = OPENSSL_strdup(PORT); @@ -934,7 +958,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; @@ -957,7 +983,10 @@ int s_client_main(int argc, char **argv) int isdtls = 0; #endif char *psksessf = NULL; - int force_pha = 0; + int enable_pha = 0; +#ifndef OPENSSL_NO_SCTP + int sctp_label_bug = 0; +#endif FD_ZERO(&readfds); FD_ZERO(&writefds); @@ -1057,6 +1086,12 @@ int s_client_main(int argc, char **argv) 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; @@ -1303,6 +1338,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: @@ -1421,7 +1461,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(); @@ -1472,8 +1514,8 @@ 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; + case OPT_ENABLE_PHA: + enable_pha = 1; break; } } @@ -1599,7 +1641,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; } @@ -1670,6 +1722,8 @@ 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); @@ -1685,6 +1739,11 @@ int s_client_main(int argc, char **argv) } } +#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; @@ -1911,11 +1970,9 @@ 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; @@ -1924,8 +1981,8 @@ int s_client_main(int argc, char **argv) if (con == NULL) goto end; - if (force_pha) - SSL_force_post_handshake_auth(con); + if (enable_pha) + SSL_set_post_handshake_auth(con, 1); if (sess_in != NULL) { SSL_SESSION *sess; @@ -2302,7 +2359,33 @@ int s_client_main(int argc, char **argv) BIO *fbio = BIO_new(BIO_f_buffer()); BIO_push(fbio, sbio); - BIO_printf(fbio, "CONNECT %s HTTP/1.0\r\n\r\n", connectstr); + BIO_printf(fbio, "CONNECT %s HTTP/1.0\r\n", connectstr); + /* + * Workaround for broken proxies which would otherwise close + * the connection when entering tunnel mode (eg Squid 2.6) + */ + BIO_printf(fbio, "Proxy-Connection: Keep-Alive\r\n"); + + /* Support for basic (base64) proxy authentication */ + if (proxyuser != NULL) { + size_t l; + char *proxyauth, *proxyauthenc; + + l = strlen(proxyuser); + if (proxypass != NULL) + l += strlen(proxypass); + proxyauth = app_malloc(l + 2, "Proxy auth string"); + BIO_snprintf(proxyauth, l + 2, "%s:%s", proxyuser, + (proxypass != NULL) ? proxypass : ""); + proxyauthenc = base64encode(proxyauth, strlen(proxyauth)); + BIO_printf(fbio, "Proxy-Authorization: Basic %s\r\n", + proxyauthenc); + OPENSSL_clear_free(proxyauth, strlen(proxyauth)); + OPENSSL_clear_free(proxyauthenc, strlen(proxyauthenc)); + } + + /* Terminate the HTTP CONNECT request */ + BIO_printf(fbio, "\r\n"); (void)BIO_flush(fbio); /* * The first line is the HTTP response. According to RFC 7230, @@ -2515,12 +2598,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); @@ -2702,8 +2789,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; @@ -2814,10 +2900,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); @@ -3014,9 +3098,7 @@ 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, @@ -3107,6 +3189,8 @@ 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; @@ -3120,7 +3204,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 @@ -3227,6 +3312,10 @@ 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"); +#endif #ifdef SSL_DEBUG { @@ -3277,7 +3366,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"); @@ -3292,9 +3381,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); @@ -3430,4 +3530,27 @@ static int ldap_ExtendedResponse_parse(const char *buf, long rem) return ret; } +/* + * BASE64 encoder: used only for encoding basic proxy authentication credentials + */ +static char *base64encode (const void *buf, size_t len) +{ + int i; + size_t outl; + char *out; + + /* Calculate size of encoded data */ + outl = (len / 3); + if (len % 3 > 0) + outl++; + outl <<= 2; + out = app_malloc(outl + 1, "base64 encode buffer"); + + i = EVP_EncodeBlock((unsigned char *)out, buf, len); + assert(i <= (int)outl); + if (i < 0) + *out = '\0'; + return out; +} + #endif /* OPENSSL_NO_SOCK */