New function X509_get0_pubkey
[openssl.git] / apps / s_client.c
index 22aa27080d0fc1801383ecd25fbbccd5b7f0504d..5aa1adc51ec88a6fd68b08805cdd5ab13ad00210 100644 (file)
  * OTHERWISE.
  */
 
-#include <assert.h>
 #include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -161,6 +160,7 @@ typedef unsigned int u_int;
 #include <openssl/rand.h>
 #include <openssl/ocsp.h>
 #include <openssl/bn.h>
+#include <openssl/async.h>
 #ifndef OPENSSL_NO_SRP
 # include <openssl/srp.h>
 #endif
@@ -176,12 +176,14 @@ typedef unsigned int u_int;
 
 #undef BUFSIZZ
 #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 int async = 0;
 static int c_nbio = 0;
 static int c_tlsextdebug = 0;
 static int c_status_req = 0;
@@ -396,7 +398,7 @@ static char *ssl_give_srp_client_pwd_cb(SSL *s, void *arg)
 
 #endif
 
-char *srtp_profiles = NULL;
+static char *srtp_profiles = NULL;
 
 #ifndef OPENSSL_NO_NEXTPROTONEG
 /* This the context that we pass to next_proto_cb */
@@ -467,11 +469,12 @@ typedef enum OPTION_choice {
     OPT_SRP_LATEUSER, OPT_SRP_MOREGROUPS, OPT_SSL3,
     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_CHAINCAPATH, OPT_VERIFYCAPATH,
-    OPT_KEY, OPT_RECONNECT, OPT_BUILD_CHAIN, OPT_CAFILE,
+    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_JPAKE,
     OPT_USE_SRTP, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, OPT_SMTPHOST,
+    OPT_ASYNC,
     OPT_V_ENUM,
     OPT_X_ENUM,
     OPT_S_ENUM,
@@ -496,6 +499,10 @@ OPTIONS s_client_options[] = {
     {"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"},
+    {"no-CAfile", OPT_NOCAFILE, '-',
+     "Do not load the default certificates file"},
+    {"no-CApath", OPT_NOCAPATH, '-',
+     "Do not load certificates from the default certificates directory"},
     {"reconnect", OPT_RECONNECT, '-',
      "Drop and re-make the connection with the same Session-ID"},
     {"pause", OPT_PAUSE, '-', "Sleep  after each read and write system call"},
@@ -513,13 +520,14 @@ OPTIONS s_client_options[] = {
     {"tls1_1", OPT_TLS1_1, '-', "Just use TLSv1.1"},
     {"tls1", OPT_TLS1, '-', "Just use TLSv1"},
     {"starttls", OPT_STARTTLS, 's',
-     "Use the STARTTLS command before starting TLS"},
-    {"xmpphost", OPT_XMPPHOST, 's', "Host to use with \"-starttls xmpp\""},
+     "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, '<',
+    {"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"},
@@ -552,13 +560,14 @@ OPTIONS s_client_options[] = {
      "types  Send empty ClientHello extensions (comma-separated numbers)"},
     {"alpn", OPT_ALPN, 's',
      "Enable ALPN extension, considering named protocols supported (comma-separated list)"},
+    {"async", OPT_ASYNC, '-', "Support asynchronous operation"},
     OPT_S_OPTIONS,
     OPT_V_OPTIONS,
     OPT_X_OPTIONS,
 #ifndef OPENSSL_NO_SSL3
     {"ssl3", OPT_SSL3, '-', "Just use SSLv3"},
 #endif
-#ifndef OPENSSL_NO_DTLS1
+#ifndef OPENSSL_NO_DTLS
     {"dtls", OPT_DTLS, '-'},
     {"dtls1", OPT_DTLS1, '-', "Just use DTLSv1"},
     {"dtls1_2", OPT_DTLS1_2, '-'},
@@ -588,7 +597,7 @@ OPTIONS s_client_options[] = {
      "SRP username into second ClientHello message"},
     {"srp_moregroups", OPT_SRP_MOREGROUPS, '-',
      "Tolerate other than the known g N values."},
-    {"srp_strength", OPT_SRP_STRENGTH, 'p', "Minimal mength in bits for N"},
+    {"srp_strength", OPT_SRP_STRENGTH, 'p', "Minimal length in bits for N"},
 #endif
 #ifndef OPENSSL_NO_NEXTPROTONEG
     {"nextprotoneg", OPT_NEXTPROTONEG, 's',
@@ -609,7 +618,9 @@ typedef enum PROTOCOL_choice {
     PROTO_FTP,
     PROTO_TELNET,
     PROTO_XMPP,
-    PROTO_CONNECT
+    PROTO_XMPP_SERVER,
+    PROTO_CONNECT,
+    PROTO_IRC
 } PROTOCOL_CHOICE;
 
 static OPT_PAIR services[] = {
@@ -618,7 +629,9 @@ static OPT_PAIR services[] = {
     {"imap", PROTO_IMAP},
     {"ftp", PROTO_FTP},
     {"xmpp", PROTO_XMPP},
+    {"xmpp-server", PROTO_XMPP_SERVER},
     {"telnet", PROTO_TELNET},
+    {"irc", PROTO_IRC},
     {NULL}
 };
 
@@ -649,12 +662,13 @@ int s_client_main(int argc, char **argv)
     struct sockaddr peer;
     struct timeval timeout, *timeoutp;
     fd_set readfds, writefds;
+    int noCApath = 0, noCAfile = 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 enable_timeouts = 0, sdebug = 0, peerlen = sizeof peer;
     int reconnect = 0, verify = SSL_VERIFY_NONE, vpmtouched = 0;
-    int ret = 1, in_init = 1, i, nbio_test = 0, s, k, width, state = 0;
+    int ret = 1, in_init = 1, i, nbio_test = 0, s = -1, k, width, state = 0;
     int sbuf_len, sbuf_off, socket_type = SOCK_STREAM, cmdletters = 1;
     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;
@@ -889,6 +903,7 @@ int s_client_main(int argc, char **argv)
                 BIO_printf(bio_err, "Not a hex number '%s'\n", psk_key);
                 goto end;
             }
+            break;
 #else
         case OPT_PSK_IDENTITY:
         case OPT_PSK:
@@ -939,7 +954,7 @@ int s_client_main(int argc, char **argv)
         case OPT_TLS1:
             meth = TLSv1_client_method();
             break;
-#ifndef OPENSSL_NO_DTLS1
+#ifndef OPENSSL_NO_DTLS
         case OPT_DTLS:
             meth = DTLS_client_method();
             socket_type = SOCK_DGRAM;
@@ -988,6 +1003,9 @@ int s_client_main(int argc, char **argv)
         case OPT_CAPATH:
             CApath = opt_arg();
             break;
+        case OPT_NOCAPATH:
+            noCApath = 1;
+            break;
         case OPT_CHAINCAPATH:
             chCApath = opt_arg();
             break;
@@ -1000,6 +1018,9 @@ int s_client_main(int argc, char **argv)
         case OPT_CAFILE:
             CAfile = opt_arg();
             break;
+        case OPT_NOCAFILE:
+            noCAfile = 1;
+            break;
         case OPT_CHAINCAFILE:
             chCAfile = opt_arg();
             break;
@@ -1044,14 +1065,14 @@ int s_client_main(int argc, char **argv)
         case OPT_KEYMATEXPORTLEN:
             keymatexportlen = atoi(opt_arg());
             break;
+        case OPT_ASYNC:
+            async = 1;
+            break;
         }
     }
     argc = opt_num_rest();
     argv = opt_rest();
 
-    if (!app_load_modules(NULL))
-        goto end;
-
     if (proxystr) {
         if (connectstr == NULL) {
             BIO_printf(bio_err, "%s: -proxy requires use of -connect\n", prog);
@@ -1159,9 +1180,9 @@ int s_client_main(int argc, char **argv)
         if (c_quiet && !c_debug) {
             bio_c_out = BIO_new(BIO_s_null());
             if (c_msg && !bio_c_msg)
-                bio_c_msg = dup_bio_out();
+                bio_c_msg = dup_bio_out(FORMAT_TEXT);
         } else if (bio_c_out == NULL)
-            bio_c_out = dup_bio_out();
+            bio_c_out = dup_bio_out(FORMAT_TEXT);
     }
 #ifndef OPENSSL_NO_SRP
     if (!app_passwd(srppass, NULL, &srp_arg.srppassin, NULL)) {
@@ -1185,6 +1206,11 @@ int s_client_main(int argc, char **argv)
         goto end;
     }
 
+    if (async) {
+        SSL_CTX_set_mode(ctx, SSL_MODE_ASYNC);
+        ASYNC_init(1, 0, 0);
+    }
+
     if (!config_ctx(cctx, ssl_args, ctx, 1, jpake_secret == NULL))
         goto end;
 
@@ -1264,7 +1290,7 @@ int s_client_main(int argc, char **argv)
 
     SSL_CTX_set_verify(ctx, verify, verify_callback);
 
-    if (!ctx_set_verify_locations(ctx, CAfile, CApath)) {
+    if (!ctx_set_verify_locations(ctx, CAfile, CApath, noCAfile, noCApath)) {
         ERR_print_errors(bio_err);
         goto end;
     }
@@ -1368,7 +1394,7 @@ int s_client_main(int argc, char **argv)
             goto end;
         }
 
-        (void)BIO_ctrl_set_connected(sbio, 1, &peer);
+        (void)BIO_ctrl_set_connected(sbio, &peer);
 
         if (enable_timeouts) {
             timeout.tv_sec = 0;
@@ -1487,8 +1513,8 @@ int s_client_main(int argc, char **argv)
             BIO_free(fbio);
             if (!foundit)
                 BIO_printf(bio_err,
-                           "didn't found starttls in server response,"
-                           " try anyway...\n");
+                           "didn't find starttls in server response,"
+                           " trying anyway...\n");
             BIO_printf(sbio, "STARTTLS\r\n");
             BIO_read(sbio, sbuf, BUFSIZZ);
         }
@@ -1525,8 +1551,8 @@ int s_client_main(int argc, char **argv)
             BIO_free(fbio);
             if (!foundit)
                 BIO_printf(bio_err,
-                           "didn't found STARTTLS in server response,"
-                           " try anyway...\n");
+                           "didn't find STARTTLS in server response,"
+                           " trying anyway...\n");
             BIO_printf(sbio, ". STARTTLS\r\n");
             BIO_read(sbio, sbuf, BUFSIZZ);
         }
