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