1 /* 2 * Copyright 2023-2026 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 #include "internal/quic_lcidm.h" 11 #include "internal/quic_types.h" 12 #include "internal/quic_vlint.h" 13 #include "internal/common.h" 14 #include "crypto/siphash.h" 15 #include <openssl/lhash.h> 16 #include <openssl/rand.h> 17 #include <openssl/err.h> 18 19 /* 20 * QUIC Local Connection ID Manager 21 * ================================ 22 */ 23 24 typedef struct quic_lcidm_conn_st QUIC_LCIDM_CONN; 25 26 enum { 27 LCID_TYPE_ODCID, /* This LCID is the ODCID from the peer */ 28 LCID_TYPE_INITIAL, /* This is our Initial SCID */ 29 LCID_TYPE_NCID /* This LCID was issued via a NCID frame */ 30 }; 31 32 typedef struct quic_lcid_st { 33 QUIC_CONN_ID cid; 34 uint64_t seq_num; 35 36 /* copy of the hash key from lcidm */ 37 uint64_t *hash_key; 38 39 /* Back-pointer to the owning QUIC_LCIDM_CONN structure. */ 40 QUIC_LCIDM_CONN *conn; 41 42 /* LCID_TYPE_* */ 43 unsigned int type : 2; 44 } QUIC_LCID; 45 46 DEFINE_LHASH_OF_EX(QUIC_LCID); 47 DEFINE_LHASH_OF_EX(QUIC_LCIDM_CONN); 48 49 struct quic_lcidm_conn_st { 50 size_t num_active_lcid; 51 LHASH_OF(QUIC_LCID) *lcids; 52 void *opaque; 53 QUIC_LCID *odcid_lcid_obj; 54 uint64_t next_seq_num; 55 56 /* Have we enrolled an ODCID? */ 57 unsigned int done_odcid : 1; 58 }; 59 60 struct quic_lcidm_st { 61 OSSL_LIB_CTX *libctx; 62 uint64_t hash_key[2]; /* random key for siphash */ 63 LHASH_OF(QUIC_LCID) *lcids; /* (QUIC_CONN_ID) -> (QUIC_LCID *) */ 64 LHASH_OF(QUIC_LCIDM_CONN) *conns; /* (void *opaque) -> (QUIC_LCIDM_CONN *) */ 65 size_t lcid_len; /* Length in bytes for all LCIDs */ 66 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 67 QUIC_CONN_ID next_lcid; 68 #endif 69 }; 70 71 static unsigned long lcid_hash(const QUIC_LCID *lcid_obj) 72 { 73 SIPHASH siphash = { 74 0, 75 }; 76 unsigned long hashval = 0; 77 unsigned char digest[SIPHASH_MIN_DIGEST_SIZE]; 78 79 /* Use a supported SipHash digest size (8 or 16); 8 is sufficient here. */ 80 if (!SipHash_set_hash_size(&siphash, SIPHASH_MIN_DIGEST_SIZE)) 81 goto out; 82 if (!SipHash_Init(&siphash, (uint8_t *)lcid_obj->hash_key, 0, 0)) 83 goto out; 84 SipHash_Update(&siphash, lcid_obj->cid.id, lcid_obj->cid.id_len); 85 if (!SipHash_Final(&siphash, digest, SIPHASH_MIN_DIGEST_SIZE)) 86 goto out; 87 88 /* 89 * Truncate the 64-bit SipHash digest into an unsigned long. 90 */ 91 memcpy(&hashval, digest, sizeof(hashval) < sizeof(digest) ? sizeof(hashval) : sizeof(digest)); 92 out: 93 return hashval; 94 } 95 96 static int lcid_comp(const QUIC_LCID *a, const QUIC_LCID *b) 97 { 98 return !ossl_quic_conn_id_eq(&a->cid, &b->cid); 99 } 100 101 static unsigned long lcidm_conn_hash(const QUIC_LCIDM_CONN *conn) 102 { 103 return (unsigned long)(uintptr_t)conn->opaque; 104 } 105 106 static int lcidm_conn_comp(const QUIC_LCIDM_CONN *a, const QUIC_LCIDM_CONN *b) 107 { 108 return a->opaque != b->opaque; 109 } 110 111 QUIC_LCIDM *ossl_quic_lcidm_new(OSSL_LIB_CTX *libctx, size_t lcid_len) 112 { 113 QUIC_LCIDM *lcidm = NULL; 114 115 if (lcid_len > QUIC_MAX_CONN_ID_LEN) 116 goto err; 117 118 if ((lcidm = OPENSSL_zalloc(sizeof(*lcidm))) == NULL) 119 goto err; 120 121 /* generate a random key for the hash tables hash function */ 122 if (!RAND_bytes_ex(libctx, (unsigned char *)&lcidm->hash_key, 123 sizeof(uint64_t) * 2, 0)) 124 goto err; 125 126 if ((lcidm->lcids = lh_QUIC_LCID_new(lcid_hash, lcid_comp)) == NULL) 127 goto err; 128 129 if ((lcidm->conns = lh_QUIC_LCIDM_CONN_new(lcidm_conn_hash, 130 lcidm_conn_comp)) 131 == NULL) 132 goto err; 133 134 lcidm->libctx = libctx; 135 lcidm->lcid_len = lcid_len; 136 return lcidm; 137 138 err: 139 if (lcidm != NULL) { 140 lh_QUIC_LCID_free(lcidm->lcids); 141 lh_QUIC_LCIDM_CONN_free(lcidm->conns); 142 OPENSSL_free(lcidm); 143 } 144 return NULL; 145 } 146 147 static void lcidm_delete_conn(QUIC_LCIDM *lcidm, QUIC_LCIDM_CONN *conn); 148 149 static void lcidm_delete_conn_(QUIC_LCIDM_CONN *conn, void *arg) 150 { 151 lcidm_delete_conn((QUIC_LCIDM *)arg, conn); 152 } 153 154 void ossl_quic_lcidm_free(QUIC_LCIDM *lcidm) 155 { 156 if (lcidm == NULL) 157 return; 158 159 /* 160 * Calling OPENSSL_lh_delete during a doall call is unsafe with our 161 * current LHASH implementation for several reasons: 162 * 163 * - firstly, because deletes can cause the hashtable to be contracted, 164 * resulting in rehashing which might cause items in later buckets to 165 * move to earlier buckets, which might cause doall to skip an item, 166 * resulting in a memory leak; 167 * 168 * - secondly, because doall in general is not safe across hashtable 169 * size changes, as it caches hashtable size and pointer values 170 * while operating. 171 * 172 * The fix for this is to disable hashtable contraction using the following 173 * call, which guarantees that no rehashing will occur so long as we only 174 * call delete and not insert. 175 */ 176 lh_QUIC_LCIDM_CONN_set_down_load(lcidm->conns, 0); 177 178 lh_QUIC_LCIDM_CONN_doall_arg(lcidm->conns, lcidm_delete_conn_, lcidm); 179 180 lh_QUIC_LCID_free(lcidm->lcids); 181 lh_QUIC_LCIDM_CONN_free(lcidm->conns); 182 OPENSSL_free(lcidm); 183 } 184 185 static QUIC_LCID *lcidm_get0_lcid(const QUIC_LCIDM *lcidm, const QUIC_CONN_ID *lcid) 186 { 187 QUIC_LCID key; 188 189 key.cid = *lcid; 190 key.hash_key = (uint64_t *)lcidm->hash_key; 191 192 if (key.cid.id_len > QUIC_MAX_CONN_ID_LEN) 193 return NULL; 194 195 return lh_QUIC_LCID_retrieve(lcidm->lcids, &key); 196 } 197 198 static QUIC_LCIDM_CONN *lcidm_get0_conn(const QUIC_LCIDM *lcidm, void *opaque) 199 { 200 QUIC_LCIDM_CONN key; 201 202 key.opaque = opaque; 203 204 return lh_QUIC_LCIDM_CONN_retrieve(lcidm->conns, &key); 205 } 206 207 static QUIC_LCIDM_CONN *lcidm_upsert_conn(const QUIC_LCIDM *lcidm, void *opaque) 208 { 209 QUIC_LCIDM_CONN *conn = lcidm_get0_conn(lcidm, opaque); 210 211 if (conn != NULL) 212 return conn; 213 214 if ((conn = OPENSSL_zalloc(sizeof(*conn))) == NULL) 215 goto err; 216 217 if ((conn->lcids = lh_QUIC_LCID_new(lcid_hash, lcid_comp)) == NULL) 218 goto err; 219 220 conn->opaque = opaque; 221 222 lh_QUIC_LCIDM_CONN_insert(lcidm->conns, conn); 223 if (lh_QUIC_LCIDM_CONN_error(lcidm->conns)) 224 goto err; 225 226 return conn; 227 228 err: 229 if (conn != NULL) { 230 lh_QUIC_LCID_free(conn->lcids); 231 OPENSSL_free(conn); 232 } 233 return NULL; 234 } 235 236 static void lcidm_delete_conn_lcid(QUIC_LCIDM *lcidm, QUIC_LCID *lcid_obj) 237 { 238 lh_QUIC_LCID_delete(lcidm->lcids, lcid_obj); 239 lh_QUIC_LCID_delete(lcid_obj->conn->lcids, lcid_obj); 240 assert(lcid_obj->conn->num_active_lcid > 0); 241 --lcid_obj->conn->num_active_lcid; 242 OPENSSL_free(lcid_obj); 243 } 244 245 /* doall_arg wrapper */ 246 static void lcidm_delete_conn_lcid_(QUIC_LCID *lcid_obj, void *arg) 247 { 248 lcidm_delete_conn_lcid((QUIC_LCIDM *)arg, lcid_obj); 249 } 250 251 static void lcidm_delete_conn(QUIC_LCIDM *lcidm, QUIC_LCIDM_CONN *conn) 252 { 253 /* See comment in ossl_quic_lcidm_free */ 254 lh_QUIC_LCID_set_down_load(conn->lcids, 0); 255 256 lh_QUIC_LCID_doall_arg(conn->lcids, lcidm_delete_conn_lcid_, lcidm); 257 lh_QUIC_LCIDM_CONN_delete(lcidm->conns, conn); 258 lh_QUIC_LCID_free(conn->lcids); 259 OPENSSL_free(conn); 260 } 261 262 static QUIC_LCID *lcidm_conn_new_lcid(QUIC_LCIDM *lcidm, QUIC_LCIDM_CONN *conn, 263 const QUIC_CONN_ID *lcid) 264 { 265 QUIC_LCID *lcid_obj = NULL; 266 267 if (lcid->id_len > QUIC_MAX_CONN_ID_LEN) 268 return NULL; 269 270 if ((lcid_obj = OPENSSL_zalloc(sizeof(*lcid_obj))) == NULL) 271 goto err; 272 273 lcid_obj->cid = *lcid; 274 lcid_obj->conn = conn; 275 lcid_obj->hash_key = lcidm->hash_key; 276 277 lh_QUIC_LCID_insert(conn->lcids, lcid_obj); 278 if (lh_QUIC_LCID_error(conn->lcids)) 279 goto err; 280 281 lh_QUIC_LCID_insert(lcidm->lcids, lcid_obj); 282 if (lh_QUIC_LCID_error(lcidm->lcids)) { 283 lh_QUIC_LCID_delete(conn->lcids, lcid_obj); 284 goto err; 285 } 286 287 ++conn->num_active_lcid; 288 return lcid_obj; 289 290 err: 291 OPENSSL_free(lcid_obj); 292 return NULL; 293 } 294 295 size_t ossl_quic_lcidm_get_lcid_len(const QUIC_LCIDM *lcidm) 296 { 297 return lcidm->lcid_len; 298 } 299 300 size_t ossl_quic_lcidm_get_num_active_lcid(const QUIC_LCIDM *lcidm, 301 void *opaque) 302 { 303 QUIC_LCIDM_CONN *conn; 304 305 conn = lcidm_get0_conn(lcidm, opaque); 306 if (conn == NULL) 307 return 0; 308 309 return conn->num_active_lcid; 310 } 311 312 static int lcidm_generate_cid(QUIC_LCIDM *lcidm, 313 QUIC_CONN_ID *cid) 314 { 315 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 316 int i; 317 318 lcidm->next_lcid.id_len = (unsigned char)lcidm->lcid_len; 319 *cid = lcidm->next_lcid; 320 321 for (i = lcidm->lcid_len - 1; i >= 0; --i) 322 if (++lcidm->next_lcid.id[i] != 0) 323 break; 324 325 return 1; 326 #else 327 return ossl_quic_gen_rand_conn_id(lcidm->libctx, lcidm->lcid_len, cid); 328 #endif 329 } 330 331 static int lcidm_generate(QUIC_LCIDM *lcidm, 332 void *opaque, 333 unsigned int type, 334 QUIC_CONN_ID *lcid_out, 335 uint64_t *seq_num) 336 { 337 QUIC_LCIDM_CONN *conn; 338 QUIC_LCID key, *lcid_obj; 339 size_t i; 340 #define MAX_RETRIES 8 341 342 if ((conn = lcidm_upsert_conn(lcidm, opaque)) == NULL) 343 return 0; 344 345 if ((type == LCID_TYPE_INITIAL && conn->next_seq_num > 0) 346 || conn->next_seq_num > OSSL_QUIC_VLINT_MAX) 347 return 0; 348 349 i = 0; 350 do { 351 if (i++ >= MAX_RETRIES) 352 /* 353 * Too many retries; should not happen but if it does, don't loop 354 * endlessly. 355 */ 356 return 0; 357 358 if (!lcidm_generate_cid(lcidm, lcid_out)) 359 return 0; 360 361 key.cid = *lcid_out; 362 key.hash_key = lcidm->hash_key; 363 364 /* If a collision occurs, retry. */ 365 } while (lh_QUIC_LCID_retrieve(lcidm->lcids, &key) != NULL); 366 367 if ((lcid_obj = lcidm_conn_new_lcid(lcidm, conn, lcid_out)) == NULL) 368 return 0; 369 370 lcid_obj->seq_num = conn->next_seq_num; 371 lcid_obj->type = type; 372 373 if (seq_num != NULL) 374 *seq_num = lcid_obj->seq_num; 375 376 ++conn->next_seq_num; 377 return 1; 378 } 379 380 int ossl_quic_lcidm_enrol_odcid(QUIC_LCIDM *lcidm, 381 void *opaque, 382 const QUIC_CONN_ID *initial_odcid) 383 { 384 QUIC_LCIDM_CONN *conn; 385 QUIC_LCID key, *lcid_obj; 386 387 if (initial_odcid == NULL || initial_odcid->id_len < QUIC_MIN_ODCID_LEN 388 || initial_odcid->id_len > QUIC_MAX_CONN_ID_LEN) 389 return 0; 390 391 if ((conn = lcidm_upsert_conn(lcidm, opaque)) == NULL) 392 return 0; 393 394 if (conn->done_odcid) 395 return 0; 396 397 key.cid = *initial_odcid; 398 key.hash_key = lcidm->hash_key; 399 if (lh_QUIC_LCID_retrieve(lcidm->lcids, &key) != NULL) 400 return 0; 401 402 if ((lcid_obj = lcidm_conn_new_lcid(lcidm, conn, initial_odcid)) == NULL) 403 return 0; 404 405 lcid_obj->seq_num = LCIDM_ODCID_SEQ_NUM; 406 lcid_obj->type = LCID_TYPE_ODCID; 407 408 conn->odcid_lcid_obj = lcid_obj; 409 conn->done_odcid = 1; 410 return 1; 411 } 412 413 int ossl_quic_lcidm_generate_initial(QUIC_LCIDM *lcidm, 414 void *opaque, 415 QUIC_CONN_ID *initial_lcid) 416 { 417 return lcidm_generate(lcidm, opaque, LCID_TYPE_INITIAL, 418 initial_lcid, NULL); 419 } 420 421 int ossl_quic_lcidm_bind_channel(QUIC_LCIDM *lcidm, void *opaque, 422 const QUIC_CONN_ID *lcid) 423 { 424 QUIC_LCIDM_CONN *conn; 425 QUIC_LCID *lcid_obj; 426 427 /* 428 * the plan is simple: 429 * make sure the lcid is still unused. 430 * do the same business as ossl_quic_lcidm_gnerate_initial() does, 431 * except we will use lcid instead of generating a new one. 432 */ 433 if (ossl_quic_lcidm_lookup(lcidm, lcid, NULL, NULL) != 0) 434 return 0; 435 436 if ((conn = lcidm_upsert_conn(lcidm, opaque)) == NULL) 437 return 0; 438 439 if ((lcid_obj = lcidm_conn_new_lcid(lcidm, conn, lcid)) == NULL) { 440 lcidm_delete_conn(lcidm, conn); 441 return 0; 442 } 443 444 lcid_obj->seq_num = conn->next_seq_num; 445 lcid_obj->type = LCID_TYPE_INITIAL; 446 conn->next_seq_num++; 447 448 return 1; 449 } 450 451 int ossl_quic_lcidm_generate(QUIC_LCIDM *lcidm, 452 void *opaque, 453 OSSL_QUIC_FRAME_NEW_CONN_ID *ncid_frame) 454 { 455 ncid_frame->seq_num = 0; 456 ncid_frame->retire_prior_to = 0; 457 458 return lcidm_generate(lcidm, opaque, LCID_TYPE_NCID, 459 &ncid_frame->conn_id, 460 &ncid_frame->seq_num); 461 } 462 463 int ossl_quic_lcidm_retire_odcid(QUIC_LCIDM *lcidm, void *opaque) 464 { 465 QUIC_LCIDM_CONN *conn; 466 467 if ((conn = lcidm_upsert_conn(lcidm, opaque)) == NULL) 468 return 0; 469 470 if (conn->odcid_lcid_obj == NULL) 471 return 0; 472 473 lcidm_delete_conn_lcid(lcidm, conn->odcid_lcid_obj); 474 conn->odcid_lcid_obj = NULL; 475 return 1; 476 } 477 478 struct retire_args { 479 QUIC_LCID *earliest_seq_num_lcid_obj; 480 uint64_t earliest_seq_num, retire_prior_to; 481 }; 482 483 static void retire_for_conn(QUIC_LCID *lcid_obj, void *arg) 484 { 485 struct retire_args *args = arg; 486 487 /* ODCID LCID cannot be retired via this API */ 488 if (lcid_obj->type == LCID_TYPE_ODCID 489 || lcid_obj->seq_num >= args->retire_prior_to) 490 return; 491 492 if (lcid_obj->seq_num < args->earliest_seq_num) { 493 args->earliest_seq_num = lcid_obj->seq_num; 494 args->earliest_seq_num_lcid_obj = lcid_obj; 495 } 496 } 497 498 int ossl_quic_lcidm_retire(QUIC_LCIDM *lcidm, 499 void *opaque, 500 uint64_t retire_prior_to, 501 const QUIC_CONN_ID *containing_pkt_dcid, 502 QUIC_CONN_ID *retired_lcid, 503 uint64_t *retired_seq_num, 504 int *did_retire) 505 { 506 QUIC_LCIDM_CONN key, *conn; 507 struct retire_args args = { 0 }; 508 509 key.opaque = opaque; 510 511 if (did_retire == NULL) 512 return 0; 513 514 *did_retire = 0; 515 if ((conn = lh_QUIC_LCIDM_CONN_retrieve(lcidm->conns, &key)) == NULL) 516 return 1; 517 518 args.retire_prior_to = retire_prior_to; 519 args.earliest_seq_num = UINT64_MAX; 520 521 lh_QUIC_LCID_doall_arg(conn->lcids, retire_for_conn, &args); 522 if (args.earliest_seq_num_lcid_obj == NULL) 523 return 1; 524 525 if (containing_pkt_dcid != NULL 526 && ossl_quic_conn_id_eq(&args.earliest_seq_num_lcid_obj->cid, 527 containing_pkt_dcid)) 528 return 0; 529 530 *did_retire = 1; 531 if (retired_lcid != NULL) 532 *retired_lcid = args.earliest_seq_num_lcid_obj->cid; 533 if (retired_seq_num != NULL) 534 *retired_seq_num = args.earliest_seq_num_lcid_obj->seq_num; 535 536 lcidm_delete_conn_lcid(lcidm, args.earliest_seq_num_lcid_obj); 537 return 1; 538 } 539 540 int ossl_quic_lcidm_cull(QUIC_LCIDM *lcidm, void *opaque) 541 { 542 QUIC_LCIDM_CONN key, *conn; 543 544 key.opaque = opaque; 545 546 if ((conn = lh_QUIC_LCIDM_CONN_retrieve(lcidm->conns, &key)) == NULL) 547 return 0; 548 549 lcidm_delete_conn(lcidm, conn); 550 return 1; 551 } 552 553 int ossl_quic_lcidm_lookup(QUIC_LCIDM *lcidm, 554 const QUIC_CONN_ID *lcid, 555 uint64_t *seq_num, 556 void **opaque) 557 { 558 QUIC_LCID *lcid_obj; 559 560 if (lcid == NULL) 561 return 0; 562 563 if ((lcid_obj = lcidm_get0_lcid(lcidm, lcid)) == NULL) 564 return 0; 565 566 if (seq_num != NULL) 567 *seq_num = lcid_obj->seq_num; 568 569 if (opaque != NULL) 570 *opaque = lcid_obj->conn->opaque; 571 572 return 1; 573 } 574 575 int ossl_quic_lcidm_debug_remove(QUIC_LCIDM *lcidm, 576 const QUIC_CONN_ID *lcid) 577 { 578 QUIC_LCID key, *lcid_obj; 579 580 key.cid = *lcid; 581 key.hash_key = lcidm->hash_key; 582 if ((lcid_obj = lh_QUIC_LCID_retrieve(lcidm->lcids, &key)) == NULL) 583 return 0; 584 585 lcidm_delete_conn_lcid(lcidm, lcid_obj); 586 return 1; 587 } 588 589 int ossl_quic_lcidm_debug_add(QUIC_LCIDM *lcidm, void *opaque, 590 const QUIC_CONN_ID *lcid, 591 uint64_t seq_num) 592 { 593 QUIC_LCIDM_CONN *conn; 594 QUIC_LCID key, *lcid_obj; 595 596 if (lcid == NULL || lcid->id_len > QUIC_MAX_CONN_ID_LEN) 597 return 0; 598 599 if ((conn = lcidm_upsert_conn(lcidm, opaque)) == NULL) 600 return 0; 601 602 key.cid = *lcid; 603 key.hash_key = lcidm->hash_key; 604 if (lh_QUIC_LCID_retrieve(lcidm->lcids, &key) != NULL) 605 return 0; 606 607 if ((lcid_obj = lcidm_conn_new_lcid(lcidm, conn, lcid)) == NULL) 608 return 0; 609 610 lcid_obj->seq_num = seq_num; 611 lcid_obj->type = LCID_TYPE_NCID; 612 return 1; 613 } 614 615 int ossl_quic_lcidm_get_unused_cid(QUIC_LCIDM *lcidm, QUIC_CONN_ID *cid) 616 { 617 int i; 618 619 for (i = 0; i < 10; i++) { 620 if (lcidm_generate_cid(lcidm, cid) 621 && lcidm_get0_lcid(lcidm, cid) == NULL) 622 return 1; /* not found <=> radomly generated cid is unused */ 623 } 624 625 return 0; 626 } 627