/src/libressl/crypto/x509/x509_issuer_cache.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* $OpenBSD: x509_issuer_cache.c,v 1.3 2022/06/27 14:23:40 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 | 0 | { |
42 | 0 | int pcmp; |
43 | 0 | if ((pcmp = memcmp(x1->parent_md, x2->parent_md, EVP_MAX_MD_SIZE)) != 0) |
44 | 0 | return pcmp; |
45 | 0 | return memcmp(x1->child_md, x2->child_md, EVP_MAX_MD_SIZE); |
46 | 0 | } |
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 | 0 | { |
68 | 0 | if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0) |
69 | 0 | return 0; |
70 | 0 | x509_issuer_cache_max = max; |
71 | 0 | (void) pthread_mutex_unlock(&x509_issuer_tree_mutex); |
72 | |
|
73 | 0 | return 1; |
74 | 0 | } |
75 | | |
76 | | /* |
77 | | * Free the oldest entry in the issuer cache. Returns 1 |
78 | | * if an entry was successfuly freed, 0 otherwise. Must |
79 | | * be called with x509_issuer_tree_mutex held. |
80 | | */ |
81 | | void |
82 | | x509_issuer_cache_free_oldest() |
83 | 0 | { |
84 | 0 | struct x509_issuer *old; |
85 | |
|
86 | 0 | if (x509_issuer_cache_count == 0) |
87 | 0 | return; |
88 | 0 | old = TAILQ_LAST(&x509_issuer_lru, lruqueue); |
89 | 0 | TAILQ_REMOVE(&x509_issuer_lru, old, queue); |
90 | 0 | RB_REMOVE(x509_issuer_tree, &x509_issuer_cache, old); |
91 | 0 | free(old->parent_md); |
92 | 0 | free(old->child_md); |
93 | 0 | free(old); |
94 | 0 | x509_issuer_cache_count--; |
95 | 0 | } |
96 | | |
97 | | /* |
98 | | * Free the entire issuer cache, discarding all entries. |
99 | | */ |
100 | | void |
101 | | x509_issuer_cache_free() |
102 | 0 | { |
103 | 0 | if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0) |
104 | 0 | return; |
105 | 0 | while (x509_issuer_cache_count > 0) |
106 | 0 | x509_issuer_cache_free_oldest(); |
107 | 0 | (void) pthread_mutex_unlock(&x509_issuer_tree_mutex); |
108 | 0 | } |
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 |
119 | | x509_issuer_cache_find(unsigned char *parent_md, unsigned char *child_md) |
120 | 0 | { |
121 | 0 | struct x509_issuer candidate, *found; |
122 | 0 | int ret = -1; |
123 | |
|
124 | 0 | memset(&candidate, 0, sizeof(candidate)); |
125 | 0 | candidate.parent_md = parent_md; |
126 | 0 | candidate.child_md = child_md; |
127 | |
|
128 | 0 | if (x509_issuer_cache_max == 0) |
129 | 0 | return -1; |
130 | | |
131 | 0 | if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0) |
132 | 0 | return -1; |
133 | 0 | if ((found = RB_FIND(x509_issuer_tree, &x509_issuer_cache, |
134 | 0 | &candidate)) != NULL) { |
135 | 0 | TAILQ_REMOVE(&x509_issuer_lru, found, queue); |
136 | 0 | TAILQ_INSERT_HEAD(&x509_issuer_lru, found, queue); |
137 | 0 | ret = found->valid; |
138 | 0 | } |
139 | 0 | (void) pthread_mutex_unlock(&x509_issuer_tree_mutex); |
140 | |
|
141 | 0 | return ret; |
142 | 0 | } |
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 |
154 | | x509_issuer_cache_add(unsigned char *parent_md, unsigned char *child_md, |
155 | | int valid) |
156 | 0 | { |
157 | 0 | struct x509_issuer *new; |
158 | |
|
159 | 0 | if (x509_issuer_cache_max == 0) |
160 | 0 | return; |
161 | 0 | if (valid != 0 && valid != 1) |
162 | 0 | return; |
163 | | |
164 | 0 | if ((new = calloc(1, sizeof(struct x509_issuer))) == NULL) |
165 | 0 | return; |
166 | 0 | if ((new->parent_md = calloc(1, EVP_MAX_MD_SIZE)) == NULL) |
167 | 0 | goto err; |
168 | 0 | memcpy(new->parent_md, parent_md, EVP_MAX_MD_SIZE); |
169 | 0 | if ((new->child_md = calloc(1, EVP_MAX_MD_SIZE)) == NULL) |
170 | 0 | goto err; |
171 | 0 | memcpy(new->child_md, child_md, EVP_MAX_MD_SIZE); |
172 | |
|
173 | 0 | new->valid = valid; |
174 | |
|
175 | 0 | if (pthread_mutex_lock(&x509_issuer_tree_mutex) != 0) |
176 | 0 | goto err; |
177 | 0 | while (x509_issuer_cache_count >= x509_issuer_cache_max) |
178 | 0 | x509_issuer_cache_free_oldest(); |
179 | 0 | if (RB_INSERT(x509_issuer_tree, &x509_issuer_cache, new) == NULL) { |
180 | 0 | TAILQ_INSERT_HEAD(&x509_issuer_lru, new, queue); |
181 | 0 | x509_issuer_cache_count++; |
182 | 0 | new = NULL; |
183 | 0 | } |
184 | 0 | (void) pthread_mutex_unlock(&x509_issuer_tree_mutex); |
185 | |
|
186 | 0 | err: |
187 | 0 | if (new != NULL) { |
188 | 0 | free(new->parent_md); |
189 | 0 | free(new->child_md); |
190 | 0 | } |
191 | 0 | free(new); |
192 | 0 | return; |
193 | 0 | } |