5155a2213492275f202e8bcbcd87f5b44732d7f6
[openssl.git] / crypto / core_namemap.c
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 "internal/namemap.h"
11 #include <openssl/lhash.h>
12 #include <openssl/safestack.h>
13
14 /* The namemap entry */
15 typedef struct {
16     int number;
17     const char *name;
18     char body[1];        /* Sized appropriately to contain the name */
19 } NAMEMAP_ENTRY;
20
21 DEFINE_LHASH_OF(NAMEMAP_ENTRY);
22 DEFINE_STACK_OF(NAMEMAP_ENTRY)
23
24 /* The namemap, which provides for bidirectional indexing */
25
26 struct ossl_namemap_st {
27     /* Flags */
28     unsigned int stored:1; /* If 1, it's stored in a library context */
29
30     CRYPTO_RWLOCK *lock;
31     LHASH_OF(NAMEMAP_ENTRY) *namenum;  /* Name->number mapping */
32     STACK_OF(NAMEMAP_ENTRY) *numname;  /* Number->name mapping */
33 };
34
35 /* LHASH callbacks */
36
37 static unsigned long namemap_hash(const NAMEMAP_ENTRY *n)
38 {
39     return OPENSSL_LH_strhash(n->name);
40 }
41
42 static int namemap_cmp(const NAMEMAP_ENTRY *a, const NAMEMAP_ENTRY *b)
43 {
44     return strcmp(a->name, b->name);
45 }
46
47 static void namemap_free(NAMEMAP_ENTRY *n)
48 {
49     OPENSSL_free(n);
50 }
51
52 /* OPENSSL_CTX_METHOD functions for a namemap stored in a library context */
53
54 static void *stored_namemap_new(OPENSSL_CTX *libctx)
55 {
56     OSSL_NAMEMAP *namemap = ossl_namemap_new();
57
58     if (namemap != NULL)
59         namemap->stored = 1;
60
61     return namemap;
62 }
63
64 static void stored_namemap_free(void *vnamemap)
65 {
66     OSSL_NAMEMAP *namemap = vnamemap;
67
68     /* Pretend it isn't stored, or ossl_namemap_free() will do nothing */
69     namemap->stored = 0;
70     ossl_namemap_free(namemap);
71 }
72
73 static const OPENSSL_CTX_METHOD stored_namemap_method = {
74     stored_namemap_new,
75     stored_namemap_free,
76 };
77
78 /* API functions */
79
80 OSSL_NAMEMAP *ossl_namemap_stored(OPENSSL_CTX *libctx)
81 {
82     return openssl_ctx_get_data(libctx, OPENSSL_CTX_NAMEMAP_INDEX,
83                                 &stored_namemap_method);
84 }
85
86 OSSL_NAMEMAP *ossl_namemap_new(void)
87 {
88     OSSL_NAMEMAP *namemap;
89
90     if ((namemap = OPENSSL_zalloc(sizeof(*namemap))) != NULL
91         && (namemap->lock = CRYPTO_THREAD_lock_new()) != NULL
92         && (namemap->numname = sk_NAMEMAP_ENTRY_new_null()) != NULL
93         && (namemap->namenum =
94             lh_NAMEMAP_ENTRY_new(namemap_hash, namemap_cmp)) != NULL) {
95         return namemap;
96     }
97
98     ossl_namemap_free(namemap);
99     return NULL;
100 }
101
102 void ossl_namemap_free(OSSL_NAMEMAP *namemap)
103 {
104     if (namemap == NULL || namemap->stored)
105         return;
106
107      /* The elements will be freed by sk_NAMEMAP_ENTRY_pop_free() */
108     lh_NAMEMAP_ENTRY_free(namemap->namenum);
109
110     sk_NAMEMAP_ENTRY_pop_free(namemap->numname, namemap_free);
111
112     CRYPTO_THREAD_lock_free(namemap->lock);
113     OPENSSL_free(namemap);
114 }
115
116 /*
117  * TODO(3.0) It isn't currently possible to have a default namemap in the
118  * FIPS module because if init and cleanup constraints, so we currently
119  * disable the code that would allow it when FIPS_MODE is defined.
120  */
121
122 const char *ossl_namemap_name(const OSSL_NAMEMAP *namemap, int number)
123 {
124     NAMEMAP_ENTRY *entry;
125
126 #ifndef FIPS_MODE
127     if (namemap == NULL)
128         namemap = ossl_namemap_stored(NULL);
129 #endif
130
131     if (namemap == NULL || number == 0)
132         return NULL;
133
134     CRYPTO_THREAD_read_lock(namemap->lock);
135     entry = sk_NAMEMAP_ENTRY_value(namemap->numname, number);
136     CRYPTO_THREAD_unlock(namemap->lock);
137
138     if (entry != NULL)
139         return entry->name;
140     return NULL;
141 }
142
143 int ossl_namemap_number(const OSSL_NAMEMAP *namemap, const char *name)
144 {
145     NAMEMAP_ENTRY *entry, template;
146
147 #ifndef FIPS_MODE
148     if (namemap == NULL)
149         namemap = ossl_namemap_stored(NULL);
150 #endif
151
152     if (namemap == NULL)
153         return 0;
154
155     template.name = name;
156     CRYPTO_THREAD_read_lock(namemap->lock);
157     entry = lh_NAMEMAP_ENTRY_retrieve(namemap->namenum, &template);
158     CRYPTO_THREAD_unlock(namemap->lock);
159
160     if (entry == NULL)
161         return 0;
162
163     return entry->number;
164 }
165
166 int ossl_namemap_add(OSSL_NAMEMAP *namemap, const char *name)
167 {
168     NAMEMAP_ENTRY *entry;
169     int number;
170
171 #ifndef FIPS_MODE
172     if (namemap == NULL)
173         namemap = ossl_namemap_stored(NULL);
174 #endif
175
176     if (name == NULL || namemap == NULL)
177         return 0;
178
179     if ((number = ossl_namemap_number(namemap, name)) != 0)
180         return number;           /* Pretend success */
181
182     if ((entry = OPENSSL_zalloc(sizeof(*entry) + strlen(name))) == NULL)
183         goto err;
184
185     strcpy(entry->body, name);
186     entry->name = entry->body;
187
188     CRYPTO_THREAD_write_lock(namemap->lock);
189
190     entry->number = sk_NAMEMAP_ENTRY_push(namemap->numname, entry);
191
192     if (entry->number == 0)
193         goto err;
194
195     (void)lh_NAMEMAP_ENTRY_insert(namemap->namenum, entry);
196     if (lh_NAMEMAP_ENTRY_error(namemap->namenum))
197         goto err;
198
199     CRYPTO_THREAD_unlock(namemap->lock);
200
201     return entry->number;
202
203  err:
204     if (entry != NULL) {
205         if (entry->number != 0)
206             (void)sk_NAMEMAP_ENTRY_pop(namemap->numname);
207         lh_NAMEMAP_ENTRY_delete(namemap->namenum, entry);
208         CRYPTO_THREAD_unlock(namemap->lock);
209     }
210     return 0;
211 }