18
18
#include "cert_cache.h"
22
23
#include <daemon.h>
23
24
#include <utils/mutex.h>
24
25
#include <utils/linked_list.h>
27
/** cache size, a power of 2 for fast modulo */
30
/** attempts to acquire a cache lock */
31
#define REPLACE_TRIES 5
28
33
typedef struct private_cert_cache_t private_cert_cache_t;
29
34
typedef struct relation_t relation_t;
37
* A trusted relation between subject and issuer
42
* subject of this relation
44
certificate_t *subject;
47
* issuer of this relation
49
certificate_t *issuer;
57
* Lock for this relation
32
63
* private data of cert_cache
34
65
struct private_cert_cache_t {
39
70
cert_cache_t public;
42
* list of trusted subject-issuer relations, as relation_t
44
linked_list_t *relations;
47
* do we have an active enumerator
49
refcount_t enumerating;
52
* have we increased the cache without a check_cache?
57
* read-write lock to sets list
63
* A trusted relation between subject and issuer
66
/** subject of this relation */
67
certificate_t *subject;
68
/** issuer of this relation */
69
certificate_t *issuer;
70
/** time of last use */
75
* destroy a relation_t structure
77
static void relation_destroy(relation_t *this)
79
this->subject->destroy(this->subject);
80
this->issuer->destroy(this->issuer);
85
* check the cache for oversize
87
static void check_cache(private_cert_cache_t *this)
89
if (this->enumerating)
73
* array of trusted subject-issuer relations
75
relation_t relations[CACHE_SIZE];
79
* Cache relation in a free slot/replace an other
81
static void cache(private_cert_cache_t *this,
82
certificate_t *subject, certificate_t *issuer)
88
/* check for a unused relation slot first */
89
for (i = 0; i < CACHE_SIZE; i++)
91
this->check_required = TRUE;
91
rel = &this->relations[i];
93
if (!rel->subject && rel->lock->try_write_lock(rel->lock))
95
/* double-check having lock */
98
rel->subject = subject->get_ref(subject);
99
rel->issuer = issuer->get_ref(issuer);
100
return rel->lock->unlock(rel->lock);
102
rel->lock->unlock(rel->lock);
104
total_hits += rel->hits;
93
else if (this->lock->try_write_lock(this->lock))
94
{ /* never blocks, only done if lock is available */
95
while (this->relations->get_count(this->relations) > CACHE_SIZE)
106
/* run several attempts to replace a random slot, never block. */
107
for (try = 0; try < REPLACE_TRIES; try++)
109
/* replace a random relation */
111
for (i = 0; i < CACHE_SIZE; i++)
97
relation_t *oldest = NULL, *current;
98
enumerator_t *enumerator;
113
rel = &this->relations[(i + offset) % CACHE_SIZE];
100
enumerator = this->relations->create_enumerator(this->relations);
101
while (enumerator->enumerate(enumerator, ¤t))
115
if (rel->hits > total_hits / CACHE_SIZE)
116
{ /* skip often used slots */
119
if (rel->lock->try_write_lock(rel->lock))
103
if (oldest == NULL || oldest->last_use <= current->last_use)
123
rel->subject->destroy(rel->subject);
124
rel->issuer->destroy(rel->issuer);
126
rel->subject = subject->get_ref(subject);
127
rel->issuer = issuer->get_ref(issuer);
129
return rel->lock->unlock(rel->lock);
108
enumerator->destroy(enumerator);
109
this->relations->remove(this->relations, oldest, NULL);
110
relation_destroy(oldest);
112
this->check_required = FALSE;
113
this->lock->unlock(this->lock);
132
/* give other threads a chance to release locks */
121
141
certificate_t *subject, certificate_t *issuer)
123
143
relation_t *found = NULL, *current;
124
enumerator_t *enumerator;
127
this->lock->read_lock(this->lock);
128
enumerator = this->relations->create_enumerator(this->relations);
129
while (enumerator->enumerate(enumerator, ¤t))
146
for (i = 0; i < CACHE_SIZE; i++)
133
/* check for equal certificates */
134
if (subject->equals(subject, current->subject))
137
subject = current->subject;
139
if (issuer->equals(issuer, current->issuer))
141
issuer = current->issuer;
142
/* if both certs match, we already have a relation */
148
current = &this->relations[i];
150
current->lock->read_lock(current->lock);
151
if (current->subject)
153
/* check for equal issuer */
154
if (issuer->equals(issuer, current->issuer))
145
current->last_use = time(NULL);
156
/* reuse issuer instance in cache() */
157
issuer = current->issuer;
158
if (subject->equals(subject, current->subject))
160
/* write hit counter is not locked, but not critical */
166
current->lock->unlock(current->lock);
151
enumerator->destroy(enumerator);
152
this->lock->unlock(this->lock);
172
/* no cache hit, check and cache signature */
173
if (subject->issued_by(subject, issuer))
175
cache(this, subject, issuer);
157
/* no cache hit, check signature */
158
if (!subject->issued_by(subject, issuer))
162
/* cache if good, respect cache limit */
163
found = malloc_thing(relation_t);
164
found->subject = subject->get_ref(subject);
165
found->issuer = issuer->get_ref(issuer);
166
found->last_use = time(NULL);
167
/* insert should be ok without lock */
168
this->relations->insert_last(this->relations, found);
174
* data associated to a cert enumeration
182
* certificate enumerator implemenation
185
/** implements enumerator_t interface */
177
187
/** type of requested certificate */
178
188
certificate_type_t cert;
179
189
/** type of requested key */
181
/** ID to get a cert from */
191
/** ID to get a cert for */
182
192
identification_t *id;
183
/** reverse pointer to cache */
184
private_cert_cache_t *this;
194
relation_t *relations;
195
/** current position in array cache */
197
/** currently locked relation */
188
202
* filter function for certs enumerator
190
static bool certs_filter(cert_data_t *data, relation_t **in, certificate_t **out)
204
static bool cert_enumerate(cert_enumerator_t *this, certificate_t **out)
192
206
public_key_t *public;
195
cert = (*in)->subject;
196
if (data->key == KEY_ANY && data->id &&
197
(data->cert == CERT_ANY || data->cert == CERT_X509_CRL) &&
198
cert->get_type(cert) == CERT_X509_CRL)
199
{ /* CRL lookup is done using issuer/authkeyidentifier */
200
if (cert->has_issuer(cert, data->id))
209
if (this->locked >= 0)
211
rel = &this->relations[this->locked];
212
rel->lock->unlock(rel->lock);
207
if ((data->cert == CERT_ANY || cert->get_type(cert) == data->cert) &&
208
(!data->id || cert->has_subject(cert, data->id)))
216
while (++this->index < CACHE_SIZE)
210
if (data->key == KEY_ANY)
215
public = cert->get_public_key(cert);
218
if (public->get_type(public) == data->key)
218
rel = &this->relations[this->index];
219
rel->lock->read_lock(rel->lock);
220
this->locked = this->index;
223
/* CRL lookup is done using issuer/authkeyidentifier */
224
if (this->key == KEY_ANY && this->id &&
225
(this->cert == CERT_ANY || this->cert == CERT_X509_CRL) &&
226
rel->subject->get_type(rel->subject) == CERT_X509_CRL &&
227
rel->subject->has_issuer(rel->subject, this->id))
220
public->destroy(public);
224
public->destroy(public);
232
if ((this->cert == CERT_ANY ||
233
rel->subject->get_type(rel->subject) == this->cert) &&
234
(!this->id || rel->subject->has_subject(rel->subject, this->id)))
236
if (this->key == KEY_ANY)
241
public = rel->subject->get_public_key(rel->subject);
244
if (public->get_type(public) == this->key)
246
public->destroy(public);
250
public->destroy(public);
255
rel->lock->unlock(rel->lock);
248
279
certificate_type_t cert, key_type_t key,
249
280
identification_t *id, bool trusted)
282
cert_enumerator_t *enumerator;
257
data = malloc_thing(cert_data_t);
288
enumerator = malloc_thing(cert_enumerator_t);
289
enumerator->public.enumerate = (void*)cert_enumerate;
290
enumerator->public.destroy = (void*)cert_enumerator_destroy;
291
enumerator->cert = cert;
292
enumerator->key = key;
294
enumerator->relations = this->relations;
295
enumerator->index = -1;
296
enumerator->locked = -1;
263
this->lock->read_lock(this->lock);
264
ref_get(&this->enumerating);
265
return enumerator_create_filter(
266
this->relations->create_enumerator(this->relations),
267
(void*)certs_filter, data, (void*)certs_destroy);
298
return &enumerator->public;
273
304
static void flush(private_cert_cache_t *this, certificate_type_t type)
275
enumerator_t *enumerator;
276
relation_t *relation;
278
this->lock->write_lock(this->lock);
279
enumerator = this->relations->create_enumerator(this->relations);
280
while (enumerator->enumerate(enumerator, &relation))
309
for (i = 0; i < CACHE_SIZE; i++)
282
if (type == CERT_ANY ||
283
type == relation->subject->get_type(relation->subject))
285
this->relations->remove_at(this->relations, enumerator);
286
relation_destroy(relation);
311
rel = &this->relations[i];
316
/* check with cheap read lock first */
317
if (type != CERT_ANY)
319
rel->lock->read_lock(rel->lock);
320
if (!rel->subject || type != rel->subject->get_type(rel->subject))
322
rel->lock->unlock(rel->lock);
325
rel->lock->unlock(rel->lock);
327
/* double check in write lock */
328
rel->lock->write_lock(rel->lock);
331
if (type == CERT_ANY || type == rel->subject->get_type(rel->subject))
333
rel->subject->destroy(rel->subject);
334
rel->issuer->destroy(rel->issuer);
340
rel->lock->unlock(rel->lock);
289
enumerator->destroy(enumerator);
290
this->lock->unlock(this->lock);