Annotation of embedaddon/strongswan/src/libstrongswan/credentials/credential_manager.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2015 Tobias Brunner
! 3: * Copyright (C) 2007 Martin Willi
! 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 "credential_manager.h"
! 18:
! 19: #include <library.h>
! 20: #include <utils/debug.h>
! 21: #include <threading/thread_value.h>
! 22: #include <threading/mutex.h>
! 23: #include <threading/rwlock.h>
! 24: #include <collections/linked_list.h>
! 25: #include <credentials/sets/cert_cache.h>
! 26: #include <credentials/sets/auth_cfg_wrapper.h>
! 27: #include <credentials/certificates/x509.h>
! 28:
! 29: /**
! 30: * Maximum length of a certificate trust chain
! 31: */
! 32: #define MAX_TRUST_PATH_LEN 7
! 33:
! 34: typedef struct private_credential_manager_t private_credential_manager_t;
! 35:
! 36: /**
! 37: * private data of credential_manager
! 38: */
! 39: struct private_credential_manager_t {
! 40:
! 41: /**
! 42: * public functions
! 43: */
! 44: credential_manager_t public;
! 45:
! 46: /**
! 47: * list of credential sets
! 48: */
! 49: linked_list_t *sets;
! 50:
! 51: /**
! 52: * thread local set of credentials, linked_list_t with credential_set_t's
! 53: */
! 54: thread_value_t *local_sets;
! 55:
! 56: /**
! 57: * Exclusive local sets, linked_list_t with credential_set_t
! 58: */
! 59: thread_value_t *exclusive_local_sets;
! 60:
! 61: /**
! 62: * trust relationship and certificate cache
! 63: */
! 64: cert_cache_t *cache;
! 65:
! 66: /**
! 67: * certificates queued for persistent caching
! 68: */
! 69: linked_list_t *cache_queue;
! 70:
! 71: /**
! 72: * list of certificate validators, cert_validator_t
! 73: */
! 74: linked_list_t *validators;
! 75:
! 76: /**
! 77: * read-write lock to sets list
! 78: */
! 79: rwlock_t *lock;
! 80:
! 81: /**
! 82: * mutex for cache queue
! 83: */
! 84: mutex_t *queue_mutex;
! 85:
! 86: /**
! 87: * Registered hook to call on validation errors
! 88: */
! 89: credential_hook_t hook;
! 90:
! 91: /**
! 92: * Registered data to pass to hook
! 93: */
! 94: void *hook_data;
! 95: };
! 96:
! 97: /** data to pass to create_private_enumerator */
! 98: typedef struct {
! 99: private_credential_manager_t *this;
! 100: key_type_t type;
! 101: identification_t* keyid;
! 102: } private_data_t;
! 103:
! 104: /** data to pass to create_cert_enumerator */
! 105: typedef struct {
! 106: private_credential_manager_t *this;
! 107: certificate_type_t cert;
! 108: key_type_t key;
! 109: identification_t *id;
! 110: bool trusted;
! 111: } cert_data_t;
! 112:
! 113: /** data to pass to create_cdp_enumerator */
! 114: typedef struct {
! 115: private_credential_manager_t *this;
! 116: certificate_type_t type;
! 117: identification_t *id;
! 118: } cdp_data_t;
! 119:
! 120: /** data to pass to create_shared_enumerator */
! 121: typedef struct {
! 122: private_credential_manager_t *this;
! 123: shared_key_type_t type;
! 124: identification_t *me;
! 125: identification_t *other;
! 126: } shared_data_t;
! 127:
! 128: /** enumerator over local and global sets */
! 129: typedef struct {
! 130: /** implements enumerator_t */
! 131: enumerator_t public;
! 132: /** enumerator over global sets */
! 133: enumerator_t *global;
! 134: /** enumerator over local sets */
! 135: enumerator_t *local;
! 136: /** enumerator over exclusive local sets */
! 137: enumerator_t *exclusive;
! 138: } sets_enumerator_t;
! 139:
! 140: METHOD(credential_manager_t, set_hook, void,
! 141: private_credential_manager_t *this, credential_hook_t hook, void *data)
! 142: {
! 143: this->hook = hook;
! 144: this->hook_data = data;
! 145: }
! 146:
! 147: METHOD(credential_manager_t, call_hook, void,
! 148: private_credential_manager_t *this, credential_hook_type_t type,
! 149: certificate_t *cert)
! 150: {
! 151: if (this->hook)
! 152: {
! 153: this->hook(this->hook_data, type, cert);
! 154: }
! 155: }
! 156:
! 157: METHOD(enumerator_t, sets_enumerate, bool,
! 158: sets_enumerator_t *this, va_list args)
! 159: {
! 160: credential_set_t **set;
! 161:
! 162: VA_ARGS_VGET(args, set);
! 163:
! 164: if (this->exclusive)
! 165: {
! 166: if (this->exclusive->enumerate(this->exclusive, set))
! 167: { /* only enumerate last added */
! 168: this->exclusive->destroy(this->exclusive);
! 169: this->exclusive = NULL;
! 170: return TRUE;
! 171: }
! 172: }
! 173: if (this->local)
! 174: {
! 175: if (this->local->enumerate(this->local, set))
! 176: {
! 177: return TRUE;
! 178: }
! 179: /* end of local sets, look for global */
! 180: this->local->destroy(this->local);
! 181: this->local = NULL;
! 182: }
! 183: if (this->global)
! 184: {
! 185: return this->global->enumerate(this->global, set);
! 186: }
! 187: return FALSE;
! 188: }
! 189:
! 190: METHOD(enumerator_t, sets_destroy, void,
! 191: sets_enumerator_t *this)
! 192: {
! 193: DESTROY_IF(this->global);
! 194: DESTROY_IF(this->local);
! 195: DESTROY_IF(this->exclusive);
! 196: free(this);
! 197: }
! 198:
! 199: /**
! 200: * create an enumerator over both, global and local sets
! 201: */
! 202: static enumerator_t *create_sets_enumerator(private_credential_manager_t *this)
! 203: {
! 204: sets_enumerator_t *enumerator;
! 205: linked_list_t *list;
! 206:
! 207: INIT(enumerator,
! 208: .public = {
! 209: .enumerate = enumerator_enumerate_default,
! 210: .venumerate = _sets_enumerate,
! 211: .destroy = _sets_destroy,
! 212: },
! 213: );
! 214:
! 215: list = this->exclusive_local_sets->get(this->exclusive_local_sets);
! 216: if (list && list->get_count(list))
! 217: {
! 218: enumerator->exclusive = list->create_enumerator(list);
! 219: }
! 220: else
! 221: {
! 222: enumerator->global = this->sets->create_enumerator(this->sets);
! 223: list = this->local_sets->get(this->local_sets);
! 224: if (list)
! 225: {
! 226: enumerator->local = list->create_enumerator(list);
! 227: }
! 228: }
! 229: return &enumerator->public;
! 230: }
! 231:
! 232: /**
! 233: * cleanup function for cert data
! 234: */
! 235: static void destroy_cert_data(cert_data_t *data)
! 236: {
! 237: data->this->lock->unlock(data->this->lock);
! 238: free(data);
! 239: }
! 240:
! 241: /**
! 242: * enumerator constructor for certificates
! 243: */
! 244: static enumerator_t *create_cert(credential_set_t *set, cert_data_t *data)
! 245: {
! 246: return set->create_cert_enumerator(set, data->cert, data->key,
! 247: data->id, data->trusted);
! 248: }
! 249:
! 250: METHOD(credential_manager_t, create_cert_enumerator, enumerator_t*,
! 251: private_credential_manager_t *this, certificate_type_t certificate,
! 252: key_type_t key, identification_t *id, bool trusted)
! 253: {
! 254: cert_data_t *data = malloc_thing(cert_data_t);
! 255: data->this = this;
! 256: data->cert = certificate;
! 257: data->key = key;
! 258: data->id = id;
! 259: data->trusted = trusted;
! 260:
! 261: this->lock->read_lock(this->lock);
! 262: return enumerator_create_nested(create_sets_enumerator(this),
! 263: (void*)create_cert, data,
! 264: (void*)destroy_cert_data);
! 265: }
! 266:
! 267: METHOD(credential_manager_t, get_cert, certificate_t*,
! 268: private_credential_manager_t *this, certificate_type_t cert, key_type_t key,
! 269: identification_t *id, bool trusted)
! 270: {
! 271: certificate_t *current, *found = NULL;
! 272: enumerator_t *enumerator;
! 273:
! 274: enumerator = create_cert_enumerator(this, cert, key, id, trusted);
! 275: if (enumerator->enumerate(enumerator, ¤t))
! 276: {
! 277: /* TODO: best match? order by keyid, subject, subjectAltName */
! 278: found = current->get_ref(current);
! 279: }
! 280: enumerator->destroy(enumerator);
! 281: return found;
! 282: }
! 283:
! 284:
! 285: /**
! 286: * cleanup function for cdp data
! 287: */
! 288: static void destroy_cdp_data(cdp_data_t *data)
! 289: {
! 290: data->this->lock->unlock(data->this->lock);
! 291: free(data);
! 292: }
! 293:
! 294: /**
! 295: * enumerator constructor for CDPs
! 296: */
! 297: static enumerator_t *create_cdp(credential_set_t *set, cdp_data_t *data)
! 298: {
! 299: return set->create_cdp_enumerator(set, data->type, data->id);
! 300: }
! 301:
! 302: METHOD(credential_manager_t, create_cdp_enumerator, enumerator_t*,
! 303: private_credential_manager_t *this, certificate_type_t type,
! 304: identification_t *id)
! 305: {
! 306: cdp_data_t *data;
! 307:
! 308: INIT(data,
! 309: .this = this,
! 310: .type = type,
! 311: .id = id,
! 312: );
! 313: this->lock->read_lock(this->lock);
! 314: return enumerator_create_nested(create_sets_enumerator(this),
! 315: (void*)create_cdp, data,
! 316: (void*)destroy_cdp_data);
! 317: }
! 318:
! 319: /**
! 320: * cleanup function for private data
! 321: */
! 322: static void destroy_private_data(private_data_t *data)
! 323: {
! 324: data->this->lock->unlock(data->this->lock);
! 325: free(data);
! 326: }
! 327:
! 328: /**
! 329: * enumerator constructor for private keys
! 330: */
! 331: static enumerator_t *create_private(credential_set_t *set, private_data_t *data)
! 332: {
! 333: return set->create_private_enumerator(set, data->type, data->keyid);
! 334: }
! 335:
! 336: /**
! 337: * Create an enumerator over private keys
! 338: */
! 339: static enumerator_t *create_private_enumerator(
! 340: private_credential_manager_t *this, key_type_t key, identification_t *keyid)
! 341: {
! 342: private_data_t *data;
! 343:
! 344: INIT(data,
! 345: .this = this,
! 346: .type = key,
! 347: .keyid = keyid,
! 348: );
! 349: this->lock->read_lock(this->lock);
! 350: return enumerator_create_nested(create_sets_enumerator(this),
! 351: (void*)create_private, data,
! 352: (void*)destroy_private_data);
! 353: }
! 354:
! 355: /**
! 356: * Look up a private key by its key identifier
! 357: */
! 358: static private_key_t* get_private_by_keyid(private_credential_manager_t *this,
! 359: key_type_t key, identification_t *keyid)
! 360: {
! 361: private_key_t *found = NULL;
! 362: enumerator_t *enumerator;
! 363:
! 364: enumerator = create_private_enumerator(this, key, keyid);
! 365: if (enumerator->enumerate(enumerator, &found))
! 366: {
! 367: found->get_ref(found);
! 368: }
! 369: enumerator->destroy(enumerator);
! 370: return found;
! 371: }
! 372:
! 373: /**
! 374: * cleanup function for shared data
! 375: */
! 376: static void destroy_shared_data(shared_data_t *data)
! 377: {
! 378: data->this->lock->unlock(data->this->lock);
! 379: free(data);
! 380: }
! 381:
! 382: /**
! 383: * enumerator constructor for shared keys
! 384: */
! 385: static enumerator_t *create_shared(credential_set_t *set, shared_data_t *data)
! 386: {
! 387: return set->create_shared_enumerator(set, data->type, data->me, data->other);
! 388: }
! 389:
! 390: METHOD(credential_manager_t, create_shared_enumerator, enumerator_t*,
! 391: private_credential_manager_t *this, shared_key_type_t type,
! 392: identification_t *me, identification_t *other)
! 393: {
! 394: shared_data_t *data;
! 395:
! 396: INIT(data,
! 397: .this = this,
! 398: .type = type,
! 399: .me = me,
! 400: .other = other,
! 401: );
! 402: this->lock->read_lock(this->lock);
! 403: return enumerator_create_nested(create_sets_enumerator(this),
! 404: (void*)create_shared, data,
! 405: (void*)destroy_shared_data);
! 406: }
! 407:
! 408: METHOD(credential_manager_t, get_shared, shared_key_t*,
! 409: private_credential_manager_t *this, shared_key_type_t type,
! 410: identification_t *me, identification_t *other)
! 411: {
! 412: shared_key_t *current, *found = NULL;
! 413: id_match_t best_me = ID_MATCH_NONE, best_other = ID_MATCH_NONE;
! 414: id_match_t match_me, match_other;
! 415: enumerator_t *enumerator;
! 416:
! 417: enumerator = create_shared_enumerator(this, type, me, other);
! 418: while (enumerator->enumerate(enumerator, ¤t, &match_me, &match_other))
! 419: {
! 420: if (match_other > best_other ||
! 421: (match_other == best_other && match_me > best_me))
! 422: {
! 423: DESTROY_IF(found);
! 424: found = current->get_ref(current);
! 425: best_me = match_me;
! 426: best_other = match_other;
! 427: }
! 428: if (best_me == ID_MATCH_PERFECT && best_other == ID_MATCH_PERFECT)
! 429: {
! 430: break;
! 431: }
! 432: }
! 433: enumerator->destroy(enumerator);
! 434: return found;
! 435: }
! 436:
! 437: METHOD(credential_manager_t, add_local_set, void,
! 438: private_credential_manager_t *this, credential_set_t *set, bool exclusive)
! 439: {
! 440: linked_list_t *sets;
! 441: thread_value_t *tv;
! 442:
! 443: if (exclusive)
! 444: {
! 445: tv = this->exclusive_local_sets;
! 446: }
! 447: else
! 448: {
! 449: tv = this->local_sets;
! 450: }
! 451: sets = tv->get(tv);
! 452: if (!sets)
! 453: {
! 454: sets = linked_list_create();
! 455: tv->set(tv, sets);
! 456: }
! 457: if (exclusive)
! 458: {
! 459: sets->insert_first(sets, set);
! 460: }
! 461: else
! 462: {
! 463: sets->insert_last(sets, set);
! 464: }
! 465: }
! 466:
! 467: METHOD(credential_manager_t, remove_local_set, void,
! 468: private_credential_manager_t *this, credential_set_t *set)
! 469: {
! 470: linked_list_t *sets;
! 471: thread_value_t *tv;
! 472:
! 473: tv = this->local_sets;
! 474: sets = tv->get(tv);
! 475: if (sets && sets->remove(sets, set, NULL) && sets->get_count(sets) == 0)
! 476: {
! 477: tv->set(tv, NULL);
! 478: sets->destroy(sets);
! 479: }
! 480: tv = this->exclusive_local_sets;
! 481: sets = tv->get(tv);
! 482: if (sets && sets->remove(sets, set, NULL) && sets->get_count(sets) == 0)
! 483: {
! 484: tv->set(tv, NULL);
! 485: sets->destroy(sets);
! 486: }
! 487: }
! 488:
! 489: METHOD(credential_manager_t, issued_by, bool,
! 490: private_credential_manager_t *this, certificate_t *subject,
! 491: certificate_t *issuer, signature_params_t **scheme)
! 492: {
! 493: if (this->cache)
! 494: {
! 495: return this->cache->issued_by(this->cache, subject, issuer, scheme);
! 496: }
! 497: return subject->issued_by(subject, issuer, scheme);
! 498: }
! 499:
! 500: METHOD(credential_manager_t, cache_cert, void,
! 501: private_credential_manager_t *this, certificate_t *cert)
! 502: {
! 503: credential_set_t *set;
! 504: enumerator_t *enumerator;
! 505:
! 506: if (this->lock->try_write_lock(this->lock))
! 507: {
! 508: enumerator = this->sets->create_enumerator(this->sets);
! 509: while (enumerator->enumerate(enumerator, &set))
! 510: {
! 511: set->cache_cert(set, cert);
! 512: }
! 513: enumerator->destroy(enumerator);
! 514: this->lock->unlock(this->lock);
! 515: }
! 516: else
! 517: { /* we can't cache now as other threads are active, queue for later */
! 518: this->queue_mutex->lock(this->queue_mutex);
! 519: this->cache_queue->insert_last(this->cache_queue, cert->get_ref(cert));
! 520: this->queue_mutex->unlock(this->queue_mutex);
! 521: }
! 522: }
! 523:
! 524: /**
! 525: * Try to cache certificates queued for caching
! 526: */
! 527: static void cache_queue(private_credential_manager_t *this)
! 528: {
! 529: credential_set_t *set;
! 530: certificate_t *cert;
! 531: enumerator_t *enumerator;
! 532:
! 533: this->queue_mutex->lock(this->queue_mutex);
! 534: if (this->cache_queue->get_count(this->cache_queue) > 0 &&
! 535: this->lock->try_write_lock(this->lock))
! 536: {
! 537: while (this->cache_queue->remove_last(this->cache_queue,
! 538: (void**)&cert) == SUCCESS)
! 539: {
! 540: enumerator = this->sets->create_enumerator(this->sets);
! 541: while (enumerator->enumerate(enumerator, &set))
! 542: {
! 543: set->cache_cert(set, cert);
! 544: }
! 545: enumerator->destroy(enumerator);
! 546: cert->destroy(cert);
! 547: }
! 548: this->lock->unlock(this->lock);
! 549: }
! 550: this->queue_mutex->unlock(this->queue_mutex);
! 551: }
! 552:
! 553: /**
! 554: * Use validators to check the lifetime of certificates
! 555: */
! 556: static bool check_lifetime(private_credential_manager_t *this,
! 557: certificate_t *cert, char *label,
! 558: int pathlen, bool trusted, auth_cfg_t *auth)
! 559: {
! 560: time_t not_before, not_after;
! 561: cert_validator_t *validator;
! 562: enumerator_t *enumerator;
! 563: status_t status = NEED_MORE;
! 564:
! 565: enumerator = this->validators->create_enumerator(this->validators);
! 566: while (enumerator->enumerate(enumerator, &validator))
! 567: {
! 568: if (!validator->check_lifetime)
! 569: {
! 570: continue;
! 571: }
! 572: status = validator->check_lifetime(validator, cert,
! 573: pathlen, trusted, auth);
! 574: if (status != NEED_MORE)
! 575: {
! 576: break;
! 577: }
! 578: }
! 579: enumerator->destroy(enumerator);
! 580:
! 581: switch (status)
! 582: {
! 583: case NEED_MORE:
! 584: if (!cert->get_validity(cert, NULL, ¬_before, ¬_after))
! 585: {
! 586: DBG1(DBG_CFG, "%s certificate invalid (valid from %T to %T)",
! 587: label, ¬_before, FALSE, ¬_after, FALSE);
! 588: break;
! 589: }
! 590: return TRUE;
! 591: case SUCCESS:
! 592: return TRUE;
! 593: case FAILED:
! 594: default:
! 595: break;
! 596: }
! 597: call_hook(this, CRED_HOOK_EXPIRED, cert);
! 598: return FALSE;
! 599: }
! 600:
! 601: /**
! 602: * check a certificate for its lifetime
! 603: */
! 604: static bool check_certificate(private_credential_manager_t *this,
! 605: certificate_t *subject, certificate_t *issuer, bool online,
! 606: int pathlen, bool trusted, auth_cfg_t *auth)
! 607: {
! 608: cert_validator_t *validator;
! 609: enumerator_t *enumerator;
! 610:
! 611: if (!check_lifetime(this, subject, "subject", pathlen, FALSE, auth) ||
! 612: !check_lifetime(this, issuer, "issuer", pathlen + 1, trusted, auth))
! 613: {
! 614: return FALSE;
! 615: }
! 616:
! 617: enumerator = this->validators->create_enumerator(this->validators);
! 618: while (enumerator->enumerate(enumerator, &validator))
! 619: {
! 620: if (!validator->validate)
! 621: {
! 622: continue;
! 623: }
! 624: if (!validator->validate(validator, subject, issuer,
! 625: online, pathlen, trusted, auth))
! 626: {
! 627: enumerator->destroy(enumerator);
! 628: return FALSE;
! 629: }
! 630: }
! 631: enumerator->destroy(enumerator);
! 632: return TRUE;
! 633: }
! 634:
! 635: /**
! 636: * Get a trusted certificate from a credential set
! 637: */
! 638: static certificate_t *get_pretrusted_cert(private_credential_manager_t *this,
! 639: key_type_t type, identification_t *id)
! 640: {
! 641: certificate_t *subject;
! 642: public_key_t *public;
! 643:
! 644: subject = get_cert(this, CERT_ANY, type, id, TRUE);
! 645: if (!subject)
! 646: {
! 647: return NULL;
! 648: }
! 649: public = subject->get_public_key(subject);
! 650: if (!public)
! 651: {
! 652: subject->destroy(subject);
! 653: return NULL;
! 654: }
! 655: public->destroy(public);
! 656: return subject;
! 657: }
! 658:
! 659: /**
! 660: * Get the issuing certificate of a subject certificate
! 661: */
! 662: static certificate_t *get_issuer_cert(private_credential_manager_t *this,
! 663: certificate_t *subject, bool trusted,
! 664: signature_params_t **scheme)
! 665: {
! 666: enumerator_t *enumerator;
! 667: certificate_t *issuer = NULL, *candidate;
! 668:
! 669: enumerator = create_cert_enumerator(this, subject->get_type(subject), KEY_ANY,
! 670: subject->get_issuer(subject), trusted);
! 671: while (enumerator->enumerate(enumerator, &candidate))
! 672: {
! 673: if (issued_by(this, subject, candidate, scheme))
! 674: {
! 675: issuer = candidate->get_ref(candidate);
! 676: break;
! 677: }
! 678: }
! 679: enumerator->destroy(enumerator);
! 680: return issuer;
! 681: }
! 682:
! 683: /**
! 684: * Get the strength of certificate, add it to auth
! 685: */
! 686: static void get_key_strength(certificate_t *cert, auth_cfg_t *auth)
! 687: {
! 688: uintptr_t strength;
! 689: public_key_t *key;
! 690: key_type_t type;
! 691:
! 692: key = cert->get_public_key(cert);
! 693: if (key)
! 694: {
! 695: type = key->get_type(key);
! 696: strength = key->get_keysize(key);
! 697: DBG2(DBG_CFG, " certificate \"%Y\" key: %d bit %N",
! 698: cert->get_subject(cert), strength, key_type_names, type);
! 699: switch (type)
! 700: {
! 701: case KEY_RSA:
! 702: auth->add(auth, AUTH_RULE_RSA_STRENGTH, strength);
! 703: break;
! 704: case KEY_ECDSA:
! 705: auth->add(auth, AUTH_RULE_ECDSA_STRENGTH, strength);
! 706: break;
! 707: case KEY_BLISS:
! 708: auth->add(auth, AUTH_RULE_BLISS_STRENGTH, strength);
! 709: break;
! 710: default:
! 711: break;
! 712: }
! 713: key->destroy(key);
! 714: }
! 715: }
! 716:
! 717: /**
! 718: * try to verify the trust chain of subject, return TRUE if trusted
! 719: */
! 720: static bool verify_trust_chain(private_credential_manager_t *this,
! 721: certificate_t *subject, auth_cfg_t *result,
! 722: bool trusted, bool online)
! 723: {
! 724: certificate_t *current, *issuer;
! 725: auth_cfg_t *auth;
! 726: signature_params_t *scheme;
! 727: int pathlen;
! 728:
! 729: auth = auth_cfg_create();
! 730: get_key_strength(subject, auth);
! 731: current = subject->get_ref(subject);
! 732: auth->add(auth, AUTH_RULE_SUBJECT_CERT, current->get_ref(current));
! 733:
! 734: for (pathlen = 0; pathlen <= MAX_TRUST_PATH_LEN; pathlen++)
! 735: {
! 736: issuer = get_issuer_cert(this, current, TRUE, &scheme);
! 737: if (issuer)
! 738: {
! 739: /* accept only self-signed CAs as trust anchor */
! 740: if (issued_by(this, issuer, issuer, NULL))
! 741: {
! 742: auth->add(auth, AUTH_RULE_CA_CERT, issuer->get_ref(issuer));
! 743: DBG1(DBG_CFG, " using trusted ca certificate \"%Y\"",
! 744: issuer->get_subject(issuer));
! 745: trusted = TRUE;
! 746: }
! 747: else
! 748: {
! 749: auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer));
! 750: DBG1(DBG_CFG, " using trusted intermediate ca certificate "
! 751: "\"%Y\"", issuer->get_subject(issuer));
! 752: }
! 753: auth->add(auth, AUTH_RULE_SIGNATURE_SCHEME, scheme);
! 754: }
! 755: else
! 756: {
! 757: issuer = get_issuer_cert(this, current, FALSE, &scheme);
! 758: if (issuer)
! 759: {
! 760: if (current->equals(current, issuer))
! 761: {
! 762: DBG1(DBG_CFG, " self-signed certificate \"%Y\" is not "
! 763: "trusted", current->get_subject(current));
! 764: issuer->destroy(issuer);
! 765: call_hook(this, CRED_HOOK_UNTRUSTED_ROOT, current);
! 766: break;
! 767: }
! 768: auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer));
! 769: DBG1(DBG_CFG, " using untrusted intermediate certificate "
! 770: "\"%Y\"", issuer->get_subject(issuer));
! 771: auth->add(auth, AUTH_RULE_SIGNATURE_SCHEME, scheme);
! 772: }
! 773: else
! 774: {
! 775: DBG1(DBG_CFG, "no issuer certificate found for \"%Y\"",
! 776: current->get_subject(current));
! 777: DBG1(DBG_CFG, " issuer is \"%Y\"",
! 778: current->get_issuer(current));
! 779: call_hook(this, CRED_HOOK_NO_ISSUER, current);
! 780: break;
! 781: }
! 782: }
! 783: if (!check_certificate(this, current, issuer, online,
! 784: pathlen, trusted, auth))
! 785: {
! 786: trusted = FALSE;
! 787: issuer->destroy(issuer);
! 788: break;
! 789: }
! 790: if (issuer)
! 791: {
! 792: get_key_strength(issuer, auth);
! 793: }
! 794: current->destroy(current);
! 795: current = issuer;
! 796: if (trusted)
! 797: {
! 798: DBG1(DBG_CFG, " reached self-signed root ca with a "
! 799: "path length of %d", pathlen);
! 800: break;
! 801: }
! 802: }
! 803: current->destroy(current);
! 804: if (pathlen > MAX_TRUST_PATH_LEN)
! 805: {
! 806: DBG1(DBG_CFG, "maximum path length of %d exceeded", MAX_TRUST_PATH_LEN);
! 807: call_hook(this, CRED_HOOK_EXCEEDED_PATH_LEN, subject);
! 808: }
! 809: if (trusted)
! 810: {
! 811: result->merge(result, auth, FALSE);
! 812: }
! 813: auth->destroy(auth);
! 814: return trusted;
! 815: }
! 816:
! 817: CALLBACK(cert_equals, bool,
! 818: certificate_t *a, va_list args)
! 819: {
! 820: certificate_t *b;
! 821:
! 822: VA_ARGS_VGET(args, b);
! 823: return a->equals(a, b);
! 824: }
! 825:
! 826: /**
! 827: * enumerator for trusted certificates
! 828: */
! 829: typedef struct {
! 830: /** implements enumerator_t interface */
! 831: enumerator_t public;
! 832: /** enumerator over candidate peer certificates */
! 833: enumerator_t *candidates;
! 834: /** reference to the credential_manager */
! 835: private_credential_manager_t *this;
! 836: /** type of the requested key */
! 837: key_type_t type;
! 838: /** identity the requested key belongs to */
! 839: identification_t *id;
! 840: /** TRUE to do CRL/OCSP checking */
! 841: bool online;
! 842: /** pretrusted certificate we have served at first invocation */
! 843: certificate_t *pretrusted;
! 844: /** currently enumerating auth config */
! 845: auth_cfg_t *auth;
! 846: /** list of failed candidates */
! 847: linked_list_t *failed;
! 848: } trusted_enumerator_t;
! 849:
! 850: METHOD(enumerator_t, trusted_enumerate, bool,
! 851: trusted_enumerator_t *this, va_list args)
! 852: {
! 853: certificate_t *current, **cert;
! 854: auth_cfg_t **auth;
! 855:
! 856: VA_ARGS_VGET(args, cert, auth);
! 857:
! 858: DESTROY_IF(this->auth);
! 859: this->auth = auth_cfg_create();
! 860:
! 861: if (!this->candidates)
! 862: {
! 863: /* first invocation, build enumerator for next one */
! 864: this->candidates = create_cert_enumerator(this->this, CERT_ANY,
! 865: this->type, this->id, FALSE);
! 866: /* check if we have a trusted certificate for that peer */
! 867: this->pretrusted = get_pretrusted_cert(this->this, this->type, this->id);
! 868: if (this->pretrusted)
! 869: {
! 870: /* if we find a trusted self signed certificate, we just accept it.
! 871: * However, in order to fulfill authorization rules, we try to build
! 872: * the trust chain if it is not self signed */
! 873: if (issued_by(this->this, this->pretrusted, this->pretrusted, NULL) ||
! 874: verify_trust_chain(this->this, this->pretrusted, this->auth,
! 875: TRUE, this->online))
! 876: {
! 877: DBG1(DBG_CFG, " using trusted certificate \"%Y\"",
! 878: this->pretrusted->get_subject(this->pretrusted));
! 879: *cert = this->pretrusted;
! 880: if (!this->auth->get(this->auth, AUTH_RULE_SUBJECT_CERT))
! 881: { /* add cert to auth info, if not returned by trustchain */
! 882: this->auth->add(this->auth, AUTH_RULE_SUBJECT_CERT,
! 883: this->pretrusted->get_ref(this->pretrusted));
! 884: }
! 885: if (auth)
! 886: {
! 887: *auth = this->auth;
! 888: }
! 889: return TRUE;
! 890: }
! 891: }
! 892: }
! 893: /* try to verify the trust chain for each certificate found */
! 894: while (this->candidates->enumerate(this->candidates, ¤t))
! 895: {
! 896: if (this->pretrusted &&
! 897: this->pretrusted->equals(this->pretrusted, current))
! 898: { /* skip pretrusted certificate we already served */
! 899: continue;
! 900: }
! 901:
! 902: if (this->failed->find_first(this->failed, cert_equals, NULL, current))
! 903: { /* check each candidate only once */
! 904: continue;
! 905: }
! 906:
! 907: DBG1(DBG_CFG, " using certificate \"%Y\"",
! 908: current->get_subject(current));
! 909: if (verify_trust_chain(this->this, current, this->auth, FALSE,
! 910: this->online))
! 911: {
! 912: *cert = current;
! 913: if (auth)
! 914: {
! 915: *auth = this->auth;
! 916: }
! 917: return TRUE;
! 918: }
! 919: this->failed->insert_last(this->failed, current->get_ref(current));
! 920: }
! 921: return FALSE;
! 922: }
! 923:
! 924: METHOD(enumerator_t, trusted_destroy, void,
! 925: trusted_enumerator_t *this)
! 926: {
! 927: DESTROY_IF(this->pretrusted);
! 928: DESTROY_IF(this->auth);
! 929: DESTROY_IF(this->candidates);
! 930: this->failed->destroy_offset(this->failed, offsetof(certificate_t, destroy));
! 931: /* check for delayed certificate cache queue */
! 932: cache_queue(this->this);
! 933: free(this);
! 934: }
! 935:
! 936: METHOD(credential_manager_t, create_trusted_enumerator, enumerator_t*,
! 937: private_credential_manager_t *this, key_type_t type,
! 938: identification_t *id, bool online)
! 939: {
! 940: trusted_enumerator_t *enumerator;
! 941:
! 942: INIT(enumerator,
! 943: .public = {
! 944: .enumerate = enumerator_enumerate_default,
! 945: .venumerate = _trusted_enumerate,
! 946: .destroy = _trusted_destroy,
! 947: },
! 948: .this = this,
! 949: .type = type,
! 950: .id = id,
! 951: .online = online,
! 952: .failed = linked_list_create(),
! 953: );
! 954: return &enumerator->public;
! 955: }
! 956:
! 957: /**
! 958: * enumerator for public keys
! 959: */
! 960: typedef struct {
! 961: /** implements enumerator_t interface */
! 962: enumerator_t public;
! 963: /** enumerator over candidate peer certificates */
! 964: enumerator_t *inner;
! 965: /** reference to the credential_manager */
! 966: private_credential_manager_t *this;
! 967: /** currently enumerating key */
! 968: public_key_t *current;
! 969: /** credset wrapper around auth config */
! 970: auth_cfg_wrapper_t *wrapper;
! 971: } public_enumerator_t;
! 972:
! 973: METHOD(enumerator_t, public_enumerate, bool,
! 974: public_enumerator_t *this, va_list args)
! 975: {
! 976: certificate_t *cert;
! 977: public_key_t **key;
! 978: auth_cfg_t **auth;
! 979:
! 980: VA_ARGS_VGET(args, key, auth);
! 981:
! 982: while (this->inner->enumerate(this->inner, &cert, auth))
! 983: {
! 984: DESTROY_IF(this->current);
! 985: this->current = cert->get_public_key(cert);
! 986: if (this->current)
! 987: {
! 988: *key = this->current;
! 989: return TRUE;
! 990: }
! 991: }
! 992: return FALSE;
! 993: }
! 994:
! 995: METHOD(enumerator_t, public_destroy, void,
! 996: public_enumerator_t *this)
! 997: {
! 998: DESTROY_IF(this->current);
! 999: this->inner->destroy(this->inner);
! 1000: if (this->wrapper)
! 1001: {
! 1002: remove_local_set(this->this, &this->wrapper->set);
! 1003: this->wrapper->destroy(this->wrapper);
! 1004: }
! 1005: this->this->lock->unlock(this->this->lock);
! 1006: /* check for delayed certificate cache queue */
! 1007: cache_queue(this->this);
! 1008: free(this);
! 1009: }
! 1010:
! 1011: METHOD(credential_manager_t, create_public_enumerator, enumerator_t*,
! 1012: private_credential_manager_t *this, key_type_t type, identification_t *id,
! 1013: auth_cfg_t *auth, bool online)
! 1014: {
! 1015: public_enumerator_t *enumerator;
! 1016:
! 1017: INIT(enumerator,
! 1018: .public = {
! 1019: .enumerate = enumerator_enumerate_default,
! 1020: .venumerate = _public_enumerate,
! 1021: .destroy = _public_destroy,
! 1022: },
! 1023: .inner = create_trusted_enumerator(this, type, id, online),
! 1024: .this = this,
! 1025: );
! 1026: if (auth)
! 1027: {
! 1028: enumerator->wrapper = auth_cfg_wrapper_create(auth);
! 1029: add_local_set(this, &enumerator->wrapper->set, FALSE);
! 1030: }
! 1031: this->lock->read_lock(this->lock);
! 1032: return &enumerator->public;
! 1033: }
! 1034:
! 1035: /**
! 1036: * Check if a helper contains a certificate as trust anchor
! 1037: */
! 1038: static bool auth_contains_cacert(auth_cfg_t *auth, certificate_t *cert)
! 1039: {
! 1040: enumerator_t *enumerator;
! 1041: identification_t *value;
! 1042: auth_rule_t type;
! 1043: bool found = FALSE;
! 1044:
! 1045: enumerator = auth->create_enumerator(auth);
! 1046: while (enumerator->enumerate(enumerator, &type, &value))
! 1047: {
! 1048: if (type == AUTH_RULE_CA_CERT &&
! 1049: cert->equals(cert, (certificate_t*)value))
! 1050: {
! 1051: found = TRUE;
! 1052: break;
! 1053: }
! 1054: }
! 1055: enumerator->destroy(enumerator);
! 1056: return found;
! 1057: }
! 1058:
! 1059: /**
! 1060: * build a trustchain from subject up to a trust anchor in trusted
! 1061: */
! 1062: static auth_cfg_t *build_trustchain(private_credential_manager_t *this,
! 1063: certificate_t *subject, auth_cfg_t *auth)
! 1064: {
! 1065: certificate_t *issuer, *current;
! 1066: auth_cfg_t *trustchain;
! 1067: int pathlen = 0;
! 1068: bool has_anchor;
! 1069:
! 1070: trustchain = auth_cfg_create();
! 1071: has_anchor = auth->get(auth, AUTH_RULE_CA_CERT) != NULL;
! 1072: current = subject->get_ref(subject);
! 1073: while (TRUE)
! 1074: {
! 1075: if (auth_contains_cacert(auth, current))
! 1076: {
! 1077: trustchain->add(trustchain, AUTH_RULE_CA_CERT, current);
! 1078: return trustchain;
! 1079: }
! 1080: if (subject == current)
! 1081: {
! 1082: trustchain->add(trustchain, AUTH_RULE_SUBJECT_CERT, current);
! 1083: }
! 1084: else
! 1085: {
! 1086: if (!has_anchor && issued_by(this, current, current, NULL))
! 1087: { /* If no trust anchor specified, accept any CA */
! 1088: trustchain->add(trustchain, AUTH_RULE_CA_CERT, current);
! 1089: return trustchain;
! 1090: }
! 1091: trustchain->add(trustchain, AUTH_RULE_IM_CERT, current);
! 1092: }
! 1093: if (pathlen++ > MAX_TRUST_PATH_LEN)
! 1094: {
! 1095: break;
! 1096: }
! 1097: issuer = get_issuer_cert(this, current, FALSE, NULL);
! 1098: if (!issuer)
! 1099: {
! 1100: if (!has_anchor)
! 1101: { /* If no trust anchor specified, accept incomplete chains */
! 1102: return trustchain;
! 1103: }
! 1104: break;
! 1105: }
! 1106: if (has_anchor && issuer->equals(issuer, current))
! 1107: {
! 1108: issuer->destroy(issuer);
! 1109: break;
! 1110: }
! 1111: current = issuer;
! 1112: }
! 1113: trustchain->destroy(trustchain);
! 1114: return NULL;
! 1115: }
! 1116:
! 1117: /**
! 1118: * find a private key of a given certificate
! 1119: */
! 1120: static private_key_t *get_private_by_cert(private_credential_manager_t *this,
! 1121: certificate_t *cert, key_type_t type)
! 1122: {
! 1123: private_key_t *private = NULL;
! 1124: identification_t *keyid;
! 1125: chunk_t chunk;
! 1126: public_key_t *public;
! 1127:
! 1128: public = cert->get_public_key(cert);
! 1129: if (public)
! 1130: {
! 1131: if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk))
! 1132: {
! 1133: keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
! 1134: private = get_private_by_keyid(this, type, keyid);
! 1135: keyid->destroy(keyid);
! 1136: }
! 1137: public->destroy(public);
! 1138: }
! 1139: return private;
! 1140: }
! 1141:
! 1142: /**
! 1143: * Move the actually used certificate to front, so it gets returned with get()
! 1144: */
! 1145: static void prefer_cert(auth_cfg_t *auth, certificate_t *cert)
! 1146: {
! 1147: enumerator_t *enumerator;
! 1148: auth_rule_t rule;
! 1149: certificate_t *current;
! 1150:
! 1151: enumerator = auth->create_enumerator(auth);
! 1152: while (enumerator->enumerate(enumerator, &rule, ¤t))
! 1153: {
! 1154: if (rule == AUTH_RULE_SUBJECT_CERT)
! 1155: {
! 1156: current->get_ref(current);
! 1157: auth->replace(auth, enumerator, AUTH_RULE_SUBJECT_CERT, cert);
! 1158: cert = current;
! 1159: }
! 1160: }
! 1161: enumerator->destroy(enumerator);
! 1162: auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert);
! 1163: }
! 1164:
! 1165: METHOD(credential_manager_t, get_private, private_key_t*,
! 1166: private_credential_manager_t *this, key_type_t type, identification_t *id,
! 1167: auth_cfg_t *auth)
! 1168: {
! 1169: enumerator_t *enumerator;
! 1170: certificate_t *cert;
! 1171: private_key_t *private = NULL;
! 1172: auth_cfg_t *trustchain;
! 1173: auth_rule_t rule;
! 1174:
! 1175: /* check if this is a lookup by key ID, and do it if so */
! 1176: if (id && id->get_type(id) == ID_KEY_ID)
! 1177: {
! 1178: private = get_private_by_keyid(this, type, id);
! 1179: if (private)
! 1180: {
! 1181: return private;
! 1182: }
! 1183: }
! 1184:
! 1185: if (auth)
! 1186: {
! 1187: /* try to find a trustchain with one of the configured subject certs */
! 1188: enumerator = auth->create_enumerator(auth);
! 1189: while (enumerator->enumerate(enumerator, &rule, &cert))
! 1190: {
! 1191: if (rule == AUTH_RULE_SUBJECT_CERT)
! 1192: {
! 1193: private = get_private_by_cert(this, cert, type);
! 1194: if (private)
! 1195: {
! 1196: trustchain = build_trustchain(this, cert, auth);
! 1197: if (trustchain)
! 1198: {
! 1199: auth->merge(auth, trustchain, FALSE);
! 1200: prefer_cert(auth, cert->get_ref(cert));
! 1201: trustchain->destroy(trustchain);
! 1202: break;
! 1203: }
! 1204: private->destroy(private);
! 1205: private = NULL;
! 1206: }
! 1207: }
! 1208: }
! 1209: enumerator->destroy(enumerator);
! 1210: if (private)
! 1211: {
! 1212: return private;
! 1213: }
! 1214:
! 1215: /* if none yielded a trustchain, enforce the first configured cert */
! 1216: cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
! 1217: if (cert)
! 1218: {
! 1219: private = get_private_by_cert(this, cert, type);
! 1220: if (private)
! 1221: {
! 1222: trustchain = build_trustchain(this, cert, auth);
! 1223: if (trustchain)
! 1224: {
! 1225: auth->merge(auth, trustchain, FALSE);
! 1226: trustchain->destroy(trustchain);
! 1227: }
! 1228: return private;
! 1229: }
! 1230: }
! 1231:
! 1232: /* try to build a trust chain for each certificate found */
! 1233: enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE);
! 1234: while (enumerator->enumerate(enumerator, &cert))
! 1235: {
! 1236: private = get_private_by_cert(this, cert, type);
! 1237: if (private)
! 1238: {
! 1239: trustchain = build_trustchain(this, cert, auth);
! 1240: if (trustchain)
! 1241: {
! 1242: auth->merge(auth, trustchain, FALSE);
! 1243: trustchain->destroy(trustchain);
! 1244: break;
! 1245: }
! 1246: private->destroy(private);
! 1247: private = NULL;
! 1248: }
! 1249: }
! 1250: enumerator->destroy(enumerator);
! 1251: }
! 1252:
! 1253: /* if no valid trustchain was found, fall back to the first usable cert */
! 1254: if (!private)
! 1255: {
! 1256: enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE);
! 1257: while (enumerator->enumerate(enumerator, &cert))
! 1258: {
! 1259: private = get_private_by_cert(this, cert, type);
! 1260: if (private)
! 1261: {
! 1262: if (auth)
! 1263: {
! 1264: auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert->get_ref(cert));
! 1265: }
! 1266: break;
! 1267: }
! 1268: }
! 1269: enumerator->destroy(enumerator);
! 1270: }
! 1271: return private;
! 1272: }
! 1273:
! 1274: METHOD(credential_manager_t, flush_cache, void,
! 1275: private_credential_manager_t *this, certificate_type_t type)
! 1276: {
! 1277: if (this->cache)
! 1278: {
! 1279: this->cache->flush(this->cache, type);
! 1280: }
! 1281: }
! 1282:
! 1283: METHOD(credential_manager_t, add_set, void,
! 1284: private_credential_manager_t *this, credential_set_t *set)
! 1285: {
! 1286: this->lock->write_lock(this->lock);
! 1287: this->sets->insert_last(this->sets, set);
! 1288: this->lock->unlock(this->lock);
! 1289: }
! 1290:
! 1291: METHOD(credential_manager_t, remove_set, void,
! 1292: private_credential_manager_t *this, credential_set_t *set)
! 1293: {
! 1294: this->lock->write_lock(this->lock);
! 1295: this->sets->remove(this->sets, set, NULL);
! 1296: this->lock->unlock(this->lock);
! 1297: }
! 1298:
! 1299: METHOD(credential_manager_t, add_validator, void,
! 1300: private_credential_manager_t *this, cert_validator_t *vdtr)
! 1301: {
! 1302: this->lock->write_lock(this->lock);
! 1303: this->validators->insert_last(this->validators, vdtr);
! 1304: this->lock->unlock(this->lock);
! 1305: }
! 1306:
! 1307: METHOD(credential_manager_t, remove_validator, void,
! 1308: private_credential_manager_t *this, cert_validator_t *vdtr)
! 1309: {
! 1310: this->lock->write_lock(this->lock);
! 1311: this->validators->remove(this->validators, vdtr, NULL);
! 1312: this->lock->unlock(this->lock);
! 1313: }
! 1314:
! 1315: METHOD(credential_manager_t, destroy, void,
! 1316: private_credential_manager_t *this)
! 1317: {
! 1318: cache_queue(this);
! 1319: this->cache_queue->destroy(this->cache_queue);
! 1320: if (this->cache)
! 1321: {
! 1322: this->sets->remove(this->sets, this->cache, NULL);
! 1323: this->cache->destroy(this->cache);
! 1324: }
! 1325: this->sets->destroy(this->sets);
! 1326: this->local_sets->destroy(this->local_sets);
! 1327: this->exclusive_local_sets->destroy(this->exclusive_local_sets);
! 1328: this->validators->destroy(this->validators);
! 1329: this->lock->destroy(this->lock);
! 1330: this->queue_mutex->destroy(this->queue_mutex);
! 1331: free(this);
! 1332: }
! 1333:
! 1334: /*
! 1335: * see header file
! 1336: */
! 1337: credential_manager_t *credential_manager_create()
! 1338: {
! 1339: private_credential_manager_t *this;
! 1340:
! 1341: INIT(this,
! 1342: .public = {
! 1343: .create_cert_enumerator = _create_cert_enumerator,
! 1344: .create_shared_enumerator = _create_shared_enumerator,
! 1345: .create_cdp_enumerator = _create_cdp_enumerator,
! 1346: .get_cert = _get_cert,
! 1347: .get_shared = _get_shared,
! 1348: .get_private = _get_private,
! 1349: .create_trusted_enumerator = _create_trusted_enumerator,
! 1350: .create_public_enumerator = _create_public_enumerator,
! 1351: .flush_cache = _flush_cache,
! 1352: .cache_cert = _cache_cert,
! 1353: .issued_by = _issued_by,
! 1354: .add_set = _add_set,
! 1355: .remove_set = _remove_set,
! 1356: .add_local_set = _add_local_set,
! 1357: .remove_local_set = _remove_local_set,
! 1358: .add_validator = _add_validator,
! 1359: .remove_validator = _remove_validator,
! 1360: .set_hook = _set_hook,
! 1361: .call_hook = _call_hook,
! 1362: .destroy = _destroy,
! 1363: },
! 1364: .sets = linked_list_create(),
! 1365: .validators = linked_list_create(),
! 1366: .cache_queue = linked_list_create(),
! 1367: .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
! 1368: .queue_mutex = mutex_create(MUTEX_TYPE_DEFAULT),
! 1369: );
! 1370:
! 1371: this->local_sets = thread_value_create((thread_cleanup_t)this->sets->destroy);
! 1372: this->exclusive_local_sets = thread_value_create((thread_cleanup_t)this->sets->destroy);
! 1373: if (lib->settings->get_bool(lib->settings, "%s.cert_cache", TRUE, lib->ns))
! 1374: {
! 1375: this->cache = cert_cache_create();
! 1376: this->sets->insert_first(this->sets, this->cache);
! 1377: }
! 1378:
! 1379: return &this->public;
! 1380: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>