/* $OpenBSD: s_client.c,v 1.42 2020/02/16 16:39:01 jsing Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ /* ==================================================================== * Copyright (c) 1998-2006 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ /* ==================================================================== * Copyright 2005 Nokia. All rights reserved. * * The portions of the attached software ("Contribution") is developed by * Nokia Corporation and is licensed pursuant to the OpenSSL open source * license. * * The Contribution, originally written by Mika Kousa and Pasi Eronen of * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites * support (see RFC 4279) to OpenSSL. * * No patent licenses or other rights except those expressly stated in * the OpenSSL open source license shall be deemed granted or received * expressly, by implication, estoppel, or otherwise. * * No assurances are provided by Nokia that the Contribution does not * infringe the patent or other intellectual property rights of any third * party or that the license provides you with all the necessary rights * to make use of the Contribution. * * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "apps.h" #include #include #include #include #include #include #include "s_apps.h" #include "timeouts.h" /*#define SSL_HOST_NAME "www.netscape.com" */ /*#define SSL_HOST_NAME "193.118.187.102" */ #define SSL_HOST_NAME "localhost" /*#define TEST_CERT "client.pem" *//* no default cert. */ #define BUFSIZZ 1024*8 static int c_nbio = 0; static int c_Pause = 0; static int c_debug = 0; static int c_tlsextdebug = 0; static int c_status_req = 0; static int c_msg = 0; static int c_showcerts = 0; static char *keymatexportlabel = NULL; static int keymatexportlen = 20; static void sc_usage(void); static void print_stuff(BIO * berr, SSL * con, int full); static int ocsp_resp_cb(SSL * s, void *arg); static BIO *bio_c_out = NULL; static int c_quiet = 0; static int c_ign_eof = 0; static void sc_usage(void) { BIO_printf(bio_err, "usage: s_client args\n"); BIO_printf(bio_err, "\n"); BIO_printf(bio_err, " -4 - Force IPv4\n"); BIO_printf(bio_err, " -6 - Force IPv6\n"); BIO_printf(bio_err, " -host host - use -connect instead\n"); BIO_printf(bio_err, " -port port - use -connect instead\n"); BIO_printf(bio_err, " -connect host:port - who to connect to (default is %s:%s)\n", SSL_HOST_NAME, PORT_STR); BIO_printf(bio_err, " -proxy host:port - connect to http proxy\n"); BIO_printf(bio_err, " -verify arg - turn on peer certificate verification\n"); BIO_printf(bio_err, " -cert arg - certificate file to use, PEM format assumed\n"); BIO_printf(bio_err, " -certform arg - certificate format (PEM or DER) PEM default\n"); BIO_printf(bio_err, " -key arg - Private key file to use, in cert file if\n"); BIO_printf(bio_err, " not specified but cert file is.\n"); BIO_printf(bio_err, " -keyform arg - key format (PEM or DER) PEM default\n"); BIO_printf(bio_err, " -pass arg - private key file pass phrase source\n"); BIO_printf(bio_err, " -CApath arg - PEM format directory of CA's\n"); BIO_printf(bio_err, " -CAfile arg - PEM format file of CA's\n"); BIO_printf(bio_err, " -reconnect - Drop and re-make the connection with the same Session-ID\n"); BIO_printf(bio_err, " -pause - sleep(1) after each read(2) and write(2) system call\n"); BIO_printf(bio_err, " -showcerts - show all certificates in the chain\n"); BIO_printf(bio_err, " -debug - extra output\n"); BIO_printf(bio_err, " -msg - Show protocol messages\n"); BIO_printf(bio_err, " -nbio_test - more ssl protocol testing\n"); BIO_printf(bio_err, " -state - print the 'ssl' states\n"); BIO_printf(bio_err, " -nbio - Run with non-blocking IO\n"); BIO_printf(bio_err, " -crlf - convert LF from terminal into CRLF\n"); BIO_printf(bio_err, " -quiet - no s_client output\n"); BIO_printf(bio_err, " -ign_eof - ignore input eof (default when -quiet)\n"); BIO_printf(bio_err, " -no_ign_eof - don't ignore input eof\n"); BIO_printf(bio_err, " -tls1_3 - just use TLSv1.3\n"); BIO_printf(bio_err, " -tls1_2 - just use TLSv1.2\n"); BIO_printf(bio_err, " -tls1_1 - just use TLSv1.1\n"); BIO_printf(bio_err, " -tls1 - just use TLSv1\n"); BIO_printf(bio_err, " -dtls1 - just use DTLSv1\n"); BIO_printf(bio_err, " -mtu - set the link layer MTU\n"); BIO_printf(bio_err, " -no_tls1_3/-no_tls1_2/-no_tls1_1/-no_tls1 - turn off that protocol\n"); BIO_printf(bio_err, " -bugs - Switch on all SSL implementation bug workarounds\n"); BIO_printf(bio_err, " -cipher - preferred cipher to use, use the 'openssl ciphers'\n"); BIO_printf(bio_err, " command to see what is available\n"); BIO_printf(bio_err, " -starttls prot - use the STARTTLS command before starting TLS\n"); BIO_printf(bio_err, " for those protocols that support it, where\n"); BIO_printf(bio_err, " 'prot' defines which one to assume. Currently,\n"); BIO_printf(bio_err, " only \"smtp\", \"lmtp\", \"pop3\", \"imap\", \"ftp\" and \"xmpp\"\n"); BIO_printf(bio_err, " are supported.\n"); BIO_printf(bio_err, " -xmpphost host - connect to this virtual host on the xmpp server\n"); BIO_printf(bio_err, " -sess_out arg - file to write SSL session to\n"); BIO_printf(bio_err, " -sess_in arg - file to read SSL session from\n"); BIO_printf(bio_err, " -servername host - Set TLS extension servername in ClientHello\n"); BIO_printf(bio_err, " -tlsextdebug - hex dump of all TLS extensions received\n"); BIO_printf(bio_err, " -status - request certificate status from server\n"); BIO_printf(bio_err, " -no_ticket - disable use of RFC4507bis session tickets\n"); BIO_printf(bio_err, " -alpn arg - enable ALPN extension, considering named protocols supported (comma-separated list)\n"); BIO_printf(bio_err, " -groups arg - specify EC curve groups (colon-separated list)\n"); #ifndef OPENSSL_NO_SRTP BIO_printf(bio_err, " -use_srtp profiles - Offer SRTP key management with a colon-separated profile list\n"); #endif BIO_printf(bio_err, " -keymatexport label - Export keying material using label\n"); BIO_printf(bio_err, " -keymatexportlen len - Export len bytes of keying material (default 20)\n"); } /* This is a context that we pass to callbacks */ typedef struct tlsextctx_st { BIO *biodebug; int ack; } tlsextctx; static int ssl_servername_cb(SSL * s, int *ad, void *arg) { tlsextctx *p = (tlsextctx *) arg; const char *hn = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); if (SSL_get_servername_type(s) != -1) p->ack = !SSL_session_reused(s) && hn != NULL; else BIO_printf(bio_err, "Can't use SSL_get_servername\n"); return SSL_TLSEXT_ERR_OK; } #ifndef OPENSSL_NO_SRTP char *srtp_profiles = NULL; #endif enum { PROTO_OFF = 0, PROTO_SMTP, PROTO_LMTP, PROTO_POP3, PROTO_IMAP, PROTO_FTP, PROTO_XMPP }; int s_client_main(int argc, char **argv) { unsigned int off = 0, clr = 0; SSL *con = NULL; int s, k, p, pending, state = 0, af = AF_UNSPEC; char *cbuf = NULL, *sbuf = NULL, *mbuf = NULL, *pbuf = NULL; int cbuf_len, cbuf_off; int sbuf_len, sbuf_off; int pbuf_len, pbuf_off; char *port = PORT_STR; int full_log = 1; char *host = SSL_HOST_NAME; char *xmpphost = NULL; char *proxy = NULL, *connect = NULL; char *cert_file = NULL, *key_file = NULL; int cert_format = FORMAT_PEM, key_format = FORMAT_PEM; char *passarg = NULL, *pass = NULL; X509 *cert = NULL; EVP_PKEY *key = NULL; char *CApath = NULL, *CAfile = NULL, *cipher = NULL; int reconnect = 0, badop = 0, verify = SSL_VERIFY_NONE, bugs = 0; int crlf = 0; int write_tty, read_tty, write_ssl, read_ssl, tty_on, ssl_pending; SSL_CTX *ctx = NULL; int ret = 1, in_init = 1, i, nbio_test = 0; int starttls_proto = PROTO_OFF; int prexit = 0; int peekaboo = 0; X509_VERIFY_PARAM *vpm = NULL; int badarg = 0; const SSL_METHOD *meth = NULL; int socket_type = SOCK_STREAM; BIO *sbio; int mbuf_len = 0; struct timeval timeout; const char *errstr = NULL; char *servername = NULL; tlsextctx tlsextcbp = {NULL, 0}; const char *alpn_in = NULL; const char *groups_in = NULL; char *sess_in = NULL; char *sess_out = NULL; struct sockaddr peer; int peerlen = sizeof(peer); int enable_timeouts = 0; long socket_mtu = 0; uint16_t min_version = 0, max_version = 0; if (single_execution) { if (pledge("stdio cpath wpath rpath inet dns tty", NULL) == -1) { perror("pledge"); exit(1); } } meth = TLS_client_method(); c_Pause = 0; c_quiet = 0; c_ign_eof = 0; c_debug = 0; c_msg = 0; c_showcerts = 0; if (((cbuf = malloc(BUFSIZZ)) == NULL) || ((sbuf = malloc(BUFSIZZ)) == NULL) || ((pbuf = malloc(BUFSIZZ)) == NULL) || ((mbuf = malloc(BUFSIZZ + 1)) == NULL)) { /* NUL byte */ BIO_printf(bio_err, "out of memory\n"); goto end; } verify_depth = 0; c_nbio = 0; argc--; argv++; while (argc >= 1) { if (strcmp(*argv, "-host") == 0) { if (--argc < 1) goto bad; host = *(++argv); } else if (strcmp(*argv, "-port") == 0) { if (--argc < 1) goto bad; port = *(++argv); if (port == NULL || *port == '\0') goto bad; } else if (strcmp(*argv, "-connect") == 0) { if (--argc < 1) goto bad; connect = *(++argv); } else if (strcmp(*argv, "-proxy") == 0) { if (--argc < 1) goto bad; proxy = *(++argv); } else if (strcmp(*argv,"-xmpphost") == 0) { if (--argc < 1) goto bad; xmpphost= *(++argv); } else if (strcmp(*argv, "-verify") == 0) { verify = SSL_VERIFY_PEER; if (--argc < 1) goto bad; verify_depth = strtonum(*(++argv), 0, INT_MAX, &errstr); if (errstr) goto bad; BIO_printf(bio_err, "verify depth is %d\n", verify_depth); } else if (strcmp(*argv, "-cert") == 0) { if (--argc < 1) goto bad; cert_file = *(++argv); } else if (strcmp(*argv, "-sess_out") == 0) { if (--argc < 1) goto bad; sess_out = *(++argv); } else if (strcmp(*argv, "-sess_in") == 0) { if (--argc < 1) goto bad; sess_in = *(++argv); } else if (strcmp(*argv, "-certform") == 0) { if (--argc < 1) goto bad; cert_format = str2fmt(*(++argv)); } else if (args_verify(&argv, &argc, &badarg, bio_err, &vpm)) { if (badarg) goto bad; continue; } else if (strcmp(*argv, "-verify_return_error") == 0) verify_return_error = 1; else if (strcmp(*argv, "-prexit") == 0) prexit = 1; else if (strcmp(*argv, "-peekaboo") == 0) peekaboo = 1; else if (strcmp(*argv, "-crlf") == 0) crlf = 1; else if (strcmp(*argv, "-quiet") == 0) { c_quiet = 1; c_ign_eof = 1; } else if (strcmp(*argv, "-ign_eof") == 0) c_ign_eof = 1; else if (strcmp(*argv, "-no_ign_eof") == 0) c_ign_eof = 0; else if (strcmp(*argv, "-pause") == 0) c_Pause = 1; else if (strcmp(*argv, "-debug") == 0) c_debug = 1; else if (strcmp(*argv, "-tlsextdebug") == 0) c_tlsextdebug = 1; else if (strcmp(*argv, "-status") == 0) c_status_req = 1; else if (strcmp(*argv, "-msg") == 0) c_msg = 1; else if (strcmp(*argv, "-showcerts") == 0) c_showcerts = 1; else if (strcmp(*argv, "-nbio_test") == 0) nbio_test = 1; else if (strcmp(*argv, "-state") == 0) state = 1; else if (strcmp(*argv, "-tls1_3") == 0) { min_version = TLS1_3_VERSION; max_version = TLS1_3_VERSION; } else if (strcmp(*argv, "-tls1_2") == 0) { min_version = TLS1_2_VERSION; max_version = TLS1_2_VERSION; } else if (strcmp(*argv, "-tls1_1") == 0) { min_version = TLS1_1_VERSION; max_version = TLS1_1_VERSION; } else if (strcmp(*argv, "-tls1") == 0) { min_version = TLS1_VERSION; max_version = TLS1_VERSION; #ifndef OPENSSL_NO_DTLS1 } else if (strcmp(*argv, "-dtls1") == 0) { meth = DTLS_client_method(); socket_type = SOCK_DGRAM; } else if (strcmp(*argv, "-timeout") == 0) enable_timeouts = 1; else if (strcmp(*argv, "-mtu") == 0) { if (--argc < 1) goto bad; socket_mtu = strtonum(*(++argv), 0, LONG_MAX, &errstr); if (errstr) goto bad; } #endif else if (strcmp(*argv, "-bugs") == 0) bugs = 1; else if (strcmp(*argv, "-keyform") == 0) { if (--argc < 1) goto bad; key_format = str2fmt(*(++argv)); } else if (strcmp(*argv, "-pass") == 0) { if (--argc < 1) goto bad; passarg = *(++argv); } else if (strcmp(*argv, "-key") == 0) { if (--argc < 1) goto bad; key_file = *(++argv); } else if (strcmp(*argv, "-reconnect") == 0) { reconnect = 5; } else if (strcmp(*argv, "-CApath") == 0) { if (--argc < 1) goto bad; CApath = *(++argv); } else if (strcmp(*argv, "-CAfile") == 0) { if (--argc < 1) goto bad; CAfile = *(++argv); } else if (strcmp(*argv, "-no_tls1_3") == 0) off |= SSL_OP_NO_TLSv1_3; else if (strcmp(*argv, "-no_tls1_2") == 0) off |= SSL_OP_NO_TLSv1_2; else if (strcmp(*argv, "-no_tls1_1") == 0) off |= SSL_OP_NO_TLSv1_1; else if (strcmp(*argv, "-no_tls1") == 0) off |= SSL_OP_NO_TLSv1; else if (strcmp(*argv, "-no_ssl3") == 0) off |= SSL_OP_NO_SSLv3; else if (strcmp(*argv, "-no_ssl2") == 0) off |= SSL_OP_NO_SSLv2; else if (strcmp(*argv, "-no_comp") == 0) { off |= SSL_OP_NO_COMPRESSION; } else if (strcmp(*argv, "-no_ticket") == 0) { off |= SSL_OP_NO_TICKET; } else if (strcmp(*argv, "-nextprotoneg") == 0) { /* Ignored. */ if (--argc < 1) goto bad; ++argv; } else if (strcmp(*argv, "-alpn") == 0) { if (--argc < 1) goto bad; alpn_in = *(++argv); } else if (strcmp(*argv, "-groups") == 0) { if (--argc < 1) goto bad; groups_in = *(++argv); } else if (strcmp(*argv, "-serverpref") == 0) off |= SSL_OP_CIPHER_SERVER_PREFERENCE; else if (strcmp(*argv, "-legacy_renegotiation") == 0) ; /* no-op */ else if (strcmp(*argv, "-legacy_server_connect") == 0) { off |= SSL_OP_LEGACY_SERVER_CONNECT; } else if (strcmp(*argv, "-no_legacy_server_connect") == 0) { clr |= SSL_OP_LEGACY_SERVER_CONNECT; } else if (strcmp(*argv, "-cipher") == 0) { if (--argc < 1) goto bad; cipher = *(++argv); } else if (strcmp(*argv, "-nbio") == 0) { c_nbio = 1; } else if (strcmp(*argv, "-starttls") == 0) { if (--argc < 1) goto bad; ++argv; if (strcmp(*argv, "smtp") == 0) starttls_proto = PROTO_SMTP; else if (strcmp(*argv, "lmtp") == 0) starttls_proto = PROTO_LMTP; else if (strcmp(*argv, "pop3") == 0) starttls_proto = PROTO_POP3; else if (strcmp(*argv, "imap") == 0) starttls_proto = PROTO_IMAP; else if (strcmp(*argv, "ftp") == 0) starttls_proto = PROTO_FTP; else if (strcmp(*argv, "xmpp") == 0) starttls_proto = PROTO_XMPP; else goto bad; } else if (strcmp(*argv, "-4") == 0) { af = AF_INET; } else if (strcmp(*argv, "-6") == 0) { af = AF_INET6; } else if (strcmp(*argv, "-servername") == 0) { if (--argc < 1) goto bad; servername = *(++argv); } #ifndef OPENSSL_NO_SRTP else if (strcmp(*argv, "-use_srtp") == 0) { if (--argc < 1) goto bad; srtp_profiles = *(++argv); } #endif else if (strcmp(*argv, "-keymatexport") == 0) { if (--argc < 1) goto bad; keymatexportlabel = *(++argv); } else if (strcmp(*argv, "-keymatexportlen") == 0) { if (--argc < 1) goto bad; keymatexportlen = strtonum(*(++argv), 1, INT_MAX, &errstr); if (errstr) goto bad; } else { BIO_printf(bio_err, "unknown option %s\n", *argv); badop = 1; break; } argc--; argv++; } if (proxy != NULL) { if (!extract_host_port(proxy, &host, NULL, &port)) goto bad; if (connect == NULL) connect = SSL_HOST_NAME; } else if (connect != NULL) { if (!extract_host_port(connect, &host, NULL, &port)) goto bad; } if (badop) { bad: if (errstr) BIO_printf(bio_err, "invalid argument %s: %s\n", *argv, errstr); else sc_usage(); goto end; } if (!app_passwd(bio_err, passarg, NULL, &pass, NULL)) { BIO_printf(bio_err, "Error getting password\n"); goto end; } if (key_file == NULL) key_file = cert_file; if (key_file) { key = load_key(bio_err, key_file, key_format, 0, pass, "client certificate private key file"); if (!key) { ERR_print_errors(bio_err); goto end; } } if (cert_file) { cert = load_cert(bio_err, cert_file, cert_format, NULL, "client certificate file"); if (!cert) { ERR_print_errors(bio_err); goto end; } } if (bio_c_out == NULL) { if (c_quiet && !c_debug && !c_msg) { bio_c_out = BIO_new(BIO_s_null()); } else { if (bio_c_out == NULL) bio_c_out = BIO_new_fp(stdout, BIO_NOCLOSE); } } ctx = SSL_CTX_new(meth); if (ctx == NULL) { ERR_print_errors(bio_err); goto end; } if (vpm) SSL_CTX_set1_param(ctx, vpm); if (!SSL_CTX_set_min_proto_version(ctx, min_version)) goto end; if (!SSL_CTX_set_max_proto_version(ctx, max_version)) goto end; #ifndef OPENSSL_NO_SRTP if (srtp_profiles != NULL) SSL_CTX_set_tlsext_use_srtp(ctx, srtp_profiles); #endif if (bugs) SSL_CTX_set_options(ctx, SSL_OP_ALL | off); else SSL_CTX_set_options(ctx, off); if (clr) SSL_CTX_clear_options(ctx, clr); /* * DTLS: partial reads end up discarding unread UDP bytes :-( Setting * read ahead solves this problem. */ if (socket_type == SOCK_DGRAM) SSL_CTX_set_read_ahead(ctx, 1); if (alpn_in) { unsigned short alpn_len; unsigned char *alpn = next_protos_parse(&alpn_len, alpn_in); if (alpn == NULL) { BIO_printf(bio_err, "Error parsing -alpn argument\n"); goto end; } SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len); free(alpn); } if (groups_in != NULL) { if (SSL_CTX_set1_groups_list(ctx, groups_in) != 1) { BIO_printf(bio_err, "Failed to set groups '%s'\n", groups_in); goto end; } } if (state) SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback); if (cipher != NULL) if (!SSL_CTX_set_cipher_list(ctx, cipher)) { BIO_printf(bio_err, "error setting cipher list\n"); ERR_print_errors(bio_err); goto end; } SSL_CTX_set_verify(ctx, verify, verify_callback); if (!set_cert_key_stuff(ctx, cert, key)) goto end; if ((CAfile || CApath) && !SSL_CTX_load_verify_locations(ctx, CAfile, CApath)) ERR_print_errors(bio_err); if (!SSL_CTX_set_default_verify_paths(ctx)) ERR_print_errors(bio_err); if (servername != NULL) { tlsextcbp.biodebug = bio_err; SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb); SSL_CTX_set_tlsext_servername_arg(ctx, &tlsextcbp); } con = SSL_new(ctx); if (sess_in) { SSL_SESSION *sess; BIO *stmp = BIO_new_file(sess_in, "r"); if (!stmp) { BIO_printf(bio_err, "Can't open session file %s\n", sess_in); ERR_print_errors(bio_err); goto end; } sess = PEM_read_bio_SSL_SESSION(stmp, NULL, 0, NULL); BIO_free(stmp); if (!sess) { BIO_printf(bio_err, "Can't open session file %s\n", sess_in); ERR_print_errors(bio_err); goto end; } SSL_set_session(con, sess); SSL_SESSION_free(sess); } if (servername != NULL) { if (!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; } } /* SSL_set_cipher_list(con,"RC4-MD5"); */ re_start: if (init_client(&s, host, port, socket_type, af) == 0) { BIO_printf(bio_err, "connect:errno=%d\n", errno); goto end; } BIO_printf(bio_c_out, "CONNECTED(%08X)\n", s); if (c_nbio) { if (!c_quiet) BIO_printf(bio_c_out, "turning on non blocking io\n"); if (!BIO_socket_nbio(s, 1)) { ERR_print_errors(bio_err); goto end; } } if (c_Pause & 0x01) SSL_set_debug(con, 1); if (SSL_version(con) == DTLS1_VERSION) { sbio = BIO_new_dgram(s, BIO_NOCLOSE); if (getsockname(s, &peer, (void *) &peerlen) == -1) { BIO_printf(bio_err, "getsockname:errno=%d\n", errno); shutdown(s, SHUT_RD); close(s); goto end; } (void) BIO_ctrl_set_connected(sbio, 1, &peer); if (enable_timeouts) { timeout.tv_sec = 0; timeout.tv_usec = DGRAM_RCV_TIMEOUT; BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); timeout.tv_sec = 0; timeout.tv_usec = DGRAM_SND_TIMEOUT; BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout); } if (socket_mtu > 28) { SSL_set_options(con, SSL_OP_NO_QUERY_MTU); SSL_set_mtu(con, socket_mtu - 28); } else /* want to do MTU discovery */ BIO_ctrl(sbio, BIO_CTRL_DGRAM_MTU_DISCOVER, 0, NULL); } else sbio = BIO_new_socket(s, BIO_NOCLOSE); if (nbio_test) { BIO *test; test = BIO_new(BIO_f_nbio_test()); sbio = BIO_push(test, sbio); } if (c_debug) { SSL_set_debug(con, 1); BIO_set_callback(sbio, bio_dump_callback); BIO_set_callback_arg(sbio, (char *) bio_c_out); } if (c_msg) { SSL_set_msg_callback(con, msg_cb); SSL_set_msg_callback_arg(con, bio_c_out); } if (c_tlsextdebug) { SSL_set_tlsext_debug_callback(con, tlsext_cb); SSL_set_tlsext_debug_arg(con, bio_c_out); } if (c_status_req) { SSL_set_tlsext_status_type(con, TLSEXT_STATUSTYPE_ocsp); SSL_CTX_set_tlsext_status_cb(ctx, ocsp_resp_cb); SSL_CTX_set_tlsext_status_arg(ctx, bio_c_out); } SSL_set_bio(con, sbio, sbio); SSL_set_connect_state(con); /* ok, lets connect */ read_tty = 1; write_tty = 0; tty_on = 0; read_ssl = 1; write_ssl = 1; cbuf_len = 0; cbuf_off = 0; sbuf_len = 0; sbuf_off = 0; pbuf_len = 0; pbuf_off = 0; /* This is an ugly hack that does a lot of assumptions */ /* * We do have to handle multi-line responses which may come in a * single packet or not. We therefore have to use BIO_gets() which * does need a buffering BIO. So during the initial chitchat we do * push a buffering BIO into the chain that is removed again later on * to not disturb the rest of the s_client operation. */ if (starttls_proto == PROTO_SMTP || starttls_proto == PROTO_LMTP) { int foundit = 0; BIO *fbio = BIO_new(BIO_f_buffer()); BIO_push(fbio, sbio); /* wait for multi-line response to end from SMTP */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); } while (mbuf_len > 3 && mbuf[3] == '-'); /* STARTTLS command requires EHLO... */ BIO_printf(fbio, "%cHLO openssl.client.net\r\n", starttls_proto == PROTO_SMTP ? 'E' : 'L'); (void) BIO_flush(fbio); /* wait for multi-line response to end EHLO SMTP response */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); if (strstr(mbuf, "STARTTLS")) foundit = 1; } 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," " try anyway...\n"); BIO_printf(sbio, "STARTTLS\r\n"); BIO_read(sbio, sbuf, BUFSIZZ); } else if (starttls_proto == PROTO_POP3) { mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ); if (mbuf_len == -1) { BIO_printf(bio_err, "BIO_read failed\n"); goto end; } BIO_printf(sbio, "STLS\r\n"); BIO_read(sbio, sbuf, BUFSIZZ); } else if (starttls_proto == PROTO_IMAP) { int foundit = 0; BIO *fbio = BIO_new(BIO_f_buffer()); BIO_push(fbio, sbio); BIO_gets(fbio, mbuf, BUFSIZZ); /* STARTTLS command requires CAPABILITY... */ BIO_printf(fbio, ". CAPABILITY\r\n"); (void) BIO_flush(fbio); /* wait for multi-line CAPABILITY response */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); if (strstr(mbuf, "STARTTLS")) foundit = 1; } while (mbuf_len > 3 && 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," " try anyway...\n"); BIO_printf(sbio, ". STARTTLS\r\n"); BIO_read(sbio, sbuf, BUFSIZZ); } else if (starttls_proto == PROTO_FTP) { BIO *fbio = BIO_new(BIO_f_buffer()); BIO_push(fbio, sbio); /* wait for multi-line response to end from FTP */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); } while (mbuf_len > 3 && mbuf[3] == '-'); (void) BIO_flush(fbio); BIO_pop(fbio); BIO_free(fbio); BIO_printf(sbio, "AUTH TLS\r\n"); BIO_read(sbio, sbuf, BUFSIZZ); } else if (starttls_proto == PROTO_XMPP) { int seen = 0; BIO_printf(sbio, "", xmpphost ? xmpphost : host); seen = BIO_read(sbio, mbuf, BUFSIZZ); if (seen <= 0) goto shut; mbuf[seen] = 0; while (!strstr(mbuf, ""); seen = BIO_read(sbio, sbuf, BUFSIZZ); sbuf[seen] = 0; if (!strstr(sbuf, " 0) full_log--; if (starttls_proto) { BIO_write(bio_err, mbuf, mbuf_len); /* We don't need to know any more */ starttls_proto = PROTO_OFF; } if (reconnect) { reconnect--; BIO_printf(bio_c_out, "drop connection and then reconnect\n"); SSL_shutdown(con); SSL_set_connect_state(con); shutdown(SSL_get_fd(con), SHUT_RD); close(SSL_get_fd(con)); goto re_start; } } } ssl_pending = read_ssl && SSL_pending(con); pfd[0].fd = -1; pfd[1].fd = -1; if (!ssl_pending) { if (tty_on) { if (read_tty) { pfd[0].fd = fileno(stdin); pfd[0].events = POLLIN; } if (write_tty) { pfd[1].fd = fileno(stdout); pfd[1].events = POLLOUT; } } pfd[2].fd = SSL_get_fd(con); pfd[2].events = 0; if (read_ssl) pfd[2].events |= POLLIN; if (write_ssl) pfd[2].events |= POLLOUT; /* printf("mode tty(%d %d%d) ssl(%d%d)\n", tty_on,read_tty,write_tty,read_ssl,write_ssl);*/ i = poll(pfd, 3, ptimeout); if (i == -1) { BIO_printf(bio_err, "bad select %d\n", errno); goto shut; /* goto end; */ } } if ((SSL_version(con) == DTLS1_VERSION) && DTLSv1_handle_timeout(con) > 0) { BIO_printf(bio_err, "TIMEOUT occured\n"); } if (!ssl_pending && (pfd[2].revents & (POLLOUT|POLLERR|POLLNVAL))) { if (pfd[2].revents & (POLLERR|POLLNVAL)) { BIO_printf(bio_err, "poll error"); goto shut; } k = SSL_write(con, &(cbuf[cbuf_off]), (unsigned int) cbuf_len); switch (SSL_get_error(con, k)) { case SSL_ERROR_NONE: cbuf_off += k; cbuf_len -= k; if (k <= 0) goto end; /* we have done a write(con,NULL,0); */ if (cbuf_len <= 0) { read_tty = 1; write_ssl = 0; } else { /* if (cbuf_len > 0) */ read_tty = 0; write_ssl = 1; } break; case SSL_ERROR_WANT_WRITE: BIO_printf(bio_c_out, "write W BLOCK\n"); write_ssl = 1; read_tty = 0; break; case SSL_ERROR_WANT_READ: BIO_printf(bio_c_out, "write R BLOCK\n"); write_tty = 0; read_ssl = 1; write_ssl = 0; break; case SSL_ERROR_WANT_X509_LOOKUP: BIO_printf(bio_c_out, "write X BLOCK\n"); break; case SSL_ERROR_ZERO_RETURN: if (cbuf_len != 0) { BIO_printf(bio_c_out, "shutdown\n"); ret = 0; goto shut; } else { read_tty = 1; write_ssl = 0; break; } case SSL_ERROR_SYSCALL: if ((k != 0) || (cbuf_len != 0)) { BIO_printf(bio_err, "write:errno=%d\n", errno); goto shut; } else { read_tty = 1; write_ssl = 0; } break; case SSL_ERROR_SSL: ERR_print_errors(bio_err); goto shut; } } else if (!ssl_pending && (pfd[1].revents & (POLLOUT|POLLERR|POLLNVAL))) { if (pfd[1].revents & (POLLERR|POLLNVAL)) { BIO_printf(bio_err, "poll error"); goto shut; } i = write(fileno(stdout), &(sbuf[sbuf_off]), sbuf_len); if (i <= 0) { BIO_printf(bio_c_out, "DONE\n"); ret = 0; goto shut; /* goto end; */ } sbuf_len -= i; sbuf_off += i; if (sbuf_len <= 0) { read_ssl = 1; write_tty = 0; } } else if (ssl_pending || (pfd[2].revents & (POLLIN|POLLHUP))) { #ifdef RENEG { static int iiii; if (++iiii == 52) { SSL_renegotiate(con); iiii = 0; } } #endif if (peekaboo) { k = p = SSL_peek(con, pbuf, 1024 /* BUFSIZZ */ ); pending = SSL_pending(con); if (SSL_get_error(con, p) == SSL_ERROR_NONE) { if (p <= 0) goto end; pbuf_off = 0; pbuf_len = p; k = SSL_read(con, sbuf, p); } } else { k = SSL_read(con, sbuf, 1024 /* BUFSIZZ */ ); } switch (SSL_get_error(con, k)) { case SSL_ERROR_NONE: if (k <= 0) goto end; sbuf_off = 0; sbuf_len = k; if (peekaboo) { if (p != pending) { ret = -1; BIO_printf(bio_err, "peeked %d but pending %d!\n", p, pending); goto shut; } if (k < p) { ret = -1; BIO_printf(bio_err, "read less than peek!\n"); goto shut; } if (p > 0 && (memcmp(sbuf, pbuf, p) != 0)) { ret = -1; BIO_printf(bio_err, "peek of %d different from read of %d!\n", p, k); goto shut; } } read_ssl = 0; write_tty = 1; break; case SSL_ERROR_WANT_WRITE: BIO_printf(bio_c_out, "read W BLOCK\n"); write_ssl = 1; read_tty = 0; break; case SSL_ERROR_WANT_READ: BIO_printf(bio_c_out, "read R BLOCK\n"); write_tty = 0; read_ssl = 1; if ((read_tty == 0) && (write_ssl == 0)) write_ssl = 1; break; case SSL_ERROR_WANT_X509_LOOKUP: BIO_printf(bio_c_out, "read X BLOCK\n"); break; case SSL_ERROR_SYSCALL: ret = errno; BIO_printf(bio_err, "read:errno=%d\n", ret); goto shut; case SSL_ERROR_ZERO_RETURN: BIO_printf(bio_c_out, "closed\n"); ret = 0; goto shut; case SSL_ERROR_SSL: ERR_print_errors(bio_err); goto shut; /* break; */ } } else if (pfd[0].revents) { if (pfd[0].revents & (POLLERR|POLLNVAL)) { BIO_printf(bio_err, "poll error"); goto shut; } if (crlf) { int j, lf_num; i = read(fileno(stdin), cbuf, BUFSIZZ / 2); lf_num = 0; /* both loops are skipped when i <= 0 */ for (j = 0; j < i; j++) if (cbuf[j] == '\n') lf_num++; for (j = i - 1; j >= 0; j--) { cbuf[j + lf_num] = cbuf[j]; if (cbuf[j] == '\n') { lf_num--; i++; cbuf[j + lf_num] = '\r'; } } assert(lf_num == 0); } else i = read(fileno(stdin), cbuf, BUFSIZZ); if ((!c_ign_eof) && ((i <= 0) || (cbuf[0] == 'Q'))) { BIO_printf(bio_err, "DONE\n"); ret = 0; goto shut; } if ((!c_ign_eof) && (cbuf[0] == 'R')) { BIO_printf(bio_err, "RENEGOTIATING\n"); SSL_renegotiate(con); cbuf_len = 0; } else { cbuf_len = i; cbuf_off = 0; } write_ssl = 1; read_tty = 0; } } ret = 0; shut: if (in_init) print_stuff(bio_c_out, con, full_log); SSL_shutdown(con); shutdown(SSL_get_fd(con), SHUT_RD); close(SSL_get_fd(con)); end: if (con != NULL) { if (prexit != 0) print_stuff(bio_c_out, con, 1); SSL_free(con); } SSL_CTX_free(ctx); X509_free(cert); EVP_PKEY_free(key); free(pass); X509_VERIFY_PARAM_free(vpm); freezero(cbuf, BUFSIZZ); freezero(sbuf, BUFSIZZ); freezero(mbuf, BUFSIZZ); if (bio_c_out != NULL) { BIO_free(bio_c_out); bio_c_out = NULL; } return (ret); } static void print_stuff(BIO * bio, SSL * s, int full) { X509 *peer = NULL; char *p; static const char *space = " "; char buf[BUFSIZ]; STACK_OF(X509) * sk; STACK_OF(X509_NAME) * sk2; const SSL_CIPHER *c; X509_NAME *xn; int j, i; unsigned char *exportedkeymat; if (full) { int got_a_chain = 0; sk = SSL_get_peer_cert_chain(s); if (sk != NULL) { got_a_chain = 1; /* we don't have it for SSL2 * (yet) */ BIO_printf(bio, "---\nCertificate chain\n"); for (i = 0; i < sk_X509_num(sk); i++) { X509_NAME_oneline(X509_get_subject_name( sk_X509_value(sk, i)), buf, sizeof buf); BIO_printf(bio, "%2d s:%s\n", i, buf); X509_NAME_oneline(X509_get_issuer_name( sk_X509_value(sk, i)), buf, sizeof buf); BIO_printf(bio, " i:%s\n", buf); if (c_showcerts) PEM_write_bio_X509(bio, sk_X509_value(sk, i)); } } BIO_printf(bio, "---\n"); peer = SSL_get_peer_certificate(s); if (peer != NULL) { BIO_printf(bio, "Server certificate\n"); if (!(c_showcerts && got_a_chain)) /* Redundant if we * showed the whole * chain */ PEM_write_bio_X509(bio, peer); X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof buf); BIO_printf(bio, "subject=%s\n", buf); X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof buf); BIO_printf(bio, "issuer=%s\n", buf); } else BIO_printf(bio, "no peer certificate available\n"); sk2 = SSL_get_client_CA_list(s); if ((sk2 != NULL) && (sk_X509_NAME_num(sk2) > 0)) { BIO_printf(bio, "---\nAcceptable client certificate CA names\n"); for (i = 0; i < sk_X509_NAME_num(sk2); i++) { xn = sk_X509_NAME_value(sk2, i); X509_NAME_oneline(xn, buf, sizeof(buf)); BIO_write(bio, buf, strlen(buf)); BIO_write(bio, "\n", 1); } } else { BIO_printf(bio, "---\nNo client certificate CA names sent\n"); } p = SSL_get_shared_ciphers(s, buf, sizeof buf); if (p != NULL) { /* * This works only for SSL 2. In later protocol * versions, the client does not know what other * ciphers (in addition to the one to be used in the * current connection) the server supports. */ BIO_printf(bio, "---\nCiphers common between both SSL endpoints:\n"); j = i = 0; while (*p) { if (*p == ':') { BIO_write(bio, space, 15 - j % 25); i++; j = 0; BIO_write(bio, ((i % 3) ? " " : "\n"), 1); } else { BIO_write(bio, p, 1); j++; } p++; } BIO_write(bio, "\n", 1); } ssl_print_tmp_key(bio, s); BIO_printf(bio, "---\nSSL handshake has read %ld bytes and written %ld bytes\n", BIO_number_read(SSL_get_rbio(s)), BIO_number_written(SSL_get_wbio(s))); } BIO_printf(bio, (SSL_cache_hit(s) ? "---\nReused, " : "---\nNew, ")); c = SSL_get_current_cipher(s); BIO_printf(bio, "%s, Cipher is %s\n", SSL_CIPHER_get_version(c), SSL_CIPHER_get_name(c)); if (peer != NULL) { EVP_PKEY *pktmp; pktmp = X509_get_pubkey(peer); BIO_printf(bio, "Server public key is %d bit\n", EVP_PKEY_bits(pktmp)); EVP_PKEY_free(pktmp); } BIO_printf(bio, "Secure Renegotiation IS%s supported\n", SSL_get_secure_renegotiation_support(s) ? "" : " NOT"); /* Compression is not supported and will always be none. */ BIO_printf(bio, "Compression: NONE\n"); BIO_printf(bio, "Expansion: NONE\n"); #ifdef SSL_DEBUG { /* Print out local port of connection: useful for debugging */ int sock; struct sockaddr_in ladd; socklen_t ladd_size = sizeof(ladd); 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)); } #endif { const unsigned char *proto; unsigned int proto_len; SSL_get0_alpn_selected(s, &proto, &proto_len); if (proto_len > 0) { BIO_printf(bio, "ALPN protocol: "); BIO_write(bio, proto, proto_len); BIO_write(bio, "\n", 1); } else BIO_printf(bio, "No ALPN negotiated\n"); } #ifndef OPENSSL_NO_SRTP { SRTP_PROTECTION_PROFILE *srtp_profile = SSL_get_selected_srtp_profile(s); if (srtp_profile) BIO_printf(bio, "SRTP Extension negotiated, profile=%s\n", srtp_profile->name); } #endif SSL_SESSION_print(bio, SSL_get_session(s)); if (keymatexportlabel != NULL) { BIO_printf(bio, "Keying material exporter:\n"); BIO_printf(bio, " Label: '%s'\n", keymatexportlabel); BIO_printf(bio, " Length: %i bytes\n", keymatexportlen); exportedkeymat = malloc(keymatexportlen); if (exportedkeymat != NULL) { if (!SSL_export_keying_material(s, exportedkeymat, keymatexportlen, keymatexportlabel, strlen(keymatexportlabel), NULL, 0, 0)) { BIO_printf(bio, " Error\n"); } else { BIO_printf(bio, " Keying material: "); for (i = 0; i < keymatexportlen; i++) BIO_printf(bio, "%02X", exportedkeymat[i]); BIO_printf(bio, "\n"); } free(exportedkeymat); } } BIO_printf(bio, "---\n"); X509_free(peer); /* flush, or debugging output gets mixed with http response */ (void) BIO_flush(bio); } static int ocsp_resp_cb(SSL * s, void *arg) { const unsigned char *p; int len; OCSP_RESPONSE *rsp; len = SSL_get_tlsext_status_ocsp_resp(s, &p); BIO_puts(arg, "OCSP response: "); if (!p) { BIO_puts(arg, "no response sent\n"); return 1; } rsp = d2i_OCSP_RESPONSE(NULL, &p, len); if (!rsp) { BIO_puts(arg, "response parse error\n"); BIO_dump_indent(arg, (char *) p, len, 4); return 0; } BIO_puts(arg, "\n======================================\n"); OCSP_RESPONSE_print(arg, rsp, 0); BIO_puts(arg, "======================================\n"); OCSP_RESPONSE_free(rsp); return 1; }