xref: /openbsd-src/regress/lib/libssl/unit/ssl_set_alpn_protos.c (revision 668a98a4246d4fdfcea8c2e850807529ba1fd2bc)
1 /*	$OpenBSD: ssl_set_alpn_protos.c,v 1.4 2024/07/11 13:51:47 tb Exp $ */
2 /*
3  * Copyright (c) 2022 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 <err.h>
19 #include <stdio.h>
20 
21 #include <openssl/ssl.h>
22 
23 static void
hexdump(const unsigned char * buf,size_t len)24 hexdump(const unsigned char *buf, size_t len)
25 {
26 	size_t i;
27 
28 	if (buf == NULL) {
29 		fprintf(stderr, "(null), len %zu\n", len);
30 		return;
31 	}
32 	for (i = 1; i <= len; i++)
33 		fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
34 	if (len % 8)
35 		fprintf(stderr, "\n");
36 }
37 
38 struct alpn_test {
39 	const char *description;
40 	const uint8_t protocols[24];
41 	size_t protocols_len;
42 	int ret;
43 };
44 
45 static const struct alpn_test alpn_tests[] = {
46 	{
47 		.description = "valid protocol list",
48 		.protocols = {
49 			6, 's', 'p', 'd', 'y', '/', '1',
50 			8, 'h', 't', 't', 'p', '/', '1', '.', '1',
51 		},
52 		.protocols_len = 16,
53 		.ret = 0,
54 	},
55 	{
56 		.description = "zero length protocol",
57 		.protocols = {
58 			0,
59 		},
60 		.protocols_len = 1,
61 		.ret = 1,
62 	},
63 	{
64 		.description = "zero length protocol at start",
65 		.protocols = {
66 			0,
67 			8, 'h', 't', 't', 'p', '/', '1', '.', '1',
68 			6, 's', 'p', 'd', 'y', '/', '1',
69 		},
70 		.protocols_len = 17,
71 		.ret = 1,
72 	},
73 	{
74 		.description = "zero length protocol embedded",
75 		.protocols = {
76 			8, 'h', 't', 't', 'p', '/', '1', '.', '1',
77 			0,
78 			6, 's', 'p', 'd', 'y', '/', '1',
79 		},
80 		.protocols_len = 17,
81 		.ret = 1,
82 	},
83 	{
84 		.description = "zero length protocol at end",
85 		.protocols = {
86 			8, 'h', 't', 't', 'p', '/', '1', '.', '1',
87 			6, 's', 'p', 'd', 'y', '/', '1',
88 			0,
89 		},
90 		.protocols_len = 17,
91 		.ret = 1,
92 	},
93 	{
94 		.description = "protocol length too short",
95 		.protocols = {
96 			6, 'h', 't', 't', 'p', '/', '1', '.', '1',
97 		},
98 		.protocols_len = 9,
99 		.ret = 1,
100 	},
101 	{
102 		.description = "protocol length too long",
103 		.protocols = {
104 			8, 's', 'p', 'd', 'y', '/', '1',
105 		},
106 		.protocols_len = 7,
107 		.ret = 1,
108 	},
109 };
110 
111 static const size_t N_ALPN_TESTS = sizeof(alpn_tests) / sizeof(alpn_tests[0]);
112 
113 static int
test_ssl_set_alpn_protos(const struct alpn_test * tc)114 test_ssl_set_alpn_protos(const struct alpn_test *tc)
115 {
116 	SSL_CTX *ctx;
117 	SSL *ssl;
118 	int ret;
119 	int failed = 0;
120 
121 	if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL)
122 		errx(1, "SSL_CTX_new");
123 
124 	ret = SSL_CTX_set_alpn_protos(ctx, tc->protocols, tc->protocols_len);
125 	if (ret != tc->ret) {
126 		warnx("%s: setting on SSL_CTX: want %d, got %d",
127 		    tc->description, tc->ret, ret);
128 		failed = 1;
129 	}
130 
131 	if ((ssl = SSL_new(ctx)) == NULL)
132 		errx(1, "SSL_new");
133 
134 	ret = SSL_set_alpn_protos(ssl, tc->protocols, tc->protocols_len);
135 	if (ret != tc->ret) {
136 		warnx("%s: setting on SSL: want %d, got %d",
137 		    tc->description, tc->ret, ret);
138 		failed = 1;
139 	}
140 
141 	SSL_CTX_free(ctx);
142 	SSL_free(ssl);
143 
144 	return failed;
145 }
146 
147 static int
test_ssl_set_alpn_protos_edge_cases(void)148 test_ssl_set_alpn_protos_edge_cases(void)
149 {
150 	SSL_CTX *ctx;
151 	SSL *ssl;
152 	const uint8_t valid[] = {
153 		6, 's', 'p', 'd', 'y', '/', '3',
154 		8, 'h', 't', 't', 'p', '/', '1', '.', '1',
155 	};
156 	int failed = 0;
157 
158 	if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL)
159 		errx(1, "SSL_CTX_new");
160 
161 	if (SSL_CTX_set_alpn_protos(ctx, valid, sizeof(valid)) != 0) {
162 		warnx("setting valid protocols on SSL_CTX failed");
163 		failed = 1;
164 	}
165 	if (SSL_CTX_set_alpn_protos(ctx, NULL, 0) != 0) {
166 		warnx("setting 'NULL, 0' on SSL_CTX failed");
167 		failed = 1;
168 	}
169 	if (SSL_CTX_set_alpn_protos(ctx, valid, 0) != 0) {
170 		warnx("setting 'valid, 0' on SSL_CTX failed");
171 		failed = 1;
172 	}
173 	if (SSL_CTX_set_alpn_protos(ctx, NULL, 43) != 0) {
174 		warnx("setting 'NULL, 43' on SSL_CTX failed");
175 		failed = 1;
176 	}
177 
178 	if ((ssl = SSL_new(ctx)) == NULL)
179 		errx(1, "SSL_new");
180 
181 	if (SSL_set_alpn_protos(ssl, valid, sizeof(valid)) != 0) {
182 		warnx("setting valid protocols on SSL failed");
183 		failed = 1;
184 	}
185 	if (SSL_set_alpn_protos(ssl, NULL, 0) != 0) {
186 		warnx("setting 'NULL, 0' on SSL failed");
187 		failed = 1;
188 	}
189 	if (SSL_set_alpn_protos(ssl, valid, 0) != 0) {
190 		warnx("setting 'valid, 0' on SSL failed");
191 		failed = 1;
192 	}
193 	if (SSL_set_alpn_protos(ssl, NULL, 43) != 0) {
194 		warnx("setting 'NULL, 43' on SSL failed");
195 		failed = 1;
196 	}
197 
198 	SSL_CTX_free(ctx);
199 	SSL_free(ssl);
200 
201 	return failed;
202 }
203 
204 static const struct select_next_proto_test {
205 	const unsigned char *peer_list;
206 	size_t peer_list_len;
207 	const unsigned char *supported_list;
208 	size_t supported_list_len;
209 	int want_ret;
210 	const unsigned char *want_out;
211 	unsigned char want_out_len; /* yes, unsigned char */
212 } select_next_proto_tests[] = {
213 	{
214 		.peer_list = "\x01" "a" "\x01" "b" "\x01" "c",
215 		.peer_list_len = 6,
216 		.supported_list = "\x01" "a",
217 		.supported_list_len = 2,
218 		.want_ret = OPENSSL_NPN_NEGOTIATED,
219 		.want_out = "a",
220 		.want_out_len = 1,
221 	},
222 	{
223 		.peer_list = "\x01" "a" "\x01" "b" "\x01" "c",
224 		.peer_list_len = 6,
225 		.supported_list = "\x02" "aa" "\x01" "b" "\x01" "c",
226 		.supported_list_len = 7,
227 		.want_ret = OPENSSL_NPN_NEGOTIATED,
228 		.want_out = "b",
229 		.want_out_len = 1,
230 	},
231 	{
232 		/* Use peer preference. */
233 		.peer_list = "\x01" "a" "\x01" "b" "\x01" "c",
234 		.peer_list_len = 6,
235 		.supported_list = "\x01" "c" "\x01" "b" "\x01" "a",
236 		.supported_list_len = 6,
237 		.want_ret = OPENSSL_NPN_NEGOTIATED,
238 		.want_out = "a",
239 		.want_out_len = 1,
240 	},
241 	{
242 		/* Again peer preference wins. */
243 		.peer_list = "\x01" "a" "\x03" "bbb" "\x02" "cc",
244 		.peer_list_len = 9,
245 		.supported_list = "\x01" "z" "\x02" "cc" "\x03" "bbb",
246 		.supported_list_len = 9,
247 		.want_ret = OPENSSL_NPN_NEGOTIATED,
248 		.want_out = "bbb",
249 		.want_out_len = 3,
250 	},
251 	{
252 		/* No overlap fails with first supported protocol. */
253 		.peer_list = "\x01" "a" "\x01" "b" "\x01" "c",
254 		.peer_list_len = 6,
255 		.supported_list = "\x01" "z" "\x01" "y",
256 		.supported_list_len = 4,
257 		.want_ret = OPENSSL_NPN_NO_OVERLAP,
258 		.want_out = "z",
259 		.want_out_len = 1,
260 	},
261 	{
262 		/* No peer protocols fails cleanly. */
263 		.peer_list = "",
264 		.peer_list_len = 0,
265 		.supported_list = "\x01" "a" "\x01" "b" "\x01" "c",
266 		.supported_list_len = 6,
267 		.want_out = "a",
268 		.want_out_len = 1,
269 		.want_ret = OPENSSL_NPN_NO_OVERLAP,
270 	},
271 	{
272 		/* NULL peer protocols fails cleanly. */
273 		.peer_list = NULL,
274 		.peer_list_len = 0,
275 		.supported_list = "\x01" "a" "\x01" "b" "\x01" "c",
276 		.supported_list_len = 6,
277 		.want_out = "a",
278 		.want_out_len = 1,
279 		.want_ret = OPENSSL_NPN_NO_OVERLAP,
280 	},
281 	{
282 		/* Malformed peer protocols fails cleanly. */
283 		.peer_list = "\x00",
284 		.peer_list_len = 1,
285 		.supported_list = "\x01" "a" "\x01" "b" "\x01" "c",
286 		.supported_list_len = 6,
287 		.want_out = "a",
288 		.want_out_len = 1,
289 		.want_ret = OPENSSL_NPN_NO_OVERLAP,
290 	},
291 	{
292 		/* Malformed peer protocols fails cleanly. */
293 		.peer_list = "\x01" "a" "\x03" "bb",
294 		.peer_list_len = 5,
295 		.supported_list = "\x01" "a" "\x01" "b" "\x01" "c",
296 		.supported_list_len = 6,
297 		.want_out = "a",
298 		.want_out_len = 1,
299 		.want_ret = OPENSSL_NPN_NO_OVERLAP,
300 	},
301 	{
302 		/* Empty supported list fails cleanly. */
303 		.peer_list = "\x01" "a",
304 		.peer_list_len = 2,
305 		.supported_list = "",
306 		.supported_list_len = 0,
307 		.want_out = NULL,
308 		.want_out_len = 0,
309 		.want_ret = OPENSSL_NPN_NO_OVERLAP,
310 	},
311 	{
312 		/* NULL supported list fails cleanly. */
313 		.peer_list = "\x01" "a",
314 		.peer_list_len = 2,
315 		.supported_list = NULL,
316 		.supported_list_len = 0,
317 		.want_out = NULL,
318 		.want_out_len = 0,
319 		.want_ret = OPENSSL_NPN_NO_OVERLAP,
320 	},
321 	{
322 		/* Malformed supported list fails cleanly. */
323 		.peer_list = "\x01" "a",
324 		.peer_list_len = 2,
325 		.supported_list = "\x01" "a" "\x02" "bb" "\x03" "cc" "\x04" "ddd",
326 		.supported_list_len = 12,
327 		.want_out = NULL,
328 		.want_out_len = 0,
329 		.want_ret = OPENSSL_NPN_NO_OVERLAP,
330 	},
331 	{
332 		/* Malformed client list fails cleanly. */
333 		.peer_list = "\x01" "a",
334 		.peer_list_len = 2,
335 		.supported_list = "\x01" "a" "\x02" "bb" "\x00" "\x03" "ddd",
336 		.supported_list_len = 10,
337 		.want_out = NULL,
338 		.want_out_len = 0,
339 		.want_ret = OPENSSL_NPN_NO_OVERLAP,
340 	},
341 
342 	/*
343 	 * Some non-toy examples.
344 	 */
345 
346 	{
347 		.peer_list = "\x08" "http/1.1" "\x06" "spdy/1",
348 		.peer_list_len = 16,
349 		.supported_list = "\x08" "http/2.0" "\x08" "http/1.1",
350 		.supported_list_len = 18,
351 		.want_out = "http/1.1",
352 		.want_out_len = 8,
353 		.want_ret = OPENSSL_NPN_NEGOTIATED,
354 	},
355 	{
356 		.peer_list = "\x08" "http/2.0" "\x06" "spdy/1",
357 		.peer_list_len = 16,
358 		.supported_list = "\x08" "http/1.0" "\x08" "http/1.1",
359 		.supported_list_len = 18,
360 		.want_out = "http/1.0",
361 		.want_out_len = 8,
362 		.want_ret = OPENSSL_NPN_NO_OVERLAP,
363 	},
364 	{
365 		.peer_list = "\x08" "http/1.1" "\x08" "http/1.0",
366 		.peer_list_len = 18,
367 		.supported_list = "\x08" "http/1.0" "\x08" "http/1.1",
368 		.supported_list_len = 18,
369 		.want_out = "http/1.1",
370 		.want_out_len = 8,
371 		.want_ret = OPENSSL_NPN_NEGOTIATED,
372 	},
373 	{
374 		/* Peer list malformed. */
375 		.peer_list = "\x08" "http/1.1" "\x07" "http/1.0",
376 		.peer_list_len = 18,
377 		.supported_list = "\x08" "http/1.0" "\x08" "http/1.1",
378 		.supported_list_len = 18,
379 		.want_out = "http/1.0",
380 		.want_out_len = 8,
381 		.want_ret = OPENSSL_NPN_NO_OVERLAP,
382 	},
383 	{
384 		/* Peer list malformed. */
385 		.peer_list = "\x07" "http/1.1" "\x08" "http/1.0",
386 		.peer_list_len = 18,
387 		.supported_list = "\x08" "http/1.0" "\x08" "http/1.1",
388 		.supported_list_len = 18,
389 		.want_out = "http/1.0",
390 		.want_out_len = 8,
391 		.want_ret = OPENSSL_NPN_NO_OVERLAP,
392 	},
393 	{
394 		/* Supported list has trailing bytes. */
395 		.peer_list = "\x08" "http/1.1" "\x08" "http/1.0",
396 		.peer_list_len = 18,
397 		.supported_list = "\x08" "http/1.0" "\x07" "http/1.1",
398 		.supported_list_len = 18,
399 		.want_out = NULL,
400 		.want_out_len = 0,
401 		.want_ret = OPENSSL_NPN_NO_OVERLAP,
402 	},
403 };
404 
405 #define N_SELECT_NEXT_PROTO_TESTS \
406     (sizeof(select_next_proto_tests) / sizeof(select_next_proto_tests[0]))
407 
408 static int
select_next_proto_testcase(const struct select_next_proto_test * test)409 select_next_proto_testcase(const struct select_next_proto_test *test)
410 {
411 	unsigned char *out;
412 	unsigned char out_len;
413 	int ret;
414 	int failed = 0;
415 
416 	ret = SSL_select_next_proto(&out, &out_len, test->peer_list,
417 	    test->peer_list_len, test->supported_list, test->supported_list_len);
418 
419 	if (ret != test->want_ret || out_len != test->want_out_len ||
420 	    (out == NULL && test->want_out != NULL) ||
421 	    (out != NULL && test->want_out == NULL) ||
422 	    (out != NULL && test->want_out != NULL &&
423 	     memcmp(out, test->want_out, out_len) != 0)) {
424 		fprintf(stderr, "FAIL: ret: %u (want %u), out_len: %u (want %u)\n",
425 		    ret, test->want_ret, out_len, test->want_out_len);
426 		fprintf(stderr, "\ngot:\n");
427 		hexdump(out, out_len);
428 		fprintf(stderr, "\nwant:\n");
429 		hexdump(test->want_out, test->want_out_len);
430 		fprintf(stderr, "\nserver:\n");
431 		hexdump(test->peer_list, test->peer_list_len);
432 		fprintf(stderr, "\nclient:\n");
433 		hexdump(test->supported_list, test->supported_list_len);
434 		fprintf(stderr, "\n");
435 		failed = 1;
436 	}
437 
438 	return failed;
439 }
440 
441 static int
test_ssl_select_next_proto(void)442 test_ssl_select_next_proto(void)
443 {
444 	size_t i;
445 	int failed = 0;
446 
447 	for (i = 0; i < N_SELECT_NEXT_PROTO_TESTS; i++)
448 		failed |= select_next_proto_testcase(&select_next_proto_tests[i]);
449 
450 	return failed;
451 }
452 
453 int
main(void)454 main(void)
455 {
456 	size_t i;
457 	int failed = 0;
458 
459 	for (i = 0; i < N_ALPN_TESTS; i++)
460 		failed |= test_ssl_set_alpn_protos(&alpn_tests[i]);
461 
462 	failed |= test_ssl_set_alpn_protos_edge_cases();
463 
464 	failed |= test_ssl_select_next_proto();
465 
466 	if (!failed)
467 		printf("PASS %s\n", __FILE__);
468 
469 	return failed;
470 }
471