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/err.h>
13e1051a39Sopenharmony_ci#include <openssl/lhash.h>
14e1051a39Sopenharmony_ci#include "internal/propertyerr.h"
15e1051a39Sopenharmony_ci#include "internal/property.h"
16e1051a39Sopenharmony_ci#include "internal/core.h"
17e1051a39Sopenharmony_ci#include "property_local.h"
18e1051a39Sopenharmony_ci
19e1051a39Sopenharmony_ci/*
20e1051a39Sopenharmony_ci * Implement a property definition cache.
21e1051a39Sopenharmony_ci * These functions assume that they are called under a write lock.
22e1051a39Sopenharmony_ci * No attempt is made to clean out the cache, except when it is shut down.
23e1051a39Sopenharmony_ci */
24e1051a39Sopenharmony_ci
25e1051a39Sopenharmony_citypedef struct {
26e1051a39Sopenharmony_ci    const char *prop;
27e1051a39Sopenharmony_ci    OSSL_PROPERTY_LIST *defn;
28e1051a39Sopenharmony_ci    char body[1];
29e1051a39Sopenharmony_ci} PROPERTY_DEFN_ELEM;
30e1051a39Sopenharmony_ci
31e1051a39Sopenharmony_ciDEFINE_LHASH_OF(PROPERTY_DEFN_ELEM);
32e1051a39Sopenharmony_ci
33e1051a39Sopenharmony_cistatic unsigned long property_defn_hash(const PROPERTY_DEFN_ELEM *a)
34e1051a39Sopenharmony_ci{
35e1051a39Sopenharmony_ci    return OPENSSL_LH_strhash(a->prop);
36e1051a39Sopenharmony_ci}
37e1051a39Sopenharmony_ci
38e1051a39Sopenharmony_cistatic int property_defn_cmp(const PROPERTY_DEFN_ELEM *a,
39e1051a39Sopenharmony_ci                             const PROPERTY_DEFN_ELEM *b)
40e1051a39Sopenharmony_ci{
41e1051a39Sopenharmony_ci    return strcmp(a->prop, b->prop);
42e1051a39Sopenharmony_ci}
43e1051a39Sopenharmony_ci
44e1051a39Sopenharmony_cistatic void property_defn_free(PROPERTY_DEFN_ELEM *elem)
45e1051a39Sopenharmony_ci{
46e1051a39Sopenharmony_ci    ossl_property_free(elem->defn);
47e1051a39Sopenharmony_ci    OPENSSL_free(elem);
48e1051a39Sopenharmony_ci}
49e1051a39Sopenharmony_ci
50e1051a39Sopenharmony_cistatic void property_defns_free(void *vproperty_defns)
51e1051a39Sopenharmony_ci{
52e1051a39Sopenharmony_ci    LHASH_OF(PROPERTY_DEFN_ELEM) *property_defns = vproperty_defns;
53e1051a39Sopenharmony_ci
54e1051a39Sopenharmony_ci    if (property_defns != NULL) {
55e1051a39Sopenharmony_ci        lh_PROPERTY_DEFN_ELEM_doall(property_defns,
56e1051a39Sopenharmony_ci                                    &property_defn_free);
57e1051a39Sopenharmony_ci        lh_PROPERTY_DEFN_ELEM_free(property_defns);
58e1051a39Sopenharmony_ci    }
59e1051a39Sopenharmony_ci}
60e1051a39Sopenharmony_ci
61e1051a39Sopenharmony_cistatic void *property_defns_new(OSSL_LIB_CTX *ctx) {
62e1051a39Sopenharmony_ci    return lh_PROPERTY_DEFN_ELEM_new(&property_defn_hash, &property_defn_cmp);
63e1051a39Sopenharmony_ci}
64e1051a39Sopenharmony_ci
65e1051a39Sopenharmony_cistatic const OSSL_LIB_CTX_METHOD property_defns_method = {
66e1051a39Sopenharmony_ci    OSSL_LIB_CTX_METHOD_DEFAULT_PRIORITY,
67e1051a39Sopenharmony_ci    property_defns_new,
68e1051a39Sopenharmony_ci    property_defns_free,
69e1051a39Sopenharmony_ci};
70e1051a39Sopenharmony_ci
71e1051a39Sopenharmony_ciOSSL_PROPERTY_LIST *ossl_prop_defn_get(OSSL_LIB_CTX *ctx, const char *prop)
72e1051a39Sopenharmony_ci{
73e1051a39Sopenharmony_ci    PROPERTY_DEFN_ELEM elem, *r;
74e1051a39Sopenharmony_ci    LHASH_OF(PROPERTY_DEFN_ELEM) *property_defns;
75e1051a39Sopenharmony_ci
76e1051a39Sopenharmony_ci    property_defns = ossl_lib_ctx_get_data(ctx,
77e1051a39Sopenharmony_ci                                           OSSL_LIB_CTX_PROPERTY_DEFN_INDEX,
78e1051a39Sopenharmony_ci                                           &property_defns_method);
79e1051a39Sopenharmony_ci    if (property_defns == NULL || !ossl_lib_ctx_read_lock(ctx))
80e1051a39Sopenharmony_ci        return NULL;
81e1051a39Sopenharmony_ci
82e1051a39Sopenharmony_ci    elem.prop = prop;
83e1051a39Sopenharmony_ci    r = lh_PROPERTY_DEFN_ELEM_retrieve(property_defns, &elem);
84e1051a39Sopenharmony_ci    ossl_lib_ctx_unlock(ctx);
85e1051a39Sopenharmony_ci    if (r == NULL || !ossl_assert(r->defn != NULL))
86e1051a39Sopenharmony_ci        return NULL;
87e1051a39Sopenharmony_ci    return r->defn;
88e1051a39Sopenharmony_ci}
89e1051a39Sopenharmony_ci
90e1051a39Sopenharmony_ci/*
91e1051a39Sopenharmony_ci * Cache the property list for a given property string *pl.
92e1051a39Sopenharmony_ci * If an entry already exists in the cache *pl is freed and
93e1051a39Sopenharmony_ci * overwritten with the existing entry from the cache.
94e1051a39Sopenharmony_ci */
95e1051a39Sopenharmony_ciint ossl_prop_defn_set(OSSL_LIB_CTX *ctx, const char *prop,
96e1051a39Sopenharmony_ci                       OSSL_PROPERTY_LIST **pl)
97e1051a39Sopenharmony_ci{
98e1051a39Sopenharmony_ci    PROPERTY_DEFN_ELEM elem, *old, *p = NULL;
99e1051a39Sopenharmony_ci    size_t len;
100e1051a39Sopenharmony_ci    LHASH_OF(PROPERTY_DEFN_ELEM) *property_defns;
101e1051a39Sopenharmony_ci    int res = 1;
102e1051a39Sopenharmony_ci
103e1051a39Sopenharmony_ci    property_defns = ossl_lib_ctx_get_data(ctx,
104e1051a39Sopenharmony_ci                                           OSSL_LIB_CTX_PROPERTY_DEFN_INDEX,
105e1051a39Sopenharmony_ci                                           &property_defns_method);
106e1051a39Sopenharmony_ci    if (property_defns == NULL)
107e1051a39Sopenharmony_ci        return 0;
108e1051a39Sopenharmony_ci
109e1051a39Sopenharmony_ci    if (prop == NULL)
110e1051a39Sopenharmony_ci        return 1;
111e1051a39Sopenharmony_ci
112e1051a39Sopenharmony_ci    if (!ossl_lib_ctx_write_lock(ctx))
113e1051a39Sopenharmony_ci        return 0;
114e1051a39Sopenharmony_ci    elem.prop = prop;
115e1051a39Sopenharmony_ci    if (pl == NULL) {
116e1051a39Sopenharmony_ci        lh_PROPERTY_DEFN_ELEM_delete(property_defns, &elem);
117e1051a39Sopenharmony_ci        goto end;
118e1051a39Sopenharmony_ci    }
119e1051a39Sopenharmony_ci    /* check if property definition is in the cache already */
120e1051a39Sopenharmony_ci    if ((p = lh_PROPERTY_DEFN_ELEM_retrieve(property_defns, &elem)) != NULL) {
121e1051a39Sopenharmony_ci        ossl_property_free(*pl);
122e1051a39Sopenharmony_ci        *pl = p->defn;
123e1051a39Sopenharmony_ci        goto end;
124e1051a39Sopenharmony_ci    }
125e1051a39Sopenharmony_ci    len = strlen(prop);
126e1051a39Sopenharmony_ci    p = OPENSSL_malloc(sizeof(*p) + len);
127e1051a39Sopenharmony_ci    if (p != NULL) {
128e1051a39Sopenharmony_ci        p->prop = p->body;
129e1051a39Sopenharmony_ci        p->defn = *pl;
130e1051a39Sopenharmony_ci        memcpy(p->body, prop, len + 1);
131e1051a39Sopenharmony_ci        old = lh_PROPERTY_DEFN_ELEM_insert(property_defns, p);
132e1051a39Sopenharmony_ci        if (!ossl_assert(old == NULL))
133e1051a39Sopenharmony_ci            /* This should not happen. An existing entry is handled above. */
134e1051a39Sopenharmony_ci            goto end;
135e1051a39Sopenharmony_ci        if (!lh_PROPERTY_DEFN_ELEM_error(property_defns))
136e1051a39Sopenharmony_ci            goto end;
137e1051a39Sopenharmony_ci    }
138e1051a39Sopenharmony_ci    OPENSSL_free(p);
139e1051a39Sopenharmony_ci    res = 0;
140e1051a39Sopenharmony_ci end:
141e1051a39Sopenharmony_ci    ossl_lib_ctx_unlock(ctx);
142e1051a39Sopenharmony_ci    return res;
143e1051a39Sopenharmony_ci}
144