Home | History | Annotate | Download | only in tool
      1 /* Copyright (c) 2014, Google Inc.
      2  *
      3  * Permission to use, copy, modify, and/or distribute this software for any
      4  * purpose with or without fee is hereby granted, provided that the above
      5  * copyright notice and this permission notice appear in all copies.
      6  *
      7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
     12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
     14 
     15 #include <openssl/base.h>
     16 
     17 #include <stdio.h>
     18 
     19 #if !defined(OPENSSL_WINDOWS)
     20 #include <sys/select.h>
     21 #else
     22 OPENSSL_MSVC_PRAGMA(warning(push, 3))
     23 #include <winsock2.h>
     24 OPENSSL_MSVC_PRAGMA(warning(pop))
     25 #endif
     26 
     27 #include <openssl/err.h>
     28 #include <openssl/pem.h>
     29 #include <openssl/ssl.h>
     30 
     31 #include "../crypto/internal.h"
     32 #include "internal.h"
     33 #include "transport_common.h"
     34 
     35 
     36 static const struct argument kArguments[] = {
     37     {
     38      "-connect", kRequiredArgument,
     39      "The hostname and port of the server to connect to, e.g. foo.com:443",
     40     },
     41     {
     42      "-cipher", kOptionalArgument,
     43      "An OpenSSL-style cipher suite string that configures the offered ciphers",
     44     },
     45     {
     46      "-max-version", kOptionalArgument,
     47      "The maximum acceptable protocol version",
     48     },
     49     {
     50      "-min-version", kOptionalArgument,
     51      "The minimum acceptable protocol version",
     52     },
     53     {
     54      "-server-name", kOptionalArgument,
     55      "The server name to advertise",
     56     },
     57     {
     58      "-select-next-proto", kOptionalArgument,
     59      "An NPN protocol to select if the server supports NPN",
     60     },
     61     {
     62      "-alpn-protos", kOptionalArgument,
     63      "A comma-separated list of ALPN protocols to advertise",
     64     },
     65     {
     66      "-fallback-scsv", kBooleanArgument,
     67      "Enable FALLBACK_SCSV",
     68     },
     69     {
     70      "-ocsp-stapling", kBooleanArgument,
     71      "Advertise support for OCSP stabling",
     72     },
     73     {
     74      "-signed-certificate-timestamps", kBooleanArgument,
     75      "Advertise support for signed certificate timestamps",
     76     },
     77     {
     78      "-channel-id-key", kOptionalArgument,
     79      "The key to use for signing a channel ID",
     80     },
     81     {
     82      "-false-start", kBooleanArgument,
     83      "Enable False Start",
     84     },
     85     { "-session-in", kOptionalArgument,
     86       "A file containing a session to resume.",
     87     },
     88     { "-session-out", kOptionalArgument,
     89       "A file to write the negotiated session to.",
     90     },
     91     {
     92       "-key", kOptionalArgument,
     93       "Private-key file to use (default is no client certificate)",
     94     },
     95     {
     96       "-starttls", kOptionalArgument,
     97       "A STARTTLS mini-protocol to run before the TLS handshake. Supported"
     98       " values: 'smtp'",
     99     },
    100     {
    101      "-grease", kBooleanArgument,
    102      "Enable GREASE",
    103     },
    104     {
    105       "-resume", kBooleanArgument,
    106       "Establish a second connection resuming the original connection.",
    107     },
    108     {
    109       "-root-certs", kOptionalArgument,
    110       "A filename containing one of more PEM root certificates. Implies that "
    111       "verification is required.",
    112     },
    113     {
    114         "-early-data", kBooleanArgument, "Allow early data",
    115     },
    116     {
    117      "", kOptionalArgument, "",
    118     },
    119 };
    120 
    121 static bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file) {
    122   bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file()));
    123   if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
    124     return nullptr;
    125   }
    126   bssl::UniquePtr<EVP_PKEY> pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr,
    127                                  nullptr, nullptr));
    128   return pkey;
    129 }
    130 
    131 static int NextProtoSelectCallback(SSL* ssl, uint8_t** out, uint8_t* outlen,
    132                                    const uint8_t* in, unsigned inlen, void* arg) {
    133   *out = reinterpret_cast<uint8_t *>(arg);
    134   *outlen = strlen(reinterpret_cast<const char *>(arg));
    135   return SSL_TLSEXT_ERR_OK;
    136 }
    137 
    138 static FILE *g_keylog_file = nullptr;
    139 
    140 static void KeyLogCallback(const SSL *ssl, const char *line) {
    141   fprintf(g_keylog_file, "%s\n", line);
    142   fflush(g_keylog_file);
    143 }
    144 
    145 static bssl::UniquePtr<BIO> session_out;
    146 static bssl::UniquePtr<SSL_SESSION> resume_session;
    147 
    148 static int NewSessionCallback(SSL *ssl, SSL_SESSION *session) {
    149   if (session_out) {
    150     if (!PEM_write_bio_SSL_SESSION(session_out.get(), session) ||
    151         BIO_flush(session_out.get()) <= 0) {
    152       fprintf(stderr, "Error while saving session:\n");
    153       ERR_print_errors_cb(PrintErrorCallback, stderr);
    154       return 0;
    155     }
    156   }
    157   resume_session = bssl::UniquePtr<SSL_SESSION>(session);
    158   return 1;
    159 }
    160 
    161 static bool WaitForSession(SSL *ssl, int sock) {
    162   fd_set read_fds;
    163   FD_ZERO(&read_fds);
    164 
    165   if (!SocketSetNonBlocking(sock, true)) {
    166     return false;
    167   }
    168 
    169   while (!resume_session) {
    170     FD_SET(sock, &read_fds);
    171     int ret = select(sock + 1, &read_fds, NULL, NULL, NULL);
    172     if (ret <= 0) {
    173       perror("select");
    174       return false;
    175     }
    176 
    177     uint8_t buffer[512];
    178     int ssl_ret = SSL_read(ssl, buffer, sizeof(buffer));
    179 
    180     if (ssl_ret <= 0) {
    181       int ssl_err = SSL_get_error(ssl, ssl_ret);
    182       if (ssl_err == SSL_ERROR_WANT_READ) {
    183         continue;
    184       }
    185       fprintf(stderr, "Error while reading: %d\n", ssl_err);
    186       ERR_print_errors_cb(PrintErrorCallback, stderr);
    187       return false;
    188     }
    189   }
    190 
    191   return true;
    192 }
    193 
    194 static bool DoConnection(SSL_CTX *ctx,
    195                          std::map<std::string, std::string> args_map,
    196                          bool (*cb)(SSL *ssl, int sock)) {
    197   int sock = -1;
    198   if (!Connect(&sock, args_map["-connect"])) {
    199     return false;
    200   }
    201 
    202   if (args_map.count("-starttls") != 0) {
    203     const std::string& starttls = args_map["-starttls"];
    204     if (starttls == "smtp") {
    205       if (!DoSMTPStartTLS(sock)) {
    206         return false;
    207       }
    208     } else {
    209       fprintf(stderr, "Unknown value for -starttls: %s\n", starttls.c_str());
    210       return false;
    211     }
    212   }
    213 
    214   bssl::UniquePtr<BIO> bio(BIO_new_socket(sock, BIO_CLOSE));
    215   bssl::UniquePtr<SSL> ssl(SSL_new(ctx));
    216 
    217   if (args_map.count("-server-name") != 0) {
    218     SSL_set_tlsext_host_name(ssl.get(), args_map["-server-name"].c_str());
    219   }
    220 
    221   if (args_map.count("-session-in") != 0) {
    222     bssl::UniquePtr<BIO> in(BIO_new_file(args_map["-session-in"].c_str(),
    223                                          "rb"));
    224     if (!in) {
    225       fprintf(stderr, "Error reading session\n");
    226       ERR_print_errors_cb(PrintErrorCallback, stderr);
    227       return false;
    228     }
    229     bssl::UniquePtr<SSL_SESSION> session(PEM_read_bio_SSL_SESSION(in.get(),
    230                                          nullptr, nullptr, nullptr));
    231     if (!session) {
    232       fprintf(stderr, "Error reading session\n");
    233       ERR_print_errors_cb(PrintErrorCallback, stderr);
    234       return false;
    235     }
    236     SSL_set_session(ssl.get(), session.get());
    237   } else if (resume_session) {
    238     SSL_set_session(ssl.get(), resume_session.get());
    239   }
    240 
    241   SSL_set_bio(ssl.get(), bio.get(), bio.get());
    242   bio.release();
    243 
    244   int ret = SSL_connect(ssl.get());
    245   if (ret != 1) {
    246     int ssl_err = SSL_get_error(ssl.get(), ret);
    247     fprintf(stderr, "Error while connecting: %d\n", ssl_err);
    248     ERR_print_errors_cb(PrintErrorCallback, stderr);
    249     return false;
    250   }
    251 
    252   fprintf(stderr, "Connected.\n");
    253   PrintConnectionInfo(ssl.get());
    254 
    255   return cb(ssl.get(), sock);
    256 }
    257 
    258 bool Client(const std::vector<std::string> &args) {
    259   if (!InitSocketLibrary()) {
    260     return false;
    261   }
    262 
    263   std::map<std::string, std::string> args_map;
    264 
    265   if (!ParseKeyValueArguments(&args_map, args, kArguments)) {
    266     PrintUsage(kArguments);
    267     return false;
    268   }
    269 
    270   bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(SSLv23_client_method()));
    271 
    272   const char *keylog_file = getenv("SSLKEYLOGFILE");
    273   if (keylog_file) {
    274     g_keylog_file = fopen(keylog_file, "a");
    275     if (g_keylog_file == nullptr) {
    276       perror("fopen");
    277       return false;
    278     }
    279     SSL_CTX_set_keylog_callback(ctx.get(), KeyLogCallback);
    280   }
    281 
    282   if (args_map.count("-cipher") != 0 &&
    283       !SSL_CTX_set_strict_cipher_list(ctx.get(), args_map["-cipher"].c_str())) {
    284     fprintf(stderr, "Failed setting cipher list\n");
    285     return false;
    286   }
    287 
    288   uint16_t max_version = TLS1_3_VERSION;
    289   if (args_map.count("-max-version") != 0 &&
    290       !VersionFromString(&max_version, args_map["-max-version"])) {
    291     fprintf(stderr, "Unknown protocol version: '%s'\n",
    292             args_map["-max-version"].c_str());
    293     return false;
    294   }
    295 
    296   if (!SSL_CTX_set_max_proto_version(ctx.get(), max_version)) {
    297     return false;
    298   }
    299 
    300   if (args_map.count("-min-version") != 0) {
    301     uint16_t version;
    302     if (!VersionFromString(&version, args_map["-min-version"])) {
    303       fprintf(stderr, "Unknown protocol version: '%s'\n",
    304               args_map["-min-version"].c_str());
    305       return false;
    306     }
    307     if (!SSL_CTX_set_min_proto_version(ctx.get(), version)) {
    308       return false;
    309     }
    310   }
    311 
    312   if (args_map.count("-select-next-proto") != 0) {
    313     const std::string &proto = args_map["-select-next-proto"];
    314     if (proto.size() > 255) {
    315       fprintf(stderr, "Bad NPN protocol: '%s'\n", proto.c_str());
    316       return false;
    317     }
    318     // |SSL_CTX_set_next_proto_select_cb| is not const-correct.
    319     SSL_CTX_set_next_proto_select_cb(ctx.get(), NextProtoSelectCallback,
    320                                      const_cast<char *>(proto.c_str()));
    321   }
    322 
    323   if (args_map.count("-alpn-protos") != 0) {
    324     const std::string &alpn_protos = args_map["-alpn-protos"];
    325     std::vector<uint8_t> wire;
    326     size_t i = 0;
    327     while (i <= alpn_protos.size()) {
    328       size_t j = alpn_protos.find(',', i);
    329       if (j == std::string::npos) {
    330         j = alpn_protos.size();
    331       }
    332       size_t len = j - i;
    333       if (len > 255) {
    334         fprintf(stderr, "Invalid ALPN protocols: '%s'\n", alpn_protos.c_str());
    335         return false;
    336       }
    337       wire.push_back(static_cast<uint8_t>(len));
    338       wire.resize(wire.size() + len);
    339       OPENSSL_memcpy(wire.data() + wire.size() - len, alpn_protos.data() + i,
    340                      len);
    341       i = j + 1;
    342     }
    343     if (SSL_CTX_set_alpn_protos(ctx.get(), wire.data(), wire.size()) != 0) {
    344       return false;
    345     }
    346   }
    347 
    348   if (args_map.count("-fallback-scsv") != 0) {
    349     SSL_CTX_set_mode(ctx.get(), SSL_MODE_SEND_FALLBACK_SCSV);
    350   }
    351 
    352   if (args_map.count("-ocsp-stapling") != 0) {
    353     SSL_CTX_enable_ocsp_stapling(ctx.get());
    354   }
    355 
    356   if (args_map.count("-signed-certificate-timestamps") != 0) {
    357     SSL_CTX_enable_signed_cert_timestamps(ctx.get());
    358   }
    359 
    360   if (args_map.count("-channel-id-key") != 0) {
    361     bssl::UniquePtr<EVP_PKEY> pkey =
    362         LoadPrivateKey(args_map["-channel-id-key"]);
    363     if (!pkey || !SSL_CTX_set1_tls_channel_id(ctx.get(), pkey.get())) {
    364       return false;
    365     }
    366   }
    367 
    368   if (args_map.count("-false-start") != 0) {
    369     SSL_CTX_set_mode(ctx.get(), SSL_MODE_ENABLE_FALSE_START);
    370   }
    371 
    372   if (args_map.count("-key") != 0) {
    373     const std::string &key = args_map["-key"];
    374     if (!SSL_CTX_use_PrivateKey_file(ctx.get(), key.c_str(), SSL_FILETYPE_PEM)) {
    375       fprintf(stderr, "Failed to load private key: %s\n", key.c_str());
    376       return false;
    377     }
    378     if (!SSL_CTX_use_certificate_chain_file(ctx.get(), key.c_str())) {
    379       fprintf(stderr, "Failed to load cert chain: %s\n", key.c_str());
    380       return false;
    381     }
    382   }
    383 
    384   SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_CLIENT);
    385   SSL_CTX_sess_set_new_cb(ctx.get(), NewSessionCallback);
    386 
    387   if (args_map.count("-session-out") != 0) {
    388     session_out.reset(BIO_new_file(args_map["-session-out"].c_str(), "wb"));
    389     if (!session_out) {
    390       fprintf(stderr, "Error while opening %s:\n",
    391               args_map["-session-out"].c_str());
    392       ERR_print_errors_cb(PrintErrorCallback, stderr);
    393       return false;
    394     }
    395   }
    396 
    397   if (args_map.count("-grease") != 0) {
    398     SSL_CTX_set_grease_enabled(ctx.get(), 1);
    399   }
    400 
    401   if (args_map.count("-root-certs") != 0) {
    402     if (!SSL_CTX_load_verify_locations(
    403             ctx.get(), args_map["-root-certs"].c_str(), nullptr)) {
    404       fprintf(stderr, "Failed to load root certificates.\n");
    405       ERR_print_errors_cb(PrintErrorCallback, stderr);
    406       return false;
    407     }
    408     SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_PEER, nullptr);
    409   }
    410 
    411   if (args_map.count("-early-data") != 0) {
    412     SSL_CTX_set_early_data_enabled(ctx.get(), 1);
    413   }
    414 
    415   if (args_map.count("-resume") != 0 &&
    416       !DoConnection(ctx.get(), args_map, &WaitForSession)) {
    417     return false;
    418   }
    419 
    420   return DoConnection(ctx.get(), args_map, &TransferData);
    421 }
    422