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