Coverage Report

Created: 2022-08-24 06:30

/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
}