1 /*	$OpenBSD: ssl_get_shared_ciphers.c,v 1.11 2022/02/05 18:19:39 tb Exp $ */
2 /*
3  * Copyright (c) 2021 Theo Buehler <tb@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include <openssl/bio.h>
24 #include <openssl/crypto.h>
25 #include <openssl/err.h>
26 #include <openssl/ssl.h>
27 
28 struct peer_config {
29 	const char *name;
30 	int server;
31 	uint16_t max_version;
32 	uint16_t min_version;
33 	const char *ciphers;
34 };
35 
36 struct ssl_shared_ciphers_test_data {
37 	const char *description;
38 	struct peer_config client_config;
39 	struct peer_config server_config;
40 	const char *shared_ciphers;
41 	const char *shared_ciphers_without_aesni;
42 };
43 
44 char *server_cert;
45 char *server_key;
46 
47 static const struct ssl_shared_ciphers_test_data ssl_shared_ciphers_tests[] = {
48 	{
49 		.description = "TLSv1.3 defaults",
50 		.client_config = {
51 			.name = "client",
52 			.server = 0,
53 			.max_version = TLS1_3_VERSION,
54 			.min_version = TLS1_3_VERSION,
55 			.ciphers =
56 			    "TLS_AES_256_GCM_SHA384:"
57 			    "TLS_CHACHA20_POLY1305_SHA256:"
58 			    "TLS_AES_128_GCM_SHA256",
59 		},
60 		.server_config = {
61 			.name = "server",
62 			.server = 1,
63 			.max_version = TLS1_3_VERSION,
64 			.min_version = TLS1_3_VERSION,
65 			.ciphers =
66 			    "TLS_AES_256_GCM_SHA384:"
67 			    "TLS_CHACHA20_POLY1305_SHA256:"
68 			    "TLS_AES_128_GCM_SHA256",
69 		},
70 		.shared_ciphers =
71 		    "TLS_AES_256_GCM_SHA384:"
72 		    "TLS_CHACHA20_POLY1305_SHA256:"
73 		    "TLS_AES_128_GCM_SHA256",
74 	},
75 
76 	{
77 		.description = "TLSv1.3, client without ChaCha",
78 		.client_config = {
79 			.name = "client",
80 			.server = 0,
81 			.max_version = TLS1_3_VERSION,
82 			.min_version = TLS1_3_VERSION,
83 			.ciphers =
84 			    "TLS_AES_256_GCM_SHA384:"
85 			    "TLS_AES_128_GCM_SHA256",
86 		},
87 		.server_config = {
88 			.name = "server",
89 			.server = 1,
90 			.max_version = TLS1_3_VERSION,
91 			.min_version = TLS1_3_VERSION,
92 			.ciphers =
93 			    "TLS_AES_256_GCM_SHA384:"
94 			    "TLS_CHACHA20_POLY1305_SHA256:"
95 			    "TLS_AES_128_GCM_SHA256",
96 		},
97 		.shared_ciphers =
98 		    "TLS_AES_256_GCM_SHA384:"
99 		    "TLS_AES_128_GCM_SHA256",
100 	},
101 
102 	{
103 		.description = "TLSv1.2",
104 		.client_config = {
105 			.name = "client",
106 			.server = 0,
107 			.max_version = TLS1_2_VERSION,
108 			.min_version = TLS1_2_VERSION,
109 			.ciphers =
110 			    "ECDHE-RSA-AES256-GCM-SHA384:"
111 			    "ECDHE-ECDSA-AES256-GCM-SHA384:"
112 			    "ECDHE-RSA-AES256-SHA384:"
113 			    "ECDHE-ECDSA-AES256-SHA384:"
114 			    "ECDHE-RSA-AES256-SHA:"
115 			    "ECDHE-ECDSA-AES256-SHA",
116 		},
117 		.server_config = {
118 			.name = "server",
119 			.server = 1,
120 			.max_version = TLS1_2_VERSION,
121 			.min_version = TLS1_2_VERSION,
122 			.ciphers =
123 			    "ECDHE-RSA-AES256-GCM-SHA384:"
124 			    "ECDHE-ECDSA-AES256-GCM-SHA384:"
125 			    "ECDHE-RSA-AES256-SHA384:"
126 			    "ECDHE-ECDSA-AES256-SHA384:"
127 			    "ECDHE-RSA-AES256-SHA:"
128 			    "ECDHE-ECDSA-AES256-SHA",
129 		},
130 		.shared_ciphers =
131 		    "ECDHE-RSA-AES256-GCM-SHA384:"
132 		    "ECDHE-ECDSA-AES256-GCM-SHA384:"
133 		    "ECDHE-RSA-AES256-SHA384:"
134 		    "ECDHE-ECDSA-AES256-SHA384:"
135 		    "ECDHE-RSA-AES256-SHA:"
136 		    "ECDHE-ECDSA-AES256-SHA",
137 	},
138 
139 	{
140 		.description = "TLSv1.2, server without ECDSA",
141 		.client_config = {
142 			.name = "client",
143 			.server = 0,
144 			.max_version = TLS1_2_VERSION,
145 			.min_version = TLS1_2_VERSION,
146 			.ciphers =
147 			    "ECDHE-RSA-AES256-GCM-SHA384:"
148 			    "ECDHE-ECDSA-AES256-GCM-SHA384:"
149 			    "ECDHE-RSA-AES256-SHA384:"
150 			    "ECDHE-ECDSA-AES256-SHA384:"
151 			    "ECDHE-RSA-AES256-SHA:"
152 			    "ECDHE-ECDSA-AES256-SHA",
153 		},
154 		.server_config = {
155 			.name = "server",
156 			.server = 1,
157 			.max_version = TLS1_2_VERSION,
158 			.min_version = TLS1_2_VERSION,
159 			.ciphers =
160 			    "ECDHE-RSA-AES256-GCM-SHA384:"
161 			    "ECDHE-RSA-AES256-SHA384:"
162 			    "ECDHE-RSA-AES256-SHA",
163 		},
164 		.shared_ciphers =
165 		    "ECDHE-RSA-AES256-GCM-SHA384:"
166 		    "ECDHE-RSA-AES256-SHA384:"
167 		    "ECDHE-RSA-AES256-SHA",
168 	},
169 
170 	{
171 		.description = "TLSv1.3 ciphers are prepended",
172 		.client_config = {
173 			.name = "client",
174 			.server = 0,
175 			.max_version = TLS1_3_VERSION,
176 			.min_version = TLS1_2_VERSION,
177 			.ciphers =
178 			    "ECDHE-RSA-AES256-GCM-SHA384",
179 		},
180 		.server_config = {
181 			.name = "server",
182 			.server = 1,
183 			.max_version = TLS1_3_VERSION,
184 			.min_version = TLS1_2_VERSION,
185 			.ciphers =
186 			    "ECDHE-RSA-AES256-GCM-SHA384",
187 		},
188 		.shared_ciphers =
189 		    "TLS_AES_256_GCM_SHA384:"
190 		    "TLS_CHACHA20_POLY1305_SHA256:"
191 		    "TLS_AES_128_GCM_SHA256:"
192 		    "ECDHE-RSA-AES256-GCM-SHA384",
193 		.shared_ciphers_without_aesni =
194 		    "TLS_CHACHA20_POLY1305_SHA256:"
195 		    "TLS_AES_256_GCM_SHA384:"
196 		    "TLS_AES_128_GCM_SHA256:"
197 		    "ECDHE-RSA-AES256-GCM-SHA384",
198 	},
199 };
200 
201 static const size_t N_SHARED_CIPHERS_TESTS =
202     sizeof(ssl_shared_ciphers_tests) / sizeof(ssl_shared_ciphers_tests[0]);
203 
204 static SSL_CTX *
205 peer_config_to_ssl_ctx(const struct peer_config *config)
206 {
207 	SSL_CTX *ctx;
208 
209 	if ((ctx = SSL_CTX_new(TLS_method())) == NULL) {
210 		fprintf(stderr, "SSL_CTX_new(%s) failed\n", config->name);
211 		goto err;
212 	}
213 	if (!SSL_CTX_set_max_proto_version(ctx, config->max_version)) {
214 		fprintf(stderr, "max_proto_version(%s) failed\n", config->name);
215 		goto err;
216 	}
217 	if (!SSL_CTX_set_min_proto_version(ctx, config->min_version)) {
218 		fprintf(stderr, "min_proto_version(%s) failed\n", config->name);
219 		goto err;
220 	}
221 	if (!SSL_CTX_set_cipher_list(ctx, config->ciphers)) {
222 		fprintf(stderr, "set_cipher_list(%s) failed\n", config->name);
223 		goto err;
224 	}
225 
226 	if (config->server) {
227 		if (!SSL_CTX_use_certificate_file(ctx, server_cert,
228 		    SSL_FILETYPE_PEM)) {
229 			fprintf(stderr, "use_certificate_file(%s) failed\n",
230 			    config->name);
231 			goto err;
232 		}
233 		if (!SSL_CTX_use_PrivateKey_file(ctx, server_key,
234 		    SSL_FILETYPE_PEM)) {
235 			fprintf(stderr, "use_PrivateKey_file(%s) failed\n",
236 			    config->name);
237 			goto err;
238 		}
239 	}
240 
241 	return ctx;
242 
243  err:
244 	SSL_CTX_free(ctx);
245 	return NULL;
246 }
247 
248 /* Connect client and server via a pair of "nonblocking" memory BIOs. */
249 static int
250 connect_peers(SSL *client_ssl, SSL *server_ssl, const char *description)
251 {
252 	BIO *client_wbio = NULL, *server_wbio = NULL;
253 	int ret = 0;
254 
255 	if ((client_wbio = BIO_new(BIO_s_mem())) == NULL) {
256 		fprintf(stderr, "%s: failed to create client BIO\n",
257 		    description);
258 		goto err;
259 	}
260 	if ((server_wbio = BIO_new(BIO_s_mem())) == NULL) {
261 		fprintf(stderr, "%s: failed to create server BIO\n",
262 		    description);
263 		goto err;
264 	}
265 	if (BIO_set_mem_eof_return(client_wbio, -1) <= 0) {
266 		fprintf(stderr, "%s: failed to set client eof return\n",
267 		    description);
268 		goto err;
269 	}
270 	if (BIO_set_mem_eof_return(server_wbio, -1) <= 0) {
271 		fprintf(stderr, "%s: failed to set server eof return\n",
272 		    description);
273 		goto err;
274 	}
275 
276 	/* Avoid double free. SSL_set_bio() takes ownership of the BIOs. */
277 	BIO_up_ref(client_wbio);
278 	BIO_up_ref(server_wbio);
279 
280 	SSL_set_bio(client_ssl, server_wbio, client_wbio);
281 	SSL_set_bio(server_ssl, client_wbio, server_wbio);
282 	client_wbio = NULL;
283 	server_wbio = NULL;
284 
285 	ret = 1;
286 
287  err:
288 	BIO_free(client_wbio);
289 	BIO_free(server_wbio);
290 
291 	return ret;
292 }
293 
294 static int
295 push_data_to_peer(SSL *ssl, int *ret, int (*func)(SSL *), const char *func_name,
296     const char *description)
297 {
298 	int ssl_err = 0;
299 
300 	if (*ret == 1)
301 		return 1;
302 
303 	/*
304 	 * Do SSL_connect/SSL_accept/SSL_shutdown once and loop while hitting
305 	 * WANT_WRITE.  If done or on WANT_READ hand off to peer.
306 	 */
307 
308 	do {
309 		if ((*ret = func(ssl)) <= 0)
310 			ssl_err = SSL_get_error(ssl, *ret);
311 	} while (*ret <= 0 && ssl_err == SSL_ERROR_WANT_WRITE);
312 
313 	/* Ignore erroneous error - see SSL_shutdown(3)... */
314 	if (func == SSL_shutdown && ssl_err == SSL_ERROR_SYSCALL)
315 		return 1;
316 
317 	if (*ret <= 0 && ssl_err != SSL_ERROR_WANT_READ) {
318 		fprintf(stderr, "%s: %s failed\n", description, func_name);
319 		ERR_print_errors_fp(stderr);
320 		return 0;
321 	}
322 
323 	return 1;
324 }
325 
326 /*
327  * Alternate between loops of SSL_connect() and SSL_accept() as long as only
328  * WANT_READ and WANT_WRITE situations are encountered. A function is repeated
329  * until WANT_READ is returned or it succeeds, then it's the other function's
330  * turn to make progress. Succeeds if SSL_connect() and SSL_accept() return 1.
331  */
332 static int
333 handshake(SSL *client_ssl, SSL *server_ssl, const char *description)
334 {
335 	int loops = 0, client_ret = 0, server_ret = 0;
336 
337 	while (loops++ < 10 && (client_ret <= 0 || server_ret <= 0)) {
338 		if (!push_data_to_peer(client_ssl, &client_ret, SSL_connect,
339 		    "SSL_connect", description))
340 			return 0;
341 
342 		if (!push_data_to_peer(server_ssl, &server_ret, SSL_accept,
343 		    "SSL_accept", description))
344 			return 0;
345 	}
346 
347 	if (client_ret != 1 || server_ret != 1) {
348 		fprintf(stderr, "%s: failed\n", __func__);
349 		return 0;
350 	}
351 
352 	return 1;
353 }
354 
355 static int
356 shutdown_peers(SSL *client_ssl, SSL *server_ssl, const char *description)
357 {
358 	int loops = 0, client_ret = 0, server_ret = 0;
359 
360 	while (loops++ < 10 && (client_ret <= 0 || server_ret <= 0)) {
361 		if (!push_data_to_peer(client_ssl, &client_ret, SSL_shutdown,
362 		    "client shutdown", description))
363 			return 0;
364 
365 		if (!push_data_to_peer(server_ssl, &server_ret, SSL_shutdown,
366 		    "server shutdown", description))
367 			return 0;
368 	}
369 
370 	if (client_ret != 1 || server_ret != 1) {
371 		fprintf(stderr, "%s: failed\n", __func__);
372 		return 0;
373 	}
374 
375 	return 1;
376 }
377 
378 /* from ssl_ciph.c */
379 static inline int
380 ssl_aes_is_accelerated(void)
381 {
382 #if defined(__i386__) || defined(__x86_64__)
383 	return ((OPENSSL_cpu_caps() & (1ULL << 57)) != 0);
384 #else
385 	return (0);
386 #endif
387 }
388 
389 static int
390 check_shared_ciphers(const struct ssl_shared_ciphers_test_data *test,
391     const char *got)
392 {
393 	const char *want = test->shared_ciphers;
394 	int failed;
395 
396 	if (!ssl_aes_is_accelerated() &&
397 	    test->shared_ciphers_without_aesni != NULL)
398 		want = test->shared_ciphers_without_aesni;
399 
400 	failed = strcmp(want, got);
401 
402 	if (failed)
403 		fprintf(stderr, "%s: want \"%s\", got \"%s\"\n",
404 		    test->description, want, got);
405 
406 	return failed;
407 }
408 
409 static int
410 test_get_shared_ciphers(const struct ssl_shared_ciphers_test_data *test)
411 {
412 	SSL_CTX *client_ctx = NULL, *server_ctx = NULL;
413 	SSL *client_ssl = NULL, *server_ssl = NULL;
414 	char buf[4096];
415 	int failed = 1;
416 
417 	if ((client_ctx = peer_config_to_ssl_ctx(&test->client_config)) == NULL)
418 		goto err;
419 	if ((server_ctx = peer_config_to_ssl_ctx(&test->server_config)) == NULL)
420 		goto err;
421 
422 	if ((client_ssl = SSL_new(client_ctx)) == NULL) {
423 		fprintf(stderr, "%s: failed to create client SSL\n",
424 		    test->description);
425 		goto err;
426 	}
427 	if ((server_ssl = SSL_new(server_ctx)) == NULL) {
428 		fprintf(stderr, "%s: failed to create server SSL\n",
429 		    test->description);
430 		goto err;
431 	}
432 
433 	if (!connect_peers(client_ssl, server_ssl, test->description))
434 		goto err;
435 
436 	if (!handshake(client_ssl, server_ssl, test->description))
437 		goto err;
438 
439 	if (SSL_get_shared_ciphers(server_ssl, buf, sizeof(buf)) == NULL) {
440 		fprintf(stderr, "%s: failed to get shared ciphers\n",
441 		    test->description);
442 		goto err;
443 	}
444 
445 	if (!shutdown_peers(client_ssl, server_ssl, test->description))
446 		goto err;
447 
448 	failed = check_shared_ciphers(test, buf);
449 
450  err:
451 	SSL_CTX_free(client_ctx);
452 	SSL_CTX_free(server_ctx);
453 	SSL_free(client_ssl);
454 	SSL_free(server_ssl);
455 
456 	return failed;
457 }
458 
459 int
460 main(int argc, char **argv)
461 {
462 	size_t i;
463 	int failed = 0;
464 
465 	if (asprintf(&server_cert, "%s/server.pem", CERTSDIR) == -1) {
466 		fprintf(stderr, "asprintf server_cert failed\n");
467 		failed = 1;
468 		goto err;
469 	}
470 	server_key = server_cert;
471 
472 	for (i = 0; i < N_SHARED_CIPHERS_TESTS; i++)
473 		failed |= test_get_shared_ciphers(&ssl_shared_ciphers_tests[i]);
474 
475 	if (failed == 0)
476 		printf("PASS %s\n", __FILE__);
477 
478  err:
479 	free(server_cert);
480 
481 	return failed;
482 }
483