Return to cert_cache.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libstrongswan / credentials / sets |
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: }