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