1 /* 2 * Copyright 1995-2021 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 <stdlib.h> 11 #include "crypto/cryptlib.h" 12 #include "internal/thread_once.h" 13 14 int ossl_do_ex_data_init(OSSL_LIB_CTX *ctx) 15 { 16 OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ctx); 17 18 if (global == NULL) 19 return 0; 20 21 global->ex_data_lock = CRYPTO_THREAD_lock_new(); 22 return global->ex_data_lock != NULL; 23 } 24 25 /* 26 * Return the EX_CALLBACKS from the |ex_data| array that corresponds to 27 * a given class. On success, *holds the lock.* 28 * The |global| parameter is assumed to be non null (checked by the caller). 29 */ 30 static EX_CALLBACKS *get_and_lock(OSSL_EX_DATA_GLOBAL *global, int class_index) 31 { 32 EX_CALLBACKS *ip; 33 34 if (class_index < 0 || class_index >= CRYPTO_EX_INDEX__COUNT) { 35 ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); 36 return NULL; 37 } 38 39 if (global->ex_data_lock == NULL) { 40 /* 41 * If we get here, someone (who?) cleaned up the lock, so just 42 * treat it as an error. 43 */ 44 return NULL; 45 } 46 47 if (!CRYPTO_THREAD_write_lock(global->ex_data_lock)) 48 return NULL; 49 ip = &global->ex_data[class_index]; 50 return ip; 51 } 52 53 static void cleanup_cb(EX_CALLBACK *funcs) 54 { 55 OPENSSL_free(funcs); 56 } 57 58 /* 59 * Release all "ex_data" state to prevent memory leaks. This can't be made 60 * thread-safe without overhauling a lot of stuff, and shouldn't really be 61 * called under potential race-conditions anyway (it's for program shutdown 62 * after all). 63 */ 64 void ossl_crypto_cleanup_all_ex_data_int(OSSL_LIB_CTX *ctx) 65 { 66 int i; 67 OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ctx); 68 69 if (global == NULL) 70 return; 71 72 for (i = 0; i < CRYPTO_EX_INDEX__COUNT; ++i) { 73 EX_CALLBACKS *ip = &global->ex_data[i]; 74 75 sk_EX_CALLBACK_pop_free(ip->meth, cleanup_cb); 76 ip->meth = NULL; 77 } 78 79 CRYPTO_THREAD_lock_free(global->ex_data_lock); 80 global->ex_data_lock = NULL; 81 } 82 83 84 /* 85 * Unregister a new index by replacing the callbacks with no-ops. 86 * Any in-use instances are leaked. 87 */ 88 static void dummy_new(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, 89 long argl, void *argp) 90 { 91 } 92 93 static void dummy_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, 94 long argl, void *argp) 95 { 96 } 97 98 static int dummy_dup(CRYPTO_EX_DATA *to, const CRYPTO_EX_DATA *from, 99 void **from_d, int idx, 100 long argl, void *argp) 101 { 102 return 1; 103 } 104 105 int ossl_crypto_free_ex_index_ex(OSSL_LIB_CTX *ctx, int class_index, int idx) 106 { 107 EX_CALLBACKS *ip; 108 EX_CALLBACK *a; 109 int toret = 0; 110 OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ctx); 111 112 if (global == NULL) 113 return 0; 114 115 ip = get_and_lock(global, class_index); 116 if (ip == NULL) 117 return 0; 118 119 if (idx < 0 || idx >= sk_EX_CALLBACK_num(ip->meth)) 120 goto err; 121 a = sk_EX_CALLBACK_value(ip->meth, idx); 122 if (a == NULL) 123 goto err; 124 a->new_func = dummy_new; 125 a->dup_func = dummy_dup; 126 a->free_func = dummy_free; 127 toret = 1; 128 err: 129 CRYPTO_THREAD_unlock(global->ex_data_lock); 130 return toret; 131 } 132 133 int CRYPTO_free_ex_index(int class_index, int idx) 134 { 135 return ossl_crypto_free_ex_index_ex(NULL, class_index, idx); 136 } 137 138 /* 139 * Register a new index. 140 */ 141 int ossl_crypto_get_ex_new_index_ex(OSSL_LIB_CTX *ctx, int class_index, 142 long argl, void *argp, 143 CRYPTO_EX_new *new_func, 144 CRYPTO_EX_dup *dup_func, 145 CRYPTO_EX_free *free_func, 146 int priority) 147 { 148 int toret = -1; 149 EX_CALLBACK *a; 150 EX_CALLBACKS *ip; 151 OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ctx); 152 153 if (global == NULL) 154 return -1; 155 156 ip = get_and_lock(global, class_index); 157 if (ip == NULL) 158 return -1; 159 160 if (ip->meth == NULL) { 161 ip->meth = sk_EX_CALLBACK_new_null(); 162 /* We push an initial value on the stack because the SSL 163 * "app_data" routines use ex_data index zero. See RT 3710. */ 164 if (ip->meth == NULL 165 || !sk_EX_CALLBACK_push(ip->meth, NULL)) { 166 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE); 167 goto err; 168 } 169 } 170 171 a = (EX_CALLBACK *)OPENSSL_malloc(sizeof(*a)); 172 if (a == NULL) { 173 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE); 174 goto err; 175 } 176 a->argl = argl; 177 a->argp = argp; 178 a->new_func = new_func; 179 a->dup_func = dup_func; 180 a->free_func = free_func; 181 a->priority = priority; 182 183 if (!sk_EX_CALLBACK_push(ip->meth, NULL)) { 184 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE); 185 OPENSSL_free(a); 186 goto err; 187 } 188 toret = sk_EX_CALLBACK_num(ip->meth) - 1; 189 (void)sk_EX_CALLBACK_set(ip->meth, toret, a); 190 191 err: 192 CRYPTO_THREAD_unlock(global->ex_data_lock); 193 return toret; 194 } 195 196 int CRYPTO_get_ex_new_index(int class_index, long argl, void *argp, 197 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, 198 CRYPTO_EX_free *free_func) 199 { 200 return ossl_crypto_get_ex_new_index_ex(NULL, class_index, argl, argp, 201 new_func, dup_func, free_func, 0); 202 } 203 204 /* 205 * Initialise a new CRYPTO_EX_DATA for use in a particular class - including 206 * calling new() callbacks for each index in the class used by this variable 207 * Thread-safe by copying a class's array of "EX_CALLBACK" entries 208 * in the lock, then using them outside the lock. Note this only applies 209 * to the global "ex_data" state (ie. class definitions), not 'ad' itself. 210 */ 211 int ossl_crypto_new_ex_data_ex(OSSL_LIB_CTX *ctx, int class_index, void *obj, 212 CRYPTO_EX_DATA *ad) 213 { 214 int mx, i; 215 void *ptr; 216 EX_CALLBACK **storage = NULL; 217 EX_CALLBACK *stack[10]; 218 EX_CALLBACKS *ip; 219 OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ctx); 220 221 if (global == NULL) 222 return 0; 223 224 ip = get_and_lock(global, class_index); 225 if (ip == NULL) 226 return 0; 227 228 ad->ctx = ctx; 229 ad->sk = NULL; 230 mx = sk_EX_CALLBACK_num(ip->meth); 231 if (mx > 0) { 232 if (mx < (int)OSSL_NELEM(stack)) 233 storage = stack; 234 else 235 storage = OPENSSL_malloc(sizeof(*storage) * mx); 236 if (storage != NULL) 237 for (i = 0; i < mx; i++) 238 storage[i] = sk_EX_CALLBACK_value(ip->meth, i); 239 } 240 CRYPTO_THREAD_unlock(global->ex_data_lock); 241 242 if (mx > 0 && storage == NULL) { 243 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE); 244 return 0; 245 } 246 for (i = 0; i < mx; i++) { 247 if (storage[i] != NULL && storage[i]->new_func != NULL) { 248 ptr = CRYPTO_get_ex_data(ad, i); 249 storage[i]->new_func(obj, ptr, ad, i, 250 storage[i]->argl, storage[i]->argp); 251 } 252 } 253 if (storage != stack) 254 OPENSSL_free(storage); 255 return 1; 256 } 257 258 int CRYPTO_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad) 259 { 260 return ossl_crypto_new_ex_data_ex(NULL, class_index, obj, ad); 261 } 262 263 /* 264 * Duplicate a CRYPTO_EX_DATA variable - including calling dup() callbacks 265 * for each index in the class used by this variable 266 */ 267 int CRYPTO_dup_ex_data(int class_index, CRYPTO_EX_DATA *to, 268 const CRYPTO_EX_DATA *from) 269 { 270 int mx, j, i; 271 void *ptr; 272 EX_CALLBACK *stack[10]; 273 EX_CALLBACK **storage = NULL; 274 EX_CALLBACKS *ip; 275 int toret = 0; 276 OSSL_EX_DATA_GLOBAL *global; 277 278 to->ctx = from->ctx; 279 if (from->sk == NULL) 280 /* Nothing to copy over */ 281 return 1; 282 283 global = ossl_lib_ctx_get_ex_data_global(from->ctx); 284 if (global == NULL) 285 return 0; 286 287 ip = get_and_lock(global, class_index); 288 if (ip == NULL) 289 return 0; 290 291 mx = sk_EX_CALLBACK_num(ip->meth); 292 j = sk_void_num(from->sk); 293 if (j < mx) 294 mx = j; 295 if (mx > 0) { 296 if (mx < (int)OSSL_NELEM(stack)) 297 storage = stack; 298 else 299 storage = OPENSSL_malloc(sizeof(*storage) * mx); 300 if (storage != NULL) 301 for (i = 0; i < mx; i++) 302 storage[i] = sk_EX_CALLBACK_value(ip->meth, i); 303 } 304 CRYPTO_THREAD_unlock(global->ex_data_lock); 305 306 if (mx == 0) 307 return 1; 308 if (storage == NULL) { 309 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE); 310 return 0; 311 } 312 /* 313 * Make sure the ex_data stack is at least |mx| elements long to avoid 314 * issues in the for loop that follows; so go get the |mx|'th element 315 * (if it does not exist CRYPTO_get_ex_data() returns NULL), and assign 316 * to itself. This is normally a no-op; but ensures the stack is the 317 * proper size 318 */ 319 if (!CRYPTO_set_ex_data(to, mx - 1, CRYPTO_get_ex_data(to, mx - 1))) 320 goto err; 321 322 for (i = 0; i < mx; i++) { 323 ptr = CRYPTO_get_ex_data(from, i); 324 if (storage[i] != NULL && storage[i]->dup_func != NULL) 325 if (!storage[i]->dup_func(to, from, &ptr, i, 326 storage[i]->argl, storage[i]->argp)) 327 goto err; 328 CRYPTO_set_ex_data(to, i, ptr); 329 } 330 toret = 1; 331 err: 332 if (storage != stack) 333 OPENSSL_free(storage); 334 return toret; 335 } 336 337 struct ex_callback_entry { 338 const EX_CALLBACK *excb; 339 int index; 340 }; 341 342 static int ex_callback_compare(const void *a, const void *b) 343 { 344 const struct ex_callback_entry *ap = (const struct ex_callback_entry *)a; 345 const struct ex_callback_entry *bp = (const struct ex_callback_entry *)b; 346 347 if (ap->excb == bp->excb) 348 return 0; 349 350 if (ap->excb == NULL) 351 return 1; 352 if (bp->excb == NULL) 353 return -1; 354 if (ap->excb->priority == bp->excb->priority) 355 return 0; 356 return ap->excb->priority > bp->excb->priority ? -1 : 1; 357 } 358 359 /* 360 * Cleanup a CRYPTO_EX_DATA variable - including calling free() callbacks for 361 * each index in the class used by this variable 362 */ 363 void CRYPTO_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad) 364 { 365 int mx, i; 366 EX_CALLBACKS *ip; 367 void *ptr; 368 const EX_CALLBACK *f; 369 struct ex_callback_entry stack[10]; 370 struct ex_callback_entry *storage = NULL; 371 OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ad->ctx); 372 373 if (global == NULL) 374 goto err; 375 376 ip = get_and_lock(global, class_index); 377 if (ip == NULL) 378 goto err; 379 380 mx = sk_EX_CALLBACK_num(ip->meth); 381 if (mx > 0) { 382 if (mx < (int)OSSL_NELEM(stack)) 383 storage = stack; 384 else 385 storage = OPENSSL_malloc(sizeof(*storage) * mx); 386 if (storage != NULL) 387 for (i = 0; i < mx; i++) { 388 storage[i].excb = sk_EX_CALLBACK_value(ip->meth, i); 389 storage[i].index = i; 390 } 391 } 392 CRYPTO_THREAD_unlock(global->ex_data_lock); 393 394 if (storage != NULL) { 395 /* Sort according to priority. High priority first */ 396 qsort(storage, mx, sizeof(*storage), ex_callback_compare); 397 for (i = 0; i < mx; i++) { 398 f = storage[i].excb; 399 400 if (f != NULL && f->free_func != NULL) { 401 ptr = CRYPTO_get_ex_data(ad, storage[i].index); 402 f->free_func(obj, ptr, ad, storage[i].index, f->argl, f->argp); 403 } 404 } 405 } 406 407 if (storage != stack) 408 OPENSSL_free(storage); 409 err: 410 sk_void_free(ad->sk); 411 ad->sk = NULL; 412 ad->ctx = NULL; 413 } 414 415 /* 416 * Allocate a given CRYPTO_EX_DATA item using the class specific allocation 417 * function 418 */ 419 int CRYPTO_alloc_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad, 420 int idx) 421 { 422 void *curval; 423 424 curval = CRYPTO_get_ex_data(ad, idx); 425 /* Already there, no need to allocate */ 426 if (curval != NULL) 427 return 1; 428 429 return ossl_crypto_alloc_ex_data_intern(class_index, obj, ad, idx); 430 } 431 432 int ossl_crypto_alloc_ex_data_intern(int class_index, void *obj, 433 CRYPTO_EX_DATA *ad, int idx) 434 { 435 EX_CALLBACK *f; 436 EX_CALLBACKS *ip; 437 OSSL_EX_DATA_GLOBAL *global; 438 439 global = ossl_lib_ctx_get_ex_data_global(ad->ctx); 440 if (global == NULL) 441 return 0; 442 443 ip = get_and_lock(global, class_index); 444 if (ip == NULL) 445 return 0; 446 f = sk_EX_CALLBACK_value(ip->meth, idx); 447 CRYPTO_THREAD_unlock(global->ex_data_lock); 448 449 /* 450 * This should end up calling CRYPTO_set_ex_data(), which allocates 451 * everything necessary to support placing the new data in the right spot. 452 */ 453 if (f->new_func == NULL) 454 return 0; 455 456 f->new_func(obj, NULL, ad, idx, f->argl, f->argp); 457 458 return 1; 459 } 460 461 /* 462 * For a given CRYPTO_EX_DATA variable, set the value corresponding to a 463 * particular index in the class used by this variable 464 */ 465 int CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int idx, void *val) 466 { 467 int i; 468 469 if (ad->sk == NULL) { 470 if ((ad->sk = sk_void_new_null()) == NULL) { 471 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE); 472 return 0; 473 } 474 } 475 476 for (i = sk_void_num(ad->sk); i <= idx; ++i) { 477 if (!sk_void_push(ad->sk, NULL)) { 478 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE); 479 return 0; 480 } 481 } 482 if (sk_void_set(ad->sk, idx, val) != val) { 483 /* Probably the index is out of bounds */ 484 ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); 485 return 0; 486 } 487 return 1; 488 } 489 490 /* 491 * For a given CRYPTO_EX_DATA_ variable, get the value corresponding to a 492 * particular index in the class used by this variable 493 */ 494 void *CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int idx) 495 { 496 if (ad->sk == NULL || idx >= sk_void_num(ad->sk)) 497 return NULL; 498 return sk_void_value(ad->sk, idx); 499 } 500 501 OSSL_LIB_CTX *ossl_crypto_ex_data_get_ossl_lib_ctx(const CRYPTO_EX_DATA *ad) 502 { 503 return ad->ctx; 504 } 505