summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjsing <>2024-08-02 10:48:54 +0000
committerjsing <>2024-08-02 10:48:54 +0000
commitffa17714e966833766f8f0403ac8e178f26fbbed (patch)
tree8d588376ef1f79f315be375339e9c5ea6e59bd83
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@
-rw-r--r--src/lib/libcrypto/Makefile4
-rw-r--r--src/lib/libcrypto/crypto_ex_data.c417
-rw-r--r--src/lib/libcrypto/ex_data.c637
3 files changed, 419 insertions, 639 deletions
diff --git a/src/lib/libcrypto/Makefile b/src/lib/libcrypto/Makefile
index ef26b0c5f5..26e76bba2d 100644
--- a/src/lib/libcrypto/Makefile
+++ b/src/lib/libcrypto/Makefile
@@ -1,4 +1,4 @@
1# $OpenBSD: Makefile,v 1.200 2024/07/10 13:30:14 beck Exp $ 1# $OpenBSD: Makefile,v 1.201 2024/08/02 10:48:54 jsing Exp $
2 2
3LIB= crypto 3LIB= crypto
4LIBREBUILD=y 4LIBREBUILD=y
@@ -61,10 +61,10 @@ SYMBOL_LIST+= ${.CURDIR}/arch/${MACHINE_CPU}/Symbols.list
61# crypto/ 61# crypto/
62SRCS+= cpt_err.c 62SRCS+= cpt_err.c
63SRCS+= cryptlib.c 63SRCS+= cryptlib.c
64SRCS+= crypto_ex_data.c
64SRCS+= crypto_init.c 65SRCS+= crypto_init.c
65SRCS+= crypto_lock.c 66SRCS+= crypto_lock.c
66SRCS+= cversion.c 67SRCS+= cversion.c
67SRCS+= ex_data.c
68SRCS+= malloc-wrapper.c 68SRCS+= malloc-wrapper.c
69SRCS+= mem_clr.c 69SRCS+= mem_clr.c
70SRCS+= mem_dbg.c 70SRCS+= mem_dbg.c
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);
diff --git a/src/lib/libcrypto/ex_data.c b/src/lib/libcrypto/ex_data.c
deleted file mode 100644
index 17db16e58d..0000000000
--- a/src/lib/libcrypto/ex_data.c
+++ /dev/null
@@ -1,637 +0,0 @@
1/* $OpenBSD: ex_data.c,v 1.23 2023/07/28 10:19:20 tb Exp $ */
2
3/*
4 * Overhaul notes;
5 *
6 * This code is now *mostly* thread-safe. It is now easier to understand in what
7 * ways it is safe and in what ways it is not, which is an improvement. Firstly,
8 * all per-class stacks and index-counters for ex_data are stored in the same
9 * global LHASH table (keyed by class). This hash table uses locking for all
10 * access with the exception of CRYPTO_cleanup_all_ex_data(), which must only be
11 * called when no other threads can possibly race against it (even if it was
12 * locked, the race would mean it's possible the hash table might have been
13 * recreated after the cleanup). As classes can only be added to the hash table,
14 * and within each class, the stack of methods can only be incremented, the
15 * locking mechanics are simpler than they would otherwise be. For example, the
16 * new/dup/free ex_data functions will lock the hash table, copy the method
17 * pointers it needs from the relevant class, then unlock the hash table before
18 * actually applying those method pointers to the task of the new/dup/free
19 * operations. As they can't be removed from the method-stack, only
20 * supplemented, there's no race conditions associated with using them outside
21 * the lock. The get/set_ex_data functions are not locked because they do not
22 * involve this global state at all - they operate directly with a previously
23 * obtained per-class method index and a particular "ex_data" variable. These
24 * variables are usually instantiated per-context (eg. each RSA structure has
25 * one) so locking on read/write access to that variable can be locked locally
26 * if required (eg. using the "RSA" lock to synchronise access to a
27 * per-RSA-structure ex_data variable if required).
28 * [Geoff]
29 */
30
31/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
32 * All rights reserved.
33 *
34 * This package is an SSL implementation written
35 * by Eric Young (eay@cryptsoft.com).
36 * The implementation was written so as to conform with Netscapes SSL.
37 *
38 * This library is free for commercial and non-commercial use as long as
39 * the following conditions are aheared to. The following conditions
40 * apply to all code found in this distribution, be it the RC4, RSA,
41 * lhash, DES, etc., code; not just the SSL code. The SSL documentation
42 * included with this distribution is covered by the same copyright terms
43 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
44 *
45 * Copyright remains Eric Young's, and as such any Copyright notices in
46 * the code are not to be removed.
47 * If this package is used in a product, Eric Young should be given attribution
48 * as the author of the parts of the library used.
49 * This can be in the form of a textual message at program startup or
50 * in documentation (online or textual) provided with the package.
51 *
52 * Redistribution and use in source and binary forms, with or without
53 * modification, are permitted provided that the following conditions
54 * are met:
55 * 1. Redistributions of source code must retain the copyright
56 * notice, this list of conditions and the following disclaimer.
57 * 2. Redistributions in binary form must reproduce the above copyright
58 * notice, this list of conditions and the following disclaimer in the
59 * documentation and/or other materials provided with the distribution.
60 * 3. All advertising materials mentioning features or use of this software
61 * must display the following acknowledgement:
62 * "This product includes cryptographic software written by
63 * Eric Young (eay@cryptsoft.com)"
64 * The word 'cryptographic' can be left out if the rouines from the library
65 * being used are not cryptographic related :-).
66 * 4. If you include any Windows specific code (or a derivative thereof) from
67 * the apps directory (application code) you must include an acknowledgement:
68 * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
69 *
70 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
71 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
72 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
73 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
74 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
75 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
76 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
77 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
78 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
79 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
80 * SUCH DAMAGE.
81 *
82 * The licence and distribution terms for any publically available version or
83 * derivative of this code cannot be changed. i.e. this code cannot simply be
84 * copied and put under another distribution licence
85 * [including the GNU Public Licence.]
86 */
87/* ====================================================================
88 * Copyright (c) 1998-2001 The OpenSSL Project. All rights reserved.
89 *
90 * Redistribution and use in source and binary forms, with or without
91 * modification, are permitted provided that the following conditions
92 * are met:
93 *
94 * 1. Redistributions of source code must retain the above copyright
95 * notice, this list of conditions and the following disclaimer.
96 *
97 * 2. Redistributions in binary form must reproduce the above copyright
98 * notice, this list of conditions and the following disclaimer in
99 * the documentation and/or other materials provided with the
100 * distribution.
101 *
102 * 3. All advertising materials mentioning features or use of this
103 * software must display the following acknowledgment:
104 * "This product includes software developed by the OpenSSL Project
105 * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
106 *
107 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
108 * endorse or promote products derived from this software without
109 * prior written permission. For written permission, please contact
110 * openssl-core@openssl.org.
111 *
112 * 5. Products derived from this software may not be called "OpenSSL"
113 * nor may "OpenSSL" appear in their names without prior written
114 * permission of the OpenSSL Project.
115 *
116 * 6. Redistributions of any form whatsoever must retain the following
117 * acknowledgment:
118 * "This product includes software developed by the OpenSSL Project
119 * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
120 *
121 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
122 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
123 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
124 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
125 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
126 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
127 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
128 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
129 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
130 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
131 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
132 * OF THE POSSIBILITY OF SUCH DAMAGE.
133 * ====================================================================
134 *
135 * This product includes cryptographic software written by Eric Young
136 * (eay@cryptsoft.com). This product includes software written by Tim
137 * Hudson (tjh@cryptsoft.com).
138 *
139 */
140
141#include <openssl/err.h>
142#include <openssl/lhash.h>
143
144typedef struct crypto_ex_data_func_st {
145 long argl; /* Arbitrary long */
146 void *argp; /* Arbitrary void * */
147 CRYPTO_EX_new *new_func;
148 CRYPTO_EX_free *free_func;
149 CRYPTO_EX_dup *dup_func;
150} CRYPTO_EX_DATA_FUNCS;
151
152DECLARE_STACK_OF(CRYPTO_EX_DATA_FUNCS)
153
154#define sk_CRYPTO_EX_DATA_FUNCS_new_null() SKM_sk_new_null(CRYPTO_EX_DATA_FUNCS)
155#define sk_CRYPTO_EX_DATA_FUNCS_num(st) SKM_sk_num(CRYPTO_EX_DATA_FUNCS, (st))
156#define sk_CRYPTO_EX_DATA_FUNCS_value(st, i) SKM_sk_value(CRYPTO_EX_DATA_FUNCS, (st), (i))
157#define sk_CRYPTO_EX_DATA_FUNCS_set(st, i, val) SKM_sk_set(CRYPTO_EX_DATA_FUNCS, (st), (i), (val))
158#define sk_CRYPTO_EX_DATA_FUNCS_push(st, val) SKM_sk_push(CRYPTO_EX_DATA_FUNCS, (st), (val))
159#define sk_CRYPTO_EX_DATA_FUNCS_pop_free(st, free_func) SKM_sk_pop_free(CRYPTO_EX_DATA_FUNCS, (st), (free_func))
160
161/* An opaque type representing an implementation of "ex_data" support */
162typedef struct st_CRYPTO_EX_DATA_IMPL CRYPTO_EX_DATA_IMPL;
163
164/* What an "implementation of ex_data functionality" looks like */
165struct st_CRYPTO_EX_DATA_IMPL {
166 /*********************/
167 /* GLOBAL OPERATIONS */
168 /* Return a new class index */
169 int (*cb_new_class)(void);
170 /* Cleanup all state used by the implementation */
171 void (*cb_cleanup)(void);
172 /************************/
173 /* PER-CLASS OPERATIONS */
174 /* Get a new method index within a class */
175 int (*cb_get_new_index)(int class_index, long argl, void *argp,
176 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
177 CRYPTO_EX_free *free_func);
178 /* Initialise a new CRYPTO_EX_DATA of a given class */
179 int (*cb_new_ex_data)(int class_index, void *obj,
180 CRYPTO_EX_DATA *ad);
181 /* Duplicate a CRYPTO_EX_DATA of a given class onto a copy */
182 int (*cb_dup_ex_data)(int class_index, CRYPTO_EX_DATA *to,
183 CRYPTO_EX_DATA *from);
184 /* Cleanup a CRYPTO_EX_DATA of a given class */
185 void (*cb_free_ex_data)(int class_index, void *obj,
186 CRYPTO_EX_DATA *ad);
187};
188
189/* The implementation we use at run-time */
190static const CRYPTO_EX_DATA_IMPL *impl = NULL;
191
192/* To call "impl" functions, use this macro rather than referring to 'impl' directly, eg.
193 * EX_IMPL(get_new_index)(...);
194*/
195#define EX_IMPL(a) impl->cb_##a
196
197/* Predeclare the "default" ex_data implementation */
198static int int_new_class(void);
199static void int_cleanup(void);
200static int int_get_new_index(int class_index, long argl, void *argp,
201 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
202 CRYPTO_EX_free *free_func);
203static int int_new_ex_data(int class_index, void *obj,
204 CRYPTO_EX_DATA *ad);
205static int int_dup_ex_data(int class_index, CRYPTO_EX_DATA *to,
206 CRYPTO_EX_DATA *from);
207static void int_free_ex_data(int class_index, void *obj,
208 CRYPTO_EX_DATA *ad);
209
210static CRYPTO_EX_DATA_IMPL impl_default = {
211 int_new_class,
212 int_cleanup,
213 int_get_new_index,
214 int_new_ex_data,
215 int_dup_ex_data,
216 int_free_ex_data
217};
218
219/* Internal function that checks whether "impl" is set and if not, sets it to
220 * the default. */
221static void
222impl_check(void)
223{
224 CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
225 if (!impl)
226 impl = &impl_default;
227 CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
228}
229/* A macro wrapper for impl_check that first uses a non-locked test before
230 * invoking the function (which checks again inside a lock). */
231#define IMPL_CHECK if(!impl) impl_check();
232
233/****************************************************************************/
234/* Interal (default) implementation of "ex_data" support. API functions are
235 * further down. */
236
237/* The type that represents what each "class" used to implement locally. A STACK
238 * of CRYPTO_EX_DATA_FUNCS plus a index-counter. The 'class_index' is the global
239 * value representing the class that is used to distinguish these items. */
240typedef struct st_ex_class_item {
241 int class_index;
242 STACK_OF(CRYPTO_EX_DATA_FUNCS) *meth;
243 int meth_num;
244} EX_CLASS_ITEM;
245
246/* When assigning new class indexes, this is our counter */
247#define CRYPTO_EX_INDEX_USER 100
248static int ex_class = CRYPTO_EX_INDEX_USER;
249
250/* The global hash table of EX_CLASS_ITEM items */
251DECLARE_LHASH_OF(EX_CLASS_ITEM);
252static LHASH_OF(EX_CLASS_ITEM) *ex_data = NULL;
253
254/* The callbacks required in the "ex_data" hash table */
255static unsigned long
256ex_class_item_hash(const EX_CLASS_ITEM *a)
257{
258 return a->class_index;
259}
260
261static IMPLEMENT_LHASH_HASH_FN(ex_class_item, EX_CLASS_ITEM)
262
263static int
264ex_class_item_cmp(const EX_CLASS_ITEM *a, const EX_CLASS_ITEM *b)
265{
266 return a->class_index - b->class_index;
267}
268
269static IMPLEMENT_LHASH_COMP_FN(ex_class_item, EX_CLASS_ITEM)
270
271/* Internal functions used by the "impl_default" implementation to access the
272 * state */
273
274static int
275ex_data_check(void)
276{
277 int toret = 1;
278 CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
279 if (!ex_data &&
280 (ex_data = lh_EX_CLASS_ITEM_new()) == NULL)
281 toret = 0;
282 CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
283 return toret;
284}
285/* This macros helps reduce the locking from repeated checks because the
286 * ex_data_check() function checks ex_data again inside a lock. */
287#define EX_DATA_CHECK(iffail) if(!ex_data && !ex_data_check()) {iffail}
288
289/* This "inner" callback is used by the callback function that follows it */
290static void
291def_cleanup_util_cb(CRYPTO_EX_DATA_FUNCS *funcs)
292{
293 free(funcs);
294}
295
296/* This callback is used in lh_doall to destroy all EX_CLASS_ITEM values from
297 * "ex_data" prior to the ex_data hash table being itself destroyed. Doesn't do
298 * any locking. */
299static void
300def_cleanup_cb(void *a_void)
301{
302 EX_CLASS_ITEM *item = (EX_CLASS_ITEM *)a_void;
303 sk_CRYPTO_EX_DATA_FUNCS_pop_free(item->meth, def_cleanup_util_cb);
304 free(item);
305}
306
307/* Return the EX_CLASS_ITEM from the "ex_data" hash table that corresponds to a
308 * given class. Handles locking. */
309static EX_CLASS_ITEM *
310def_get_class(int class_index)
311{
312 EX_CLASS_ITEM d, *p, *gen;
313 EX_DATA_CHECK(return NULL;)
314 d.class_index = class_index;
315 if (!OPENSSL_init_crypto(0, NULL))
316 return NULL;
317 CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
318 p = lh_EX_CLASS_ITEM_retrieve(ex_data, &d);
319 if (!p) {
320 gen = malloc(sizeof(EX_CLASS_ITEM));
321 if (gen) {
322 gen->class_index = class_index;
323 gen->meth_num = 1;
324 gen->meth = sk_CRYPTO_EX_DATA_FUNCS_new_null();
325 if (!gen->meth)
326 free(gen);
327 else {
328 /* Because we're inside the ex_data lock, the
329 * return value from the insert will be NULL */
330 (void)lh_EX_CLASS_ITEM_insert(ex_data, gen);
331 p = gen;
332 }
333 }
334 }
335 CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
336 if (!p)
337 CRYPTOerror(ERR_R_MALLOC_FAILURE);
338 return p;
339}
340
341/* Add a new method to the given EX_CLASS_ITEM and return the corresponding
342 * index (or -1 for error). Handles locking. */
343static int
344def_add_index(EX_CLASS_ITEM *item, long argl, void *argp,
345 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func)
346{
347 int toret = -1;
348 CRYPTO_EX_DATA_FUNCS *a = malloc(sizeof(CRYPTO_EX_DATA_FUNCS));
349
350 if (!a) {
351 CRYPTOerror(ERR_R_MALLOC_FAILURE);
352 return -1;
353 }
354 a->argl = argl;
355 a->argp = argp;
356 a->new_func = new_func;
357 a->dup_func = dup_func;
358 a->free_func = free_func;
359 CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
360 while (sk_CRYPTO_EX_DATA_FUNCS_num(item->meth) <= item->meth_num) {
361 if (!sk_CRYPTO_EX_DATA_FUNCS_push(item->meth, NULL)) {
362 CRYPTOerror(ERR_R_MALLOC_FAILURE);
363 free(a);
364 goto err;
365 }
366 }
367 toret = item->meth_num++;
368 (void)sk_CRYPTO_EX_DATA_FUNCS_set(item->meth, toret, a);
369err:
370 CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
371 return toret;
372}
373
374/**************************************************************/
375/* The functions in the default CRYPTO_EX_DATA_IMPL structure */
376
377static int
378int_new_class(void)
379{
380 int toret;
381
382 CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
383 toret = ex_class++;
384 CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
385 return toret;
386}
387
388static void
389int_cleanup(void)
390{
391 EX_DATA_CHECK(return;)
392 lh_EX_CLASS_ITEM_doall(ex_data, def_cleanup_cb);
393 lh_EX_CLASS_ITEM_free(ex_data);
394 ex_data = NULL;
395 impl = NULL;
396}
397
398static int
399int_get_new_index(int class_index, long argl, void *argp,
400 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
401 CRYPTO_EX_free *free_func)
402{
403 EX_CLASS_ITEM *item = def_get_class(class_index);
404
405 if (!item)
406 return -1;
407 return def_add_index(item, argl, argp, new_func, dup_func, free_func);
408}
409
410/* Thread-safe by copying a class's array of "CRYPTO_EX_DATA_FUNCS" entries in
411 * the lock, then using them outside the lock. NB: Thread-safety only applies to
412 * the global "ex_data" state (ie. class definitions), not thread-safe on 'ad'
413 * itself. */
414static int
415int_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
416{
417 int mx, i;
418 void *ptr;
419 CRYPTO_EX_DATA_FUNCS **storage = NULL;
420 EX_CLASS_ITEM *item = def_get_class(class_index);
421
422 if (!item)
423 /* error is already set */
424 return 0;
425 ad->sk = NULL;
426 CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
427 mx = sk_CRYPTO_EX_DATA_FUNCS_num(item->meth);
428 if (mx > 0) {
429 storage = reallocarray(NULL, mx, sizeof(CRYPTO_EX_DATA_FUNCS*));
430 if (!storage)
431 goto skip;
432 for (i = 0; i < mx; i++)
433 storage[i] = sk_CRYPTO_EX_DATA_FUNCS_value(
434 item->meth, i);
435 }
436skip:
437 CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
438 if ((mx > 0) && !storage) {
439 CRYPTOerror(ERR_R_MALLOC_FAILURE);
440 return 0;
441 }
442 for (i = 0; i < mx; i++) {
443 if (storage[i] && storage[i]->new_func) {
444 ptr = CRYPTO_get_ex_data(ad, i);
445 storage[i]->new_func(obj, ptr, ad, i,
446 storage[i]->argl, storage[i]->argp);
447 }
448 }
449 free(storage);
450 return 1;
451}
452
453/* Same thread-safety notes as for "int_new_ex_data" */
454static int
455int_dup_ex_data(int class_index, CRYPTO_EX_DATA *to, CRYPTO_EX_DATA *from)
456{
457 int mx, j, i;
458 char *ptr;
459 CRYPTO_EX_DATA_FUNCS **storage = NULL;
460 EX_CLASS_ITEM *item;
461
462 if (!from->sk)
463 /* 'to' should be "blank" which *is* just like 'from' */
464 return 1;
465 if ((item = def_get_class(class_index)) == NULL)
466 return 0;
467 CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
468 mx = sk_CRYPTO_EX_DATA_FUNCS_num(item->meth);
469 j = sk_void_num(from->sk);
470 if (j < mx)
471 mx = j;
472 if (mx > 0) {
473 storage = reallocarray(NULL, mx, sizeof(CRYPTO_EX_DATA_FUNCS*));
474 if (!storage)
475 goto skip;
476 for (i = 0; i < mx; i++)
477 storage[i] = sk_CRYPTO_EX_DATA_FUNCS_value(
478 item->meth, i);
479 }
480skip:
481 CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
482 if ((mx > 0) && !storage) {
483 CRYPTOerror(ERR_R_MALLOC_FAILURE);
484 return 0;
485 }
486 for (i = 0; i < mx; i++) {
487 ptr = CRYPTO_get_ex_data(from, i);
488 if (storage[i] && storage[i]->dup_func)
489 storage[i]->dup_func(to, from, &ptr, i,
490 storage[i]->argl, storage[i]->argp);
491 CRYPTO_set_ex_data(to, i, ptr);
492 }
493 free(storage);
494 return 1;
495}
496
497/* Same thread-safety notes as for "int_new_ex_data" */
498static void
499int_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
500{
501 int mx, i;
502 EX_CLASS_ITEM *item;
503 void *ptr;
504 CRYPTO_EX_DATA_FUNCS **storage = NULL;
505
506 if ((item = def_get_class(class_index)) == NULL)
507 return;
508 CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
509 mx = sk_CRYPTO_EX_DATA_FUNCS_num(item->meth);
510 if (mx > 0) {
511 storage = reallocarray(NULL, mx, sizeof(CRYPTO_EX_DATA_FUNCS*));
512 if (!storage)
513 goto skip;
514 for (i = 0; i < mx; i++)
515 storage[i] = sk_CRYPTO_EX_DATA_FUNCS_value(
516 item->meth, i);
517 }
518skip:
519 CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
520 if ((mx > 0) && !storage) {
521 CRYPTOerror(ERR_R_MALLOC_FAILURE);
522 return;
523 }
524 for (i = 0; i < mx; i++) {
525 if (storage[i] && storage[i]->free_func) {
526 ptr = CRYPTO_get_ex_data(ad, i);
527 storage[i]->free_func(obj, ptr, ad, i,
528 storage[i]->argl, storage[i]->argp);
529 }
530 }
531 free(storage);
532 if (ad->sk) {
533 sk_void_free(ad->sk);
534 ad->sk = NULL;
535 }
536}
537
538/********************************************************************/
539/* API functions that defer all "state" operations to the "ex_data"
540 * implementation we have set. */
541
542/* Release all "ex_data" state to prevent memory leaks. This can't be made
543 * thread-safe without overhauling a lot of stuff, and shouldn't really be
544 * called under potential race-conditions anyway (it's for program shutdown
545 * after all). */
546void
547CRYPTO_cleanup_all_ex_data(void)
548{
549 IMPL_CHECK
550 EX_IMPL(cleanup)();
551}
552LCRYPTO_ALIAS(CRYPTO_cleanup_all_ex_data);
553
554/* Inside an existing class, get/register a new index. */
555int
556CRYPTO_get_ex_new_index(int class_index, long argl, void *argp,
557 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func)
558{
559 int ret = -1;
560
561 IMPL_CHECK
562 ret = EX_IMPL(get_new_index)(class_index,
563 argl, argp, new_func, dup_func, free_func);
564 return ret;
565}
566LCRYPTO_ALIAS(CRYPTO_get_ex_new_index);
567
568/* Initialise a new CRYPTO_EX_DATA for use in a particular class - including
569 * calling new() callbacks for each index in the class used by this variable */
570int
571CRYPTO_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
572{
573 IMPL_CHECK
574 return EX_IMPL(new_ex_data)(class_index, obj, ad);
575}
576LCRYPTO_ALIAS(CRYPTO_new_ex_data);
577
578/* Duplicate a CRYPTO_EX_DATA variable - including calling dup() callbacks for
579 * each index in the class used by this variable */
580int
581CRYPTO_dup_ex_data(int class_index, CRYPTO_EX_DATA *to, CRYPTO_EX_DATA *from)
582{
583 IMPL_CHECK
584 return EX_IMPL(dup_ex_data)(class_index, to, from);
585}
586LCRYPTO_ALIAS(CRYPTO_dup_ex_data);
587
588/* Cleanup a CRYPTO_EX_DATA variable - including calling free() callbacks for
589 * each index in the class used by this variable */
590void
591CRYPTO_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
592{
593 IMPL_CHECK
594 EX_IMPL(free_ex_data)(class_index, obj, ad);
595}
596LCRYPTO_ALIAS(CRYPTO_free_ex_data);
597
598/* For a given CRYPTO_EX_DATA variable, set the value corresponding to a
599 * particular index in the class used by this variable */
600int
601CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int idx, void *val)
602{
603 int i;
604
605 if (ad->sk == NULL) {
606 if ((ad->sk = sk_void_new_null()) == NULL) {
607 CRYPTOerror(ERR_R_MALLOC_FAILURE);
608 return (0);
609 }
610 }
611 i = sk_void_num(ad->sk);
612
613 while (i <= idx) {
614 if (!sk_void_push(ad->sk, NULL)) {
615 CRYPTOerror(ERR_R_MALLOC_FAILURE);
616 return (0);
617 }
618 i++;
619 }
620 sk_void_set(ad->sk, idx, val);
621 return (1);
622}
623LCRYPTO_ALIAS(CRYPTO_set_ex_data);
624
625/* For a given CRYPTO_EX_DATA_ variable, get the value corresponding to a
626 * particular index in the class used by this variable */
627void *
628CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int idx)
629{
630 if (ad->sk == NULL)
631 return (0);
632 else if (idx >= sk_void_num(ad->sk))
633 return (0);
634 else
635 return (sk_void_value(ad->sk, idx));
636}
637LCRYPTO_ALIAS(CRYPTO_get_ex_data);