1 | /* |
2 | * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. |
3 | * |
4 | * Licensed under the Apache License 2.0 (the "License"). You may not use |
5 | * this file except in compliance with the License. You can obtain a copy |
6 | * in the file LICENSE in the source distribution or at |
7 | * https://www.openssl.org/source/license.html |
8 | */ |
9 | |
10 | #include "e_os.h" /* strcasecmp */ |
11 | #include "internal/namemap.h" |
12 | #include <openssl/lhash.h> |
13 | #include "crypto/lhash.h" /* openssl_lh_strcasehash */ |
14 | |
15 | /*- |
16 | * The namenum entry |
17 | * ================= |
18 | */ |
19 | typedef struct { |
20 | char *name; |
21 | int number; |
22 | } ; |
23 | |
24 | DEFINE_LHASH_OF(NAMENUM_ENTRY); |
25 | |
26 | /*- |
27 | * The namemap itself |
28 | * ================== |
29 | */ |
30 | |
31 | struct ossl_namemap_st { |
32 | /* Flags */ |
33 | unsigned int stored:1; /* If 1, it's stored in a library context */ |
34 | |
35 | CRYPTO_RWLOCK *lock; |
36 | LHASH_OF(NAMENUM_ENTRY) *; /* Name->number mapping */ |
37 | int max_number; /* Current max number */ |
38 | }; |
39 | |
40 | /* LHASH callbacks */ |
41 | |
42 | static unsigned long (const NAMENUM_ENTRY *n) |
43 | { |
44 | return openssl_lh_strcasehash(n->name); |
45 | } |
46 | |
47 | static int (const NAMENUM_ENTRY *a, const NAMENUM_ENTRY *b) |
48 | { |
49 | return strcasecmp(a->name, b->name); |
50 | } |
51 | |
52 | static void (NAMENUM_ENTRY *n) |
53 | { |
54 | if (n != NULL) |
55 | OPENSSL_free(n->name); |
56 | OPENSSL_free(n); |
57 | } |
58 | |
59 | /* OPENSSL_CTX_METHOD functions for a namemap stored in a library context */ |
60 | |
61 | static void *stored_namemap_new(OPENSSL_CTX *libctx) |
62 | { |
63 | OSSL_NAMEMAP *namemap = ossl_namemap_new(); |
64 | |
65 | if (namemap != NULL) |
66 | namemap->stored = 1; |
67 | |
68 | return namemap; |
69 | } |
70 | |
71 | static void stored_namemap_free(void *vnamemap) |
72 | { |
73 | OSSL_NAMEMAP *namemap = vnamemap; |
74 | |
75 | if (namemap != NULL) { |
76 | /* Pretend it isn't stored, or ossl_namemap_free() will do nothing */ |
77 | namemap->stored = 0; |
78 | ossl_namemap_free(namemap); |
79 | } |
80 | } |
81 | |
82 | static const OPENSSL_CTX_METHOD stored_namemap_method = { |
83 | stored_namemap_new, |
84 | stored_namemap_free, |
85 | }; |
86 | |
87 | /*- |
88 | * API functions |
89 | * ============= |
90 | */ |
91 | |
92 | OSSL_NAMEMAP *ossl_namemap_stored(OPENSSL_CTX *libctx) |
93 | { |
94 | return openssl_ctx_get_data(libctx, OPENSSL_CTX_NAMEMAP_INDEX, |
95 | &stored_namemap_method); |
96 | } |
97 | |
98 | OSSL_NAMEMAP *ossl_namemap_new(void) |
99 | { |
100 | OSSL_NAMEMAP *namemap; |
101 | |
102 | if ((namemap = OPENSSL_zalloc(sizeof(*namemap))) != NULL |
103 | && (namemap->lock = CRYPTO_THREAD_lock_new()) != NULL |
104 | && (namemap->namenum = |
105 | lh_NAMENUM_ENTRY_new(namenum_hash, namenum_cmp)) != NULL) |
106 | return namemap; |
107 | |
108 | ossl_namemap_free(namemap); |
109 | return NULL; |
110 | } |
111 | |
112 | void ossl_namemap_free(OSSL_NAMEMAP *namemap) |
113 | { |
114 | if (namemap == NULL || namemap->stored) |
115 | return; |
116 | |
117 | lh_NAMENUM_ENTRY_doall(namemap->namenum, namenum_free); |
118 | lh_NAMENUM_ENTRY_free(namemap->namenum); |
119 | |
120 | CRYPTO_THREAD_lock_free(namemap->lock); |
121 | OPENSSL_free(namemap); |
122 | } |
123 | |
124 | int ossl_namemap_empty(OSSL_NAMEMAP *namemap) |
125 | { |
126 | int rv = 0; |
127 | |
128 | CRYPTO_THREAD_read_lock(namemap->lock); |
129 | if (namemap->max_number == 0) |
130 | rv = 1; |
131 | CRYPTO_THREAD_unlock(namemap->lock); |
132 | |
133 | return rv; |
134 | } |
135 | |
136 | typedef struct doall_names_data_st { |
137 | int number; |
138 | void (*fn)(const char *name, void *data); |
139 | void *data; |
140 | } DOALL_NAMES_DATA; |
141 | |
142 | static void do_name(const NAMENUM_ENTRY *, DOALL_NAMES_DATA *data) |
143 | { |
144 | if (namenum->number == data->number) |
145 | data->fn(namenum->name, data->data); |
146 | } |
147 | |
148 | IMPLEMENT_LHASH_DOALL_ARG_CONST(NAMENUM_ENTRY, DOALL_NAMES_DATA); |
149 | |
150 | void ossl_namemap_doall_names(const OSSL_NAMEMAP *namemap, int number, |
151 | void (*fn)(const char *name, void *data), |
152 | void *data) |
153 | { |
154 | DOALL_NAMES_DATA cbdata; |
155 | |
156 | cbdata.number = number; |
157 | cbdata.fn = fn; |
158 | cbdata.data = data; |
159 | CRYPTO_THREAD_read_lock(namemap->lock); |
160 | lh_NAMENUM_ENTRY_doall_DOALL_NAMES_DATA(namemap->namenum, do_name, |
161 | &cbdata); |
162 | CRYPTO_THREAD_unlock(namemap->lock); |
163 | } |
164 | |
165 | int ossl_namemap_name2num_n(const OSSL_NAMEMAP *namemap, |
166 | const char *name, size_t name_len) |
167 | { |
168 | NAMENUM_ENTRY *, ; |
169 | int number = 0; |
170 | |
171 | #ifndef FIPS_MODE |
172 | if (namemap == NULL) |
173 | namemap = ossl_namemap_stored(NULL); |
174 | #endif |
175 | |
176 | if (namemap == NULL) |
177 | return 0; |
178 | |
179 | if ((namenum_tmpl.name = OPENSSL_strndup(name, name_len)) == NULL) |
180 | return 0; |
181 | namenum_tmpl.number = 0; |
182 | CRYPTO_THREAD_read_lock(namemap->lock); |
183 | namenum_entry = |
184 | lh_NAMENUM_ENTRY_retrieve(namemap->namenum, &namenum_tmpl); |
185 | if (namenum_entry != NULL) |
186 | number = namenum_entry->number; |
187 | CRYPTO_THREAD_unlock(namemap->lock); |
188 | OPENSSL_free(namenum_tmpl.name); |
189 | |
190 | return number; |
191 | } |
192 | |
193 | int ossl_namemap_name2num(const OSSL_NAMEMAP *namemap, const char *name) |
194 | { |
195 | if (name == NULL) |
196 | return 0; |
197 | |
198 | return ossl_namemap_name2num_n(namemap, name, strlen(name)); |
199 | } |
200 | |
201 | struct num2name_data_st { |
202 | size_t idx; /* Countdown */ |
203 | const char *name; /* Result */ |
204 | }; |
205 | |
206 | static void do_num2name(const char *name, void *vdata) |
207 | { |
208 | struct num2name_data_st *data = vdata; |
209 | |
210 | if (data->idx > 0) |
211 | data->idx--; |
212 | else if (data->name == NULL) |
213 | data->name = name; |
214 | } |
215 | |
216 | const char *ossl_namemap_num2name(const OSSL_NAMEMAP *namemap, int number, |
217 | size_t idx) |
218 | { |
219 | struct num2name_data_st data; |
220 | |
221 | data.idx = idx; |
222 | data.name = NULL; |
223 | ossl_namemap_doall_names(namemap, number, do_num2name, &data); |
224 | return data.name; |
225 | } |
226 | |
227 | int ossl_namemap_add_name_n(OSSL_NAMEMAP *namemap, int number, |
228 | const char *name, size_t name_len) |
229 | { |
230 | NAMENUM_ENTRY * = NULL; |
231 | int tmp_number; |
232 | |
233 | #ifndef FIPS_MODE |
234 | if (namemap == NULL) |
235 | namemap = ossl_namemap_stored(NULL); |
236 | #endif |
237 | |
238 | if (name == NULL || name_len == 0 || namemap == NULL) |
239 | return 0; |
240 | |
241 | if ((tmp_number = ossl_namemap_name2num_n(namemap, name, name_len)) != 0) |
242 | return tmp_number; /* Pretend success */ |
243 | |
244 | CRYPTO_THREAD_write_lock(namemap->lock); |
245 | |
246 | if ((namenum = OPENSSL_zalloc(sizeof(*namenum))) == NULL |
247 | || (namenum->name = OPENSSL_strndup(name, name_len)) == NULL) |
248 | goto err; |
249 | |
250 | namenum->number = tmp_number = |
251 | number != 0 ? number : ++namemap->max_number; |
252 | (void)lh_NAMENUM_ENTRY_insert(namemap->namenum, namenum); |
253 | |
254 | if (lh_NAMENUM_ENTRY_error(namemap->namenum)) |
255 | goto err; |
256 | |
257 | CRYPTO_THREAD_unlock(namemap->lock); |
258 | |
259 | return tmp_number; |
260 | |
261 | err: |
262 | namenum_free(namenum); |
263 | |
264 | CRYPTO_THREAD_unlock(namemap->lock); |
265 | return 0; |
266 | } |
267 | |
268 | int ossl_namemap_add_name(OSSL_NAMEMAP *namemap, int number, const char *name) |
269 | { |
270 | if (name == NULL) |
271 | return 0; |
272 | |
273 | return ossl_namemap_add_name_n(namemap, number, name, strlen(name)); |
274 | } |
275 | |
276 | int ossl_namemap_add_names(OSSL_NAMEMAP *namemap, int number, |
277 | const char *names, const char separator) |
278 | { |
279 | const char *p, *q; |
280 | size_t l; |
281 | |
282 | /* Check that we have a namemap */ |
283 | if (!ossl_assert(namemap != NULL)) { |
284 | ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER); |
285 | return 0; |
286 | } |
287 | |
288 | /* |
289 | * Check that no name is an empty string, and that all names have at |
290 | * most one numeric identity together. |
291 | */ |
292 | for (p = names; *p != '\0'; p = (q == NULL ? p + l : q + 1)) { |
293 | int this_number; |
294 | |
295 | if ((q = strchr(p, separator)) == NULL) |
296 | l = strlen(p); /* offset to \0 */ |
297 | else |
298 | l = q - p; /* offset to the next separator */ |
299 | |
300 | this_number = ossl_namemap_name2num_n(namemap, p, l); |
301 | |
302 | if (*p == '\0' || *p == separator) { |
303 | ERR_raise(ERR_LIB_CRYPTO, CRYPTO_R_BAD_ALGORITHM_NAME); |
304 | return 0; |
305 | } |
306 | if (number == 0) { |
307 | number = this_number; |
308 | } else if (this_number != 0 && this_number != number) { |
309 | ERR_raise_data(ERR_LIB_CRYPTO, CRYPTO_R_CONFLICTING_NAMES, |
310 | "\"%.*s\" has an existing different identity %d (from \"%s\")" , |
311 | l, p, this_number, names); |
312 | return 0; |
313 | } |
314 | } |
315 | |
316 | /* Now that we have checked, register all names */ |
317 | for (p = names; *p != '\0'; p = (q == NULL ? p + l : q + 1)) { |
318 | int this_number; |
319 | |
320 | if ((q = strchr(p, separator)) == NULL) |
321 | l = strlen(p); /* offset to \0 */ |
322 | else |
323 | l = q - p; /* offset to the next separator */ |
324 | |
325 | this_number = ossl_namemap_add_name_n(namemap, number, p, l); |
326 | if (number == 0) { |
327 | number = this_number; |
328 | } else if (this_number != number) { |
329 | ERR_raise_data(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR, |
330 | "Got number %d when expecting %d" , |
331 | this_number, number); |
332 | return 0; |
333 | } |
334 | } |
335 | |
336 | return number; |
337 | } |
338 | |