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 45 static unsigned long property_hash(const PROPERTY_STRING *a) 46 { 47 return OPENSSL_LH_strhash(a->s); 48 } 49 50 static int property_cmp(const PROPERTY_STRING *a, const PROPERTY_STRING *b) 51 { 52 return strcmp(a->s, b->s); 53 } 54 55 static void property_free(PROPERTY_STRING *ps) 56 { 57 OPENSSL_free(ps); 58 } 59 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 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 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 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 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 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 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 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 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 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 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