1/*
2 * Copyright 2019-2023 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 "internal/namemap.h"
11#include <openssl/lhash.h>
12#include "crypto/lhash.h"      /* ossl_lh_strcasehash */
13#include "internal/tsan_assist.h"
14#include "internal/sizes.h"
15
16/*-
17 * The namenum entry
18 * =================
19 */
20typedef struct {
21    char *name;
22    int number;
23} NAMENUM_ENTRY;
24
25DEFINE_LHASH_OF(NAMENUM_ENTRY);
26
27/*-
28 * The namemap itself
29 * ==================
30 */
31
32struct ossl_namemap_st {
33    /* Flags */
34    unsigned int stored:1; /* If 1, it's stored in a library context */
35
36    CRYPTO_RWLOCK *lock;
37    LHASH_OF(NAMENUM_ENTRY) *namenum;  /* Name->number mapping */
38
39    TSAN_QUALIFIER int max_number;     /* Current max number */
40};
41
42/* LHASH callbacks */
43
44static unsigned long namenum_hash(const NAMENUM_ENTRY *n)
45{
46    return ossl_lh_strcasehash(n->name);
47}
48
49static int namenum_cmp(const NAMENUM_ENTRY *a, const NAMENUM_ENTRY *b)
50{
51    return OPENSSL_strcasecmp(a->name, b->name);
52}
53
54static void namenum_free(NAMENUM_ENTRY *n)
55{
56    if (n != NULL)
57        OPENSSL_free(n->name);
58    OPENSSL_free(n);
59}
60
61/* OSSL_LIB_CTX_METHOD functions for a namemap stored in a library context */
62
63static void *stored_namemap_new(OSSL_LIB_CTX *libctx)
64{
65    OSSL_NAMEMAP *namemap = ossl_namemap_new();
66
67    if (namemap != NULL)
68        namemap->stored = 1;
69
70    return namemap;
71}
72
73static void stored_namemap_free(void *vnamemap)
74{
75    OSSL_NAMEMAP *namemap = vnamemap;
76
77    if (namemap != NULL) {
78        /* Pretend it isn't stored, or ossl_namemap_free() will do nothing */
79        namemap->stored = 0;
80        ossl_namemap_free(namemap);
81    }
82}
83
84static const OSSL_LIB_CTX_METHOD stored_namemap_method = {
85    OSSL_LIB_CTX_METHOD_DEFAULT_PRIORITY,
86    stored_namemap_new,
87    stored_namemap_free,
88};
89
90/*-
91 * API functions
92 * =============
93 */
94
95int ossl_namemap_empty(OSSL_NAMEMAP *namemap)
96{
97#ifdef TSAN_REQUIRES_LOCKING
98    /* No TSAN support */
99    int rv;
100
101    if (namemap == NULL)
102        return 1;
103
104    if (!CRYPTO_THREAD_read_lock(namemap->lock))
105        return -1;
106    rv = namemap->max_number == 0;
107    CRYPTO_THREAD_unlock(namemap->lock);
108    return rv;
109#else
110    /* Have TSAN support */
111    return namemap == NULL || tsan_load(&namemap->max_number) == 0;
112#endif
113}
114
115typedef struct doall_names_data_st {
116    int number;
117    const char **names;
118    int found;
119} DOALL_NAMES_DATA;
120
121static void do_name(const NAMENUM_ENTRY *namenum, DOALL_NAMES_DATA *data)
122{
123    if (namenum->number == data->number)
124        data->names[data->found++] = namenum->name;
125}
126
127IMPLEMENT_LHASH_DOALL_ARG_CONST(NAMENUM_ENTRY, DOALL_NAMES_DATA);
128
129/*
130 * Call the callback for all names in the namemap with the given number.
131 * A return value 1 means that the callback was called for all names. A
132 * return value of 0 means that the callback was not called for any names.
133 */
134int ossl_namemap_doall_names(const OSSL_NAMEMAP *namemap, int number,
135                             void (*fn)(const char *name, void *data),
136                             void *data)
137{
138    DOALL_NAMES_DATA cbdata;
139    size_t num_names;
140    int i;
141
142    cbdata.number = number;
143    cbdata.found = 0;
144
145    if (namemap == NULL)
146        return 0;
147
148    /*
149     * We collect all the names first under a read lock. Subsequently we call
150     * the user function, so that we're not holding the read lock when in user
151     * code. This could lead to deadlocks.
152     */
153    if (!CRYPTO_THREAD_read_lock(namemap->lock))
154        return 0;
155
156    num_names = lh_NAMENUM_ENTRY_num_items(namemap->namenum);
157    if (num_names == 0) {
158        CRYPTO_THREAD_unlock(namemap->lock);
159        return 0;
160    }
161    cbdata.names = OPENSSL_malloc(sizeof(*cbdata.names) * num_names);
162    if (cbdata.names == NULL) {
163        CRYPTO_THREAD_unlock(namemap->lock);
164        return 0;
165    }
166    lh_NAMENUM_ENTRY_doall_DOALL_NAMES_DATA(namemap->namenum, do_name,
167                                            &cbdata);
168    CRYPTO_THREAD_unlock(namemap->lock);
169
170    for (i = 0; i < cbdata.found; i++)
171        fn(cbdata.names[i], data);
172
173    OPENSSL_free(cbdata.names);
174    return 1;
175}
176
177static int namemap_name2num_n(const OSSL_NAMEMAP *namemap,
178                              const char *name, size_t name_len)
179{
180    NAMENUM_ENTRY *namenum_entry, namenum_tmpl;
181
182    if ((namenum_tmpl.name = OPENSSL_strndup(name, name_len)) == NULL)
183        return 0;
184    namenum_tmpl.number = 0;
185    namenum_entry =
186        lh_NAMENUM_ENTRY_retrieve(namemap->namenum, &namenum_tmpl);
187    OPENSSL_free(namenum_tmpl.name);
188    return namenum_entry != NULL ? namenum_entry->number : 0;
189}
190
191int ossl_namemap_name2num_n(const OSSL_NAMEMAP *namemap,
192                            const char *name, size_t name_len)
193{
194    int number;
195
196#ifndef FIPS_MODULE
197    if (namemap == NULL)
198        namemap = ossl_namemap_stored(NULL);
199#endif
200
201    if (namemap == NULL)
202        return 0;
203
204    if (!CRYPTO_THREAD_read_lock(namemap->lock))
205        return 0;
206    number = namemap_name2num_n(namemap, name, name_len);
207    CRYPTO_THREAD_unlock(namemap->lock);
208
209    return number;
210}
211
212int ossl_namemap_name2num(const OSSL_NAMEMAP *namemap, const char *name)
213{
214    if (name == NULL)
215        return 0;
216
217    return ossl_namemap_name2num_n(namemap, name, strlen(name));
218}
219
220struct num2name_data_st {
221    size_t idx;                  /* Countdown */
222    const char *name;            /* Result */
223};
224
225static void do_num2name(const char *name, void *vdata)
226{
227    struct num2name_data_st *data = vdata;
228
229    if (data->idx > 0)
230        data->idx--;
231    else if (data->name == NULL)
232        data->name = name;
233}
234
235const char *ossl_namemap_num2name(const OSSL_NAMEMAP *namemap, int number,
236                                  size_t idx)
237{
238    struct num2name_data_st data;
239
240    data.idx = idx;
241    data.name = NULL;
242    if (!ossl_namemap_doall_names(namemap, number, do_num2name, &data))
243        return NULL;
244    return data.name;
245}
246
247static int namemap_add_name_n(OSSL_NAMEMAP *namemap, int number,
248                              const char *name, size_t name_len)
249{
250    NAMENUM_ENTRY *namenum = NULL;
251    int tmp_number;
252
253    /* If it already exists, we don't add it */
254    if ((tmp_number = namemap_name2num_n(namemap, name, name_len)) != 0)
255        return tmp_number;
256
257    if ((namenum = OPENSSL_zalloc(sizeof(*namenum))) == NULL
258        || (namenum->name = OPENSSL_strndup(name, name_len)) == NULL)
259        goto err;
260
261    /* The tsan_counter use here is safe since we're under lock */
262    namenum->number =
263        number != 0 ? number : 1 + tsan_counter(&namemap->max_number);
264    (void)lh_NAMENUM_ENTRY_insert(namemap->namenum, namenum);
265
266    if (lh_NAMENUM_ENTRY_error(namemap->namenum))
267        goto err;
268    return namenum->number;
269
270 err:
271    namenum_free(namenum);
272    return 0;
273}
274
275int ossl_namemap_add_name_n(OSSL_NAMEMAP *namemap, int number,
276                            const char *name, size_t name_len)
277{
278    int tmp_number;
279
280#ifndef FIPS_MODULE
281    if (namemap == NULL)
282        namemap = ossl_namemap_stored(NULL);
283#endif
284
285    if (name == NULL || name_len == 0 || namemap == NULL)
286        return 0;
287
288    if (!CRYPTO_THREAD_write_lock(namemap->lock))
289        return 0;
290    tmp_number = namemap_add_name_n(namemap, number, name, name_len);
291    CRYPTO_THREAD_unlock(namemap->lock);
292    return tmp_number;
293}
294
295int ossl_namemap_add_name(OSSL_NAMEMAP *namemap, int number, const char *name)
296{
297    if (name == NULL)
298        return 0;
299
300    return ossl_namemap_add_name_n(namemap, number, name, strlen(name));
301}
302
303int ossl_namemap_add_names(OSSL_NAMEMAP *namemap, int number,
304                           const char *names, const char separator)
305{
306    const char *p, *q;
307    size_t l;
308
309    /* Check that we have a namemap */
310    if (!ossl_assert(namemap != NULL)) {
311        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
312        return 0;
313    }
314
315    if (!CRYPTO_THREAD_write_lock(namemap->lock))
316        return 0;
317    /*
318     * Check that no name is an empty string, and that all names have at
319     * most one numeric identity together.
320     */
321    for (p = names; *p != '\0'; p = (q == NULL ? p + l : q + 1)) {
322        int this_number;
323
324        if ((q = strchr(p, separator)) == NULL)
325            l = strlen(p);       /* offset to \0 */
326        else
327            l = q - p;           /* offset to the next separator */
328
329        this_number = namemap_name2num_n(namemap, p, l);
330
331        if (*p == '\0' || *p == separator) {
332            ERR_raise(ERR_LIB_CRYPTO, CRYPTO_R_BAD_ALGORITHM_NAME);
333            goto err;
334        }
335        if (number == 0) {
336            number = this_number;
337        } else if (this_number != 0 && this_number != number) {
338            ERR_raise_data(ERR_LIB_CRYPTO, CRYPTO_R_CONFLICTING_NAMES,
339                           "\"%.*s\" has an existing different identity %d (from \"%s\")",
340                           l, p, this_number, names);
341            goto err;
342        }
343    }
344
345    /* Now that we have checked, register all names */
346    for (p = names; *p != '\0'; p = (q == NULL ? p + l : q + 1)) {
347        int this_number;
348
349        if ((q = strchr(p, separator)) == NULL)
350            l = strlen(p);       /* offset to \0 */
351        else
352            l = q - p;           /* offset to the next separator */
353
354        this_number = namemap_add_name_n(namemap, number, p, l);
355        if (number == 0) {
356            number = this_number;
357        } else if (this_number != number) {
358            ERR_raise_data(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR,
359                           "Got number %d when expecting %d",
360                           this_number, number);
361            goto err;
362        }
363    }
364
365    CRYPTO_THREAD_unlock(namemap->lock);
366    return number;
367
368 err:
369    CRYPTO_THREAD_unlock(namemap->lock);
370    return 0;
371}
372
373/*-
374 * Pre-population
375 * ==============
376 */
377
378#ifndef FIPS_MODULE
379#include <openssl/evp.h>
380
381/* Creates an initial namemap with names found in the legacy method db */
382static void get_legacy_evp_names(int base_nid, int nid, const char *pem_name,
383                                 void *arg)
384{
385    int num = 0;
386    ASN1_OBJECT *obj;
387
388    if (base_nid != NID_undef) {
389        num = ossl_namemap_add_name(arg, num, OBJ_nid2sn(base_nid));
390        num = ossl_namemap_add_name(arg, num, OBJ_nid2ln(base_nid));
391    }
392
393    if (nid != NID_undef) {
394        num = ossl_namemap_add_name(arg, num, OBJ_nid2sn(nid));
395        num = ossl_namemap_add_name(arg, num, OBJ_nid2ln(nid));
396        if ((obj = OBJ_nid2obj(nid)) != NULL) {
397            char txtoid[OSSL_MAX_NAME_SIZE];
398
399            if (OBJ_obj2txt(txtoid, sizeof(txtoid), obj, 1) > 0)
400                num = ossl_namemap_add_name(arg, num, txtoid);
401        }
402    }
403    if (pem_name != NULL)
404        num = ossl_namemap_add_name(arg, num, pem_name);
405}
406
407static void get_legacy_cipher_names(const OBJ_NAME *on, void *arg)
408{
409    const EVP_CIPHER *cipher = (void *)OBJ_NAME_get(on->name, on->type);
410
411    if (cipher != NULL)
412        get_legacy_evp_names(NID_undef, EVP_CIPHER_get_type(cipher), NULL, arg);
413}
414
415static void get_legacy_md_names(const OBJ_NAME *on, void *arg)
416{
417    const EVP_MD *md = (void *)OBJ_NAME_get(on->name, on->type);
418
419    if (md != NULL)
420        get_legacy_evp_names(0, EVP_MD_get_type(md), NULL, arg);
421}
422
423static void get_legacy_pkey_meth_names(const EVP_PKEY_ASN1_METHOD *ameth,
424                                       void *arg)
425{
426    int nid = 0, base_nid = 0, flags = 0;
427    const char *pem_name = NULL;
428
429    EVP_PKEY_asn1_get0_info(&nid, &base_nid, &flags, NULL, &pem_name, ameth);
430    if (nid != NID_undef) {
431        if ((flags & ASN1_PKEY_ALIAS) == 0) {
432            switch (nid) {
433            case EVP_PKEY_DHX:
434                /* We know that the name "DHX" is used too */
435                get_legacy_evp_names(0, nid, "DHX", arg);
436                /* FALLTHRU */
437            default:
438                get_legacy_evp_names(0, nid, pem_name, arg);
439            }
440        } else {
441            /*
442             * Treat aliases carefully, some of them are undesirable, or
443             * should not be treated as such for providers.
444             */
445
446            switch (nid) {
447            case EVP_PKEY_SM2:
448                /*
449                 * SM2 is a separate keytype with providers, not an alias for
450                 * EC.
451                 */
452                get_legacy_evp_names(0, nid, pem_name, arg);
453                break;
454            default:
455                /* Use the short name of the base nid as the common reference */
456                get_legacy_evp_names(base_nid, nid, pem_name, arg);
457            }
458        }
459    }
460}
461#endif
462
463/*-
464 * Constructors / destructors
465 * ==========================
466 */
467
468OSSL_NAMEMAP *ossl_namemap_stored(OSSL_LIB_CTX *libctx)
469{
470#ifndef FIPS_MODULE
471    int nms;
472#endif
473    OSSL_NAMEMAP *namemap =
474        ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_NAMEMAP_INDEX,
475                              &stored_namemap_method);
476
477    if (namemap == NULL)
478        return NULL;
479
480#ifndef FIPS_MODULE
481    nms = ossl_namemap_empty(namemap);
482    if (nms < 0) {
483        /*
484         * Could not get lock to make the count, so maybe internal objects
485         * weren't added. This seems safest.
486         */
487        return NULL;
488    }
489    if (nms == 1) {
490        int i, end;
491
492        /* Before pilfering, we make sure the legacy database is populated */
493        OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
494                            | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
495
496        OBJ_NAME_do_all(OBJ_NAME_TYPE_CIPHER_METH,
497                        get_legacy_cipher_names, namemap);
498        OBJ_NAME_do_all(OBJ_NAME_TYPE_MD_METH,
499                        get_legacy_md_names, namemap);
500
501        /* We also pilfer data from the legacy EVP_PKEY_ASN1_METHODs */
502        for (i = 0, end = EVP_PKEY_asn1_get_count(); i < end; i++)
503            get_legacy_pkey_meth_names(EVP_PKEY_asn1_get0(i), namemap);
504    }
505#endif
506
507    return namemap;
508}
509
510OSSL_NAMEMAP *ossl_namemap_new(void)
511{
512    OSSL_NAMEMAP *namemap;
513
514    if ((namemap = OPENSSL_zalloc(sizeof(*namemap))) != NULL
515        && (namemap->lock = CRYPTO_THREAD_lock_new()) != NULL
516        && (namemap->namenum =
517            lh_NAMENUM_ENTRY_new(namenum_hash, namenum_cmp)) != NULL)
518        return namemap;
519
520    ossl_namemap_free(namemap);
521    return NULL;
522}
523
524void ossl_namemap_free(OSSL_NAMEMAP *namemap)
525{
526    if (namemap == NULL || namemap->stored)
527        return;
528
529    lh_NAMENUM_ENTRY_doall(namemap->namenum, namenum_free);
530    lh_NAMENUM_ENTRY_free(namemap->namenum);
531
532    CRYPTO_THREAD_lock_free(namemap->lock);
533    OPENSSL_free(namemap);
534}
535