summaryrefslogtreecommitdiff
path: root/src/lib/libcrypto/crypto_ex_data.c
diff options
context:
space:
mode:
authorjsing <>2024-08-02 10:48:54 +0000
committerjsing <>2024-08-02 10:48:54 +0000
commitffa17714e966833766f8f0403ac8e178f26fbbed (patch)
tree8d588376ef1f79f315be375339e9c5ea6e59bd83 /src/lib/libcrypto/crypto_ex_data.c
parentea51d67aadf6bac6328d485fa936685da3bbba4f (diff)
downloadopenbsd-ffa17714e966833766f8f0403ac8e178f26fbbed.tar.gz
openbsd-ffa17714e966833766f8f0403ac8e178f26fbbed.tar.bz2
openbsd-ffa17714e966833766f8f0403ac8e178f26fbbed.zip
Rewrite CRYPTO_EX_DATA.
CRYPTO_EX_DATA exists as a way to allow an application to attach data to various components in libcrypto and libssl. The general idea is that there are various "classes" (e.g. RSA) and an application can get an "index" (which can have new/dup/free functions provided). The application can then use the index to store a pointer to some form of data within that class, for later retrieval. However, even by OpenSSL standards, this is an insane API. The current implementation allows for data to be set without calling new, indexes can be used without allocation, new can be called without actually getting an index and dup can be called either after new or without new (see regress and RSA_get_ex_new_index(3)/CRYPTO_set_ex_data(3) for more details). On top of this, the previous "overhaul" of the code was written to be infinitely extensible. For now, the rewrite intends to maintain the existing behaviour - once we bed this down we can attempt to ratchet the API requirements and require some sort of sensible sequence. The only intentional change is that there is now a hard limit on the number of indexes that can be allocated (previously there was none, relying only on ENOMEM). ok tb@
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);