xref: /openbsd-src/lib/libcrypto/stack/stack.c (revision a8396bba287763f3617961762e98baaf9a2528eb)
1 /* $OpenBSD: stack.c,v 1.28 2024/03/02 11:20:36 tb Exp $ */
2 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3  * All rights reserved.
4  *
5  * This package is an SSL implementation written
6  * by Eric Young (eay@cryptsoft.com).
7  * The implementation was written so as to conform with Netscapes SSL.
8  *
9  * This library is free for commercial and non-commercial use as long as
10  * the following conditions are aheared to.  The following conditions
11  * apply to all code found in this distribution, be it the RC4, RSA,
12  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
13  * included with this distribution is covered by the same copyright terms
14  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
15  *
16  * Copyright remains Eric Young's, and as such any Copyright notices in
17  * the code are not to be removed.
18  * If this package is used in a product, Eric Young should be given attribution
19  * as the author of the parts of the library used.
20  * This can be in the form of a textual message at program startup or
21  * in documentation (online or textual) provided with the package.
22  *
23  * Redistribution and use in source and binary forms, with or without
24  * modification, are permitted provided that the following conditions
25  * are met:
26  * 1. Redistributions of source code must retain the copyright
27  *    notice, this list of conditions and the following disclaimer.
28  * 2. Redistributions in binary form must reproduce the above copyright
29  *    notice, this list of conditions and the following disclaimer in the
30  *    documentation and/or other materials provided with the distribution.
31  * 3. All advertising materials mentioning features or use of this software
32  *    must display the following acknowledgement:
33  *    "This product includes cryptographic software written by
34  *     Eric Young (eay@cryptsoft.com)"
35  *    The word 'cryptographic' can be left out if the rouines from the library
36  *    being used are not cryptographic related :-).
37  * 4. If you include any Windows specific code (or a derivative thereof) from
38  *    the apps directory (application code) you must include an acknowledgement:
39  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
40  *
41  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
42  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  *
53  * The licence and distribution terms for any publically available version or
54  * derivative of this code cannot be changed.  i.e. this code cannot simply be
55  * copied and put under another distribution licence
56  * [including the GNU Public Licence.]
57  */
58 
59 #include <errno.h>
60 #include <stdio.h>
61 #include <string.h>
62 
63 #include <openssl/objects.h>
64 #include <openssl/stack.h>
65 
66 #include "stack_local.h"
67 
68 #undef MIN_NODES
69 #define MIN_NODES	4
70 
71 #define OBJ_BSEARCH_VALUE_ON_NOMATCH		0x01
72 #define OBJ_BSEARCH_FIRST_VALUE_ON_MATCH	0x02
73 
74 int
75 (*sk_set_cmp_func(_STACK *sk, int (*c)(const void *, const void *)))(
76     const void *, const void *)
77 {
78 	int (*old)(const void *, const void *) = sk->comp;
79 
80 	if (sk->comp != c)
81 		sk->sorted = 0;
82 	sk->comp = c;
83 
84 	return old;
85 }
86 LCRYPTO_ALIAS(sk_set_cmp_func);
87 
88 _STACK *
89 sk_dup(_STACK *sk)
90 {
91 	_STACK *ret;
92 	char **s;
93 
94 	if ((ret = sk_new(sk->comp)) == NULL)
95 		goto err;
96 	s = reallocarray(ret->data, sk->num_alloc, sizeof(char *));
97 	if (s == NULL)
98 		goto err;
99 	ret->data = s;
100 
101 	ret->num = sk->num;
102 	memcpy(ret->data, sk->data, sizeof(char *) * sk->num);
103 	ret->sorted = sk->sorted;
104 	ret->num_alloc = sk->num_alloc;
105 	ret->comp = sk->comp;
106 	return (ret);
107 
108 err:
109 	if (ret)
110 		sk_free(ret);
111 	return (NULL);
112 }
113 LCRYPTO_ALIAS(sk_dup);
114 
115 _STACK *
116 sk_new_null(void)
117 {
118 	return sk_new((int (*)(const void *, const void *))0);
119 }
120 LCRYPTO_ALIAS(sk_new_null);
121 
122 _STACK *
123 sk_new(int (*c)(const void *, const void *))
124 {
125 	_STACK *ret;
126 	int i;
127 
128 	if ((ret = malloc(sizeof(_STACK))) == NULL)
129 		goto err;
130 	if ((ret->data = reallocarray(NULL, MIN_NODES, sizeof(char *))) == NULL)
131 		goto err;
132 	for (i = 0; i < MIN_NODES; i++)
133 		ret->data[i] = NULL;
134 	ret->comp = c;
135 	ret->num_alloc = MIN_NODES;
136 	ret->num = 0;
137 	ret->sorted = 0;
138 	return (ret);
139 
140 err:
141 	free(ret);
142 	return (NULL);
143 }
144 LCRYPTO_ALIAS(sk_new);
145 
146 int
147 sk_insert(_STACK *st, void *data, int loc)
148 {
149 	char **s;
150 
151 	if (st == NULL)
152 		return 0;
153 	if (st->num_alloc <= st->num + 1) {
154 		s = reallocarray(st->data, st->num_alloc, 2 * sizeof(char *));
155 		if (s == NULL)
156 			return (0);
157 		st->data = s;
158 		st->num_alloc *= 2;
159 	}
160 	if ((loc >= (int)st->num) || (loc < 0))
161 		st->data[st->num] = data;
162 	else {
163 		memmove(&(st->data[loc + 1]), &(st->data[loc]),
164 		    sizeof(char *)*(st->num - loc));
165 		st->data[loc] = data;
166 	}
167 	st->num++;
168 	st->sorted = 0;
169 	return (st->num);
170 }
171 LCRYPTO_ALIAS(sk_insert);
172 
173 void *
174 sk_delete_ptr(_STACK *st, void *p)
175 {
176 	int i;
177 
178 	for (i = 0; i < st->num; i++)
179 		if (st->data[i] == p)
180 			return (sk_delete(st, i));
181 	return (NULL);
182 }
183 LCRYPTO_ALIAS(sk_delete_ptr);
184 
185 void *
186 sk_delete(_STACK *st, int loc)
187 {
188 	char *ret;
189 
190 	if (!st || (loc < 0) || (loc >= st->num))
191 		return NULL;
192 
193 	ret = st->data[loc];
194 	if (loc != st->num - 1) {
195 		memmove(&(st->data[loc]), &(st->data[loc + 1]),
196 		    sizeof(char *)*(st->num - 1 - loc));
197 	}
198 	st->num--;
199 	return (ret);
200 }
201 LCRYPTO_ALIAS(sk_delete);
202 
203 static const void *
204 obj_bsearch_ex(const void *key, const void *base_, int num, int size,
205     int (*cmp)(const void *, const void *), int flags)
206 {
207 	const char *base = base_;
208 	int l, h, i = 0, c = 0;
209 	const char *p = NULL;
210 
211 	if (num == 0)
212 		return (NULL);
213 	l = 0;
214 	h = num;
215 	while (l < h) {
216 		i = (l + h) / 2;
217 		p = &(base[i * size]);
218 		c = (*cmp)(key, p);
219 		if (c < 0)
220 			h = i;
221 		else if (c > 0)
222 			l = i + 1;
223 		else
224 			break;
225 	}
226 	if (c != 0 && !(flags & OBJ_BSEARCH_VALUE_ON_NOMATCH))
227 		p = NULL;
228 	else if (c == 0 && (flags & OBJ_BSEARCH_FIRST_VALUE_ON_MATCH)) {
229 		while (i > 0 && (*cmp)(key, &(base[(i - 1) * size])) == 0)
230 			i--;
231 		p = &(base[i * size]);
232 	}
233 	return (p);
234 }
235 
236 static int
237 internal_find(_STACK *st, void *data, int ret_val_options)
238 {
239 	const void * const *r;
240 	int i;
241 
242 	if (st == NULL)
243 		return -1;
244 
245 	if (st->comp == NULL) {
246 		for (i = 0; i < st->num; i++)
247 			if (st->data[i] == data)
248 				return (i);
249 		return (-1);
250 	}
251 	sk_sort(st);
252 	if (data == NULL)
253 		return (-1);
254 	r = obj_bsearch_ex(&data, st->data, st->num, sizeof(void *), st->comp,
255 	    ret_val_options);
256 	if (r == NULL)
257 		return (-1);
258 	return (int)((char **)r - st->data);
259 }
260 
261 int
262 sk_find(_STACK *st, void *data)
263 {
264 	return internal_find(st, data, OBJ_BSEARCH_FIRST_VALUE_ON_MATCH);
265 }
266 LCRYPTO_ALIAS(sk_find);
267 
268 int
269 sk_push(_STACK *st, void *data)
270 {
271 	return (sk_insert(st, data, st->num));
272 }
273 LCRYPTO_ALIAS(sk_push);
274 
275 int
276 sk_unshift(_STACK *st, void *data)
277 {
278 	return (sk_insert(st, data, 0));
279 }
280 LCRYPTO_ALIAS(sk_unshift);
281 
282 void *
283 sk_shift(_STACK *st)
284 {
285 	if (st == NULL)
286 		return (NULL);
287 	if (st->num <= 0)
288 		return (NULL);
289 	return (sk_delete(st, 0));
290 }
291 LCRYPTO_ALIAS(sk_shift);
292 
293 void *
294 sk_pop(_STACK *st)
295 {
296 	if (st == NULL)
297 		return (NULL);
298 	if (st->num <= 0)
299 		return (NULL);
300 	return (sk_delete(st, st->num - 1));
301 }
302 LCRYPTO_ALIAS(sk_pop);
303 
304 void
305 sk_zero(_STACK *st)
306 {
307 	if (st == NULL)
308 		return;
309 	if (st->num <= 0)
310 		return;
311 	memset(st->data, 0, sizeof(st->data)*st->num);
312 	st->num = 0;
313 }
314 LCRYPTO_ALIAS(sk_zero);
315 
316 void
317 sk_pop_free(_STACK *st, void (*func)(void *))
318 {
319 	int i;
320 
321 	if (st == NULL)
322 		return;
323 	for (i = 0; i < st->num; i++)
324 		if (st->data[i] != NULL)
325 			func(st->data[i]);
326 	sk_free(st);
327 }
328 LCRYPTO_ALIAS(sk_pop_free);
329 
330 void
331 sk_free(_STACK *st)
332 {
333 	if (st == NULL)
334 		return;
335 	free(st->data);
336 	free(st);
337 }
338 LCRYPTO_ALIAS(sk_free);
339 
340 int
341 sk_num(const _STACK *st)
342 {
343 	if (st == NULL)
344 		return -1;
345 	return st->num;
346 }
347 LCRYPTO_ALIAS(sk_num);
348 
349 void *
350 sk_value(const _STACK *st, int i)
351 {
352 	if (!st || (i < 0) || (i >= st->num))
353 		return NULL;
354 	return st->data[i];
355 }
356 LCRYPTO_ALIAS(sk_value);
357 
358 void *
359 sk_set(_STACK *st, int i, void *value)
360 {
361 	if (!st || (i < 0) || (i >= st->num))
362 		return NULL;
363 	st->sorted = 0;
364 	return (st->data[i] = value);
365 }
366 LCRYPTO_ALIAS(sk_set);
367 
368 void
369 sk_sort(_STACK *st)
370 {
371 	if (st && !st->sorted) {
372 		int (*comp_func)(const void *, const void *);
373 
374 		/* same comment as in sk_find ... previously st->comp was declared
375 		 * as a (void*,void*) callback type, but this made the population
376 		 * of the callback pointer illogical - our callbacks compare
377 		 * type** with type**, so we leave the casting until absolutely
378 		 * necessary (ie. "now"). */
379 		comp_func = (int (*)(const void *, const void *))(st->comp);
380 		qsort(st->data, st->num, sizeof(char *), comp_func);
381 		st->sorted = 1;
382 	}
383 }
384 LCRYPTO_ALIAS(sk_sort);
385 
386 int
387 sk_is_sorted(const _STACK *st)
388 {
389 	if (st == NULL)
390 		return 1;
391 
392 	if (st->sorted)
393 		return 1;
394 
395 	/* If there is no comparison function we cannot sort. */
396 	if (st->comp == NULL)
397 		return 0;
398 
399 	/* Lists with zero or one elements are always sorted. */
400 	return st->num <= 1;
401 }
402 LCRYPTO_ALIAS(sk_is_sorted);
403