Annotation of embedaddon/strongswan/src/libstrongswan/credentials/sets/cert_cache.c, revision 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>