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 #include "crypto/context.h" 17 18 /* 19 * Property strings are a consolidation of all strings seen by the property 20 * subsystem. There are two name spaces to keep property names separate from 21 * property values (numeric values are not expected to be cached however). 22 * They allow a rapid conversion from a string to a unique index and any 23 * subsequent string comparison can be done via an integer compare. 24 * 25 * This implementation uses OpenSSL's standard hash table. There are more 26 * space and time efficient algorithms if this becomes a bottleneck. 27 */ 28 29 typedef struct { 30 const char *s; 31 OSSL_PROPERTY_IDX idx; 32 char body[1]; 33 } PROPERTY_STRING; 34 35 DEFINE_LHASH_OF_EX(PROPERTY_STRING); 36 typedef LHASH_OF(PROPERTY_STRING) PROP_TABLE; 37 38 typedef struct { 39 CRYPTO_RWLOCK *lock; 40 PROP_TABLE *prop_names; 41 PROP_TABLE *prop_values; 42 OSSL_PROPERTY_IDX prop_name_idx; 43 OSSL_PROPERTY_IDX prop_value_idx; 44 #ifndef OPENSSL_SMALL_FOOTPRINT 45 STACK_OF(OPENSSL_CSTRING) *prop_namelist; 46 STACK_OF(OPENSSL_CSTRING) *prop_valuelist; 47 #endif 48 } PROPERTY_STRING_DATA; 49 50 static unsigned long property_hash(const PROPERTY_STRING *a) 51 { 52 return OPENSSL_LH_strhash(a->s); 53 } 54 55 static int property_cmp(const PROPERTY_STRING *a, const PROPERTY_STRING *b) 56 { 57 return strcmp(a->s, b->s); 58 } 59 60 static void property_free(PROPERTY_STRING *ps) 61 { 62 OPENSSL_free(ps); 63 } 64 65 static void property_table_free(PROP_TABLE **pt) 66 { 67 PROP_TABLE *t = *pt; 68 69 if (t != NULL) { 70 lh_PROPERTY_STRING_doall(t, &property_free); 71 lh_PROPERTY_STRING_free(t); 72 *pt = NULL; 73 } 74 } 75 76 void ossl_property_string_data_free(void *vpropdata) 77 { 78 PROPERTY_STRING_DATA *propdata = vpropdata; 79 80 if (propdata == NULL) 81 return; 82 83 CRYPTO_THREAD_lock_free(propdata->lock); 84 property_table_free(&propdata->prop_names); 85 property_table_free(&propdata->prop_values); 86 #ifndef OPENSSL_SMALL_FOOTPRINT 87 sk_OPENSSL_CSTRING_free(propdata->prop_namelist); 88 sk_OPENSSL_CSTRING_free(propdata->prop_valuelist); 89 propdata->prop_namelist = propdata->prop_valuelist = NULL; 90 #endif 91 propdata->prop_name_idx = propdata->prop_value_idx = 0; 92 93 OPENSSL_free(propdata); 94 } 95 96 void *ossl_property_string_data_new(OSSL_LIB_CTX *ctx) 97 { 98 PROPERTY_STRING_DATA *propdata = OPENSSL_zalloc(sizeof(*propdata)); 99 100 if (propdata == NULL) 101 return NULL; 102 103 propdata->lock = CRYPTO_THREAD_lock_new(); 104 propdata->prop_names = lh_PROPERTY_STRING_new(&property_hash, 105 &property_cmp); 106 propdata->prop_values = lh_PROPERTY_STRING_new(&property_hash, 107 &property_cmp); 108 #ifndef OPENSSL_SMALL_FOOTPRINT 109 propdata->prop_namelist = sk_OPENSSL_CSTRING_new_null(); 110 propdata->prop_valuelist = sk_OPENSSL_CSTRING_new_null(); 111 #endif 112 if (propdata->lock == NULL 113 #ifndef OPENSSL_SMALL_FOOTPRINT 114 || propdata->prop_namelist == NULL 115 || propdata->prop_valuelist == NULL 116 #endif 117 || propdata->prop_names == NULL 118 || propdata->prop_values == NULL) { 119 ossl_property_string_data_free(propdata); 120 return NULL; 121 } 122 return propdata; 123 } 124 125 static PROPERTY_STRING *new_property_string(const char *s, 126 OSSL_PROPERTY_IDX *pidx) 127 { 128 const size_t l = strlen(s); 129 PROPERTY_STRING *ps = OPENSSL_malloc(sizeof(*ps) + l); 130 131 if (ps != NULL) { 132 memcpy(ps->body, s, l + 1); 133 ps->s = ps->body; 134 ps->idx = ++*pidx; 135 if (ps->idx == 0) { 136 OPENSSL_free(ps); 137 return NULL; 138 } 139 } 140 return ps; 141 } 142 143 static OSSL_PROPERTY_IDX ossl_property_string(OSSL_LIB_CTX *ctx, int name, 144 int create, const char *s) 145 { 146 PROPERTY_STRING p, *ps, *ps_new; 147 PROP_TABLE *t; 148 OSSL_PROPERTY_IDX *pidx; 149 PROPERTY_STRING_DATA *propdata 150 = ossl_lib_ctx_get_data(ctx, OSSL_LIB_CTX_PROPERTY_STRING_INDEX); 151 152 if (propdata == NULL) 153 return 0; 154 155 t = name ? propdata->prop_names : propdata->prop_values; 156 p.s = s; 157 if (!CRYPTO_THREAD_read_lock(propdata->lock)) { 158 ERR_raise(ERR_LIB_CRYPTO, ERR_R_UNABLE_TO_GET_READ_LOCK); 159 return 0; 160 } 161 ps = lh_PROPERTY_STRING_retrieve(t, &p); 162 if (ps == NULL && create) { 163 CRYPTO_THREAD_unlock(propdata->lock); 164 if (!CRYPTO_THREAD_write_lock(propdata->lock)) { 165 ERR_raise(ERR_LIB_CRYPTO, ERR_R_UNABLE_TO_GET_WRITE_LOCK); 166 return 0; 167 } 168 pidx = name ? &propdata->prop_name_idx : &propdata->prop_value_idx; 169 ps = lh_PROPERTY_STRING_retrieve(t, &p); 170 if (ps == NULL && (ps_new = new_property_string(s, pidx)) != NULL) { 171 #ifndef OPENSSL_SMALL_FOOTPRINT 172 STACK_OF(OPENSSL_CSTRING) *slist; 173 174 slist = name ? propdata->prop_namelist : propdata->prop_valuelist; 175 if (sk_OPENSSL_CSTRING_push(slist, ps_new->s) <= 0) { 176 property_free(ps_new); 177 CRYPTO_THREAD_unlock(propdata->lock); 178 return 0; 179 } 180 #endif 181 lh_PROPERTY_STRING_insert(t, ps_new); 182 if (lh_PROPERTY_STRING_error(t)) { 183 /*- 184 * Undo the previous push which means also decrementing the 185 * index and freeing the allocated storage. 186 */ 187 #ifndef OPENSSL_SMALL_FOOTPRINT 188 sk_OPENSSL_CSTRING_pop(slist); 189 #endif 190 property_free(ps_new); 191 --*pidx; 192 CRYPTO_THREAD_unlock(propdata->lock); 193 return 0; 194 } 195 ps = ps_new; 196 } 197 } 198 CRYPTO_THREAD_unlock(propdata->lock); 199 return ps != NULL ? ps->idx : 0; 200 } 201 202 #ifdef OPENSSL_SMALL_FOOTPRINT 203 struct find_str_st { 204 const char *str; 205 OSSL_PROPERTY_IDX idx; 206 }; 207 208 static void find_str_fn(PROPERTY_STRING *prop, void *vfindstr) 209 { 210 struct find_str_st *findstr = vfindstr; 211 212 if (prop->idx == findstr->idx) 213 findstr->str = prop->s; 214 } 215 #endif 216 217 static const char *ossl_property_str(int name, OSSL_LIB_CTX *ctx, 218 OSSL_PROPERTY_IDX idx) 219 { 220 const char *r; 221 PROPERTY_STRING_DATA *propdata 222 = ossl_lib_ctx_get_data(ctx, OSSL_LIB_CTX_PROPERTY_STRING_INDEX); 223 224 if (propdata == NULL) 225 return NULL; 226 227 if (!CRYPTO_THREAD_read_lock(propdata->lock)) { 228 ERR_raise(ERR_LIB_CRYPTO, ERR_R_UNABLE_TO_GET_READ_LOCK); 229 return NULL; 230 } 231 #ifdef OPENSSL_SMALL_FOOTPRINT 232 { 233 struct find_str_st findstr; 234 235 findstr.str = NULL; 236 findstr.idx = idx; 237 238 lh_PROPERTY_STRING_doall_arg(name ? propdata->prop_names 239 : propdata->prop_values, 240 find_str_fn, &findstr); 241 r = findstr.str; 242 } 243 #else 244 r = sk_OPENSSL_CSTRING_value(name ? propdata->prop_namelist 245 : propdata->prop_valuelist, 246 idx - 1); 247 #endif 248 CRYPTO_THREAD_unlock(propdata->lock); 249 250 return r; 251 } 252 253 OSSL_PROPERTY_IDX ossl_property_name(OSSL_LIB_CTX *ctx, const char *s, 254 int create) 255 { 256 return ossl_property_string(ctx, 1, create, s); 257 } 258 259 const char *ossl_property_name_str(OSSL_LIB_CTX *ctx, OSSL_PROPERTY_IDX idx) 260 { 261 return ossl_property_str(1, ctx, idx); 262 } 263 264 OSSL_PROPERTY_IDX ossl_property_value(OSSL_LIB_CTX *ctx, const char *s, 265 int create) 266 { 267 return ossl_property_string(ctx, 0, create, s); 268 } 269 270 const char *ossl_property_value_str(OSSL_LIB_CTX *ctx, OSSL_PROPERTY_IDX idx) 271 { 272 return ossl_property_str(0, ctx, idx); 273 } 274