summaryrefslogtreecommitdiff
path: root/src/lib/libcrypto/crypto_ex_data.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libcrypto/crypto_ex_data.c')
-rw-r--r--src/lib/libcrypto/crypto_ex_data.c417
1 files changed, 417 insertions, 0 deletions
diff --git a/src/lib/libcrypto/crypto_ex_data.c b/src/lib/libcrypto/crypto_ex_data.c
new file mode 100644
index 0000000000..947e02669b
--- /dev/null
+++ b/src/lib/libcrypto/crypto_ex_data.c
@@ -0,0 +1,417 @@
1/* $OpenBSD: crypto_ex_data.c,v 1.1 2024/08/02 10:48:54 jsing 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 16
23
24struct crypto_ex_data {
25 int class_index;
26 void **slots;
27 size_t slots_len;
28};
29
30struct 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
38struct crypto_ex_data_class {
39 struct crypto_ex_data_index **indexes;
40 size_t indexes_len;
41 size_t next_index;
42};
43
44static struct crypto_ex_data_class **classes;
45
46static int
47crypto_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
70static struct crypto_ex_data_class *
71crypto_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
87int
88CRYPTO_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}
150LCRYPTO_ALIAS(CRYPTO_get_ex_new_index);
151
152void
153CRYPTO_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 }
169
170 free(class);
171 }
172
173 free(classes);
174 classes = NULL;
175}
176LCRYPTO_ALIAS(CRYPTO_cleanup_all_ex_data);
177
178static void
179crypto_ex_data_clear(CRYPTO_EX_DATA *exdata)
180{
181 struct crypto_ex_data *ced;
182
183 if (exdata == NULL)
184 return;
185
186 if ((ced = exdata->sk) != NULL) {
187 freezero(ced->slots, ced->slots_len * sizeof(void *));
188 freezero(ced, sizeof(*ced));
189 }
190
191 exdata->sk = NULL;
192}
193
194static int
195crypto_ex_data_init(CRYPTO_EX_DATA *exdata)
196{
197 struct crypto_ex_data *ced = NULL;
198
199 if (exdata->sk != NULL)
200 goto err;
201
202 if ((ced = calloc(1, sizeof(struct crypto_ex_data))) == NULL)
203 goto err;
204
205 ced->class_index = -1;
206
207 if ((ced->slots = calloc(CRYPTO_EX_DATA_MAX_INDEX, sizeof(void *))) == NULL)
208 goto err;
209 ced->slots_len = CRYPTO_EX_DATA_MAX_INDEX;
210
211 exdata->sk = ced;
212
213 return 1;
214
215 err:
216 if (ced != NULL) {
217 free(ced->slots);
218 free(ced);
219 }
220 crypto_ex_data_clear(exdata);
221
222 return 0;
223}
224
225int
226CRYPTO_new_ex_data(int class_index, void *parent, CRYPTO_EX_DATA *exdata)
227{
228 struct crypto_ex_data_class *class;
229 struct crypto_ex_data_index *index;
230 struct crypto_ex_data *ced;
231 size_t i, last_index;
232
233 if (!crypto_ex_data_init(exdata))
234 goto err;
235 if ((ced = exdata->sk) == NULL)
236 goto err;
237
238 if (!crypto_ex_data_classes_init())
239 goto err;
240 if ((class = crypto_ex_data_class_lookup(class_index)) == NULL)
241 goto done;
242
243 ced->class_index = class_index;
244
245 /* Existing indexes are immutable, we just have to know when to stop. */
246 CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
247 last_index = class->next_index;
248 CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
249
250 for (i = 0; i < last_index; i++) {
251 if ((index = class->indexes[i]) == NULL)
252 continue;
253 if (index->new_func == NULL)
254 continue;
255 if (!index->new_func(parent, NULL, exdata, i, index->argl,
256 index->argp))
257 goto err;
258 }
259
260 done:
261 return 1;
262
263 err:
264 CRYPTO_free_ex_data(class_index, parent, exdata);
265
266 return 0;
267}
268LCRYPTO_ALIAS(CRYPTO_new_ex_data);
269
270int
271CRYPTO_dup_ex_data(int class_index, CRYPTO_EX_DATA *dst, CRYPTO_EX_DATA *src)
272{
273 struct crypto_ex_data *dst_ced, *src_ced;
274 struct crypto_ex_data_class *class;
275 struct crypto_ex_data_index *index;
276 size_t i, last_index;
277 void *val;
278
279 if (dst == NULL || src == NULL)
280 goto err;
281
282 /*
283 * Some code calls CRYPTO_new_ex_data() before dup, others never call
284 * CRYPTO_new_ex_data()... so we get to handle both.
285 */
286 /* XXX - parent == NULL? */
287 CRYPTO_free_ex_data(class_index, NULL, dst);
288
289 if (!crypto_ex_data_init(dst))
290 goto err;
291
292 if ((dst_ced = dst->sk) == NULL)
293 goto err;
294 if ((src_ced = src->sk) == NULL)
295 goto err;
296
297 if ((class = crypto_ex_data_class_lookup(class_index)) == NULL) {
298 for (i = 0; i < CRYPTO_EX_DATA_MAX_INDEX; i++)
299 dst_ced->slots[i] = src_ced->slots[i];
300 goto done;
301 }
302
303 OPENSSL_assert(src_ced->class_index == class_index);
304
305 dst_ced->class_index = class_index;
306
307 /* Existing indexes are immutable, we just have to know when to stop. */
308 CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
309 last_index = class->next_index;
310 CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
311
312 for (i = 0; i < last_index; i++) {
313 if ((index = class->indexes[i]) == NULL)
314 continue;
315
316 /* If there is no dup function, we copy the pointer. */
317 val = src_ced->slots[i];
318 if (index->dup_func != NULL) {
319 if (!index->dup_func(dst, src, &val, i, index->argl,
320 index->argp))
321 goto err;
322 }
323 /* If the dup function set data, we will potentially leak. */
324 if (dst_ced->slots[i] != NULL)
325 goto err;
326 dst_ced->slots[i] = val;
327 }
328
329 done:
330 return 1;
331
332 err:
333 /* XXX - parent == NULL? */
334 CRYPTO_free_ex_data(class_index, NULL, dst);
335
336 return 0;
337}
338LCRYPTO_ALIAS(CRYPTO_dup_ex_data);
339
340void
341CRYPTO_free_ex_data(int class_index, void *parent, CRYPTO_EX_DATA *exdata)
342{
343 struct crypto_ex_data_class *class;
344 struct crypto_ex_data_index *index;
345 struct crypto_ex_data *ced;
346 size_t i, last_index;
347
348 if (exdata == NULL)
349 return;
350 if ((ced = exdata->sk) == NULL)
351 goto done;
352 if (ced->class_index == -1)
353 goto done;
354
355 if ((class = crypto_ex_data_class_lookup(class_index)) == NULL)
356 goto done;
357
358 OPENSSL_assert(ced->class_index == class_index);
359
360 /* Existing indexes are immutable, we just have to know when to stop. */
361 CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
362 last_index = class->next_index;
363 CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
364
365 for (i = 0; i < last_index; i++) {
366 if ((index = class->indexes[i]) == NULL)
367 continue;
368 if (index->free_func == NULL)
369 continue;
370 index->free_func(parent, ced->slots[i], exdata, i, index->argl,
371 index->argp);
372 }
373
374 done:
375 crypto_ex_data_clear(exdata);
376}
377LCRYPTO_ALIAS(CRYPTO_free_ex_data);
378
379int
380CRYPTO_set_ex_data(CRYPTO_EX_DATA *exdata, int idx, void *val)
381{
382 struct crypto_ex_data *ced;
383
384 /*
385 * Preserve horrible historical behaviour - allow set to work even if
386 * new has not been called first.
387 */
388 if ((ced = exdata->sk) == NULL) {
389 if (!crypto_ex_data_init(exdata))
390 return 0;
391 ced = exdata->sk;
392 }
393
394 /* XXX - consider preventing set for an unallocated index. */
395
396 if (idx < 0 || idx >= ced->slots_len)
397 return 0;
398
399 ced->slots[idx] = val;
400
401 return 1;
402}
403LCRYPTO_ALIAS(CRYPTO_set_ex_data);
404
405void *
406CRYPTO_get_ex_data(const CRYPTO_EX_DATA *exdata, int idx)
407{
408 struct crypto_ex_data *ced;
409
410 if ((ced = exdata->sk) == NULL)
411 return NULL;
412 if (idx < 0 || idx >= ced->slots_len)
413 return NULL;
414
415 return ced->slots[idx];
416}
417LCRYPTO_ALIAS(CRYPTO_get_ex_data);