1 /* 2 * Copyright 2019-2025 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/namemap.h" 11 #include "internal/tsan_assist.h" 12 #include "internal/hashtable.h" 13 #include "internal/sizes.h" 14 #include "crypto/context.h" 15 16 #define NAMEMAP_HT_BUCKETS 512 17 18 HT_START_KEY_DEFN(namenum_key) 19 HT_DEF_KEY_FIELD_CHAR_ARRAY(name, 64) 20 HT_END_KEY_DEFN(NAMENUM_KEY) 21 22 /*- 23 * The namemap itself 24 * ================== 25 */ 26 27 typedef STACK_OF(OPENSSL_STRING) NAMES; 28 29 DEFINE_STACK_OF(NAMES) 30 31 struct ossl_namemap_st { 32 /* Flags */ 33 unsigned int stored : 1; /* If 1, it's stored in a library context */ 34 35 HT *namenum_ht; /* Name->number mapping */ 36 37 CRYPTO_RWLOCK *lock; 38 STACK_OF(NAMES) *numnames; 39 40 TSAN_QUALIFIER int max_number; /* Current max number */ 41 }; 42 43 static void name_string_free(char *name) 44 { 45 OPENSSL_free(name); 46 } 47 48 static void names_free(NAMES *n) 49 { 50 sk_OPENSSL_STRING_pop_free(n, name_string_free); 51 } 52 53 /* OSSL_LIB_CTX_METHOD functions for a namemap stored in a library context */ 54 55 void *ossl_stored_namemap_new(OSSL_LIB_CTX *libctx) 56 { 57 OSSL_NAMEMAP *namemap = ossl_namemap_new(libctx); 58 59 if (namemap != NULL) 60 namemap->stored = 1; 61 62 return namemap; 63 } 64 65 void ossl_stored_namemap_free(void *vnamemap) 66 { 67 OSSL_NAMEMAP *namemap = vnamemap; 68 69 if (namemap != NULL) { 70 /* Pretend it isn't stored, or ossl_namemap_free() will do nothing */ 71 namemap->stored = 0; 72 ossl_namemap_free(namemap); 73 } 74 } 75 76 /*- 77 * API functions 78 * ============= 79 */ 80 81 int ossl_namemap_empty(OSSL_NAMEMAP *namemap) 82 { 83 #ifdef TSAN_REQUIRES_LOCKING 84 /* No TSAN support */ 85 int rv; 86 87 if (namemap == NULL) 88 return 1; 89 90 if (!CRYPTO_THREAD_read_lock(namemap->lock)) 91 return -1; 92 rv = namemap->max_number == 0; 93 CRYPTO_THREAD_unlock(namemap->lock); 94 return rv; 95 #else 96 /* Have TSAN support */ 97 return namemap == NULL || tsan_load(&namemap->max_number) == 0; 98 #endif 99 } 100 101 /* 102 * Call the callback for all names in the namemap with the given number. 103 * A return value 1 means that the callback was called for all names. A 104 * return value of 0 means that the callback was not called for any names. 105 */ 106 int ossl_namemap_doall_names(const OSSL_NAMEMAP *namemap, int number, 107 void (*fn)(const char *name, void *data), 108 void *data) 109 { 110 int i; 111 NAMES *names; 112 113 if (namemap == NULL || number <= 0) 114 return 0; 115 116 /* 117 * We duplicate the NAMES stack under a read lock. Subsequently we call 118 * the user function, so that we're not holding the read lock when in user 119 * code. This could lead to deadlocks. 120 */ 121 if (!CRYPTO_THREAD_read_lock(namemap->lock)) 122 return 0; 123 124 names = sk_NAMES_value(namemap->numnames, number - 1); 125 if (names != NULL) 126 names = sk_OPENSSL_STRING_dup(names); 127 128 CRYPTO_THREAD_unlock(namemap->lock); 129 130 if (names == NULL) 131 return 0; 132 133 for (i = 0; i < sk_OPENSSL_STRING_num(names); i++) 134 fn(sk_OPENSSL_STRING_value(names, i), data); 135 136 sk_OPENSSL_STRING_free(names); 137 return i > 0; 138 } 139 140 int ossl_namemap_name2num(const OSSL_NAMEMAP *namemap, const char *name) 141 { 142 int number = 0; 143 HT_VALUE *val; 144 NAMENUM_KEY key; 145 146 #ifndef FIPS_MODULE 147 if (namemap == NULL) 148 namemap = ossl_namemap_stored(NULL); 149 #endif 150 151 if (namemap == NULL) 152 return 0; 153 154 HT_INIT_KEY(&key); 155 HT_SET_KEY_STRING_CASE(&key, name, name); 156 157 val = ossl_ht_get(namemap->namenum_ht, TO_HT_KEY(&key)); 158 159 if (val != NULL) 160 /* We store a (small) int directly instead of a pointer to it. */ 161 number = (int)(intptr_t)val->value; 162 163 return number; 164 } 165 166 int ossl_namemap_name2num_n(const OSSL_NAMEMAP *namemap, 167 const char *name, size_t name_len) 168 { 169 int number = 0; 170 HT_VALUE *val; 171 NAMENUM_KEY key; 172 173 #ifndef FIPS_MODULE 174 if (namemap == NULL) 175 namemap = ossl_namemap_stored(NULL); 176 #endif 177 178 if (namemap == NULL) 179 return 0; 180 181 HT_INIT_KEY(&key); 182 HT_SET_KEY_STRING_CASE_N(&key, name, name, name_len); 183 184 val = ossl_ht_get(namemap->namenum_ht, TO_HT_KEY(&key)); 185 186 if (val != NULL) 187 /* We store a (small) int directly instead of a pointer to it. */ 188 number = (int)(intptr_t)val->value; 189 190 return number; 191 } 192 193 const char *ossl_namemap_num2name(const OSSL_NAMEMAP *namemap, int number, 194 size_t idx) 195 { 196 NAMES *names; 197 const char *ret = NULL; 198 199 if (namemap == NULL || number <= 0) 200 return NULL; 201 202 if (!CRYPTO_THREAD_read_lock(namemap->lock)) 203 return NULL; 204 205 names = sk_NAMES_value(namemap->numnames, number - 1); 206 if (names != NULL) 207 ret = sk_OPENSSL_STRING_value(names, idx); 208 209 CRYPTO_THREAD_unlock(namemap->lock); 210 211 return ret; 212 } 213 214 /* This function is not thread safe, the namemap must be locked */ 215 static int numname_insert(OSSL_NAMEMAP *namemap, int number, 216 const char *name) 217 { 218 NAMES *names; 219 char *tmpname; 220 221 if (number > 0) { 222 names = sk_NAMES_value(namemap->numnames, number - 1); 223 if (!ossl_assert(names != NULL)) { 224 /* cannot happen */ 225 return 0; 226 } 227 } else { 228 /* a completely new entry */ 229 names = sk_OPENSSL_STRING_new_null(); 230 if (names == NULL) 231 return 0; 232 } 233 234 if ((tmpname = OPENSSL_strdup(name)) == NULL) 235 goto err; 236 237 if (!sk_OPENSSL_STRING_push(names, tmpname)) 238 goto err; 239 tmpname = NULL; 240 241 if (number <= 0) { 242 if (!sk_NAMES_push(namemap->numnames, names)) 243 goto err; 244 number = sk_NAMES_num(namemap->numnames); 245 } 246 return number; 247 248 err: 249 if (number <= 0) 250 sk_OPENSSL_STRING_pop_free(names, name_string_free); 251 OPENSSL_free(tmpname); 252 return 0; 253 } 254 255 /* This function is not thread safe, the namemap must be locked */ 256 static int namemap_add_name(OSSL_NAMEMAP *namemap, int number, 257 const char *name) 258 { 259 int ret; 260 HT_VALUE val = { 0 }; 261 NAMENUM_KEY key; 262 263 /* If it already exists, we don't add it */ 264 if ((ret = ossl_namemap_name2num(namemap, name)) != 0) 265 return ret; 266 267 if ((number = numname_insert(namemap, number, name)) == 0) 268 return 0; 269 270 /* Using tsan_store alone here is safe since we're under lock */ 271 tsan_store(&namemap->max_number, number); 272 273 HT_INIT_KEY(&key); 274 HT_SET_KEY_STRING_CASE(&key, name, name); 275 val.value = (void *)(intptr_t)number; 276 ret = ossl_ht_insert(namemap->namenum_ht, TO_HT_KEY(&key), &val, NULL); 277 if (!ossl_assert(ret != 0)) /* cannot happen as we are under write lock */ 278 return 0; 279 if (ret < 1) { 280 /* unable to insert due to too many collisions */ 281 ERR_raise(ERR_LIB_CRYPTO, CRYPTO_R_TOO_MANY_NAMES); 282 return 0; 283 } 284 return number; 285 } 286 287 int ossl_namemap_add_name(OSSL_NAMEMAP *namemap, int number, 288 const char *name) 289 { 290 int tmp_number; 291 292 #ifndef FIPS_MODULE 293 if (namemap == NULL) 294 namemap = ossl_namemap_stored(NULL); 295 #endif 296 297 if (name == NULL || *name == 0 || namemap == NULL) 298 return 0; 299 300 if (!CRYPTO_THREAD_write_lock(namemap->lock)) 301 return 0; 302 tmp_number = namemap_add_name(namemap, number, name); 303 CRYPTO_THREAD_unlock(namemap->lock); 304 return tmp_number; 305 } 306 307 int ossl_namemap_add_names(OSSL_NAMEMAP *namemap, int number, 308 const char *names, const char separator) 309 { 310 char *tmp, *p, *q, *endp; 311 312 /* Check that we have a namemap */ 313 if (!ossl_assert(namemap != NULL)) { 314 ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER); 315 return 0; 316 } 317 318 if ((tmp = OPENSSL_strdup(names)) == NULL) 319 return 0; 320 321 if (!CRYPTO_THREAD_write_lock(namemap->lock)) { 322 OPENSSL_free(tmp); 323 return 0; 324 } 325 /* 326 * Check that no name is an empty string, and that all names have at 327 * most one numeric identity together. 328 */ 329 for (p = tmp; *p != '\0'; p = q) { 330 int this_number; 331 size_t l; 332 333 if ((q = strchr(p, separator)) == NULL) { 334 l = strlen(p); /* offset to \0 */ 335 q = p + l; 336 } else { 337 l = q - p; /* offset to the next separator */ 338 *q++ = '\0'; 339 } 340 341 if (*p == '\0') { 342 ERR_raise(ERR_LIB_CRYPTO, CRYPTO_R_BAD_ALGORITHM_NAME); 343 number = 0; 344 goto end; 345 } 346 347 this_number = ossl_namemap_name2num(namemap, p); 348 349 if (number == 0) { 350 number = this_number; 351 } else if (this_number != 0 && this_number != number) { 352 ERR_raise_data(ERR_LIB_CRYPTO, CRYPTO_R_CONFLICTING_NAMES, 353 "\"%s\" has an existing different identity %d (from \"%s\")", 354 p, this_number, names); 355 number = 0; 356 goto end; 357 } 358 } 359 endp = p; 360 361 /* Now that we have checked, register all names */ 362 for (p = tmp; p < endp; p = q) { 363 int this_number; 364 365 q = p + strlen(p) + 1; 366 367 this_number = namemap_add_name(namemap, number, p); 368 if (number == 0) { 369 number = this_number; 370 } else if (this_number != number) { 371 ERR_raise_data(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR, 372 "Got number %d when expecting %d", 373 this_number, number); 374 number = 0; 375 goto end; 376 } 377 } 378 379 end: 380 CRYPTO_THREAD_unlock(namemap->lock); 381 OPENSSL_free(tmp); 382 return number; 383 } 384 385 /*- 386 * Pre-population 387 * ============== 388 */ 389 390 #ifndef FIPS_MODULE 391 #include <openssl/evp.h> 392 393 /* Creates an initial namemap with names found in the legacy method db */ 394 static void get_legacy_evp_names(int base_nid, int nid, const char *pem_name, 395 void *arg) 396 { 397 int num = 0; 398 ASN1_OBJECT *obj; 399 400 if (base_nid != NID_undef) { 401 num = ossl_namemap_add_name(arg, num, OBJ_nid2sn(base_nid)); 402 num = ossl_namemap_add_name(arg, num, OBJ_nid2ln(base_nid)); 403 } 404 405 if (nid != NID_undef) { 406 num = ossl_namemap_add_name(arg, num, OBJ_nid2sn(nid)); 407 num = ossl_namemap_add_name(arg, num, OBJ_nid2ln(nid)); 408 if ((obj = OBJ_nid2obj(nid)) != NULL) { 409 char txtoid[OSSL_MAX_NAME_SIZE]; 410 411 if (OBJ_obj2txt(txtoid, sizeof(txtoid), obj, 1) > 0) 412 num = ossl_namemap_add_name(arg, num, txtoid); 413 } 414 } 415 if (pem_name != NULL) 416 num = ossl_namemap_add_name(arg, num, pem_name); 417 } 418 419 static void get_legacy_cipher_names(const OBJ_NAME *on, void *arg) 420 { 421 const EVP_CIPHER *cipher = (void *)OBJ_NAME_get(on->name, on->type); 422 423 if (cipher != NULL) 424 get_legacy_evp_names(NID_undef, EVP_CIPHER_get_type(cipher), NULL, arg); 425 } 426 427 static void get_legacy_md_names(const OBJ_NAME *on, void *arg) 428 { 429 const EVP_MD *md = (void *)OBJ_NAME_get(on->name, on->type); 430 431 if (md != NULL) 432 get_legacy_evp_names(0, EVP_MD_get_type(md), NULL, arg); 433 } 434 435 static void get_legacy_pkey_meth_names(const EVP_PKEY_ASN1_METHOD *ameth, 436 void *arg) 437 { 438 int nid = 0, base_nid = 0, flags = 0; 439 const char *pem_name = NULL; 440 441 EVP_PKEY_asn1_get0_info(&nid, &base_nid, &flags, NULL, &pem_name, ameth); 442 if (nid != NID_undef) { 443 if ((flags & ASN1_PKEY_ALIAS) == 0) { 444 switch (nid) { 445 case EVP_PKEY_DHX: 446 /* We know that the name "DHX" is used too */ 447 get_legacy_evp_names(0, nid, "DHX", arg); 448 /* FALLTHRU */ 449 default: 450 get_legacy_evp_names(0, nid, pem_name, arg); 451 } 452 } else { 453 /* 454 * Treat aliases carefully, some of them are undesirable, or 455 * should not be treated as such for providers. 456 */ 457 458 switch (nid) { 459 case EVP_PKEY_SM2: 460 /* 461 * SM2 is a separate keytype with providers, not an alias for 462 * EC. 463 */ 464 get_legacy_evp_names(0, nid, pem_name, arg); 465 break; 466 default: 467 /* Use the short name of the base nid as the common reference */ 468 get_legacy_evp_names(base_nid, nid, pem_name, arg); 469 } 470 } 471 } 472 } 473 #endif 474 475 /*- 476 * Constructors / destructors 477 * ========================== 478 */ 479 480 OSSL_NAMEMAP *ossl_namemap_stored(OSSL_LIB_CTX *libctx) 481 { 482 #ifndef FIPS_MODULE 483 int nms; 484 #endif 485 OSSL_NAMEMAP *namemap = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_NAMEMAP_INDEX); 486 487 if (namemap == NULL) 488 return NULL; 489 490 #ifndef FIPS_MODULE 491 nms = ossl_namemap_empty(namemap); 492 if (nms < 0) { 493 /* 494 * Could not get lock to make the count, so maybe internal objects 495 * weren't added. This seems safest. 496 */ 497 return NULL; 498 } 499 if (nms == 1) { 500 int i, end; 501 502 /* Before pilfering, we make sure the legacy database is populated */ 503 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS 504 | OPENSSL_INIT_ADD_ALL_DIGESTS, 505 NULL); 506 507 OBJ_NAME_do_all(OBJ_NAME_TYPE_CIPHER_METH, 508 get_legacy_cipher_names, namemap); 509 OBJ_NAME_do_all(OBJ_NAME_TYPE_MD_METH, 510 get_legacy_md_names, namemap); 511 512 /* We also pilfer data from the legacy EVP_PKEY_ASN1_METHODs */ 513 for (i = 0, end = EVP_PKEY_asn1_get_count(); i < end; i++) 514 get_legacy_pkey_meth_names(EVP_PKEY_asn1_get0(i), namemap); 515 } 516 #endif 517 518 return namemap; 519 } 520 521 OSSL_NAMEMAP *ossl_namemap_new(OSSL_LIB_CTX *libctx) 522 { 523 OSSL_NAMEMAP *namemap; 524 HT_CONFIG htconf = { NULL, NULL, NULL, NAMEMAP_HT_BUCKETS, 1, 1 }; 525 526 htconf.ctx = libctx; 527 528 if ((namemap = OPENSSL_zalloc(sizeof(*namemap))) == NULL) 529 goto err; 530 531 if ((namemap->lock = CRYPTO_THREAD_lock_new()) == NULL) 532 goto err; 533 534 if ((namemap->namenum_ht = ossl_ht_new(&htconf)) == NULL) 535 goto err; 536 537 if ((namemap->numnames = sk_NAMES_new_null()) == NULL) 538 goto err; 539 540 return namemap; 541 542 err: 543 ossl_namemap_free(namemap); 544 return NULL; 545 } 546 547 void ossl_namemap_free(OSSL_NAMEMAP *namemap) 548 { 549 if (namemap == NULL || namemap->stored) 550 return; 551 552 sk_NAMES_pop_free(namemap->numnames, names_free); 553 554 ossl_ht_free(namemap->namenum_ht); 555 556 CRYPTO_THREAD_lock_free(namemap->lock); 557 OPENSSL_free(namemap); 558 } 559