xref: /dflybsd-src/crypto/libressl/crypto/x509/x509_issuer_cache.c (revision 961e30ea7dc61d1112b778ea4981eac68129fb86)
1*de0e0e4dSAntonio Huete Jimenez /* $OpenBSD: x509_issuer_cache.c,v 1.3 2022/06/27 14:23:40 beck Exp $ */
28edacedfSDaniel Fojt /*
38edacedfSDaniel Fojt  * Copyright (c) 2020 Bob Beck <beck@openbsd.org>
48edacedfSDaniel Fojt  *
58edacedfSDaniel Fojt  * Permission to use, copy, modify, and distribute this software for any
68edacedfSDaniel Fojt  * purpose with or without fee is hereby granted, provided that the above
78edacedfSDaniel Fojt  * copyright notice and this permission notice appear in all copies.
88edacedfSDaniel Fojt  *
98edacedfSDaniel Fojt  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
108edacedfSDaniel Fojt  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
118edacedfSDaniel Fojt  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
128edacedfSDaniel Fojt  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
138edacedfSDaniel Fojt  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
148edacedfSDaniel Fojt  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
158edacedfSDaniel Fojt  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
168edacedfSDaniel Fojt  */
178edacedfSDaniel Fojt 
188edacedfSDaniel Fojt /* x509_issuer_cache */
198edacedfSDaniel Fojt 
208edacedfSDaniel Fojt /*
218edacedfSDaniel Fojt  * The issuer cache is a cache of parent and child x509 certificate
228edacedfSDaniel Fojt  * hashes with a signature validation result.
238edacedfSDaniel Fojt  *
248edacedfSDaniel Fojt  * Entries should only be added to the cache with a validation result
258edacedfSDaniel Fojt  * from checking the public key math that "parent" signed "child".
268edacedfSDaniel Fojt  *
278edacedfSDaniel Fojt  * Finding an entry in the cache gets us the result of a previously
288edacedfSDaniel Fojt  * performed validation of the signature of "parent" signing for the
298edacedfSDaniel Fojt  * validity of "child". It allows us to skip doing the public key math
308edacedfSDaniel Fojt  * when validating a certificate chain. It does not allow us to skip
318edacedfSDaniel Fojt  * any other steps of validation (times, names, key usage, etc.)
328edacedfSDaniel Fojt  */
338edacedfSDaniel Fojt 
348edacedfSDaniel Fojt #include <pthread.h>
358edacedfSDaniel Fojt #include <string.h>
368edacedfSDaniel Fojt 
378edacedfSDaniel Fojt #include "x509_issuer_cache.h"
388edacedfSDaniel Fojt 
398edacedfSDaniel Fojt static int
x509_issuer_cmp(struct x509_issuer * x1,struct x509_issuer * x2)408edacedfSDaniel Fojt x509_issuer_cmp(struct x509_issuer *x1, struct x509_issuer *x2)
418edacedfSDaniel Fojt {
428edacedfSDaniel Fojt 	int pcmp;
438edacedfSDaniel Fojt 	if ((pcmp = memcmp(x1->parent_md, x2->parent_md, EVP_MAX_MD_SIZE)) != 0)
448edacedfSDaniel Fojt 		return pcmp;
458edacedfSDaniel Fojt 	return memcmp(x1->child_md, x2->child_md, EVP_MAX_MD_SIZE);
468edacedfSDaniel Fojt }
478edacedfSDaniel Fojt 
488edacedfSDaniel Fojt static size_t x509_issuer_cache_count;
498edacedfSDaniel Fojt static size_t x509_issuer_cache_max = X509_ISSUER_CACHE_MAX;
508edacedfSDaniel Fojt static RB_HEAD(x509_issuer_tree, x509_issuer) x509_issuer_cache =
518edacedfSDaniel Fojt     RB_INITIALIZER(&x509_issuer_cache);
528edacedfSDaniel Fojt static TAILQ_HEAD(lruqueue, x509_issuer) x509_issuer_lru =
538edacedfSDaniel Fojt     TAILQ_HEAD_INITIALIZER(x509_issuer_lru);
548edacedfSDaniel Fojt static pthread_mutex_t x509_issuer_tree_mutex = PTHREAD_MUTEX_INITIALIZER;
558edacedfSDaniel Fojt 
568edacedfSDaniel Fojt RB_PROTOTYPE(x509_issuer_tree, x509_issuer, entry, x509_issuer_cmp);
578edacedfSDaniel Fojt RB_GENERATE(x509_issuer_tree, x509_issuer, entry, x509_issuer_cmp);
588edacedfSDaniel Fojt 
598edacedfSDaniel Fojt /*
608edacedfSDaniel Fojt  * Set the maximum number of cached entries. On additions to the cache
618edacedfSDaniel Fojt  * the least recently used entries will be discarded so that the cache
628edacedfSDaniel Fojt  * stays under the maximum number of entries.  Setting a maximum of 0
638edacedfSDaniel Fojt  * disables the cache.
648edacedfSDaniel Fojt  */
658edacedfSDaniel Fojt int
x509_issuer_cache_set_max(size_t max)668edacedfSDaniel Fojt x509_issuer_cache_set_max(size_t max)
678edacedfSDaniel Fojt {
688edacedfSDaniel Fojt 	if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0)
698edacedfSDaniel Fojt 		return 0;
708edacedfSDaniel Fojt 	x509_issuer_cache_max = max;
718edacedfSDaniel Fojt 	(void) pthread_mutex_unlock(&x509_issuer_tree_mutex);
728edacedfSDaniel Fojt 
738edacedfSDaniel Fojt 	return 1;
748edacedfSDaniel Fojt }
758edacedfSDaniel Fojt 
768edacedfSDaniel Fojt /*
77*de0e0e4dSAntonio Huete Jimenez  * Free the oldest entry in the issuer cache. Returns 1
78*de0e0e4dSAntonio Huete Jimenez  * if an entry was successfuly freed, 0 otherwise. Must
79*de0e0e4dSAntonio Huete Jimenez  * be called with x509_issuer_tree_mutex held.
80*de0e0e4dSAntonio Huete Jimenez  */
81*de0e0e4dSAntonio Huete Jimenez void
x509_issuer_cache_free_oldest()82*de0e0e4dSAntonio Huete Jimenez x509_issuer_cache_free_oldest()
83*de0e0e4dSAntonio Huete Jimenez {
84*de0e0e4dSAntonio Huete Jimenez 	struct x509_issuer *old;
85*de0e0e4dSAntonio Huete Jimenez 
86*de0e0e4dSAntonio Huete Jimenez 	if (x509_issuer_cache_count == 0)
87*de0e0e4dSAntonio Huete Jimenez 		return;
88*de0e0e4dSAntonio Huete Jimenez 	old = TAILQ_LAST(&x509_issuer_lru, lruqueue);
89*de0e0e4dSAntonio Huete Jimenez 	TAILQ_REMOVE(&x509_issuer_lru, old, queue);
90*de0e0e4dSAntonio Huete Jimenez 	RB_REMOVE(x509_issuer_tree, &x509_issuer_cache, old);
91*de0e0e4dSAntonio Huete Jimenez 	free(old->parent_md);
92*de0e0e4dSAntonio Huete Jimenez 	free(old->child_md);
93*de0e0e4dSAntonio Huete Jimenez 	free(old);
94*de0e0e4dSAntonio Huete Jimenez 	x509_issuer_cache_count--;
95*de0e0e4dSAntonio Huete Jimenez }
96*de0e0e4dSAntonio Huete Jimenez 
97*de0e0e4dSAntonio Huete Jimenez /*
98*de0e0e4dSAntonio Huete Jimenez  * Free the entire issuer cache, discarding all entries.
99*de0e0e4dSAntonio Huete Jimenez  */
100*de0e0e4dSAntonio Huete Jimenez void
x509_issuer_cache_free()101*de0e0e4dSAntonio Huete Jimenez x509_issuer_cache_free()
102*de0e0e4dSAntonio Huete Jimenez {
103*de0e0e4dSAntonio Huete Jimenez 	if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0)
104*de0e0e4dSAntonio Huete Jimenez 		return;
105*de0e0e4dSAntonio Huete Jimenez 	while (x509_issuer_cache_count > 0)
106*de0e0e4dSAntonio Huete Jimenez 		x509_issuer_cache_free_oldest();
107*de0e0e4dSAntonio Huete Jimenez 	(void) pthread_mutex_unlock(&x509_issuer_tree_mutex);
108*de0e0e4dSAntonio Huete Jimenez }
109*de0e0e4dSAntonio Huete Jimenez 
110*de0e0e4dSAntonio Huete Jimenez /*
1118edacedfSDaniel Fojt  * Find a previous result of checking if parent signed child
1128edacedfSDaniel Fojt  *
1138edacedfSDaniel Fojt  * Returns:
1148edacedfSDaniel Fojt  *	-1 : No entry exists in the cache. signature must be checked.
1158edacedfSDaniel Fojt  *	0 : The signature of parent signing child is invalid.
1168edacedfSDaniel Fojt  *	1 : The signature of parent signing child is valid.
1178edacedfSDaniel Fojt  */
1188edacedfSDaniel Fojt int
x509_issuer_cache_find(unsigned char * parent_md,unsigned char * child_md)1198edacedfSDaniel Fojt x509_issuer_cache_find(unsigned char *parent_md, unsigned char *child_md)
1208edacedfSDaniel Fojt {
1218edacedfSDaniel Fojt 	struct x509_issuer candidate, *found;
1228edacedfSDaniel Fojt 	int ret = -1;
1238edacedfSDaniel Fojt 
1248edacedfSDaniel Fojt 	memset(&candidate, 0, sizeof(candidate));
1258edacedfSDaniel Fojt 	candidate.parent_md = parent_md;
1268edacedfSDaniel Fojt 	candidate.child_md = child_md;
1278edacedfSDaniel Fojt 
1288edacedfSDaniel Fojt 	if (x509_issuer_cache_max == 0)
1298edacedfSDaniel Fojt 		return -1;
1308edacedfSDaniel Fojt 
1318edacedfSDaniel Fojt 	if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0)
1328edacedfSDaniel Fojt 		return -1;
1338edacedfSDaniel Fojt 	if ((found = RB_FIND(x509_issuer_tree, &x509_issuer_cache,
1348edacedfSDaniel Fojt 	    &candidate)) != NULL) {
1358edacedfSDaniel Fojt 		TAILQ_REMOVE(&x509_issuer_lru, found, queue);
1368edacedfSDaniel Fojt 		TAILQ_INSERT_HEAD(&x509_issuer_lru, found, queue);
1378edacedfSDaniel Fojt 		ret = found->valid;
1388edacedfSDaniel Fojt 	}
1398edacedfSDaniel Fojt 	(void) pthread_mutex_unlock(&x509_issuer_tree_mutex);
1408edacedfSDaniel Fojt 
1418edacedfSDaniel Fojt 	return ret;
1428edacedfSDaniel Fojt }
1438edacedfSDaniel Fojt 
1448edacedfSDaniel Fojt /*
1458edacedfSDaniel Fojt  * Attempt to add a validation result to the cache.
1468edacedfSDaniel Fojt  *
1478edacedfSDaniel Fojt  * valid must be:
1488edacedfSDaniel Fojt  *	0: The signature of parent signing child is invalid.
1498edacedfSDaniel Fojt  *	1: The signature of parent signing child is valid.
1508edacedfSDaniel Fojt  *
1518edacedfSDaniel Fojt  * Previously added entries for the same parent and child are *not* replaced.
1528edacedfSDaniel Fojt  */
1538edacedfSDaniel Fojt void
x509_issuer_cache_add(unsigned char * parent_md,unsigned char * child_md,int valid)1548edacedfSDaniel Fojt x509_issuer_cache_add(unsigned char *parent_md, unsigned char *child_md,
1558edacedfSDaniel Fojt     int valid)
1568edacedfSDaniel Fojt {
1578edacedfSDaniel Fojt 	struct x509_issuer *new;
1588edacedfSDaniel Fojt 
1598edacedfSDaniel Fojt 	if (x509_issuer_cache_max == 0)
1608edacedfSDaniel Fojt 		return;
1618edacedfSDaniel Fojt 	if (valid != 0 && valid != 1)
1628edacedfSDaniel Fojt 		return;
1638edacedfSDaniel Fojt 
1648edacedfSDaniel Fojt 	if ((new = calloc(1, sizeof(struct x509_issuer))) == NULL)
1658edacedfSDaniel Fojt 		return;
1668edacedfSDaniel Fojt 	if ((new->parent_md = calloc(1, EVP_MAX_MD_SIZE)) == NULL)
1678edacedfSDaniel Fojt 		goto err;
1688edacedfSDaniel Fojt 	memcpy(new->parent_md, parent_md, EVP_MAX_MD_SIZE);
1698edacedfSDaniel Fojt 	if ((new->child_md = calloc(1, EVP_MAX_MD_SIZE)) == NULL)
1708edacedfSDaniel Fojt 		goto err;
1718edacedfSDaniel Fojt 	memcpy(new->child_md, child_md, EVP_MAX_MD_SIZE);
1728edacedfSDaniel Fojt 
1738edacedfSDaniel Fojt 	new->valid = valid;
1748edacedfSDaniel Fojt 
1758edacedfSDaniel Fojt 	if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0)
1768edacedfSDaniel Fojt 		goto err;
177*de0e0e4dSAntonio Huete Jimenez 	while (x509_issuer_cache_count >= x509_issuer_cache_max)
178*de0e0e4dSAntonio Huete Jimenez 		x509_issuer_cache_free_oldest();
1798edacedfSDaniel Fojt 	if (RB_INSERT(x509_issuer_tree, &x509_issuer_cache, new) == NULL) {
1808edacedfSDaniel Fojt 		TAILQ_INSERT_HEAD(&x509_issuer_lru, new, queue);
1818edacedfSDaniel Fojt 		x509_issuer_cache_count++;
1828edacedfSDaniel Fojt 		new = NULL;
1838edacedfSDaniel Fojt 	}
1848edacedfSDaniel Fojt 	(void) pthread_mutex_unlock(&x509_issuer_tree_mutex);
185*de0e0e4dSAntonio Huete Jimenez 
186*de0e0e4dSAntonio Huete Jimenez  err:
1878edacedfSDaniel Fojt 	if (new != NULL) {
1888edacedfSDaniel Fojt 		free(new->parent_md);
1898edacedfSDaniel Fojt 		free(new->child_md);
1908edacedfSDaniel Fojt 	}
1918edacedfSDaniel Fojt 	free(new);
1928edacedfSDaniel Fojt 	return;
1938edacedfSDaniel Fojt }
194