@@ -1548,11 +1574,13 @@ int s_client_main(int argc, char **argv)
         }
         break;
     case PROTO_XMPP:
+    case PROTO_XMPP_SERVER:
         {
             int seen = 0;
             BIO_printf(sbio, "<stream:stream "
                        "xmlns:stream='http://etherx.jabber.org/streams' "
-                       "xmlns='jabber:client' to='%s' version='1.0'>",
+                       "xmlns='jabber:%s' to='%s' version='1.0'>",
+                       starttls_proto == PROTO_XMPP ? "client" : "server",
                        xmpphost ? xmpphost : host);
             seen = BIO_read(sbio, mbuf, BUFSIZZ);
             mbuf[seen] = 0;
@@ -1631,6 +1659,67 @@ int s_client_main(int argc, char **argv)
             }
         }
         break;
+    case PROTO_IRC:
+        {
+            int numeric;
+            BIO *fbio = BIO_new(BIO_f_buffer());
+
+            BIO_push(fbio, sbio);
+            BIO_printf(fbio, "STARTTLS\r\n");
+            (void)BIO_flush(fbio);
+            width = SSL_get_fd(con) + 1;
+
+            do {
+                numeric = 0;
+
+                FD_ZERO(&readfds);
+                openssl_fdset(SSL_get_fd(con), &readfds);
+                timeout.tv_sec = S_CLIENT_IRC_READ_TIMEOUT;
+                timeout.tv_usec = 0;
+                /*
+                 * If the IRCd doesn't respond within
+                 * S_CLIENT_IRC_READ_TIMEOUT seconds, assume
+                 * it doesn't support STARTTLS. Many IRCds
+                 * will not give _any_ sort of response to a
+                 * STARTTLS command when it's not supported.
+                 */
+                if (!BIO_get_buffer_num_lines(fbio)
+                    && !BIO_pending(fbio)
+                    && !BIO_pending(sbio)
+                    && select(width, (void *)&readfds, NULL, NULL,
+                              &timeout) < 1) {
+                    BIO_printf(bio_err,
+                               "Timeout waiting for response (%d seconds).\n",
+                               S_CLIENT_IRC_READ_TIMEOUT);
+                    break;
+                }
+
+                mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
+                if (mbuf_len < 1 || sscanf(mbuf, "%*s %d", &numeric) != 1)
+                    break;
+                /* :example.net 451 STARTTLS :You have not registered */
+                /* :example.net 421 STARTTLS :Unknown command */
+                if ((numeric == 451 || numeric == 421)
+                    && strstr(mbuf, "STARTTLS") != NULL) {
+                    BIO_printf(bio_err, "STARTTLS not supported: %s", mbuf);
+                    break;
+                }
+                if (numeric == 691) {
+                    BIO_printf(bio_err, "STARTTLS negotiation failed: ");
+                    ERR_print_errors(bio_err);
+                    break;
+                }
+            } while (numeric != 670);
+
+            (void)BIO_flush(fbio);
+            BIO_pop(fbio);
+            BIO_free(fbio);
+            if (numeric != 670) {
+                BIO_printf(bio_err, "Server does not support STARTTLS.\n");
+                ret = 1;
+                goto shut;
+            }
+        }
     }
 
     for (;;) {
@@ -1806,6 +1895,12 @@ int s_client_main(int argc, char **argv)
                 write_ssl = 1;
                 read_tty = 0;
                 break;
+            case SSL_ERROR_WANT_ASYNC:
+                BIO_printf(bio_c_out, "write A BLOCK\n");
+                wait_for_async(con);
+                write_ssl = 1;
+                read_tty = 0;
+                break;
             case SSL_ERROR_WANT_READ:
                 BIO_printf(bio_c_out, "write R BLOCK\n");
                 write_tty = 0;
@@ -1888,6 +1983,14 @@ int s_client_main(int argc, char **argv)
                 read_ssl = 0;
                 write_tty = 1;
                 break;
+            case SSL_ERROR_WANT_ASYNC:
+                BIO_printf(bio_c_out, "read A BLOCK\n");
+                wait_for_async(con);
+                write_tty = 0;
+                read_ssl = 1;
+                if ((read_tty == 0) && (write_ssl == 0))
+                    write_ssl = 1;
+                break;
             case SSL_ERROR_WANT_WRITE:
                 BIO_printf(bio_c_out, "read W BLOCK\n");
                 write_ssl = 1;
@@ -1998,6 +2101,9 @@ int s_client_main(int argc, char **argv)
             print_stuff(bio_c_out, con, 1);
         SSL_free(con);
     }
