From 845d36969e9acd8b177c5710dfbe3eb2841624fa Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 6 Feb 2015 11:58:17 +0100 Subject: [PATCH 1/6] stroke: Refactor load_certdir function --- src/libcharon/plugins/stroke/stroke_cred.c | 282 ++++++++++++--------- 1 file changed, 166 insertions(+), 116 deletions(-) diff --git a/src/libcharon/plugins/stroke/stroke_cred.c b/src/libcharon/plugins/stroke/stroke_cred.c index 0dc03ec16..0ac3f8247 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.c +++ b/src/libcharon/plugins/stroke/stroke_cred.c @@ -373,134 +373,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->creds->add_cert(this->creds, 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->creds->add_cert(this->creds, 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, From d69cf39bb45e57bf936b16ca7b9328a5637f6c72 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 6 Feb 2015 12:21:12 +0100 Subject: [PATCH 2/6] stroke: Use separate credential sets for CA/AA certificates --- src/libcharon/plugins/stroke/stroke_cred.c | 24 +++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/libcharon/plugins/stroke/stroke_cred.c b/src/libcharon/plugins/stroke/stroke_cred.c index 0ac3f8247..31d9e0723 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) @@ -405,7 +415,7 @@ static void load_x509_ca(private_stroke_cred_t *this, char *file) { DBG1(DBG_CFG, " loaded ca certificate \"%Y\" from '%s'", cert->get_subject(cert), file); - this->creds->add_cert(this->creds, TRUE, cert); + this->cacerts->add_cert(this->cacerts, TRUE, cert); } } else @@ -428,7 +438,7 @@ static void load_x509_aa(private_stroke_cred_t *this, char *file) { DBG1(DBG_CFG, " loaded AA certificate \"%Y\" from '%s'", cert->get_subject(cert), file); - this->creds->add_cert(this->creds, TRUE, cert); + this->aacerts->add_cert(this->aacerts, TRUE, cert); } else { @@ -1409,7 +1419,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); } @@ -1442,9 +1456,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", From aba46b104ec0e3e8555f51af2a718ab7f528dde9 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 6 Feb 2015 12:22:32 +0100 Subject: [PATCH 3/6] stroke: Purge existing CA/AA certificates during reread --- src/libcharon/plugins/stroke/stroke_cred.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libcharon/plugins/stroke/stroke_cred.c b/src/libcharon/plugins/stroke/stroke_cred.c index 31d9e0723..1f021027d 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.c +++ b/src/libcharon/plugins/stroke/stroke_cred.c @@ -1381,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) @@ -1394,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) From ef2c61bc9279a3f7880df67d6af12b785c94694a Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 6 Feb 2015 12:34:30 +0100 Subject: [PATCH 4/6] mem-cred: Add a method to unify certificate references, without adding it In contrast to add_cert_ref(), get_cert_ref() does not add the certificate to the set, but only finds a reference to the same certificate, if found. --- src/libstrongswan/credentials/sets/mem_cred.c | 19 +++++++++++++++++++ src/libstrongswan/credentials/sets/mem_cred.h | 12 ++++++++++++ 2 files changed, 31 insertions(+) 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. * From 11c14bd2f516975670d9f04c51b54438730fa64d Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 6 Feb 2015 12:43:33 +0100 Subject: [PATCH 5/6] stroke: Serve ca section CA certificates directly, not over central CA set This makes these CA certificates independent from the purge issued by reread commands. Certificates loaded by CA sections can be removed through ipsec.conf update/reread, while CA certificates loaded implicitly from ipsec.d/cacerts can individually be reread using ipsec rereadcacerts. --- src/libcharon/plugins/stroke/stroke_ca.c | 81 +++++++++++++++++++++- src/libcharon/plugins/stroke/stroke_cred.c | 2 +- src/libcharon/plugins/stroke/stroke_cred.h | 7 +- 3 files changed, 85 insertions(+), 5 deletions(-) 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 1f021027d..5e423f1de 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.c +++ b/src/libcharon/plugins/stroke/stroke_cred.c @@ -241,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; } 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); From 9dde9d69ed7d4af5cb5fecae0d2049527e0250c3 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 6 Feb 2015 14:23:17 +0100 Subject: [PATCH 6/6] ipsec: Update rereadcacerts/aacerts command description in manpage --- src/ipsec/_ipsec.8.in | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) 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"