1 /*
2  * Copyright 2019-2022 The OpenSSL Project Authors. All Rights Reserved.
3  * Copyright (c) 2019, Oracle and/or its affiliates.  All rights reserved.
4  *
5  * Licensed under the Apache License 2.0 (the "License").  You may not use
6  * this file except in compliance with the License.  You can obtain a copy
7  * in the file LICENSE in the source distribution or at
8  * https://www.openssl.org/source/license.html
9  */
10 
11 #include <string.h>
12 #include <openssl/crypto.h>
13 #include <openssl/lhash.h>
14 #include "crypto/lhash.h"
15 #include "property_local.h"
16 
17 /*
18  * Property strings are a consolidation of all strings seen by the property
19  * subsystem.  There are two name spaces to keep property names separate from
20  * property values (numeric values are not expected to be cached however).
21  * They allow a rapid conversion from a string to a unique index and any
22  * subsequent string comparison can be done via an integer compare.
23  *
24  * This implementation uses OpenSSL's standard hash table.  There are more
25  * space and time efficient algorithms if this becomes a bottleneck.
26  */
27 
28 typedef struct {
29     const char *s;
30     OSSL_PROPERTY_IDX idx;
31     char body[1];
32 } PROPERTY_STRING;
33 
34 DEFINE_LHASH_OF(PROPERTY_STRING);
35 typedef LHASH_OF(PROPERTY_STRING) PROP_TABLE;
36 
37 typedef struct {
38     CRYPTO_RWLOCK *lock;
39     PROP_TABLE *prop_names;
40     PROP_TABLE *prop_values;
41     OSSL_PROPERTY_IDX prop_name_idx;
42     OSSL_PROPERTY_IDX prop_value_idx;
43 } PROPERTY_STRING_DATA;
44 
property_hash(const PROPERTY_STRING * a)45 static unsigned long property_hash(const PROPERTY_STRING *a)
46 {
47     return OPENSSL_LH_strhash(a->s);
48 }
49 
property_cmp(const PROPERTY_STRING * a,const PROPERTY_STRING * b)50 static int property_cmp(const PROPERTY_STRING *a, const PROPERTY_STRING *b)
51 {
52     return strcmp(a->s, b->s);
53 }
54 
property_free(PROPERTY_STRING * ps)55 static void property_free(PROPERTY_STRING *ps)
56 {
57     OPENSSL_free(ps);
58 }
59 
property_table_free(PROP_TABLE ** pt)60 static void property_table_free(PROP_TABLE **pt)
61 {
62     PROP_TABLE *t = *pt;
63 
64     if (t != NULL) {
65         lh_PROPERTY_STRING_doall(t, &property_free);
66         lh_PROPERTY_STRING_free(t);
67         *pt = NULL;
68     }
69 }
70 
property_string_data_free(void * vpropdata)71 static void property_string_data_free(void *vpropdata)
72 {
73     PROPERTY_STRING_DATA *propdata = vpropdata;
74 
75     if (propdata == NULL)
76         return;
77 
78     CRYPTO_THREAD_lock_free(propdata->lock);
79     property_table_free(&propdata->prop_names);
80     property_table_free(&propdata->prop_values);
81     propdata->prop_name_idx = propdata->prop_value_idx = 0;
82 
83     OPENSSL_free(propdata);
84 }
85 
property_string_data_new(OSSL_LIB_CTX * ctx)86 static void *property_string_data_new(OSSL_LIB_CTX *ctx) {
87     PROPERTY_STRING_DATA *propdata = OPENSSL_zalloc(sizeof(*propdata));
88 
89     if (propdata == NULL)
90         return NULL;
91 
92     propdata->lock = CRYPTO_THREAD_lock_new();
93     if (propdata->lock == NULL)
94         goto err;
95 
96     propdata->prop_names = lh_PROPERTY_STRING_new(&property_hash,
97                                                   &property_cmp);
98     if (propdata->prop_names == NULL)
99         goto err;
100 
101     propdata->prop_values = lh_PROPERTY_STRING_new(&property_hash,
102                                                    &property_cmp);
103     if (propdata->prop_values == NULL)
104         goto err;
105 
106     return propdata;
107 
108 err:
109     property_string_data_free(propdata);
110     return NULL;
111 }
112 
113 static const OSSL_LIB_CTX_METHOD property_string_data_method = {
114     OSSL_LIB_CTX_METHOD_DEFAULT_PRIORITY,
115     property_string_data_new,
116     property_string_data_free,
117 };
118 
new_property_string(const char * s,OSSL_PROPERTY_IDX * pidx)119 static PROPERTY_STRING *new_property_string(const char *s,
120                                             OSSL_PROPERTY_IDX *pidx)
121 {
122     const size_t l = strlen(s);
123     PROPERTY_STRING *ps = OPENSSL_malloc(sizeof(*ps) + l);
124 
125     if (ps != NULL) {
126         memcpy(ps->body, s, l + 1);
127         ps->s = ps->body;
128         ps->idx = ++*pidx;
129         if (ps->idx == 0) {
130             OPENSSL_free(ps);
131             return NULL;
132         }
133     }
134     return ps;
135 }
136 
ossl_property_string(CRYPTO_RWLOCK * lock,PROP_TABLE * t,OSSL_PROPERTY_IDX * pidx,const char * s)137 static OSSL_PROPERTY_IDX ossl_property_string(CRYPTO_RWLOCK *lock,
138                                               PROP_TABLE *t,
139                                               OSSL_PROPERTY_IDX *pidx,
140                                               const char *s)
141 {
142     PROPERTY_STRING p, *ps, *ps_new;
143 
144     p.s = s;
145     if (!CRYPTO_THREAD_read_lock(lock)) {
146         ERR_raise(ERR_LIB_CRYPTO, ERR_R_UNABLE_TO_GET_READ_LOCK);
147         return 0;
148     }
149     ps = lh_PROPERTY_STRING_retrieve(t, &p);
150     if (ps == NULL && pidx != NULL) {
151         CRYPTO_THREAD_unlock(lock);
152         if (!CRYPTO_THREAD_write_lock(lock)) {
153             ERR_raise(ERR_LIB_CRYPTO, ERR_R_UNABLE_TO_GET_WRITE_LOCK);
154             return 0;
155         }
156         ps = lh_PROPERTY_STRING_retrieve(t, &p);
157         if (ps == NULL && (ps_new = new_property_string(s, pidx)) != NULL) {
158             lh_PROPERTY_STRING_insert(t, ps_new);
159             if (lh_PROPERTY_STRING_error(t)) {
160                 property_free(ps_new);
161                 CRYPTO_THREAD_unlock(lock);
162                 return 0;
163             }
164             ps = ps_new;
165         }
166     }
167     CRYPTO_THREAD_unlock(lock);
168     return ps != NULL ? ps->idx : 0;
169 }
170 
171 struct find_str_st {
172     const char *str;
173     OSSL_PROPERTY_IDX idx;
174 };
175 
find_str_fn(PROPERTY_STRING * prop,void * vfindstr)176 static void find_str_fn(PROPERTY_STRING *prop, void *vfindstr)
177 {
178     struct find_str_st *findstr = vfindstr;
179 
180     if (prop->idx == findstr->idx)
181         findstr->str = prop->s;
182 }
183 
ossl_property_str(int name,OSSL_LIB_CTX * ctx,OSSL_PROPERTY_IDX idx)184 static const char *ossl_property_str(int name, OSSL_LIB_CTX *ctx,
185                                      OSSL_PROPERTY_IDX idx)
186 {
187     struct find_str_st findstr;
188     PROPERTY_STRING_DATA *propdata
189         = ossl_lib_ctx_get_data(ctx, OSSL_LIB_CTX_PROPERTY_STRING_INDEX,
190                                 &property_string_data_method);
191 
192     if (propdata == NULL)
193         return NULL;
194 
195     findstr.str = NULL;
196     findstr.idx = idx;
197 
198     if (!CRYPTO_THREAD_read_lock(propdata->lock)) {
199         ERR_raise(ERR_LIB_CRYPTO, ERR_R_UNABLE_TO_GET_READ_LOCK);
200         return NULL;
201     }
202     lh_PROPERTY_STRING_doall_arg(name ? propdata->prop_names
203                                       : propdata->prop_values,
204                                  find_str_fn, &findstr);
205     CRYPTO_THREAD_unlock(propdata->lock);
206 
207     return findstr.str;
208 }
209 
ossl_property_name(OSSL_LIB_CTX * ctx,const char * s,int create)210 OSSL_PROPERTY_IDX ossl_property_name(OSSL_LIB_CTX *ctx, const char *s,
211                                      int create)
212 {
213     PROPERTY_STRING_DATA *propdata
214         = ossl_lib_ctx_get_data(ctx, OSSL_LIB_CTX_PROPERTY_STRING_INDEX,
215                                 &property_string_data_method);
216 
217     if (propdata == NULL)
218         return 0;
219     return ossl_property_string(propdata->lock, propdata->prop_names,
220                                 create ? &propdata->prop_name_idx : NULL,
221                                 s);
222 }
223 
ossl_property_name_str(OSSL_LIB_CTX * ctx,OSSL_PROPERTY_IDX idx)224 const char *ossl_property_name_str(OSSL_LIB_CTX *ctx, OSSL_PROPERTY_IDX idx)
225 {
226     return ossl_property_str(1, ctx, idx);
227 }
228 
ossl_property_value(OSSL_LIB_CTX * ctx,const char * s,int create)229 OSSL_PROPERTY_IDX ossl_property_value(OSSL_LIB_CTX *ctx, const char *s,
230                                       int create)
231 {
232     PROPERTY_STRING_DATA *propdata
233         = ossl_lib_ctx_get_data(ctx, OSSL_LIB_CTX_PROPERTY_STRING_INDEX,
234                                 &property_string_data_method);
235 
236     if (propdata == NULL)
237         return 0;
238     return ossl_property_string(propdata->lock, propdata->prop_values,
239                                 create ? &propdata->prop_value_idx : NULL,
240                                 s);
241 }
242 
ossl_property_value_str(OSSL_LIB_CTX * ctx,OSSL_PROPERTY_IDX idx)243 const char *ossl_property_value_str(OSSL_LIB_CTX *ctx, OSSL_PROPERTY_IDX idx)
244 {
245     return ossl_property_str(0, ctx, idx);
246 }
247