1 /* $OpenBSD: crypto_ex_data.c,v 1.4 2024/08/03 07:45:26 tb Exp $ */ 2 /* 3 * Copyright (c) 2023 Joel Sing <jsing@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 <stdlib.h> 19 20 #include <openssl/crypto.h> 21 22 #define CRYPTO_EX_DATA_MAX_INDEX 32 23 24 struct crypto_ex_data { 25 int class_index; 26 void **slots; 27 size_t slots_len; 28 }; 29 30 struct crypto_ex_data_index { 31 CRYPTO_EX_new *new_func; 32 CRYPTO_EX_dup *dup_func; 33 CRYPTO_EX_free *free_func; 34 long argl; 35 void *argp; 36 }; 37 38 struct crypto_ex_data_class { 39 struct crypto_ex_data_index **indexes; 40 size_t indexes_len; 41 size_t next_index; 42 }; 43 44 static struct crypto_ex_data_class **classes; 45 46 static int 47 crypto_ex_data_classes_init(void) 48 { 49 struct crypto_ex_data_class **classes_new = NULL; 50 51 if (classes != NULL) 52 return 1; 53 54 if ((classes_new = calloc(CRYPTO_EX_INDEX__COUNT, 55 sizeof(struct crypto_ex_data_index))) == NULL) 56 return 0; 57 58 CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA); 59 if (classes == NULL) { 60 classes = classes_new; 61 classes_new = NULL; 62 } 63 CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA); 64 65 free(classes_new); 66 67 return 1; 68 } 69 70 static struct crypto_ex_data_class * 71 crypto_ex_data_class_lookup(int class_index) 72 { 73 struct crypto_ex_data_class *class; 74 75 if (classes == NULL) 76 return NULL; 77 if (class_index < 0 || class_index >= CRYPTO_EX_INDEX__COUNT) 78 return NULL; 79 80 CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA); 81 class = classes[class_index]; 82 CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA); 83 84 return class; 85 } 86 87 int 88 CRYPTO_get_ex_new_index(int class_index, long argl, void *argp, 89 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func) 90 { 91 struct crypto_ex_data_class *new_class = NULL; 92 struct crypto_ex_data_index *index = NULL; 93 struct crypto_ex_data_class *class; 94 int idx = -1; 95 96 if (!crypto_ex_data_classes_init()) 97 goto err; 98 99 if (class_index < 0 || class_index >= CRYPTO_EX_INDEX__COUNT) 100 goto err; 101 102 if ((class = classes[class_index]) == NULL) { 103 if ((new_class = calloc(1, 104 sizeof(struct crypto_ex_data_class))) == NULL) 105 goto err; 106 if ((new_class->indexes = calloc(CRYPTO_EX_DATA_MAX_INDEX, 107 sizeof(struct crypto_ex_data_index *))) == NULL) 108 goto err; 109 new_class->indexes_len = CRYPTO_EX_DATA_MAX_INDEX; 110 new_class->next_index = 1; 111 112 CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA); 113 if (classes[class_index] == NULL) { 114 classes[class_index] = new_class; 115 new_class = NULL; 116 } 117 CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA); 118 119 class = classes[class_index]; 120 } 121 122 if ((index = calloc(1, sizeof(struct crypto_ex_data_index))) == NULL) 123 goto err; 124 125 index->new_func = new_func; 126 index->dup_func = dup_func; 127 index->free_func = free_func; 128 129 index->argl = argl; 130 index->argp = argp; 131 132 CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA); 133 if (class->next_index < class->indexes_len) { 134 idx = class->next_index++; 135 class->indexes[idx] = index; 136 index = NULL; 137 } 138 CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA); 139 140 141 err: 142 if (new_class != NULL) { 143 free(new_class->indexes); 144 free(new_class); 145 } 146 free(index); 147 148 return idx; 149 } 150 LCRYPTO_ALIAS(CRYPTO_get_ex_new_index); 151 152 void 153 CRYPTO_cleanup_all_ex_data(void) 154 { 155 struct crypto_ex_data_class *class; 156 int i, j; 157 158 if (classes == NULL) 159 return; 160 161 for (i = 0; i < CRYPTO_EX_INDEX__COUNT; i++) { 162 if ((class = classes[i]) == NULL) 163 continue; 164 165 if (class->indexes != NULL) { 166 for (j = 0; j < CRYPTO_EX_DATA_MAX_INDEX; j++) 167 free(class->indexes[j]); 168 free(class->indexes); 169 } 170 171 free(class); 172 } 173 174 free(classes); 175 classes = NULL; 176 } 177 LCRYPTO_ALIAS(CRYPTO_cleanup_all_ex_data); 178 179 static void 180 crypto_ex_data_clear(CRYPTO_EX_DATA *exdata) 181 { 182 struct crypto_ex_data *ced; 183 184 if (exdata == NULL) 185 return; 186 187 if ((ced = exdata->sk) != NULL) { 188 freezero(ced->slots, ced->slots_len * sizeof(void *)); 189 freezero(ced, sizeof(*ced)); 190 } 191 192 exdata->sk = NULL; 193 } 194 195 static int 196 crypto_ex_data_init(CRYPTO_EX_DATA *exdata) 197 { 198 struct crypto_ex_data *ced = NULL; 199 200 if (exdata->sk != NULL) 201 goto err; 202 203 if ((ced = calloc(1, sizeof(struct crypto_ex_data))) == NULL) 204 goto err; 205 206 ced->class_index = -1; 207 208 if ((ced->slots = calloc(CRYPTO_EX_DATA_MAX_INDEX, sizeof(void *))) == NULL) 209 goto err; 210 ced->slots_len = CRYPTO_EX_DATA_MAX_INDEX; 211 212 exdata->sk = ced; 213 214 return 1; 215 216 err: 217 if (ced != NULL) { 218 free(ced->slots); 219 free(ced); 220 } 221 crypto_ex_data_clear(exdata); 222 223 return 0; 224 } 225 226 int 227 CRYPTO_new_ex_data(int class_index, void *parent, CRYPTO_EX_DATA *exdata) 228 { 229 struct crypto_ex_data_class *class; 230 struct crypto_ex_data_index *index; 231 struct crypto_ex_data *ced; 232 size_t i, last_index; 233 234 if (!crypto_ex_data_init(exdata)) 235 goto err; 236 if ((ced = exdata->sk) == NULL) 237 goto err; 238 239 if (!crypto_ex_data_classes_init()) 240 goto err; 241 if ((class = crypto_ex_data_class_lookup(class_index)) == NULL) 242 goto done; 243 244 ced->class_index = class_index; 245 246 /* Existing indexes are immutable, we just have to know when to stop. */ 247 CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA); 248 last_index = class->next_index; 249 CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA); 250 251 for (i = 0; i < last_index; i++) { 252 if ((index = class->indexes[i]) == NULL) 253 continue; 254 if (index->new_func == NULL) 255 continue; 256 if (!index->new_func(parent, NULL, exdata, i, index->argl, 257 index->argp)) 258 goto err; 259 } 260 261 done: 262 return 1; 263 264 err: 265 CRYPTO_free_ex_data(class_index, parent, exdata); 266 267 return 0; 268 } 269 LCRYPTO_ALIAS(CRYPTO_new_ex_data); 270 271 int 272 CRYPTO_dup_ex_data(int class_index, CRYPTO_EX_DATA *dst, CRYPTO_EX_DATA *src) 273 { 274 struct crypto_ex_data *dst_ced, *src_ced; 275 struct crypto_ex_data_class *class; 276 struct crypto_ex_data_index *index; 277 size_t i, last_index; 278 void *val; 279 280 if (dst == NULL || src == NULL) 281 goto err; 282 283 /* 284 * Some code calls CRYPTO_new_ex_data() before dup, others never call 285 * CRYPTO_new_ex_data()... so we get to handle both. 286 */ 287 /* XXX - parent == NULL? */ 288 CRYPTO_free_ex_data(class_index, NULL, dst); 289 290 if (!crypto_ex_data_init(dst)) 291 goto err; 292 293 if ((dst_ced = dst->sk) == NULL) 294 goto err; 295 if ((src_ced = src->sk) == NULL) 296 goto err; 297 298 if ((class = crypto_ex_data_class_lookup(class_index)) == NULL) { 299 for (i = 0; i < CRYPTO_EX_DATA_MAX_INDEX; i++) 300 dst_ced->slots[i] = src_ced->slots[i]; 301 goto done; 302 } 303 304 OPENSSL_assert(src_ced->class_index == class_index); 305 306 dst_ced->class_index = class_index; 307 308 /* Existing indexes are immutable, we just have to know when to stop. */ 309 CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA); 310 last_index = class->next_index; 311 CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA); 312 313 for (i = 0; i < last_index; i++) { 314 if ((index = class->indexes[i]) == NULL) 315 continue; 316 317 /* If there is no dup function, we copy the pointer. */ 318 val = src_ced->slots[i]; 319 if (index->dup_func != NULL) { 320 if (!index->dup_func(dst, src, &val, i, index->argl, 321 index->argp)) 322 goto err; 323 } 324 /* If the dup function set data, we will potentially leak. */ 325 if (dst_ced->slots[i] != NULL) 326 goto err; 327 dst_ced->slots[i] = val; 328 } 329 330 done: 331 return 1; 332 333 err: 334 /* XXX - parent == NULL? */ 335 CRYPTO_free_ex_data(class_index, NULL, dst); 336 337 return 0; 338 } 339 LCRYPTO_ALIAS(CRYPTO_dup_ex_data); 340 341 void 342 CRYPTO_free_ex_data(int class_index, void *parent, CRYPTO_EX_DATA *exdata) 343 { 344 struct crypto_ex_data_class *class; 345 struct crypto_ex_data_index *index; 346 struct crypto_ex_data *ced; 347 size_t i, last_index; 348 349 if (exdata == NULL) 350 return; 351 if ((ced = exdata->sk) == NULL) 352 goto done; 353 if (ced->class_index == -1) 354 goto done; 355 356 if ((class = crypto_ex_data_class_lookup(class_index)) == NULL) 357 goto done; 358 359 OPENSSL_assert(ced->class_index == class_index); 360 361 /* Existing indexes are immutable, we just have to know when to stop. */ 362 CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA); 363 last_index = class->next_index; 364 CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA); 365 366 for (i = 0; i < last_index; i++) { 367 if ((index = class->indexes[i]) == NULL) 368 continue; 369 if (index->free_func == NULL) 370 continue; 371 index->free_func(parent, ced->slots[i], exdata, i, index->argl, 372 index->argp); 373 } 374 375 done: 376 crypto_ex_data_clear(exdata); 377 } 378 LCRYPTO_ALIAS(CRYPTO_free_ex_data); 379 380 int 381 CRYPTO_set_ex_data(CRYPTO_EX_DATA *exdata, int idx, void *val) 382 { 383 struct crypto_ex_data *ced; 384 385 /* 386 * Preserve horrible historical behaviour - allow set to work even if 387 * new has not been called first. 388 */ 389 if ((ced = exdata->sk) == NULL) { 390 if (!crypto_ex_data_init(exdata)) 391 return 0; 392 ced = exdata->sk; 393 } 394 395 /* XXX - consider preventing set for an unallocated index. */ 396 397 if (idx < 0 || idx >= ced->slots_len) 398 return 0; 399 400 ced->slots[idx] = val; 401 402 return 1; 403 } 404 LCRYPTO_ALIAS(CRYPTO_set_ex_data); 405 406 void * 407 CRYPTO_get_ex_data(const CRYPTO_EX_DATA *exdata, int idx) 408 { 409 struct crypto_ex_data *ced; 410 411 if ((ced = exdata->sk) == NULL) 412 return NULL; 413 if (idx < 0 || idx >= ced->slots_len) 414 return NULL; 415 416 return ced->slots[idx]; 417 } 418 LCRYPTO_ALIAS(CRYPTO_get_ex_data); 419