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
LoadPrivateKey(const std::string & file)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
NextProtoSelectCallback(SSL * ssl,uint8_t ** out,uint8_t * outlen,const uint8_t * in,unsigned inlen,void * arg)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
KeyLogCallback(const SSL * ssl,const char * line)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
NewSessionCallback(SSL * ssl,SSL_SESSION * session)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
WaitForSession(SSL * ssl,int sock)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
DoConnection(SSL_CTX * ctx,std::map<std::string,std::string> args_map,bool (* cb)(SSL * ssl,int sock))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
Client(const std::vector<std::string> & args)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