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