property.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. /*
  2. * Copyright 2019 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. #include <string.h>
  11. #include <stdio.h>
  12. #include <stdarg.h>
  13. #include <openssl/crypto.h>
  14. #include "internal/property.h"
  15. #include "internal/ctype.h"
  16. #include <openssl/lhash.h>
  17. #include <openssl/rand.h>
  18. #include "internal/thread_once.h"
  19. #include "internal/lhash.h"
  20. #include "internal/sparse_array.h"
  21. #include "property_lcl.h"
  22. /* The number of elements in the query cache before we initiate a flush */
  23. #define IMPL_CACHE_FLUSH_THRESHOLD 500
  24. typedef struct {
  25. OSSL_PROPERTY_LIST *properties;
  26. void *method;
  27. void (*method_destruct)(void *);
  28. } IMPLEMENTATION;
  29. DEFINE_STACK_OF(IMPLEMENTATION)
  30. typedef struct {
  31. const char *query;
  32. void *method;
  33. char body[1];
  34. } QUERY;
  35. DEFINE_LHASH_OF(QUERY);
  36. typedef struct {
  37. int nid;
  38. STACK_OF(IMPLEMENTATION) *impls;
  39. LHASH_OF(QUERY) *cache;
  40. } ALGORITHM;
  41. struct ossl_method_store_st {
  42. OPENSSL_CTX *ctx;
  43. size_t nelem;
  44. SPARSE_ARRAY_OF(ALGORITHM) *algs;
  45. OSSL_PROPERTY_LIST *global_properties;
  46. int need_flush;
  47. unsigned int nbits;
  48. unsigned char rand_bits[(IMPL_CACHE_FLUSH_THRESHOLD + 7) / 8];
  49. CRYPTO_RWLOCK *lock;
  50. };
  51. typedef struct {
  52. OSSL_METHOD_STORE *store;
  53. LHASH_OF(QUERY) *cache;
  54. size_t nelem;
  55. } IMPL_CACHE_FLUSH;
  56. DEFINE_SPARSE_ARRAY_OF(ALGORITHM);
  57. static void ossl_method_cache_flush(OSSL_METHOD_STORE *store, int nid);
  58. static void ossl_method_cache_flush_all(OSSL_METHOD_STORE *c);
  59. int ossl_property_read_lock(OSSL_METHOD_STORE *p)
  60. {
  61. return p != NULL ? CRYPTO_THREAD_read_lock(p->lock) : 0;
  62. }
  63. int ossl_property_write_lock(OSSL_METHOD_STORE *p)
  64. {
  65. return p != NULL ? CRYPTO_THREAD_write_lock(p->lock) : 0;
  66. }
  67. int ossl_property_unlock(OSSL_METHOD_STORE *p)
  68. {
  69. return p != 0 ? CRYPTO_THREAD_unlock(p->lock) : 0;
  70. }
  71. static openssl_ctx_run_once_fn do_method_store_init;
  72. int do_method_store_init(OPENSSL_CTX *ctx)
  73. {
  74. return ossl_property_parse_init(ctx);
  75. }
  76. static unsigned long query_hash(const QUERY *a)
  77. {
  78. return OPENSSL_LH_strhash(a->query);
  79. }
  80. static int query_cmp(const QUERY *a, const QUERY *b)
  81. {
  82. return strcmp(a->query, b->query);
  83. }
  84. static void impl_free(IMPLEMENTATION *impl)
  85. {
  86. if (impl != NULL) {
  87. if (impl->method_destruct)
  88. impl->method_destruct(impl->method);
  89. OPENSSL_free(impl);
  90. }
  91. }
  92. static void impl_cache_free(QUERY *elem)
  93. {
  94. OPENSSL_free(elem);
  95. }
  96. static void alg_cleanup(ossl_uintmax_t idx, ALGORITHM *a)
  97. {
  98. if (a != NULL) {
  99. sk_IMPLEMENTATION_pop_free(a->impls, &impl_free);
  100. lh_QUERY_doall(a->cache, &impl_cache_free);
  101. lh_QUERY_free(a->cache);
  102. OPENSSL_free(a);
  103. }
  104. }
  105. /*
  106. * The OPENSSL_CTX param here allows access to underlying property data needed
  107. * for computation
  108. */
  109. OSSL_METHOD_STORE *ossl_method_store_new(OPENSSL_CTX *ctx)
  110. {
  111. OSSL_METHOD_STORE *res;
  112. if (!openssl_ctx_run_once(ctx,
  113. OPENSSL_CTX_METHOD_STORE_RUN_ONCE_INDEX,
  114. do_method_store_init))
  115. return NULL;
  116. res = OPENSSL_zalloc(sizeof(*res));
  117. if (res != NULL) {
  118. res->ctx = ctx;
  119. if ((res->algs = ossl_sa_ALGORITHM_new()) == NULL) {
  120. OPENSSL_free(res);
  121. return NULL;
  122. }
  123. if ((res->lock = CRYPTO_THREAD_lock_new()) == NULL) {
  124. OPENSSL_free(res->algs);
  125. OPENSSL_free(res);
  126. return NULL;
  127. }
  128. }
  129. return res;
  130. }
  131. void ossl_method_store_free(OSSL_METHOD_STORE *store)
  132. {
  133. if (store != NULL) {
  134. ossl_sa_ALGORITHM_doall(store->algs, &alg_cleanup);
  135. ossl_sa_ALGORITHM_free(store->algs);
  136. ossl_property_free(store->global_properties);
  137. CRYPTO_THREAD_lock_free(store->lock);
  138. OPENSSL_free(store);
  139. }
  140. }
  141. static ALGORITHM *ossl_method_store_retrieve(OSSL_METHOD_STORE *store, int nid)
  142. {
  143. return ossl_sa_ALGORITHM_get(store->algs, nid);
  144. }
  145. static int ossl_method_store_insert(OSSL_METHOD_STORE *store, ALGORITHM *alg)
  146. {
  147. return ossl_sa_ALGORITHM_set(store->algs, alg->nid, alg);
  148. }
  149. int ossl_method_store_add(OSSL_METHOD_STORE *store,
  150. int nid, const char *properties,
  151. void *method, void (*method_destruct)(void *))
  152. {
  153. ALGORITHM *alg = NULL;
  154. IMPLEMENTATION *impl;
  155. int ret = 0;
  156. if (nid <= 0 || method == NULL || store == NULL)
  157. return 0;
  158. if (properties == NULL)
  159. properties = "";
  160. /* Create new entry */
  161. impl = OPENSSL_malloc(sizeof(*impl));
  162. if (impl == NULL)
  163. return 0;
  164. impl->method = method;
  165. impl->method_destruct = method_destruct;
  166. /*
  167. * Insert into the hash table if required.
  168. *
  169. * A write lock is used unconditionally because we wend our way down to the
  170. * property string code which isn't locking friendly.
  171. */
  172. ossl_property_write_lock(store);
  173. ossl_method_cache_flush(store, nid);
  174. if ((impl->properties = ossl_prop_defn_get(store->ctx, properties)) == NULL) {
  175. impl->properties = ossl_parse_property(store->ctx, properties);
  176. if (impl->properties == NULL)
  177. goto err;
  178. ossl_prop_defn_set(store->ctx, properties, impl->properties);
  179. }
  180. alg = ossl_method_store_retrieve(store, nid);
  181. if (alg == NULL) {
  182. if ((alg = OPENSSL_zalloc(sizeof(*alg))) == NULL
  183. || (alg->impls = sk_IMPLEMENTATION_new_null()) == NULL
  184. || (alg->cache = lh_QUERY_new(&query_hash, &query_cmp)) == NULL)
  185. goto err;
  186. alg->nid = nid;
  187. if (!ossl_method_store_insert(store, alg))
  188. goto err;
  189. }
  190. /* Push onto stack */
  191. if (sk_IMPLEMENTATION_push(alg->impls, impl))
  192. ret = 1;
  193. ossl_property_unlock(store);
  194. if (ret == 0)
  195. impl_free(impl);
  196. return ret;
  197. err:
  198. ossl_property_unlock(store);
  199. alg_cleanup(0, alg);
  200. impl_free(impl);
  201. return 0;
  202. }
  203. int ossl_method_store_remove(OSSL_METHOD_STORE *store, int nid,
  204. const void *method)
  205. {
  206. ALGORITHM *alg = NULL;
  207. int i;
  208. if (nid <= 0 || method == NULL || store == NULL)
  209. return 0;
  210. ossl_property_write_lock(store);
  211. ossl_method_cache_flush(store, nid);
  212. alg = ossl_method_store_retrieve(store, nid);
  213. if (alg == NULL) {
  214. ossl_property_unlock(store);
  215. return 0;
  216. }
  217. /*
  218. * A sorting find then a delete could be faster but these stacks should be
  219. * relatively small, so we avoid the overhead. Sorting could also surprise
  220. * users when result orderings change (even though they are not guaranteed).
  221. */
  222. for (i = 0; i < sk_IMPLEMENTATION_num(alg->impls); i++) {
  223. IMPLEMENTATION *impl = sk_IMPLEMENTATION_value(alg->impls, i);
  224. if (impl->method == method) {
  225. sk_IMPLEMENTATION_delete(alg->impls, i);
  226. ossl_property_unlock(store);
  227. impl_free(impl);
  228. return 1;
  229. }
  230. }
  231. ossl_property_unlock(store);
  232. return 0;
  233. }
  234. int ossl_method_store_fetch(OSSL_METHOD_STORE *store, int nid,
  235. const char *prop_query, void **method)
  236. {
  237. ALGORITHM *alg;
  238. IMPLEMENTATION *impl;
  239. OSSL_PROPERTY_LIST *pq = NULL, *p2;
  240. int ret = 0;
  241. int j, best = -1, score, optional;
  242. if (nid <= 0 || method == NULL || store == NULL)
  243. return 0;
  244. /*
  245. * This only needs to be a read lock, because queries never create property
  246. * names or value and thus don't modify any of the property string layer.
  247. */
  248. ossl_property_read_lock(store);
  249. alg = ossl_method_store_retrieve(store, nid);
  250. if (alg == NULL) {
  251. ossl_property_unlock(store);
  252. return 0;
  253. }
  254. if (prop_query == NULL) {
  255. if ((impl = sk_IMPLEMENTATION_value(alg->impls, 0)) != NULL) {
  256. *method = impl->method;
  257. ret = 1;
  258. }
  259. goto fin;
  260. }
  261. pq = ossl_parse_query(store->ctx, prop_query);
  262. if (pq == NULL)
  263. goto fin;
  264. if (store->global_properties != NULL) {
  265. p2 = ossl_property_merge(pq, store->global_properties);
  266. if (p2 == NULL)
  267. goto fin;
  268. ossl_property_free(pq);
  269. pq = p2;
  270. }
  271. optional = ossl_property_has_optional(pq);
  272. for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) {
  273. impl = sk_IMPLEMENTATION_value(alg->impls, j);
  274. score = ossl_property_match_count(pq, impl->properties);
  275. if (score > best) {
  276. *method = impl->method;
  277. ret = 1;
  278. if (!optional)
  279. goto fin;
  280. best = score;
  281. }
  282. }
  283. fin:
  284. ossl_property_unlock(store);
  285. ossl_property_free(pq);
  286. return ret;
  287. }
  288. int ossl_method_store_set_global_properties(OSSL_METHOD_STORE *store,
  289. const char *prop_query) {
  290. int ret = 0;
  291. if (store == NULL)
  292. return 1;
  293. ossl_property_write_lock(store);
  294. ossl_method_cache_flush_all(store);
  295. if (prop_query == NULL) {
  296. ossl_property_free(store->global_properties);
  297. store->global_properties = NULL;
  298. ossl_property_unlock(store);
  299. return 1;
  300. }
  301. store->global_properties = ossl_parse_query(store->ctx, prop_query);
  302. ret = store->global_properties != NULL;
  303. ossl_property_unlock(store);
  304. return ret;
  305. }
  306. static void impl_cache_flush_alg(ossl_uintmax_t idx, ALGORITHM *alg)
  307. {
  308. lh_QUERY_doall(alg->cache, &impl_cache_free);
  309. lh_QUERY_flush(alg->cache);
  310. }
  311. static void ossl_method_cache_flush(OSSL_METHOD_STORE *store, int nid)
  312. {
  313. ALGORITHM *alg = ossl_method_store_retrieve(store, nid);
  314. if (alg != NULL) {
  315. store->nelem -= lh_QUERY_num_items(alg->cache);
  316. impl_cache_flush_alg(0, alg);
  317. }
  318. }
  319. static void ossl_method_cache_flush_all(OSSL_METHOD_STORE *store)
  320. {
  321. ossl_sa_ALGORITHM_doall(store->algs, &impl_cache_flush_alg);
  322. store->nelem = 0;
  323. }
  324. IMPLEMENT_LHASH_DOALL_ARG(QUERY, IMPL_CACHE_FLUSH);
  325. /*
  326. * Flush an element from the query cache (perhaps).
  327. *
  328. * In order to avoid taking a write lock to keep accurate LRU information or
  329. * using atomic operations to approximate similar, the procedure used here
  330. * is to stochastically flush approximately half the cache. Since generating
  331. * random numbers is relatively expensive, we produce them in blocks and
  332. * consume them as we go, saving generated bits between generations of flushes.
  333. *
  334. * This procedure isn't ideal, LRU would be better. However, in normal
  335. * operation, reaching a full cache would be quite unexpected. It means
  336. * that no steady state of algorithm queries has been reached. I.e. it is most
  337. * likely an attack of some form. A suboptimal clearance strategy that doesn't
  338. * degrade performance of the normal case is preferable to a more refined
  339. * approach that imposes a performance impact.
  340. */
  341. static void impl_cache_flush_cache(QUERY *c, IMPL_CACHE_FLUSH *state)
  342. {
  343. #if !defined(FIPS_MODE)
  344. /* TODO(3.0): No RAND_bytes yet in FIPS module. Add this back when available */
  345. OSSL_METHOD_STORE *store = state->store;
  346. unsigned int n;
  347. if (store->nbits == 0) {
  348. if (!RAND_bytes(store->rand_bits, sizeof(store->rand_bits)))
  349. return;
  350. store->nbits = sizeof(store->rand_bits) * 8;
  351. }
  352. n = --store->nbits;
  353. if ((store->rand_bits[n >> 3] & (1 << (n & 7))) != 0)
  354. OPENSSL_free(lh_QUERY_delete(state->cache, c));
  355. else
  356. state->nelem++;
  357. #endif /* !defined(FIPS_MODE) */
  358. }
  359. static void impl_cache_flush_one_alg(ossl_uintmax_t idx, ALGORITHM *alg,
  360. void *v)
  361. {
  362. IMPL_CACHE_FLUSH *state = (IMPL_CACHE_FLUSH *)v;
  363. state->cache = alg->cache;
  364. lh_QUERY_doall_IMPL_CACHE_FLUSH(state->cache, &impl_cache_flush_cache,
  365. state);
  366. }
  367. static void ossl_method_cache_flush_some(OSSL_METHOD_STORE *store)
  368. {
  369. IMPL_CACHE_FLUSH state;
  370. state.nelem = 0;
  371. state.store = store;
  372. ossl_sa_ALGORITHM_doall_arg(store->algs, &impl_cache_flush_one_alg, &state);
  373. store->need_flush = 0;
  374. store->nelem = state.nelem;
  375. }
  376. int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, int nid,
  377. const char *prop_query, void **method)
  378. {
  379. ALGORITHM *alg;
  380. QUERY elem, *r;
  381. if (nid <= 0 || store == NULL)
  382. return 0;
  383. ossl_property_read_lock(store);
  384. alg = ossl_method_store_retrieve(store, nid);
  385. if (alg == NULL) {
  386. ossl_property_unlock(store);
  387. return 0;
  388. }
  389. elem.query = prop_query != NULL ? prop_query : "";
  390. r = lh_QUERY_retrieve(alg->cache, &elem);
  391. if (r == NULL) {
  392. ossl_property_unlock(store);
  393. return 0;
  394. }
  395. *method = r->method;
  396. ossl_property_unlock(store);
  397. return 1;
  398. }
  399. int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, int nid,
  400. const char *prop_query, void *method)
  401. {
  402. QUERY elem, *old, *p = NULL;
  403. ALGORITHM *alg;
  404. size_t len;
  405. if (nid <= 0 || store == NULL)
  406. return 0;
  407. if (prop_query == NULL)
  408. return 1;
  409. ossl_property_write_lock(store);
  410. if (store->need_flush)
  411. ossl_method_cache_flush_some(store);
  412. alg = ossl_method_store_retrieve(store, nid);
  413. if (alg == NULL) {
  414. ossl_property_unlock(store);
  415. return 0;
  416. }
  417. if (method == NULL) {
  418. elem.query = prop_query;
  419. lh_QUERY_delete(alg->cache, &elem);
  420. ossl_property_unlock(store);
  421. return 1;
  422. }
  423. p = OPENSSL_malloc(sizeof(*p) + (len = strlen(prop_query)));
  424. if (p != NULL) {
  425. p->query = p->body;
  426. p->method = method;
  427. memcpy((char *)p->query, prop_query, len + 1);
  428. if ((old = lh_QUERY_insert(alg->cache, p)) != NULL)
  429. OPENSSL_free(old);
  430. if (old != NULL || !lh_QUERY_error(alg->cache)) {
  431. store->nelem++;
  432. if (store->nelem >= IMPL_CACHE_FLUSH_THRESHOLD)
  433. store->need_flush = 1;
  434. ossl_property_unlock(store);
  435. return 1;
  436. }
  437. }
  438. ossl_property_unlock(store);
  439. OPENSSL_free(p);
  440. return 0;
  441. }