xref: /openbsd-src/lib/libcrypto/crypto_ex_data.c (revision ef8f2a3fe6e2c5ed025ef7496fffcd52e968ba95)
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