1 /* $OpenBSD: x509_issuer_cache.c,v 1.1 2020/09/11 14:30:51 beck Exp $ */
2 /*
3  * Copyright (c) 2020 Bob Beck <beck@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /* x509_issuer_cache */
19 
20 /*
21  * The issuer cache is a cache of parent and child x509 certificate
22  * hashes with a signature validation result.
23  *
24  * Entries should only be added to the cache with a validation result
25  * from checking the public key math that "parent" signed "child".
26  *
27  * Finding an entry in the cache gets us the result of a previously
28  * performed validation of the signature of "parent" signing for the
29  * validity of "child". It allows us to skip doing the public key math
30  * when validating a certificate chain. It does not allow us to skip
31  * any other steps of validation (times, names, key usage, etc.)
32  */
33 
34 #include <pthread.h>
35 #include <string.h>
36 
37 #include "x509_issuer_cache.h"
38 
39 static int
40 x509_issuer_cmp(struct x509_issuer *x1, struct x509_issuer *x2)
41 {
42 	int pcmp;
43 	if ((pcmp = memcmp(x1->parent_md, x2->parent_md, EVP_MAX_MD_SIZE)) != 0)
44 		return pcmp;
45 	return memcmp(x1->child_md, x2->child_md, EVP_MAX_MD_SIZE);
46 }
47 
48 static size_t x509_issuer_cache_count;
49 static size_t x509_issuer_cache_max = X509_ISSUER_CACHE_MAX;
50 static RB_HEAD(x509_issuer_tree, x509_issuer) x509_issuer_cache =
51     RB_INITIALIZER(&x509_issuer_cache);
52 static TAILQ_HEAD(lruqueue, x509_issuer) x509_issuer_lru =
53     TAILQ_HEAD_INITIALIZER(x509_issuer_lru);
54 static pthread_mutex_t x509_issuer_tree_mutex = PTHREAD_MUTEX_INITIALIZER;
55 
56 RB_PROTOTYPE(x509_issuer_tree, x509_issuer, entry, x509_issuer_cmp);
57 RB_GENERATE(x509_issuer_tree, x509_issuer, entry, x509_issuer_cmp);
58 
59 /*
60  * Set the maximum number of cached entries. On additions to the cache
61  * the least recently used entries will be discarded so that the cache
62  * stays under the maximum number of entries.  Setting a maximum of 0
63  * disables the cache.
64  */
65 int
66 x509_issuer_cache_set_max(size_t max)
67 {
68 	if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0)
69 		return 0;
70 	x509_issuer_cache_max = max;
71 	(void) pthread_mutex_unlock(&x509_issuer_tree_mutex);
72 
73 	return 1;
74 }
75 
76 /*
77  * Find a previous result of checking if parent signed child
78  *
79  * Returns:
80  * 	-1 : No entry exists in the cache. signature must be checked.
81  * 	0 : The signature of parent signing child is invalid.
82  * 	1 : The signature of parent signing child is valid.
83  */
84 int
85 x509_issuer_cache_find(unsigned char *parent_md, unsigned char *child_md)
86 {
87 	struct x509_issuer candidate, *found;
88 	int ret = -1;
89 
90 	memset(&candidate, 0, sizeof(candidate));
91 	candidate.parent_md = parent_md;
92 	candidate.child_md = child_md;
93 
94 	if (x509_issuer_cache_max == 0)
95 		return -1;
96 
97 	if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0)
98 		return -1;
99 	if ((found = RB_FIND(x509_issuer_tree, &x509_issuer_cache,
100 	    &candidate)) != NULL) {
101 	 	TAILQ_REMOVE(&x509_issuer_lru, found, queue);
102 		TAILQ_INSERT_HEAD(&x509_issuer_lru, found, queue);
103 		ret = found->valid;
104 	}
105 	(void) pthread_mutex_unlock(&x509_issuer_tree_mutex);
106 
107 	return ret;
108 }
109 
110 /*
111  * Attempt to add a validation result to the cache.
112  *
113  * valid must be:
114  * 	0: The signature of parent signing child is invalid.
115  *	1: The signature of parent signing child is valid.
116  *
117  * Previously added entries for the same parent and child are *not* replaced.
118  */
119 void
120 x509_issuer_cache_add(unsigned char *parent_md, unsigned char *child_md,
121     int valid)
122 {
123 	struct x509_issuer *new;
124 
125 	if (x509_issuer_cache_max == 0)
126 		return;
127 	if (valid != 0 && valid != 1)
128 		return;
129 
130 	if ((new = calloc(1, sizeof(struct x509_issuer))) == NULL)
131 		return;
132 	if ((new->parent_md = calloc(1, EVP_MAX_MD_SIZE)) == NULL)
133 		goto err;
134 	memcpy(new->parent_md, parent_md, EVP_MAX_MD_SIZE);
135 	if ((new->child_md = calloc(1, EVP_MAX_MD_SIZE)) == NULL)
136 		goto err;
137 	memcpy(new->child_md, child_md, EVP_MAX_MD_SIZE);
138 
139 	new->valid = valid;
140 
141 	if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0)
142 		goto err;
143 	while (x509_issuer_cache_count >= x509_issuer_cache_max) {
144 		struct x509_issuer *old;
145 		if ((old = TAILQ_LAST(&x509_issuer_lru, lruqueue)) == NULL)
146 			goto err;
147 		TAILQ_REMOVE(&x509_issuer_lru, old, queue);
148 		RB_REMOVE(x509_issuer_tree, &x509_issuer_cache, old);
149 		free(old->parent_md);
150 		free(old->child_md);
151 		free(old);
152 		x509_issuer_cache_count--;
153 	}
154 	if (RB_INSERT(x509_issuer_tree, &x509_issuer_cache, new) == NULL) {
155 		TAILQ_INSERT_HEAD(&x509_issuer_lru, new, queue);
156 		x509_issuer_cache_count++;
157 		new = NULL;
158 	}
159  err:
160 	(void) pthread_mutex_unlock(&x509_issuer_tree_mutex);
161 	if (new != NULL) {
162 		free(new->parent_md);
163 		free(new->child_md);
164 	}
165 	free(new);
166 	return;
167 }
168