1 /* $OpenBSD: x509_issuer_cache.c,v 1.7 2023/12/30 18:26:13 tb 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
x509_issuer_cmp(struct x509_issuer * x1,struct x509_issuer * x2)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
x509_issuer_cache_set_max(size_t max)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 * Free the oldest entry in the issuer cache. Returns 1
78 * if an entry was successfully freed, 0 otherwise. Must
79 * be called with x509_issuer_tree_mutex held.
80 */
81 static void
x509_issuer_cache_free_oldest(void)82 x509_issuer_cache_free_oldest(void)
83 {
84 struct x509_issuer *old;
85
86 if (x509_issuer_cache_count == 0)
87 return;
88 old = TAILQ_LAST(&x509_issuer_lru, lruqueue);
89 TAILQ_REMOVE(&x509_issuer_lru, old, queue);
90 RB_REMOVE(x509_issuer_tree, &x509_issuer_cache, old);
91 free(old->parent_md);
92 free(old->child_md);
93 free(old);
94 x509_issuer_cache_count--;
95 }
96
97 /*
98 * Free the entire issuer cache, discarding all entries.
99 */
100 void
x509_issuer_cache_free(void)101 x509_issuer_cache_free(void)
102 {
103 if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0)
104 return;
105 while (x509_issuer_cache_count > 0)
106 x509_issuer_cache_free_oldest();
107 (void) pthread_mutex_unlock(&x509_issuer_tree_mutex);
108 }
109
110 /*
111 * Find a previous result of checking if parent signed child
112 *
113 * Returns:
114 * -1 : No entry exists in the cache. signature must be checked.
115 * 0 : The signature of parent signing child is invalid.
116 * 1 : The signature of parent signing child is valid.
117 */
118 int
x509_issuer_cache_find(unsigned char * parent_md,unsigned char * child_md)119 x509_issuer_cache_find(unsigned char *parent_md, unsigned char *child_md)
120 {
121 struct x509_issuer candidate, *found;
122 int ret = -1;
123
124 memset(&candidate, 0, sizeof(candidate));
125 candidate.parent_md = parent_md;
126 candidate.child_md = child_md;
127
128 if (x509_issuer_cache_max == 0)
129 return -1;
130
131 if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0)
132 return -1;
133 if ((found = RB_FIND(x509_issuer_tree, &x509_issuer_cache,
134 &candidate)) != NULL) {
135 TAILQ_REMOVE(&x509_issuer_lru, found, queue);
136 TAILQ_INSERT_HEAD(&x509_issuer_lru, found, queue);
137 ret = found->valid;
138 }
139 (void) pthread_mutex_unlock(&x509_issuer_tree_mutex);
140
141 return ret;
142 }
143
144 /*
145 * Attempt to add a validation result to the cache.
146 *
147 * valid must be:
148 * 0: The signature of parent signing child is invalid.
149 * 1: The signature of parent signing child is valid.
150 *
151 * Previously added entries for the same parent and child are *not* replaced.
152 */
153 void
x509_issuer_cache_add(unsigned char * parent_md,unsigned char * child_md,int valid)154 x509_issuer_cache_add(unsigned char *parent_md, unsigned char *child_md,
155 int valid)
156 {
157 struct x509_issuer *new;
158
159 if (x509_issuer_cache_max == 0)
160 return;
161 if (valid != 0 && valid != 1)
162 return;
163
164 if ((new = calloc(1, sizeof(struct x509_issuer))) == NULL)
165 return;
166 if ((new->parent_md = calloc(1, EVP_MAX_MD_SIZE)) == NULL)
167 goto err;
168 memcpy(new->parent_md, parent_md, EVP_MAX_MD_SIZE);
169 if ((new->child_md = calloc(1, EVP_MAX_MD_SIZE)) == NULL)
170 goto err;
171 memcpy(new->child_md, child_md, EVP_MAX_MD_SIZE);
172
173 new->valid = valid;
174
175 if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0)
176 goto err;
177 while (x509_issuer_cache_count >= x509_issuer_cache_max)
178 x509_issuer_cache_free_oldest();
179 if (RB_INSERT(x509_issuer_tree, &x509_issuer_cache, new) == NULL) {
180 TAILQ_INSERT_HEAD(&x509_issuer_lru, new, queue);
181 x509_issuer_cache_count++;
182 new = NULL;
183 }
184 (void) pthread_mutex_unlock(&x509_issuer_tree_mutex);
185
186 err:
187 if (new != NULL) {
188 free(new->parent_md);
189 free(new->child_md);
190 }
191 free(new);
192 return;
193 }
194