property.c 15 KB

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