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