+    if (async) {
+        ASYNC_cleanup(1);
+    }
 #if !defined(OPENSSL_NO_NEXTPROTONEG)
     OPENSSL_free(next_proto.data);
 #endif
@@ -2007,6 +2113,9 @@ int s_client_main(int argc, char **argv)
     EVP_PKEY_free(key);
     sk_X509_pop_free(chain, X509_free);
     OPENSSL_free(pass);
+#ifndef OPENSSL_NO_SRP
+    OPENSSL_free(srp_arg.srppassin);
+#endif
     X509_VERIFY_PARAM_free(vpm);
     ssl_excert_free(exc);
     sk_OPENSSL_STRING_free(ssl_args);
@@ -2087,7 +2196,7 @@ static void print_stuff(BIO *bio, SSL *s, int full)
         ssl_print_tmp_key(bio, s);
 
         BIO_printf(bio,
-                   "---\nSSL handshake has read %ld bytes and written %ld bytes\n",
+                   "---\nSSL handshake has read %"PRIu64" bytes and written %"PRIu64" bytes\n",
                    BIO_number_read(SSL_get_rbio(s)),
                    BIO_number_written(SSL_get_wbio(s)));
     }
@@ -2097,10 +2206,9 @@ static void print_stuff(BIO *bio, SSL *s, int full)
                SSL_CIPHER_get_version(c), SSL_CIPHER_get_name(c));
     if (peer != NULL) {
         EVP_PKEY *pktmp;
-        pktmp = X509_get_pubkey(peer);
+        pktmp = X509_get0_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");