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 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 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 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 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 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 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