| /* |
| * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved. |
| * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. |
| * |
| * Licensed under the Apache License 2.0 (the "License"). You may not use |
| * this file except in compliance with the License. You can obtain a copy |
| * in the file LICENSE in the source distribution or at |
| * https://www.openssl.org/source/license.html |
| */ |
| |
| #include <string.h> |
| #include <openssl/crypto.h> |
| #include <openssl/lhash.h> |
| #include "crypto/lhash.h" |
| #include "property_local.h" |
| #include "crypto/context.h" |
| |
| /* |
| * Property strings are a consolidation of all strings seen by the property |
| * subsystem. There are two name spaces to keep property names separate from |
| * property values (numeric values are not expected to be cached however). |
| * They allow a rapid conversion from a string to a unique index and any |
| * subsequent string comparison can be done via an integer compare. |
| * |
| * This implementation uses OpenSSL's standard hash table. There are more |
| * space and time efficient algorithms if this becomes a bottleneck. |
| */ |
| |
| typedef struct { |
| const char *s; |
| OSSL_PROPERTY_IDX idx; |
| char body[1]; |
| } PROPERTY_STRING; |
| |
| DEFINE_LHASH_OF(PROPERTY_STRING); |
| typedef LHASH_OF(PROPERTY_STRING) PROP_TABLE; |
| |
| typedef struct { |
| CRYPTO_RWLOCK *lock; |
| PROP_TABLE *prop_names; |
| PROP_TABLE *prop_values; |
| OSSL_PROPERTY_IDX prop_name_idx; |
| OSSL_PROPERTY_IDX prop_value_idx; |
| #ifndef OPENSSL_SMALL_FOOTPRINT |
| STACK_OF(OPENSSL_CSTRING) *prop_namelist; |
| STACK_OF(OPENSSL_CSTRING) *prop_valuelist; |
| #endif |
| } PROPERTY_STRING_DATA; |
| |
| static unsigned long property_hash(const PROPERTY_STRING *a) |
| { |
| return OPENSSL_LH_strhash(a->s); |
| } |
| |
| static int property_cmp(const PROPERTY_STRING *a, const PROPERTY_STRING *b) |
| { |
| return strcmp(a->s, b->s); |
| } |
| |
| static void property_free(PROPERTY_STRING *ps) |
| { |
| OPENSSL_free(ps); |
| } |
| |
| static void property_table_free(PROP_TABLE **pt) |
| { |
| PROP_TABLE *t = *pt; |
| |
| if (t != NULL) { |
| lh_PROPERTY_STRING_doall(t, &property_free); |
| lh_PROPERTY_STRING_free(t); |
| *pt = NULL; |
| } |
| } |
| |
| void ossl_property_string_data_free(void *vpropdata) |
| { |
| PROPERTY_STRING_DATA *propdata = vpropdata; |
| |
| if (propdata == NULL) |
| return; |
| |
| CRYPTO_THREAD_lock_free(propdata->lock); |
| property_table_free(&propdata->prop_names); |
| property_table_free(&propdata->prop_values); |
| #ifndef OPENSSL_SMALL_FOOTPRINT |
| sk_OPENSSL_CSTRING_free(propdata->prop_namelist); |
| sk_OPENSSL_CSTRING_free(propdata->prop_valuelist); |
| propdata->prop_namelist = propdata->prop_valuelist = NULL; |
| #endif |
| propdata->prop_name_idx = propdata->prop_value_idx = 0; |
| |
| OPENSSL_free(propdata); |
| } |
| |
| void *ossl_property_string_data_new(OSSL_LIB_CTX *ctx) { |
| PROPERTY_STRING_DATA *propdata = OPENSSL_zalloc(sizeof(*propdata)); |
| |
| if (propdata == NULL) |
| return NULL; |
| |
| propdata->lock = CRYPTO_THREAD_lock_new(); |
| propdata->prop_names = lh_PROPERTY_STRING_new(&property_hash, |
| &property_cmp); |
| propdata->prop_values = lh_PROPERTY_STRING_new(&property_hash, |
| &property_cmp); |
| #ifndef OPENSSL_SMALL_FOOTPRINT |
| propdata->prop_namelist = sk_OPENSSL_CSTRING_new_null(); |
| propdata->prop_valuelist = sk_OPENSSL_CSTRING_new_null(); |
| #endif |
| if (propdata->lock == NULL |
| #ifndef OPENSSL_SMALL_FOOTPRINT |
| || propdata->prop_namelist == NULL |
| || propdata->prop_valuelist == NULL |
| #endif |
| || propdata->prop_names == NULL |
| || propdata->prop_values == NULL) { |
| ossl_property_string_data_free(propdata); |
| return NULL; |
| } |
| return propdata; |
| } |
| |
| static PROPERTY_STRING *new_property_string(const char *s, |
| OSSL_PROPERTY_IDX *pidx) |
| { |
| const size_t l = strlen(s); |
| PROPERTY_STRING *ps = OPENSSL_malloc(sizeof(*ps) + l); |
| |
| if (ps != NULL) { |
| memcpy(ps->body, s, l + 1); |
| ps->s = ps->body; |
| ps->idx = ++*pidx; |
| if (ps->idx == 0) { |
| OPENSSL_free(ps); |
| return NULL; |
| } |
| } |
| return ps; |
| } |
| |
| static OSSL_PROPERTY_IDX ossl_property_string(OSSL_LIB_CTX *ctx, int name, |
| int create, const char *s) |
| { |
| PROPERTY_STRING p, *ps, *ps_new; |
| PROP_TABLE *t; |
| OSSL_PROPERTY_IDX *pidx; |
| PROPERTY_STRING_DATA *propdata |
| = ossl_lib_ctx_get_data(ctx, OSSL_LIB_CTX_PROPERTY_STRING_INDEX); |
| |
| if (propdata == NULL) |
| return 0; |
| |
| t = name ? propdata->prop_names : propdata->prop_values; |
| p.s = s; |
| if (!CRYPTO_THREAD_read_lock(propdata->lock)) { |
| ERR_raise(ERR_LIB_CRYPTO, ERR_R_UNABLE_TO_GET_READ_LOCK); |
| return 0; |
| } |
| ps = lh_PROPERTY_STRING_retrieve(t, &p); |
| if (ps == NULL && create) { |
| CRYPTO_THREAD_unlock(propdata->lock); |
| if (!CRYPTO_THREAD_write_lock(propdata->lock)) { |
| ERR_raise(ERR_LIB_CRYPTO, ERR_R_UNABLE_TO_GET_WRITE_LOCK); |
| return 0; |
| } |
| pidx = name ? &propdata->prop_name_idx : &propdata->prop_value_idx; |
| ps = lh_PROPERTY_STRING_retrieve(t, &p); |
| if (ps == NULL && (ps_new = new_property_string(s, pidx)) != NULL) { |
| #ifndef OPENSSL_SMALL_FOOTPRINT |
| STACK_OF(OPENSSL_CSTRING) *slist; |
| |
| slist = name ? propdata->prop_namelist : propdata->prop_valuelist; |
| if (sk_OPENSSL_CSTRING_push(slist, ps_new->s) <= 0) { |
| property_free(ps_new); |
| CRYPTO_THREAD_unlock(propdata->lock); |
| return 0; |
| } |
| #endif |
| lh_PROPERTY_STRING_insert(t, ps_new); |
| if (lh_PROPERTY_STRING_error(t)) { |
| /*- |
| * Undo the previous push which means also decrementing the |
| * index and freeing the allocated storage. |
| */ |
| #ifndef OPENSSL_SMALL_FOOTPRINT |
| sk_OPENSSL_CSTRING_pop(slist); |
| #endif |
| property_free(ps_new); |
| --*pidx; |
| CRYPTO_THREAD_unlock(propdata->lock); |
| return 0; |
| } |
| ps = ps_new; |
| } |
| } |
| CRYPTO_THREAD_unlock(propdata->lock); |
| return ps != NULL ? ps->idx : 0; |
| } |
| |
| #ifdef OPENSSL_SMALL_FOOTPRINT |
| struct find_str_st { |
| const char *str; |
| OSSL_PROPERTY_IDX idx; |
| }; |
| |
| static void find_str_fn(PROPERTY_STRING *prop, void *vfindstr) |
| { |
| struct find_str_st *findstr = vfindstr; |
| |
| if (prop->idx == findstr->idx) |
| findstr->str = prop->s; |
| } |
| #endif |
| |
| static const char *ossl_property_str(int name, OSSL_LIB_CTX *ctx, |
| OSSL_PROPERTY_IDX idx) |
| { |
| const char *r; |
| PROPERTY_STRING_DATA *propdata |
| = ossl_lib_ctx_get_data(ctx, OSSL_LIB_CTX_PROPERTY_STRING_INDEX); |
| |
| if (propdata == NULL) |
| return NULL; |
| |
| if (!CRYPTO_THREAD_read_lock(propdata->lock)) { |
| ERR_raise(ERR_LIB_CRYPTO, ERR_R_UNABLE_TO_GET_READ_LOCK); |
| return NULL; |
| } |
| #ifdef OPENSSL_SMALL_FOOTPRINT |
| { |
| struct find_str_st findstr; |
| |
| findstr.str = NULL; |
| findstr.idx = idx; |
| |
| lh_PROPERTY_STRING_doall_arg(name ? propdata->prop_names |
| : propdata->prop_values, |
| find_str_fn, &findstr); |
| r = findstr.str; |
| } |
| #else |
| r = sk_OPENSSL_CSTRING_value(name ? propdata->prop_namelist |
| : propdata->prop_valuelist, idx - 1); |
| #endif |
| CRYPTO_THREAD_unlock(propdata->lock); |
| |
| return r; |
| } |
| |
| OSSL_PROPERTY_IDX ossl_property_name(OSSL_LIB_CTX *ctx, const char *s, |
| int create) |
| { |
| return ossl_property_string(ctx, 1, create, s); |
| } |
| |
| const char *ossl_property_name_str(OSSL_LIB_CTX *ctx, OSSL_PROPERTY_IDX idx) |
| { |
| return ossl_property_str(1, ctx, idx); |
| } |
| |
| OSSL_PROPERTY_IDX ossl_property_value(OSSL_LIB_CTX *ctx, const char *s, |
| int create) |
| { |
| return ossl_property_string(ctx, 0, create, s); |
| } |
| |
| const char *ossl_property_value_str(OSSL_LIB_CTX *ctx, OSSL_PROPERTY_IDX idx) |
| { |
| return ossl_property_str(0, ctx, idx); |
| } |