1 /* $OpenBSD: o_names.c,v 1.20 2015/02/10 11:22:21 jsing Exp $ */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 
6 #include <openssl/opensslconf.h>
7 
8 #include <openssl/err.h>
9 #include <openssl/lhash.h>
10 #include <openssl/objects.h>
11 #include <openssl/safestack.h>
12 
13 /* I use the ex_data stuff to manage the identifiers for the obj_name_types
14  * that applications may define.  I only really use the free function field.
15  */
16 DECLARE_LHASH_OF(OBJ_NAME);
17 static LHASH_OF(OBJ_NAME) *names_lh = NULL;
18 static int names_type_num = OBJ_NAME_TYPE_NUM;
19 
20 typedef struct name_funcs_st {
21 	unsigned long (*hash_func)(const char *name);
22 	int (*cmp_func)(const char *a, const char *b);
23 	void (*free_func)(const char *, int, const char *);
24 } NAME_FUNCS;
25 
26 DECLARE_STACK_OF(NAME_FUNCS)
27 
28 static STACK_OF(NAME_FUNCS) *name_funcs_stack;
29 
30 /* The LHASH callbacks now use the raw "void *" prototypes and do per-variable
31  * casting in the functions. This prevents function pointer casting without the
32  * need for macro-generated wrapper functions. */
33 
34 /* static unsigned long obj_name_hash(OBJ_NAME *a); */
35 static unsigned long obj_name_hash(const void *a_void);
36 /* static int obj_name_cmp(OBJ_NAME *a,OBJ_NAME *b); */
37 static int obj_name_cmp(const void *a_void, const void *b_void);
38 
39 static IMPLEMENT_LHASH_HASH_FN(obj_name, OBJ_NAME)
40 static IMPLEMENT_LHASH_COMP_FN(obj_name, OBJ_NAME)
41 
42 int
43 OBJ_NAME_init(void)
44 {
45 	if (names_lh != NULL)
46 		return (1);
47 	names_lh = lh_OBJ_NAME_new();
48 	return (names_lh != NULL);
49 }
50 
51 int
52 OBJ_NAME_new_index(unsigned long (*hash_func)(const char *),
53     int (*cmp_func)(const char *, const char *),
54     void (*free_func)(const char *, int, const char *))
55 {
56 	int ret;
57 	int i;
58 	NAME_FUNCS *name_funcs;
59 
60 	if (name_funcs_stack == NULL)
61 		name_funcs_stack = sk_NAME_FUNCS_new_null();
62 	if (name_funcs_stack == NULL)
63 		return (0);
64 
65 	ret = names_type_num;
66 	names_type_num++;
67 	for (i = sk_NAME_FUNCS_num(name_funcs_stack); i < names_type_num; i++) {
68 		name_funcs = malloc(sizeof(NAME_FUNCS));
69 		if (!name_funcs) {
70 			OBJerr(OBJ_F_OBJ_NAME_NEW_INDEX, ERR_R_MALLOC_FAILURE);
71 			return (0);
72 		}
73 		name_funcs->hash_func = lh_strhash;
74 		name_funcs->cmp_func = strcmp;
75 		name_funcs->free_func = NULL;
76 		if (sk_NAME_FUNCS_push(name_funcs_stack, name_funcs) == 0) {
77 			free(name_funcs);
78 			OBJerr(OBJ_F_OBJ_NAME_NEW_INDEX, ERR_R_MALLOC_FAILURE);
79 			return (0);
80 		}
81 	}
82 	name_funcs = sk_NAME_FUNCS_value(name_funcs_stack, ret);
83 	if (hash_func != NULL)
84 		name_funcs->hash_func = hash_func;
85 	if (cmp_func != NULL)
86 		name_funcs->cmp_func = cmp_func;
87 	if (free_func != NULL)
88 		name_funcs->free_func = free_func;
89 	return (ret);
90 }
91 
92 /* static int obj_name_cmp(OBJ_NAME *a, OBJ_NAME *b) */
93 static int
94 obj_name_cmp(const void *a_void, const void *b_void)
95 {
96 	int ret;
97 	const OBJ_NAME *a = (const OBJ_NAME *)a_void;
98 	const OBJ_NAME *b = (const OBJ_NAME *)b_void;
99 
100 	ret = a->type - b->type;
101 	if (ret == 0) {
102 		if ((name_funcs_stack != NULL) &&
103 		    (sk_NAME_FUNCS_num(name_funcs_stack) > a->type)) {
104 			ret = sk_NAME_FUNCS_value(name_funcs_stack,
105 			    a->type)->cmp_func(a->name, b->name);
106 		} else
107 			ret = strcmp(a->name, b->name);
108 	}
109 	return (ret);
110 }
111 
112 /* static unsigned long obj_name_hash(OBJ_NAME *a) */
113 static unsigned long
114 obj_name_hash(const void *a_void)
115 {
116 	unsigned long ret;
117 	const OBJ_NAME *a = (const OBJ_NAME *)a_void;
118 
119 	if ((name_funcs_stack != NULL) &&
120 	    (sk_NAME_FUNCS_num(name_funcs_stack) > a->type)) {
121 		ret = sk_NAME_FUNCS_value(name_funcs_stack,
122 		    a->type)->hash_func(a->name);
123 	} else {
124 		ret = lh_strhash(a->name);
125 	}
126 	ret ^= a->type;
127 	return (ret);
128 }
129 
130 const char *
131 OBJ_NAME_get(const char *name, int type)
132 {
133 	OBJ_NAME on, *ret;
134 	int num = 0, alias;
135 
136 	if (name == NULL)
137 		return (NULL);
138 	if ((names_lh == NULL) && !OBJ_NAME_init())
139 		return (NULL);
140 
141 	alias = type&OBJ_NAME_ALIAS;
142 	type&= ~OBJ_NAME_ALIAS;
143 
144 	on.name = name;
145 	on.type = type;
146 
147 	for (;;) {
148 		ret = lh_OBJ_NAME_retrieve(names_lh, &on);
149 		if (ret == NULL)
150 			return (NULL);
151 		if ((ret->alias) && !alias) {
152 			if (++num > 10)
153 				return (NULL);
154 			on.name = ret->data;
155 		} else {
156 			return (ret->data);
157 		}
158 	}
159 }
160 
161 int
162 OBJ_NAME_add(const char *name, int type, const char *data)
163 {
164 	OBJ_NAME *onp, *ret;
165 	int alias;
166 
167 	if ((names_lh == NULL) && !OBJ_NAME_init())
168 		return (0);
169 
170 	alias = type & OBJ_NAME_ALIAS;
171 	type &= ~OBJ_NAME_ALIAS;
172 
173 	onp = malloc(sizeof(OBJ_NAME));
174 	if (onp == NULL) {
175 		/* ERROR */
176 		return (0);
177 	}
178 
179 	onp->name = name;
180 	onp->alias = alias;
181 	onp->type = type;
182 	onp->data = data;
183 
184 	ret = lh_OBJ_NAME_insert(names_lh, onp);
185 	if (ret != NULL) {
186 		/* free things */
187 		if ((name_funcs_stack != NULL) &&
188 		    (sk_NAME_FUNCS_num(name_funcs_stack) > ret->type)) {
189 			/* XXX: I'm not sure I understand why the free
190 			 * function should get three arguments...
191 			 * -- Richard Levitte
192 			 */
193 			sk_NAME_FUNCS_value(
194 			    name_funcs_stack, ret->type)->free_func(
195 			    ret->name, ret->type, ret->data);
196 		}
197 		free(ret);
198 	} else {
199 		if (lh_OBJ_NAME_error(names_lh)) {
200 			/* ERROR */
201 			return (0);
202 		}
203 	}
204 	return (1);
205 }
206 
207 int
208 OBJ_NAME_remove(const char *name, int type)
209 {
210 	OBJ_NAME on, *ret;
211 
212 	if (names_lh == NULL)
213 		return (0);
214 
215 	type &= ~OBJ_NAME_ALIAS;
216 	on.name = name;
217 	on.type = type;
218 	ret = lh_OBJ_NAME_delete(names_lh, &on);
219 	if (ret != NULL) {
220 		/* free things */
221 		if ((name_funcs_stack != NULL) &&
222 		    (sk_NAME_FUNCS_num(name_funcs_stack) > ret->type)) {
223 			/* XXX: I'm not sure I understand why the free
224 			 * function should get three arguments...
225 			 * -- Richard Levitte
226 			 */
227 			sk_NAME_FUNCS_value(
228 			    name_funcs_stack, ret->type)->free_func(
229 			    ret->name, ret->type, ret->data);
230 		}
231 		free(ret);
232 		return (1);
233 	} else
234 		return (0);
235 }
236 
237 struct doall {
238 	int type;
239 	void (*fn)(const OBJ_NAME *, void *arg);
240 	void *arg;
241 };
242 
243 static void
244 do_all_fn_doall_arg(const OBJ_NAME *name, struct doall *d)
245 {
246 	if (name->type == d->type)
247 		d->fn(name, d->arg);
248 }
249 
250 static IMPLEMENT_LHASH_DOALL_ARG_FN(do_all_fn, const OBJ_NAME, struct doall)
251 
252 void
253 OBJ_NAME_do_all(int type, void (*fn)(const OBJ_NAME *, void *arg), void *arg)
254 {
255 	struct doall d;
256 
257 	d.type = type;
258 	d.fn = fn;
259 	d.arg = arg;
260 
261 	lh_OBJ_NAME_doall_arg(names_lh, LHASH_DOALL_ARG_FN(do_all_fn),
262 	    struct doall, &d);
263 }
264 
265 struct doall_sorted {
266 	int type;
267 	int n;
268 	const OBJ_NAME **names;
269 };
270 
271 static void
272 do_all_sorted_fn(const OBJ_NAME *name, void *d_)
273 {
274 	struct doall_sorted *d = d_;
275 
276 	if (name->type != d->type)
277 		return;
278 
279 	d->names[d->n++] = name;
280 }
281 
282 static int
283 do_all_sorted_cmp(const void *n1_, const void *n2_)
284 {
285 	const OBJ_NAME * const *n1 = n1_;
286 	const OBJ_NAME * const *n2 = n2_;
287 
288 	return strcmp((*n1)->name, (*n2)->name);
289 }
290 
291 void
292 OBJ_NAME_do_all_sorted(int type, void (*fn)(const OBJ_NAME *, void *arg),
293     void *arg)
294 {
295 	struct doall_sorted d;
296 	int n;
297 
298 	d.type = type;
299 	d.names = reallocarray(NULL, lh_OBJ_NAME_num_items(names_lh),
300 	    sizeof *d.names);
301 	d.n = 0;
302 	if (d.names != NULL) {
303 		OBJ_NAME_do_all(type, do_all_sorted_fn, &d);
304 
305 		qsort((void *)d.names, d.n, sizeof *d.names, do_all_sorted_cmp);
306 
307 		for (n = 0; n < d.n; ++n)
308 			fn(d.names[n], arg);
309 
310 		free(d.names);
311 	}
312 }
313 
314 static int free_type;
315 
316 static void
317 names_lh_free_doall(OBJ_NAME *onp)
318 {
319 	if (onp == NULL)
320 		return;
321 
322 	if (free_type < 0 || free_type == onp->type)
323 		OBJ_NAME_remove(onp->name, onp->type);
324 }
325 
326 static IMPLEMENT_LHASH_DOALL_FN(names_lh_free, OBJ_NAME)
327 
328 static void
329 name_funcs_free(NAME_FUNCS *ptr)
330 {
331 	free(ptr);
332 }
333 
334 void
335 OBJ_NAME_cleanup(int type)
336 {
337 	unsigned long down_load;
338 
339 	if (names_lh == NULL)
340 		return;
341 
342 	free_type = type;
343 	down_load = lh_OBJ_NAME_down_load(names_lh);
344 	lh_OBJ_NAME_down_load(names_lh) = 0;
345 
346 	lh_OBJ_NAME_doall(names_lh, LHASH_DOALL_FN(names_lh_free));
347 	if (type < 0) {
348 		lh_OBJ_NAME_free(names_lh);
349 		sk_NAME_FUNCS_pop_free(name_funcs_stack, name_funcs_free);
350 		names_lh = NULL;
351 		name_funcs_stack = NULL;
352 	} else
353 		lh_OBJ_NAME_down_load(names_lh) = down_load;
354 }
355