diff --git a/src/ipsec/_ipsec.8.in b/src/ipsec/_ipsec.8.in index 210d74ef8..0aef8c031 100644 --- a/src/ipsec/_ipsec.8.in +++ b/src/ipsec/_ipsec.8.in @@ -210,15 +210,18 @@ flushes and rereads all secrets defined in \fIipsec.secrets\fP. . .TP .B "rereadcacerts" -reads all certificate files contained in the \fI/etc/ipsec.d/cacerts\fP -directory and adds them to the list of Certification Authority (CA) -certificates. +removes previously loaded CA certificates, reads all certificate files +contained in the \fI/etc/ipsec.d/cacerts\fP directory and adds them to the list +of Certification Authority (CA) certificates. This does not affect certificates +explicitly defined in a +.BR ipsec.conf (5) +ca section, which may be separately updated using the \fBupdate\fP command. . .TP .B "rereadaacerts" -reads all certificate files contained in the \fI/etc/ipsec.d/aacerts\fP -directory and adds them to the list of Authorization Authority (AA) -certificates. +removes previously loaded AA certificates, reads all certificate files +contained in the \fI/etc/ipsec.d/aacerts\fP directory and adds them to the list +of Authorization Authority (AA) certificates. . .TP .B "rereadocspcerts" diff --git a/src/libcharon/plugins/stroke/stroke_ca.c b/src/libcharon/plugins/stroke/stroke_ca.c index f8026875f..b470b81c6 100644 --- a/src/libcharon/plugins/stroke/stroke_ca.c +++ b/src/libcharon/plugins/stroke/stroke_ca.c @@ -119,6 +119,84 @@ static void ca_section_destroy(ca_section_t *this) free(this); } +/** + * Data for the certificate enumerator + */ +typedef struct { + private_stroke_ca_t *this; + certificate_type_t cert; + key_type_t key; + identification_t *id; +} cert_data_t; + +/** + * destroy cert_data + */ +static void cert_data_destroy(cert_data_t *data) +{ + data->this->lock->unlock(data->this->lock); + free(data); +} + +/** + * filter function for certs enumerator + */ +static bool certs_filter(cert_data_t *data, ca_section_t **in, + certificate_t **out) +{ + public_key_t *public; + certificate_t *cert = (*in)->cert; + + if (data->cert == CERT_ANY || data->cert == cert->get_type(cert)) + { + public = cert->get_public_key(cert); + if (public) + { + if (data->key == KEY_ANY || data->key == public->get_type(public)) + { + if (data->id && public->has_fingerprint(public, + data->id->get_encoding(data->id))) + { + public->destroy(public); + *out = cert; + return TRUE; + } + } + public->destroy(public); + } + else if (data->key != KEY_ANY) + { + return FALSE; + } + if (data->id == NULL || cert->has_subject(cert, data->id)) + { + *out = cert; + return TRUE; + } + } + return FALSE; +} + +METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, + private_stroke_ca_t *this, certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted) +{ + enumerator_t *enumerator; + cert_data_t *data; + + INIT(data, + .this = this, + .cert = cert, + .key = key, + .id = id, + ); + + this->lock->read_lock(this->lock); + enumerator = this->sections->create_enumerator(this->sections); + return enumerator_create_filter(enumerator, (void*)certs_filter, data, + (void*)cert_data_destroy); +} + /** * data to pass to create_inner_cdp */ @@ -438,7 +516,7 @@ stroke_ca_t *stroke_ca_create(stroke_cred_t *cred) .public = { .set = { .create_private_enumerator = (void*)return_null, - .create_cert_enumerator = (void*)return_null, + .create_cert_enumerator = _create_cert_enumerator, .create_shared_enumerator = (void*)return_null, .create_cdp_enumerator = _create_cdp_enumerator, .cache_cert = (void*)nop, @@ -456,4 +534,3 @@ stroke_ca_t *stroke_ca_create(stroke_cred_t *cred) return &this->public; } - diff --git a/src/libcharon/plugins/stroke/stroke_cred.c b/src/libcharon/plugins/stroke/stroke_cred.c index 0dc03ec16..5e423f1de 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.c +++ b/src/libcharon/plugins/stroke/stroke_cred.c @@ -70,10 +70,20 @@ struct private_stroke_cred_t { char *secrets_file; /** - * credentials + * credentials: end entity certs, attribute certs, CRLs, etc. */ mem_cred_t *creds; + /** + * CA certificates + */ + mem_cred_t *cacerts; + + /** + * Attribute Authority certificates + */ + mem_cred_t *aacerts; + /** * ignore missing CA basic constraint (i.e. treat all certificates in * ipsec.conf ca sections and ipsec.d/cacerts as CA certificates) @@ -231,7 +241,7 @@ METHOD(stroke_cred_t, load_ca, certificate_t*, } DBG1(DBG_CFG, " loaded ca certificate \"%Y\" from '%s'", cert->get_subject(cert), filename); - return this->creds->add_cert_ref(this->creds, TRUE, cert); + return this->creds->get_cert_ref(this->creds, cert); } return NULL; } @@ -373,134 +383,184 @@ METHOD(stroke_cred_t, load_pubkey, certificate_t*, return NULL; } +/** + * Load a CA certificate from disk + */ +static void load_x509_ca(private_stroke_cred_t *this, char *file) +{ + certificate_t *cert; + + if (this->force_ca_cert) + { /* treat certificate as CA cert even it has no CA basic constraint */ + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, file, + BUILD_X509_FLAG, X509_CA, BUILD_END); + } + else + { + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, file, BUILD_END); + } + if (cert) + { + x509_t *x509 = (x509_t*)cert; + + if (!(x509->get_flags(x509) & X509_CA)) + { + DBG1(DBG_CFG, " ca certificate \"%Y\" lacks ca basic constraint, " + "discarded", cert->get_subject(cert)); + cert->destroy(cert); + } + else + { + DBG1(DBG_CFG, " loaded ca certificate \"%Y\" from '%s'", + cert->get_subject(cert), file); + this->cacerts->add_cert(this->cacerts, TRUE, cert); + } + } + else + { + DBG1(DBG_CFG, " loading ca certificate from '%s' failed", file); + } +} + +/** + * Load AA certificate with flags from disk + */ +static void load_x509_aa(private_stroke_cred_t *this, char *file) +{ + certificate_t *cert; + + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, file, + BUILD_X509_FLAG, X509_AA, BUILD_END); + if (cert) + { + DBG1(DBG_CFG, " loaded AA certificate \"%Y\" from '%s'", + cert->get_subject(cert), file); + this->aacerts->add_cert(this->aacerts, TRUE, cert); + } + else + { + DBG1(DBG_CFG, " loading AA certificate from '%s' failed", file); + } +} + +/** + * Load a certificate with flags from disk + */ +static void load_x509(private_stroke_cred_t *this, char *file, x509_flag_t flag) +{ + certificate_t *cert; + + /* for all other flags, we add them to the certificate. */ + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, file, + BUILD_X509_FLAG, flag, BUILD_END); + if (cert) + { + DBG1(DBG_CFG, " loaded certificate \"%Y\" from '%s'", + cert->get_subject(cert), file); + this->creds->add_cert(this->creds, TRUE, cert); + } + else + { + DBG1(DBG_CFG, " loading certificate from '%s' failed", file); + } +} + +/** + * Load a CRL from a file + */ +static void load_x509_crl(private_stroke_cred_t *this, char *file) +{ + certificate_t *cert; + + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL, + BUILD_FROM_FILE, file, BUILD_END); + if (cert) + { + this->creds->add_crl(this->creds, (crl_t*)cert); + DBG1(DBG_CFG, " loaded crl from '%s'", file); + } + else + { + DBG1(DBG_CFG, " loading crl from '%s' failed", file); + } +} + +/** + * Load an attribute certificate from a file + */ +static void load_x509_ac(private_stroke_cred_t *this, char *file) +{ + certificate_t *cert; + + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_AC, + BUILD_FROM_FILE, file, BUILD_END); + if (cert) + { + DBG1(DBG_CFG, " loaded attribute certificate from '%s'", file); + this->creds->add_cert(this->creds, FALSE, cert); + } + else + { + DBG1(DBG_CFG, " loading attribute certificate from '%s' failed", file); + } +} + /** * load trusted certificates from a directory */ static void load_certdir(private_stroke_cred_t *this, char *path, certificate_type_t type, x509_flag_t flag) { + enumerator_t *enumerator; struct stat st; char *file; - enumerator_t *enumerator = enumerator_create_directory(path); - - if (!enumerator) + enumerator = enumerator_create_directory(path); + if (enumerator) + { + while (enumerator->enumerate(enumerator, NULL, &file, &st)) + { + if (!S_ISREG(st.st_mode)) + { + /* skip special file */ + continue; + } + switch (type) + { + case CERT_X509: + if (flag & X509_CA) + { + load_x509_ca(this, file); + } + else if (flag & X509_AA) + { + load_x509_aa(this, file); + } + else + { + load_x509(this, file, flag); + } + break; + case CERT_X509_CRL: + load_x509_crl(this, file); + break; + case CERT_X509_AC: + load_x509_ac(this, file); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + } + else { DBG1(DBG_CFG, " reading directory failed"); - return; } - - while (enumerator->enumerate(enumerator, NULL, &file, &st)) - { - certificate_t *cert; - - if (!S_ISREG(st.st_mode)) - { - /* skip special file */ - continue; - } - switch (type) - { - case CERT_X509: - if (flag & X509_CA) - { - if (this->force_ca_cert) - { /* treat this certificate as CA cert even it has no - * CA basic constraint */ - cert = lib->creds->create(lib->creds, - CRED_CERTIFICATE, CERT_X509, - BUILD_FROM_FILE, file, BUILD_X509_FLAG, - X509_CA, BUILD_END); - } - else - { - cert = lib->creds->create(lib->creds, - CRED_CERTIFICATE, CERT_X509, - BUILD_FROM_FILE, file, BUILD_END); - } - if (cert) - { - x509_t *x509 = (x509_t*)cert; - - if (!(x509->get_flags(x509) & X509_CA)) - { - DBG1(DBG_CFG, " ca certificate \"%Y\" lacks " - "ca basic constraint, discarded", - cert->get_subject(cert)); - cert->destroy(cert); - cert = NULL; - } - else - { - DBG1(DBG_CFG, " loaded ca certificate \"%Y\" " - "from '%s'", cert->get_subject(cert), file); - } - } - else - { - DBG1(DBG_CFG, " loading ca certificate from '%s' " - "failed", file); - } - } - else - { /* for all other flags, we add them to the certificate. */ - cert = lib->creds->create(lib->creds, - CRED_CERTIFICATE, CERT_X509, - BUILD_FROM_FILE, file, - BUILD_X509_FLAG, flag, BUILD_END); - if (cert) - { - DBG1(DBG_CFG, " loaded certificate \"%Y\" from '%s'", - cert->get_subject(cert), file); - } - else - { - DBG1(DBG_CFG, " loading certificate from '%s' " - "failed", file); - } - } - if (cert) - { - this->creds->add_cert(this->creds, TRUE, cert); - } - break; - case CERT_X509_CRL: - cert = lib->creds->create(lib->creds, - CRED_CERTIFICATE, CERT_X509_CRL, - BUILD_FROM_FILE, file, - BUILD_END); - if (cert) - { - this->creds->add_crl(this->creds, (crl_t*)cert); - DBG1(DBG_CFG, " loaded crl from '%s'", file); - } - else - { - DBG1(DBG_CFG, " loading crl from '%s' failed", file); - } - break; - case CERT_X509_AC: - cert = lib->creds->create(lib->creds, - CRED_CERTIFICATE, CERT_X509_AC, - BUILD_FROM_FILE, file, - BUILD_END); - if (cert) - { - this->creds->add_cert(this->creds, FALSE, cert); - DBG1(DBG_CFG, " loaded attribute certificate from '%s'", - file); - } - else - { - DBG1(DBG_CFG, " loading attribute certificate from '%s' " - "failed", file); - } - break; - default: - break; - } - } - enumerator->destroy(enumerator); } METHOD(stroke_cred_t, cache_cert, void, @@ -1321,6 +1381,8 @@ METHOD(stroke_cred_t, reread, void, { DBG1(DBG_CFG, "rereading ca certificates from '%s'", CA_CERTIFICATE_DIR); + this->cacerts->clear(this->cacerts); + lib->credmgr->flush_cache(lib->credmgr, CERT_X509); load_certdir(this, CA_CERTIFICATE_DIR, CERT_X509, X509_CA); } if (msg->reread.flags & REREAD_OCSPCERTS) @@ -1334,6 +1396,8 @@ METHOD(stroke_cred_t, reread, void, { DBG1(DBG_CFG, "rereading aa certificates from '%s'", AA_CERTIFICATE_DIR); + this->aacerts->clear(this->aacerts); + lib->credmgr->flush_cache(lib->credmgr, CERT_X509); load_certdir(this, AA_CERTIFICATE_DIR, CERT_X509, X509_AA); } if (msg->reread.flags & REREAD_ACERTS) @@ -1359,7 +1423,11 @@ METHOD(stroke_cred_t, add_shared, void, METHOD(stroke_cred_t, destroy, void, private_stroke_cred_t *this) { + lib->credmgr->remove_set(lib->credmgr, &this->aacerts->set); + lib->credmgr->remove_set(lib->credmgr, &this->cacerts->set); lib->credmgr->remove_set(lib->credmgr, &this->creds->set); + this->aacerts->destroy(this->aacerts); + this->cacerts->destroy(this->cacerts); this->creds->destroy(this->creds); free(this); } @@ -1392,9 +1460,13 @@ stroke_cred_t *stroke_cred_create() "%s.plugins.stroke.secrets_file", SECRETS_FILE, lib->ns), .creds = mem_cred_create(), + .cacerts = mem_cred_create(), + .aacerts = mem_cred_create(), ); lib->credmgr->add_set(lib->credmgr, &this->creds->set); + lib->credmgr->add_set(lib->credmgr, &this->cacerts->set); + lib->credmgr->add_set(lib->credmgr, &this->aacerts->set); this->force_ca_cert = lib->settings->get_bool(lib->settings, "%s.plugins.stroke.ignore_missing_ca_basic_constraint", diff --git a/src/libcharon/plugins/stroke/stroke_cred.h b/src/libcharon/plugins/stroke/stroke_cred.h index f6fbb96d3..9434629ef 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.h +++ b/src/libcharon/plugins/stroke/stroke_cred.h @@ -50,10 +50,13 @@ struct stroke_cred_t { void (*reread)(stroke_cred_t *this, stroke_msg_t *msg, FILE *prompt); /** - * Load a CA certificate, and serve it through the credential_set. + * Load a CA certificate. + * + * This method does not add the loaded CA certificate to the internal + * credentail set, but returns it only. * * @param filename file to load CA cert from - * @return reference to loaded certificate, or NULL + * @return loaded certificate, or NULL */ certificate_t* (*load_ca)(stroke_cred_t *this, char *filename); diff --git a/src/libstrongswan/credentials/sets/mem_cred.c b/src/libstrongswan/credentials/sets/mem_cred.c index d8f568d36..7ad011b5e 100644 --- a/src/libstrongswan/credentials/sets/mem_cred.c +++ b/src/libstrongswan/credentials/sets/mem_cred.c @@ -192,6 +192,24 @@ METHOD(mem_cred_t, add_cert_ref, certificate_t*, return add_cert_internal(this, trusted, cert); } +METHOD(mem_cred_t, get_cert_ref, certificate_t*, + private_mem_cred_t *this, certificate_t *cert) +{ + certificate_t *cached; + + this->lock->write_lock(this->lock); + if (this->untrusted->find_first(this->untrusted, + (linked_list_match_t)certificate_equals, + (void**)&cached, cert) == SUCCESS) + { + cert->destroy(cert); + cert = cached->get_ref(cached); + } + this->lock->unlock(this->lock); + + return cert; +} + METHOD(mem_cred_t, add_crl, bool, private_mem_cred_t *this, crl_t *crl) { @@ -736,6 +754,7 @@ mem_cred_t *mem_cred_create() }, .add_cert = _add_cert, .add_cert_ref = _add_cert_ref, + .get_cert_ref = _get_cert_ref, .add_crl = _add_crl, .add_key = _add_key, .add_shared = _add_shared, diff --git a/src/libstrongswan/credentials/sets/mem_cred.h b/src/libstrongswan/credentials/sets/mem_cred.h index d0dd51da1..3ce815abc 100644 --- a/src/libstrongswan/credentials/sets/mem_cred.h +++ b/src/libstrongswan/credentials/sets/mem_cred.h @@ -58,6 +58,18 @@ struct mem_cred_t { certificate_t *(*add_cert_ref)(mem_cred_t *this, bool trusted, certificate_t *cert); + /** + * Get an existing reference to the same certificate. + * + * Searches for the same certficate in the set, and returns a reference + * to it, destroying the passed certificate. If the passed certificate + * is not found, it is just returned. + * + * @param cert certificate to look up + * @return the same certificate, potentially different instance + */ + certificate_t* (*get_cert_ref)(mem_cred_t *this, certificate_t *cert); + /** * Add an X.509 CRL to the credential set. *