blob: 31378d6b2f8f14b344aa932ac8666e28b18c3e24 [file] [log] [blame]
Adam Langleyaacec172014-06-20 12:00:00 -07001/* 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
David Benjamind28f59c2015-11-17 22:32:50 -050017#include <stdio.h>
18
Steven Valdez87c0bb22016-12-07 10:31:16 -050019#if !defined(OPENSSL_WINDOWS)
20#include <sys/select.h>
21#else
David Benjamin7c7ab212017-01-10 15:20:19 -050022OPENSSL_MSVC_PRAGMA(warning(push, 3))
Steven Valdez87c0bb22016-12-07 10:31:16 -050023#include <winsock2.h>
David Benjamin7c7ab212017-01-10 15:20:19 -050024OPENSSL_MSVC_PRAGMA(warning(pop))
Steven Valdez87c0bb22016-12-07 10:31:16 -050025#endif
26
Adam Langleyaacec172014-06-20 12:00:00 -070027#include <openssl/err.h>
David Benjamin05709232015-03-23 19:01:33 -040028#include <openssl/pem.h>
Adam Langleyaacec172014-06-20 12:00:00 -070029#include <openssl/ssl.h>
30
David Benjamin17cf2cb2016-12-13 01:07:13 -050031#include "../crypto/internal.h"
Adam Langleyaacec172014-06-20 12:00:00 -070032#include "internal.h"
Dave Tapuskab8a824d2014-12-10 19:09:52 -050033#include "transport_common.h"
Adam Langleyaacec172014-06-20 12:00:00 -070034
35
36static const struct argument kArguments[] = {
37 {
David Benjamin712f3722017-04-05 14:52:09 -040038 "-connect", kRequiredArgument,
39 "The hostname and port of the server to connect to, e.g. foo.com:443",
Adam Langleyaacec172014-06-20 12:00:00 -070040 },
41 {
David Benjamin712f3722017-04-05 14:52:09 -040042 "-cipher", kOptionalArgument,
43 "An OpenSSL-style cipher suite string that configures the offered "
44 "ciphers",
Adam Langley5f51c252014-10-28 15:45:39 -070045 },
46 {
Piotr Sikorad0757062017-04-14 02:59:34 -070047 "-curves", kOptionalArgument,
48 "An OpenSSL-style ECDH curves list that configures the offered curves",
49 },
50 {
David Benjaminbf17f4f2020-02-05 17:37:40 -050051 "-sigalgs", kOptionalArgument,
52 "An OpenSSL-style signature algorithms list that configures the "
53 "signature algorithm preferences",
54 },
55 {
David Benjamin712f3722017-04-05 14:52:09 -040056 "-max-version", kOptionalArgument,
57 "The maximum acceptable protocol version",
David Benjamin05709232015-03-23 19:01:33 -040058 },
59 {
David Benjamin712f3722017-04-05 14:52:09 -040060 "-min-version", kOptionalArgument,
61 "The minimum acceptable protocol version",
David Benjamin05709232015-03-23 19:01:33 -040062 },
63 {
David Benjamin712f3722017-04-05 14:52:09 -040064 "-server-name", kOptionalArgument, "The server name to advertise",
David Benjamin05709232015-03-23 19:01:33 -040065 },
66 {
David Benjamin712f3722017-04-05 14:52:09 -040067 "-select-next-proto", kOptionalArgument,
68 "An NPN protocol to select if the server supports NPN",
David Benjamin05709232015-03-23 19:01:33 -040069 },
70 {
David Benjamin712f3722017-04-05 14:52:09 -040071 "-alpn-protos", kOptionalArgument,
72 "A comma-separated list of ALPN protocols to advertise",
David Benjamin05709232015-03-23 19:01:33 -040073 },
74 {
David Benjamin712f3722017-04-05 14:52:09 -040075 "-fallback-scsv", kBooleanArgument, "Enable FALLBACK_SCSV",
David Benjamin05709232015-03-23 19:01:33 -040076 },
77 {
David Benjamin712f3722017-04-05 14:52:09 -040078 "-ocsp-stapling", kBooleanArgument,
79 "Advertise support for OCSP stabling",
David Benjamin05709232015-03-23 19:01:33 -040080 },
81 {
David Benjamin712f3722017-04-05 14:52:09 -040082 "-signed-certificate-timestamps", kBooleanArgument,
83 "Advertise support for signed certificate timestamps",
David Benjamin05709232015-03-23 19:01:33 -040084 },
85 {
David Benjamin712f3722017-04-05 14:52:09 -040086 "-channel-id-key", kOptionalArgument,
87 "The key to use for signing a channel ID",
David Benjamin05709232015-03-23 19:01:33 -040088 },
89 {
David Benjamin712f3722017-04-05 14:52:09 -040090 "-false-start", kBooleanArgument, "Enable False Start",
David Benjamin621f95a2015-08-28 15:08:34 -040091 },
David Benjamin1043ac02015-06-04 15:26:04 -040092 {
David Benjamin712f3722017-04-05 14:52:09 -040093 "-session-in", kOptionalArgument,
94 "A file containing a session to resume.",
David Benjamin86e412d2015-12-02 19:34:58 -050095 },
96 {
David Benjamin712f3722017-04-05 14:52:09 -040097 "-session-out", kOptionalArgument,
98 "A file to write the negotiated session to.",
Adam Langley403c52a2016-07-07 14:52:02 -070099 },
100 {
David Benjamin712f3722017-04-05 14:52:09 -0400101 "-key", kOptionalArgument,
David Benjamincb3af3e2017-04-09 09:52:47 -0400102 "PEM-encoded file containing the private key.",
103 },
104 {
105 "-cert", kOptionalArgument,
106 "PEM-encoded file containing the leaf certificate and optional "
107 "certificate chain. This is taken from the -key argument if this "
108 "argument is not provided.",
David Benjamin65ac9972016-09-02 21:35:25 -0400109 },
110 {
David Benjamin712f3722017-04-05 14:52:09 -0400111 "-starttls", kOptionalArgument,
112 "A STARTTLS mini-protocol to run before the TLS handshake. Supported"
113 " values: 'smtp'",
Steven Valdez87c0bb22016-12-07 10:31:16 -0500114 },
115 {
David Benjamin712f3722017-04-05 14:52:09 -0400116 "-grease", kBooleanArgument, "Enable GREASE",
117 },
118 {
119 "-test-resumption", kBooleanArgument,
120 "Connect to the server twice. The first connection is closed once a "
121 "session is established. The second connection offers it.",
122 },
123 {
124 "-root-certs", kOptionalArgument,
Pete Bentley3e96cd42020-01-10 12:15:34 +0000125 "A filename containing one or more PEM root certificates. Implies that "
David Benjamin712f3722017-04-05 14:52:09 -0400126 "verification is required.",
Adam Langleye5dfb522017-02-03 10:31:00 -0800127 },
128 {
Pete Bentley3e96cd42020-01-10 12:15:34 +0000129 "-root-cert-dir", kOptionalArgument,
130 "A directory containing one or more root certificate PEM files in "
131 "OpenSSL's hashed-directory format. Implies that verification is "
132 "required.",
133 },
134 {
David Benjamin3bcbb372017-11-11 13:16:46 +0800135 "-early-data", kOptionalArgument, "Enable early data. The argument to "
Steven Valdez1530ef32017-11-30 12:02:42 -0500136 "this flag is the early data to send or if it starts with '@', the "
137 "file to read from for early data.",
Steven Valdez2d850622017-01-11 11:34:52 -0500138 },
139 {
David Benjaminee7aa022017-07-07 16:47:59 -0400140 "-http-tunnel", kOptionalArgument,
141 "An HTTP proxy server to tunnel the TCP connection through",
142 },
143 {
David Benjamin6df76672017-08-16 13:02:24 -0400144 "-renegotiate-freely", kBooleanArgument,
145 "Allow renegotiations from the peer.",
146 },
147 {
David Benjaminf60bcfb2017-08-18 15:23:44 -0400148 "-debug", kBooleanArgument,
149 "Print debug information about the handshake",
150 },
151 {
David Benjamin712f3722017-04-05 14:52:09 -0400152 "", kOptionalArgument, "",
Adam Langleyaacec172014-06-20 12:00:00 -0700153 },
154};
155
Matt Braithwaited17d74d2016-08-17 20:10:28 -0700156static bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file) {
157 bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file()));
David Benjamin05709232015-03-23 19:01:33 -0400158 if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
159 return nullptr;
160 }
Matt Braithwaited17d74d2016-08-17 20:10:28 -0700161 bssl::UniquePtr<EVP_PKEY> pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr,
162 nullptr, nullptr));
David Benjamin05709232015-03-23 19:01:33 -0400163 return pkey;
164}
165
David Benjamin05709232015-03-23 19:01:33 -0400166static int NextProtoSelectCallback(SSL* ssl, uint8_t** out, uint8_t* outlen,
167 const uint8_t* in, unsigned inlen, void* arg) {
168 *out = reinterpret_cast<uint8_t *>(arg);
169 *outlen = strlen(reinterpret_cast<const char *>(arg));
170 return SSL_TLSEXT_ERR_OK;
171}
172
David Benjamind28f59c2015-11-17 22:32:50 -0500173static FILE *g_keylog_file = nullptr;
174
175static void KeyLogCallback(const SSL *ssl, const char *line) {
176 fprintf(g_keylog_file, "%s\n", line);
177 fflush(g_keylog_file);
178}
179
Matt Braithwaited17d74d2016-08-17 20:10:28 -0700180static bssl::UniquePtr<BIO> session_out;
Steven Valdez87c0bb22016-12-07 10:31:16 -0500181static bssl::UniquePtr<SSL_SESSION> resume_session;
Steven Valdez7b689f62016-08-02 15:59:26 -0400182
183static int NewSessionCallback(SSL *ssl, SSL_SESSION *session) {
184 if (session_out) {
185 if (!PEM_write_bio_SSL_SESSION(session_out.get(), session) ||
186 BIO_flush(session_out.get()) <= 0) {
187 fprintf(stderr, "Error while saving session:\n");
David Benjamin3c37d0a2018-05-05 00:42:23 -0400188 ERR_print_errors_fp(stderr);
Steven Valdez7b689f62016-08-02 15:59:26 -0400189 return 0;
190 }
191 }
Steven Valdez87c0bb22016-12-07 10:31:16 -0500192 resume_session = bssl::UniquePtr<SSL_SESSION>(session);
193 return 1;
194}
Steven Valdez7b689f62016-08-02 15:59:26 -0400195
Steven Valdez87c0bb22016-12-07 10:31:16 -0500196static bool WaitForSession(SSL *ssl, int sock) {
197 fd_set read_fds;
198 FD_ZERO(&read_fds);
199
200 if (!SocketSetNonBlocking(sock, true)) {
201 return false;
202 }
203
204 while (!resume_session) {
David Benjamin02afbd32017-10-05 15:04:08 -0400205#if defined(OPENSSL_WINDOWS)
206 // Windows sockets are really of type SOCKET, not int, but everything here
207 // casts them to ints. Clang gets unhappy about signed values as a result.
208 //
209 // TODO(davidben): Keep everything as the appropriate platform type.
210 FD_SET(static_cast<SOCKET>(sock), &read_fds);
211#else
Steven Valdez87c0bb22016-12-07 10:31:16 -0500212 FD_SET(sock, &read_fds);
David Benjamin02afbd32017-10-05 15:04:08 -0400213#endif
Steven Valdez87c0bb22016-12-07 10:31:16 -0500214 int ret = select(sock + 1, &read_fds, NULL, NULL, NULL);
215 if (ret <= 0) {
216 perror("select");
217 return false;
218 }
219
220 uint8_t buffer[512];
221 int ssl_ret = SSL_read(ssl, buffer, sizeof(buffer));
222
223 if (ssl_ret <= 0) {
224 int ssl_err = SSL_get_error(ssl, ssl_ret);
225 if (ssl_err == SSL_ERROR_WANT_READ) {
226 continue;
227 }
David Benjamin3c37d0a2018-05-05 00:42:23 -0400228 PrintSSLError(stderr, "Error while reading", ssl_err, ssl_ret);
Steven Valdez87c0bb22016-12-07 10:31:16 -0500229 return false;
230 }
231 }
232
233 return true;
234}
235
236static bool DoConnection(SSL_CTX *ctx,
237 std::map<std::string, std::string> args_map,
238 bool (*cb)(SSL *ssl, int sock)) {
239 int sock = -1;
David Benjaminee7aa022017-07-07 16:47:59 -0400240 if (args_map.count("-http-tunnel") != 0) {
241 if (!Connect(&sock, args_map["-http-tunnel"]) ||
242 !DoHTTPTunnel(sock, args_map["-connect"])) {
243 return false;
244 }
245 } else if (!Connect(&sock, args_map["-connect"])) {
Steven Valdez87c0bb22016-12-07 10:31:16 -0500246 return false;
247 }
248
249 if (args_map.count("-starttls") != 0) {
250 const std::string& starttls = args_map["-starttls"];
251 if (starttls == "smtp") {
252 if (!DoSMTPStartTLS(sock)) {
253 return false;
254 }
255 } else {
256 fprintf(stderr, "Unknown value for -starttls: %s\n", starttls.c_str());
257 return false;
258 }
259 }
260
261 bssl::UniquePtr<BIO> bio(BIO_new_socket(sock, BIO_CLOSE));
262 bssl::UniquePtr<SSL> ssl(SSL_new(ctx));
263
264 if (args_map.count("-server-name") != 0) {
265 SSL_set_tlsext_host_name(ssl.get(), args_map["-server-name"].c_str());
266 }
267
268 if (args_map.count("-session-in") != 0) {
269 bssl::UniquePtr<BIO> in(BIO_new_file(args_map["-session-in"].c_str(),
270 "rb"));
271 if (!in) {
272 fprintf(stderr, "Error reading session\n");
David Benjamin3c37d0a2018-05-05 00:42:23 -0400273 ERR_print_errors_fp(stderr);
Steven Valdez87c0bb22016-12-07 10:31:16 -0500274 return false;
275 }
276 bssl::UniquePtr<SSL_SESSION> session(PEM_read_bio_SSL_SESSION(in.get(),
277 nullptr, nullptr, nullptr));
278 if (!session) {
279 fprintf(stderr, "Error reading session\n");
David Benjamin3c37d0a2018-05-05 00:42:23 -0400280 ERR_print_errors_fp(stderr);
Steven Valdez87c0bb22016-12-07 10:31:16 -0500281 return false;
282 }
283 SSL_set_session(ssl.get(), session.get());
David Benjamin712f3722017-04-05 14:52:09 -0400284 }
285
David Benjamin6df76672017-08-16 13:02:24 -0400286 if (args_map.count("-renegotiate-freely") != 0) {
287 SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_freely);
288 }
289
David Benjamin712f3722017-04-05 14:52:09 -0400290 if (resume_session) {
Steven Valdez87c0bb22016-12-07 10:31:16 -0500291 SSL_set_session(ssl.get(), resume_session.get());
292 }
293
294 SSL_set_bio(ssl.get(), bio.get(), bio.get());
295 bio.release();
296
297 int ret = SSL_connect(ssl.get());
298 if (ret != 1) {
299 int ssl_err = SSL_get_error(ssl.get(), ret);
David Benjamin3c37d0a2018-05-05 00:42:23 -0400300 PrintSSLError(stderr, "Error while connecting", ssl_err, ret);
Steven Valdez87c0bb22016-12-07 10:31:16 -0500301 return false;
302 }
303
Steven Valdeze831a812017-03-09 14:56:07 -0500304 if (args_map.count("-early-data") != 0 && SSL_in_early_data(ssl.get())) {
Steven Valdez1530ef32017-11-30 12:02:42 -0500305 std::string early_data = args_map["-early-data"];
306 if (early_data.size() > 0 && early_data[0] == '@') {
307 const char *filename = early_data.c_str() + 1;
308 std::vector<uint8_t> data;
309 ScopedFILE f(fopen(filename, "rb"));
310 if (f == nullptr || !ReadAll(&data, f.get())) {
311 fprintf(stderr, "Error reading %s.\n", filename);
312 return false;
313 }
314 early_data = std::string(data.begin(), data.end());
315 }
316 int ed_size = early_data.size();
317 int ssl_ret = SSL_write(ssl.get(), early_data.data(), ed_size);
Steven Valdeze831a812017-03-09 14:56:07 -0500318 if (ssl_ret <= 0) {
319 int ssl_err = SSL_get_error(ssl.get(), ssl_ret);
David Benjamin3c37d0a2018-05-05 00:42:23 -0400320 PrintSSLError(stderr, "Error while writing", ssl_err, ssl_ret);
Steven Valdeze831a812017-03-09 14:56:07 -0500321 return false;
322 } else if (ssl_ret != ed_size) {
323 fprintf(stderr, "Short write from SSL_write.\n");
324 return false;
325 }
326 }
327
Steven Valdez87c0bb22016-12-07 10:31:16 -0500328 fprintf(stderr, "Connected.\n");
Peter Wu5663b632017-09-15 15:09:03 +0100329 bssl::UniquePtr<BIO> bio_stderr(BIO_new_fp(stderr, BIO_NOCLOSE));
330 PrintConnectionInfo(bio_stderr.get(), ssl.get());
Steven Valdez87c0bb22016-12-07 10:31:16 -0500331
332 return cb(ssl.get(), sock);
Steven Valdez7b689f62016-08-02 15:59:26 -0400333}
334
David Benjaminf60bcfb2017-08-18 15:23:44 -0400335static void InfoCallback(const SSL *ssl, int type, int value) {
336 switch (type) {
337 case SSL_CB_HANDSHAKE_START:
338 fprintf(stderr, "Handshake started.\n");
339 break;
340 case SSL_CB_HANDSHAKE_DONE:
341 fprintf(stderr, "Handshake done.\n");
342 break;
343 case SSL_CB_CONNECT_LOOP:
344 fprintf(stderr, "Handshake progress: %s\n", SSL_state_string_long(ssl));
345 break;
346 }
347}
348
Adam Langleyaacec172014-06-20 12:00:00 -0700349bool Client(const std::vector<std::string> &args) {
Brian Smith33970e62015-01-27 22:32:08 -0800350 if (!InitSocketLibrary()) {
351 return false;
352 }
353
Adam Langleyaacec172014-06-20 12:00:00 -0700354 std::map<std::string, std::string> args_map;
355
356 if (!ParseKeyValueArguments(&args_map, args, kArguments)) {
357 PrintUsage(kArguments);
358 return false;
359 }
360
David Benjamin65b87ce2017-08-17 13:11:23 -0400361 bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
Adam Langleyaacec172014-06-20 12:00:00 -0700362
David Benjamin859ec3c2014-09-02 16:29:36 -0400363 const char *keylog_file = getenv("SSLKEYLOGFILE");
364 if (keylog_file) {
David Benjamind28f59c2015-11-17 22:32:50 -0500365 g_keylog_file = fopen(keylog_file, "a");
366 if (g_keylog_file == nullptr) {
367 perror("fopen");
David Benjamin859ec3c2014-09-02 16:29:36 -0400368 return false;
369 }
David Benjamind28f59c2015-11-17 22:32:50 -0500370 SSL_CTX_set_keylog_callback(ctx.get(), KeyLogCallback);
David Benjamin859ec3c2014-09-02 16:29:36 -0400371 }
372
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500373 if (args_map.count("-cipher") != 0 &&
Matthew Braithwaitea57dcfb2017-02-17 22:08:23 -0800374 !SSL_CTX_set_strict_cipher_list(ctx.get(), args_map["-cipher"].c_str())) {
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500375 fprintf(stderr, "Failed setting cipher list\n");
376 return false;
Adam Langley5f51c252014-10-28 15:45:39 -0700377 }
378
Piotr Sikorad0757062017-04-14 02:59:34 -0700379 if (args_map.count("-curves") != 0 &&
380 !SSL_CTX_set1_curves_list(ctx.get(), args_map["-curves"].c_str())) {
381 fprintf(stderr, "Failed setting curves list\n");
382 return false;
383 }
384
David Benjaminbf17f4f2020-02-05 17:37:40 -0500385 if (args_map.count("-sigalgs") != 0 &&
386 !SSL_CTX_set1_sigalgs_list(ctx.get(), args_map["-sigalgs"].c_str())) {
387 fprintf(stderr, "Failed setting signature algorithms list\n");
388 return false;
389 }
390
Adam Langley040bc492017-02-09 15:30:52 -0800391 uint16_t max_version = TLS1_3_VERSION;
392 if (args_map.count("-max-version") != 0 &&
393 !VersionFromString(&max_version, args_map["-max-version"])) {
394 fprintf(stderr, "Unknown protocol version: '%s'\n",
395 args_map["-max-version"].c_str());
396 return false;
397 }
398
399 if (!SSL_CTX_set_max_proto_version(ctx.get(), max_version)) {
400 return false;
David Benjamin05709232015-03-23 19:01:33 -0400401 }
402
403 if (args_map.count("-min-version") != 0) {
404 uint16_t version;
405 if (!VersionFromString(&version, args_map["-min-version"])) {
406 fprintf(stderr, "Unknown protocol version: '%s'\n",
407 args_map["-min-version"].c_str());
408 return false;
409 }
David Benjamine4706902016-09-20 15:12:23 -0400410 if (!SSL_CTX_set_min_proto_version(ctx.get(), version)) {
David Benjamin2dc02042016-09-19 19:57:37 -0400411 return false;
412 }
David Benjamin05709232015-03-23 19:01:33 -0400413 }
414
415 if (args_map.count("-select-next-proto") != 0) {
416 const std::string &proto = args_map["-select-next-proto"];
417 if (proto.size() > 255) {
418 fprintf(stderr, "Bad NPN protocol: '%s'\n", proto.c_str());
419 return false;
420 }
421 // |SSL_CTX_set_next_proto_select_cb| is not const-correct.
422 SSL_CTX_set_next_proto_select_cb(ctx.get(), NextProtoSelectCallback,
423 const_cast<char *>(proto.c_str()));
424 }
425
426 if (args_map.count("-alpn-protos") != 0) {
427 const std::string &alpn_protos = args_map["-alpn-protos"];
428 std::vector<uint8_t> wire;
429 size_t i = 0;
430 while (i <= alpn_protos.size()) {
431 size_t j = alpn_protos.find(',', i);
432 if (j == std::string::npos) {
433 j = alpn_protos.size();
434 }
435 size_t len = j - i;
436 if (len > 255) {
437 fprintf(stderr, "Invalid ALPN protocols: '%s'\n", alpn_protos.c_str());
438 return false;
439 }
440 wire.push_back(static_cast<uint8_t>(len));
441 wire.resize(wire.size() + len);
David Benjamin17cf2cb2016-12-13 01:07:13 -0500442 OPENSSL_memcpy(wire.data() + wire.size() - len, alpn_protos.data() + i,
443 len);
David Benjamin05709232015-03-23 19:01:33 -0400444 i = j + 1;
445 }
446 if (SSL_CTX_set_alpn_protos(ctx.get(), wire.data(), wire.size()) != 0) {
447 return false;
448 }
449 }
450
451 if (args_map.count("-fallback-scsv") != 0) {
452 SSL_CTX_set_mode(ctx.get(), SSL_MODE_SEND_FALLBACK_SCSV);
453 }
454
455 if (args_map.count("-ocsp-stapling") != 0) {
456 SSL_CTX_enable_ocsp_stapling(ctx.get());
457 }
458
459 if (args_map.count("-signed-certificate-timestamps") != 0) {
460 SSL_CTX_enable_signed_cert_timestamps(ctx.get());
461 }
462
463 if (args_map.count("-channel-id-key") != 0) {
Matt Braithwaited17d74d2016-08-17 20:10:28 -0700464 bssl::UniquePtr<EVP_PKEY> pkey =
465 LoadPrivateKey(args_map["-channel-id-key"]);
David Benjamin05709232015-03-23 19:01:33 -0400466 if (!pkey || !SSL_CTX_set1_tls_channel_id(ctx.get(), pkey.get())) {
467 return false;
468 }
David Benjamin05709232015-03-23 19:01:33 -0400469 }
470
David Benjamin1043ac02015-06-04 15:26:04 -0400471 if (args_map.count("-false-start") != 0) {
472 SSL_CTX_set_mode(ctx.get(), SSL_MODE_ENABLE_FALSE_START);
473 }
474
David Benjamin86e412d2015-12-02 19:34:58 -0500475 if (args_map.count("-key") != 0) {
476 const std::string &key = args_map["-key"];
David Benjamincb3af3e2017-04-09 09:52:47 -0400477 if (!SSL_CTX_use_PrivateKey_file(ctx.get(), key.c_str(),
478 SSL_FILETYPE_PEM)) {
David Benjamin86e412d2015-12-02 19:34:58 -0500479 fprintf(stderr, "Failed to load private key: %s\n", key.c_str());
480 return false;
481 }
David Benjamincb3af3e2017-04-09 09:52:47 -0400482 const std::string &cert =
483 args_map.count("-cert") != 0 ? args_map["-cert"] : key;
484 if (!SSL_CTX_use_certificate_chain_file(ctx.get(), cert.c_str())) {
485 fprintf(stderr, "Failed to load cert chain: %s\n", cert.c_str());
David Benjamin86e412d2015-12-02 19:34:58 -0500486 return false;
487 }
488 }
489
Steven Valdez87c0bb22016-12-07 10:31:16 -0500490 SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_CLIENT);
491 SSL_CTX_sess_set_new_cb(ctx.get(), NewSessionCallback);
492
Steven Valdez7b689f62016-08-02 15:59:26 -0400493 if (args_map.count("-session-out") != 0) {
494 session_out.reset(BIO_new_file(args_map["-session-out"].c_str(), "wb"));
495 if (!session_out) {
David Benjamin70728842016-09-06 18:18:52 -0400496 fprintf(stderr, "Error while opening %s:\n",
497 args_map["-session-out"].c_str());
David Benjamin3c37d0a2018-05-05 00:42:23 -0400498 ERR_print_errors_fp(stderr);
Steven Valdez7b689f62016-08-02 15:59:26 -0400499 return false;
500 }
Steven Valdez7b689f62016-08-02 15:59:26 -0400501 }
502
David Benjamin65ac9972016-09-02 21:35:25 -0400503 if (args_map.count("-grease") != 0) {
504 SSL_CTX_set_grease_enabled(ctx.get(), 1);
505 }
506
Adam Langleye5dfb522017-02-03 10:31:00 -0800507 if (args_map.count("-root-certs") != 0) {
508 if (!SSL_CTX_load_verify_locations(
509 ctx.get(), args_map["-root-certs"].c_str(), nullptr)) {
510 fprintf(stderr, "Failed to load root certificates.\n");
David Benjamin3c37d0a2018-05-05 00:42:23 -0400511 ERR_print_errors_fp(stderr);
Adam Langleye5dfb522017-02-03 10:31:00 -0800512 return false;
513 }
514 SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_PEER, nullptr);
515 }
516
Pete Bentley3e96cd42020-01-10 12:15:34 +0000517 if (args_map.count("-root-cert-dir") != 0) {
518 if (!SSL_CTX_load_verify_locations(
519 ctx.get(), nullptr, args_map["-root-cert-dir"].c_str())) {
520 fprintf(stderr, "Failed to load root certificates.\n");
521 ERR_print_errors_fp(stderr);
522 return false;
523 }
524 SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_PEER, nullptr);
525 }
526
Steven Valdez2d850622017-01-11 11:34:52 -0500527 if (args_map.count("-early-data") != 0) {
528 SSL_CTX_set_early_data_enabled(ctx.get(), 1);
529 }
530
David Benjaminf60bcfb2017-08-18 15:23:44 -0400531 if (args_map.count("-debug") != 0) {
532 SSL_CTX_set_info_callback(ctx.get(), InfoCallback);
533 }
534
David Benjamin712f3722017-04-05 14:52:09 -0400535 if (args_map.count("-test-resumption") != 0) {
536 if (args_map.count("-session-in") != 0) {
537 fprintf(stderr,
538 "Flags -session-in and -test-resumption are incompatible.\n");
539 return false;
540 }
541
542 if (!DoConnection(ctx.get(), args_map, &WaitForSession)) {
543 return false;
544 }
Adam Langleyaacec172014-06-20 12:00:00 -0700545 }
546
Steven Valdez87c0bb22016-12-07 10:31:16 -0500547 return DoConnection(ctx.get(), args_map, &TransferData);
Adam Langleyaacec172014-06-20 12:00:00 -0700548}