Annotation of embedaddon/strongswan/src/libstrongswan/credentials/sets/cert_cache.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2008 Martin Willi
3: * Copyright (C) 2016 Andreas Steffen
4: * HSR Hochschule fuer Technik Rapperswil
5: *
6: * This program is free software; you can redistribute it and/or modify it
7: * under the terms of the GNU General Public License as published by the
8: * Free Software Foundation; either version 2 of the License, or (at your
9: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10: *
11: * This program is distributed in the hope that it will be useful, but
12: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14: * for more details.
15: */
16:
17: #include "cert_cache.h"
18:
19: #include <time.h>
20:
21: #include <library.h>
22: #include <threading/rwlock.h>
23: #include <collections/linked_list.h>
24: #include <credentials/certificates/crl.h>
25:
26: /** cache size, a power of 2 for fast modulo */
27: #define CACHE_SIZE 32
28:
29: /** attempts to acquire a cache lock */
30: #define REPLACE_TRIES 5
31:
32: typedef struct private_cert_cache_t private_cert_cache_t;
33: typedef struct relation_t relation_t;
34:
35: /**
36: * A trusted relation between subject and issuer
37: */
38: struct relation_t {
39:
40: /**
41: * subject of this relation
42: */
43: certificate_t *subject;
44:
45: /**
46: * issuer of this relation
47: */
48: certificate_t *issuer;
49:
50: /**
51: * Signature scheme and parameters used to sign this relation
52: */
53: signature_params_t *scheme;
54:
55: /**
56: * Cache hits
57: */
58: u_int hits;
59:
60: /**
61: * Lock for this relation
62: */
63: rwlock_t *lock;
64: };
65:
66: /**
67: * private data of cert_cache
68: */
69: struct private_cert_cache_t {
70:
71: /**
72: * public functions
73: */
74: cert_cache_t public;
75:
76: /**
77: * array of trusted subject-issuer relations
78: */
79: relation_t relations[CACHE_SIZE];
80: };
81:
82: /**
83: * Cache relation in a free slot/replace an other
84: */
85: static void cache(private_cert_cache_t *this,
86: certificate_t *subject, certificate_t *issuer,
87: signature_params_t *scheme)
88: {
89: relation_t *rel;
90: int i, offset, try;
91: u_int total_hits = 0;
92:
93: /* cache a CRL by replacing a previous CRL cache entry if present */
94: if (subject->get_type(subject) == CERT_X509_CRL)
95: {
96: crl_t *crl, *cached_crl;
97:
98: /* cache a delta CRL ? */
99: crl = (crl_t*)subject;
100:
101: for (i = 0; i < CACHE_SIZE; i++)
102: {
103: rel = &this->relations[i];
104:
105: if (rel->subject &&
106: rel->subject->get_type(rel->subject) == CERT_X509_CRL &&
107: rel->lock->try_write_lock(rel->lock))
108: {
109: /* double-check having lock */
110: if (rel->subject->get_type(rel->subject) == CERT_X509_CRL &&
111: rel->issuer->equals(rel->issuer, issuer))
112: {
113: cached_crl = (crl_t*)rel->subject;
114:
115: if (cached_crl->is_delta_crl(cached_crl, NULL) ==
116: crl->is_delta_crl(crl, NULL) &&
117: crl_is_newer(crl, cached_crl))
118: {
119: rel->subject->destroy(rel->subject);
120: rel->subject = subject->get_ref(subject);
121: signature_params_destroy(rel->scheme);
122: rel->scheme = signature_params_clone(scheme);
123: return rel->lock->unlock(rel->lock);
124: }
125: }
126: rel->lock->unlock(rel->lock);
127: }
128: }
129: }
130:
131: /* check for a unused relation slot first */
132: for (i = 0; i < CACHE_SIZE; i++)
133: {
134: rel = &this->relations[i];
135:
136: if (!rel->subject && rel->lock->try_write_lock(rel->lock))
137: {
138: /* double-check having lock */
139: if (!rel->subject)
140: {
141: rel->subject = subject->get_ref(subject);
142: rel->issuer = issuer->get_ref(issuer);
143: rel->scheme = signature_params_clone(scheme);
144: return rel->lock->unlock(rel->lock);
145: }
146: rel->lock->unlock(rel->lock);
147: }
148: total_hits += rel->hits;
149: }
150: /* run several attempts to replace a random slot, never block. */
151: for (try = 0; try < REPLACE_TRIES; try++)
152: {
153: /* replace a random relation */
154: offset = random();
155: for (i = 0; i < CACHE_SIZE; i++)
156: {
157: rel = &this->relations[(i + offset) % CACHE_SIZE];
158:
159: if (rel->hits > total_hits / CACHE_SIZE)
160: { /* skip often used slots */
161: continue;
162: }
163: if (rel->lock->try_write_lock(rel->lock))
164: {
165: if (rel->subject)
166: {
167: rel->subject->destroy(rel->subject);
168: rel->issuer->destroy(rel->issuer);
169: signature_params_destroy(rel->scheme);
170: }
171: rel->subject = subject->get_ref(subject);
172: rel->issuer = issuer->get_ref(issuer);
173: rel->scheme = signature_params_clone(scheme);
174: rel->hits = 0;
175: return rel->lock->unlock(rel->lock);
176: }
177: }
178: /* give other threads a chance to release locks */
179: sched_yield();
180: }
181: }
182:
183: METHOD(cert_cache_t, issued_by, bool,
184: private_cert_cache_t *this, certificate_t *subject, certificate_t *issuer,
185: signature_params_t **schemep)
186: {
187: certificate_t *cached_issuer = NULL;
188: relation_t *found = NULL, *current;
189: signature_params_t *scheme;
190: int i;
191:
192: for (i = 0; i < CACHE_SIZE; i++)
193: {
194: current = &this->relations[i];
195:
196: current->lock->read_lock(current->lock);
197: if (current->subject)
198: {
199: if (issuer->equals(issuer, current->issuer))
200: {
201: if (subject->equals(subject, current->subject))
202: {
203: current->hits++;
204: found = current;
205: if (schemep)
206: {
207: *schemep = signature_params_clone(current->scheme);
208: }
209: }
210: else if (!cached_issuer)
211: {
212: cached_issuer = current->issuer->get_ref(current->issuer);
213: }
214: }
215: }
216: current->lock->unlock(current->lock);
217: if (found)
218: {
219: DESTROY_IF(cached_issuer);
220: return TRUE;
221: }
222: }
223: if (subject->issued_by(subject, issuer, &scheme))
224: {
225: cache(this, subject, cached_issuer ?: issuer, scheme);
226: if (schemep)
227: {
228: *schemep = scheme;
229: }
230: else
231: {
232: signature_params_destroy(scheme);
233: }
234: DESTROY_IF(cached_issuer);
235: return TRUE;
236: }
237: DESTROY_IF(cached_issuer);
238: return FALSE;
239: }
240:
241: /**
242: * certificate enumerator implementation
243: */
244: typedef struct {
245: /** implements enumerator_t interface */
246: enumerator_t public;
247: /** type of requested certificate */
248: certificate_type_t cert;
249: /** type of requested key */
250: key_type_t key;
251: /** ID to get a cert for */
252: identification_t *id;
253: /** cache */
254: relation_t *relations;
255: /** current position in array cache */
256: int index;
257: /** currently locked relation */
258: int locked;
259: } cert_enumerator_t;
260:
261: METHOD(enumerator_t, cert_enumerate, bool,
262: cert_enumerator_t *this, va_list args)
263: {
264: public_key_t *public;
265: relation_t *rel;
266: certificate_t **out;
267:
268: VA_ARGS_VGET(args, out);
269:
270: if (this->locked >= 0)
271: {
272: rel = &this->relations[this->locked];
273: rel->lock->unlock(rel->lock);
274: this->locked = -1;
275: }
276:
277: while (++this->index < CACHE_SIZE)
278: {
279: rel = &this->relations[this->index];
280: rel->lock->read_lock(rel->lock);
281: this->locked = this->index;
282: if (rel->subject)
283: {
284: /* CRL lookup is done using issuer/authkeyidentifier */
285: if (this->key == KEY_ANY && this->id &&
286: (this->cert == CERT_ANY || this->cert == CERT_X509_CRL) &&
287: rel->subject->get_type(rel->subject) == CERT_X509_CRL &&
288: rel->subject->has_issuer(rel->subject, this->id))
289: {
290: *out = rel->subject;
291: return TRUE;
292: }
293: if ((this->cert == CERT_ANY ||
294: rel->subject->get_type(rel->subject) == this->cert) &&
295: (!this->id || rel->subject->has_subject(rel->subject, this->id)))
296: {
297: if (this->key == KEY_ANY)
298: {
299: *out = rel->subject;
300: return TRUE;
301: }
302: public = rel->subject->get_public_key(rel->subject);
303: if (public)
304: {
305: if (public->get_type(public) == this->key)
306: {
307: public->destroy(public);
308: *out = rel->subject;
309: return TRUE;
310: }
311: public->destroy(public);
312: }
313: }
314: }
315: this->locked = -1;
316: rel->lock->unlock(rel->lock);
317: }
318: return FALSE;
319: }
320:
321: METHOD(enumerator_t, cert_enumerator_destroy, void,
322: cert_enumerator_t *this)
323: {
324: relation_t *rel;
325:
326: if (this->locked >= 0)
327: {
328: rel = &this->relations[this->locked];
329: rel->lock->unlock(rel->lock);
330: }
331: free(this);
332: }
333:
334: METHOD(credential_set_t, create_enumerator, enumerator_t*,
335: private_cert_cache_t *this, certificate_type_t cert, key_type_t key,
336: identification_t *id, bool trusted)
337: {
338: cert_enumerator_t *enumerator;
339:
340: if (trusted)
341: {
342: return NULL;
343: }
344: INIT(enumerator,
345: .public = {
346: .enumerate = enumerator_enumerate_default,
347: .venumerate = _cert_enumerate,
348: .destroy = _cert_enumerator_destroy,
349: },
350: .cert = cert,
351: .key = key,
352: .id = id,
353: .relations = this->relations,
354: .index = -1,
355: .locked = -1,
356: );
357: return &enumerator->public;
358: }
359:
360: METHOD(cert_cache_t, flush, void,
361: private_cert_cache_t *this, certificate_type_t type)
362: {
363: relation_t *rel;
364: int i;
365:
366: for (i = 0; i < CACHE_SIZE; i++)
367: {
368: rel = &this->relations[i];
369: if (!rel->subject)
370: {
371: continue;
372: }
373: /* check with cheap read lock first */
374: if (type != CERT_ANY)
375: {
376: rel->lock->read_lock(rel->lock);
377: if (!rel->subject || type != rel->subject->get_type(rel->subject))
378: {
379: rel->lock->unlock(rel->lock);
380: continue;
381: }
382: rel->lock->unlock(rel->lock);
383: }
384: /* double check in write lock */
385: rel->lock->write_lock(rel->lock);
386: if (rel->subject)
387: {
388: if (type == CERT_ANY || type == rel->subject->get_type(rel->subject))
389: {
390: rel->subject->destroy(rel->subject);
391: rel->issuer->destroy(rel->issuer);
392: signature_params_destroy(rel->scheme);
393: rel->subject = NULL;
394: rel->issuer = NULL;
395: rel->scheme = NULL;
396: rel->hits = 0;
397: }
398: }
399: rel->lock->unlock(rel->lock);
400: }
401: }
402:
403: METHOD(cert_cache_t, destroy, void,
404: private_cert_cache_t *this)
405: {
406: relation_t *rel;
407: int i;
408:
409: for (i = 0; i < CACHE_SIZE; i++)
410: {
411: rel = &this->relations[i];
412: if (rel->subject)
413: {
414: rel->subject->destroy(rel->subject);
415: rel->issuer->destroy(rel->issuer);
416: signature_params_destroy(rel->scheme);
417: }
418: rel->lock->destroy(rel->lock);
419: }
420: free(this);
421: }
422:
423: /*
424: * see header file
425: */
426: cert_cache_t *cert_cache_create()
427: {
428: private_cert_cache_t *this;
429: int i;
430:
431: INIT(this,
432: .public = {
433: .set = {
434: .create_cert_enumerator = _create_enumerator,
435: .create_private_enumerator = (void*)return_null,
436: .create_shared_enumerator = (void*)return_null,
437: .create_cdp_enumerator = (void*)return_null,
438: .cache_cert = (void*)nop,
439: },
440: .issued_by = _issued_by,
441: .flush = _flush,
442: .destroy = _destroy,
443: },
444: );
445:
446: for (i = 0; i < CACHE_SIZE; i++)
447: {
448: this->relations[i].subject = NULL;
449: this->relations[i].issuer = NULL;
450: this->relations[i].scheme = NULL;
451: this->relations[i].hits = 0;
452: this->relations[i].lock = rwlock_create(RWLOCK_TYPE_DEFAULT);
453: }
454:
455: return &this->public;
456: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>