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 { 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 * 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 { 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 101 x509_issuer_cache_free() 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 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 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