diff --git a/src/pluto/ca.c b/src/pluto/ca.c index 2f59a90..d9db082 100644 --- a/src/pluto/ca.c +++ b/src/pluto/ca.c @@ -27,7 +27,6 @@ #include "constants.h" #include "defs.h" #include "log.h" -#include "x509.h" #include "ca.h" #include "certs.h" #include "whack.h" @@ -36,13 +35,180 @@ /* chained list of X.509 authority certificates (ca, aa, and ocsp) */ -static x509cert_t *x509authcerts = NULL; +static linked_list_t *authcerts = NULL; /* chained list of X.509 certification authority information records */ static ca_info_t *ca_infos = NULL; +/** + * Initialize the linked list of authority certificates + */ +void ca_initialize(void) +{ + authcerts = linked_list_create(); +} + +/** + * Free the linked list of authority certificates + */ +void ca_finalize(void) +{ + lock_authcert_list("free_authcerts"); + authcerts->destroy_offset(authcerts, offsetof(certificate_t, destroy)); + unlock_authcert_list("free_authcerts"); +} + /* + * get a X.509 authority certificate with a given subject or keyid + */ +certificate_t* ca_get_cert(identification_t *subject, chunk_t keyid, + x509_flag_t auth_flags) +{ + enumerator_t *enumerator; + certificate_t *cert, *found = NULL; + + enumerator = authcerts->create_enumerator(authcerts); + while (enumerator->enumerate(enumerator, &cert)) + { + x509_t *x509 = (x509_t*)cert; + + /* skip non-matching types of authority certificates */ + if (!(x509->get_flags(x509) & auth_flags)) + { + continue; + } + + /* compare the keyid with the certificate's subjectKeyIdentifier */ + if (keyid.ptr) + { + chunk_t subjectKeyId; + + subjectKeyId = x509->get_subjectKeyIdentifier(x509); + if (subjectKeyId.ptr && !chunk_equals(keyid, subjectKeyId)) + { + continue; + } + } + + /* compare the subjectDistinguishedNames */ + if (cert->has_subject(cert, subject)) + { + found = cert; + break; + } + } + enumerator->destroy(enumerator); + return found; +} + +/** + * Add an authority certificate to the chained list + */ +certificate_t* ca_add_cert(certificate_t *cert) +{ + identification_t *subject = cert->get_subject(cert); + x509_t *x509 = (x509_t*)cert; + x509_flag_t flags = x509->get_flags(x509); + chunk_t keyid = x509->get_subjectKeyIdentifier(x509); + + certificate_t *old_cert; + enumerator_t *enumerator; + bool add = TRUE; + + lock_authcert_list("add_authcert"); + + enumerator = acerts->create_enumerator(acerts); + while (enumerator->enumerate(enumerator, &cert_old)) + { + x509_t *x509_old = (x509_t*)cert_old; + + /* skip non-matching types of authority certificates */ + if (!(x509_old->get_flags(x509_old) & flags)) + { + continue; + } + + /* compare the keyid with the certificate's subjectKeyIdentifier */ + if (keyid.ptr) + { + chunk_t subjectKeyId; + + subjectKeyId = x509_old->get_subjectKeyIdentifier(x509_old); + if (subjectKeyId.ptr && !chunk_equals(keyid, subjectKeyId)) + { + continue; + } + } + + /* compare the subjectDistinguishedNames */ + if (!cert_old->has_subject(cert_old, subject)) + { + continue, + } + + if (cert->equals(cert, cert_old)) + { + DBG1(" authcert is already present and identical") + + cert->destroy(cert); + cert = cert_old; + add = FALSE; + } + else + { + authcerts->remove_at(authcerts, enumerator); + DBG1(" existing authcert replaced"); + } + break; + } + enumerator->destroy(enumerator); + + if (add) + { + authcerts->insert_last(authcerts, cert); + } + unlock_authcert_list("add_authcert"); + + return cert; +} + +/** + * Loads authority certificates + */ +void ca_load_authcerts(char *type, char *path, x509_flag_t auth_flags) +{ + enumerator_t *enumerator; + struct stat st; + char *file; + + DBG1("loading %s certificates from '%s'", type, path); + + enumerator = enumerator_create_directory(path); + if (!enumerator) + { + DBG1(" reading directory '%s' failed"); + return; + } + + while (enumerator->enumerate(enumerator, NULL, &file, &st)) + { + cert_t cert; + + if (!S_ISREG(st.st_mode)) + { + /* skip special file */ + continue; + } + if (load_cert(file, type, auth_flags, &cert)) + { + add_authcert(cert.u.x509, auth_flags); + } + } + enumerator->destroy(enumerator); +} + +/** * Checks if CA a is trusted by CA b */ bool trusted_ca(identification_t *a, identification_t *b, int *pathlen) @@ -76,20 +242,18 @@ bool trusted_ca(identification_t *a, identification_t *b, int *pathlen) while ((*pathlen)++ < MAX_CA_PATH_LEN) { - certificate_t *certificate; + certificate_t *cacert; identification_t *issuer; - x509cert_t *cacert; - cacert = get_authcert(a, chunk_empty, X509_CA); + cacert = ca_get_cert(a, chunk_empty, X509_CA); if (cacert == NULL) { break; } - certificate = cacert->cert; /* is the certificate self-signed? */ { - x509_t *x509 = (x509_t*)certificate; + x509_t *x509 = (x509_t*)cacert; if (x509->get_flags(x509) & X509_SELF_SIGNED) { @@ -98,7 +262,7 @@ bool trusted_ca(identification_t *a, identification_t *b, int *pathlen) } /* does the issuer of CA a match CA b? */ - issuer = certificate->get_issuer(certificate); + issuer = cert->get_issuer(cert); match = b->equals(b, issuer); /* we have a match and exit the loop */ @@ -114,8 +278,8 @@ bool trusted_ca(identification_t *a, identification_t *b, int *pathlen) return match; } -/* - * does our CA match one of the requested CAs? +/** + * Does our CA match one of the requested CAs? */ bool match_requested_ca(linked_list_t *requested_ca, identification_t *our_ca, int *our_pathlen) @@ -156,167 +320,6 @@ bool match_requested_ca(linked_list_t *requested_ca, identification_t *our_ca, } /* - * free the first authority certificate in the chain - */ -static void free_first_authcert(void) -{ - x509cert_t *first = x509authcerts; - x509authcerts = first->next; - free_x509cert(first); -} - -/* - * free all CA certificates - */ -void free_authcerts(void) -{ - lock_authcert_list("free_authcerts"); - - while (x509authcerts != NULL) - free_first_authcert(); - - unlock_authcert_list("free_authcerts"); -} - -/* - * get a X.509 authority certificate with a given subject or keyid - */ -x509cert_t* get_authcert(identification_t *subject, chunk_t keyid, - x509_flag_t auth_flags) -{ - x509cert_t *cert, *prev_cert = NULL; - - /* the authority certificate list is empty */ - if (x509authcerts == NULL) - { - return NULL; - } - - for (cert = x509authcerts; cert != NULL; prev_cert = cert, cert = cert->next) - { - certificate_t *certificate = cert->cert; - x509_t *x509 = (x509_t*)certificate; - - /* skip non-matching types of authority certificates */ - if (!(x509->get_flags(x509) & auth_flags)) - { - continue; - } - - /* compare the keyid with the certificate's subjectKeyIdentifier */ - if (keyid.ptr) - { - chunk_t subjectKeyId; - - subjectKeyId = x509->get_subjectKeyIdentifier(x509); - if (subjectKeyId.ptr && !chunk_equals(keyid, subjectKeyId)) - { - continue; - } - } - - /* compare the subjectDistinguishedNames */ - if (!certificate->has_subject(certificate, subject)) - { - continue; - } - - /* found the authcert */ - if (cert != x509authcerts) - { - /* bring the certificate up front */ - prev_cert->next = cert->next; - cert->next = x509authcerts; - x509authcerts = cert; - } - return cert; - } - return NULL; -} - -/* - * add an authority certificate to the chained list - */ -x509cert_t* add_authcert(x509cert_t *cert, x509_flag_t auth_flags) -{ - certificate_t *certificate = cert->cert; - x509_t *x509 = (x509_t*)certificate; - x509cert_t *old_cert; - - lock_authcert_list("add_authcert"); - - old_cert = get_authcert(certificate->get_subject(certificate), - x509->get_subjectKeyIdentifier(x509), - auth_flags); - if (old_cert) - { - if (certificate->equals(certificate, old_cert->cert)) - { - DBG(DBG_CONTROL | DBG_PARSING , - DBG_log(" authcert is already present and identical") - ) - unlock_authcert_list("add_authcert"); - - free_x509cert(cert); - return old_cert; - } - else - { - /* cert is already present but will be replaced by new cert */ - free_first_authcert(); - DBG(DBG_CONTROL | DBG_PARSING , - DBG_log(" existing authcert deleted") - ) - } - } - - /* add new authcert to chained list */ - cert->next = x509authcerts; - x509authcerts = cert; - share_x509cert(cert); /* set count to one */ - DBG(DBG_CONTROL | DBG_PARSING, - DBG_log(" authcert inserted") - ) - unlock_authcert_list("add_authcert"); - return cert; -} - -/* - * Loads authority certificates - */ -void load_authcerts(char *type, char *path, x509_flag_t auth_flags) -{ - enumerator_t *enumerator; - struct stat st; - char *file; - - DBG1("loading %s certificates from '%s'", type, path); - - enumerator = enumerator_create_directory(path); - if (!enumerator) - { - DBG1(" reading directory '%s' failed"); - return; - } - - while (enumerator->enumerate(enumerator, NULL, &file, &st)) - { - cert_t cert; - - if (!S_ISREG(st.st_mode)) - { - /* skip special file */ - continue; - } - if (load_cert(file, type, auth_flags, &cert)) - { - add_authcert(cert.u.x509, auth_flags); - } - } - enumerator->destroy(enumerator); -} - -/* * list all X.509 authcerts with given auth flags in a chained list */ void list_authcerts(const char *caption, x509_flag_t auth_flags, bool utc) @@ -368,7 +371,7 @@ static const x509cert_t* get_alt_cacert(identification_t *subject, chunk_t keyid /* establish trust into a candidate authcert by going up the trust chain. * validity and revocation status are not checked. */ -bool trust_authcert_candidate(const x509cert_t *cert, const x509cert_t *alt_chain) +bool trust_authcert_candidate(certificate_t *cert, linked_list_t *alt_chain) { int pathlen; @@ -448,8 +451,8 @@ bool trust_authcert_candidate(const x509cert_t *cert, const x509cert_t *alt_chai return FALSE; } -/* - * get a CA info record with a given authName or authKeyID +/** + * Get a CA info record with a given authName or authKeyID */ ca_info_t* get_ca_info(identification_t *name, chunk_t keyid) { @@ -468,11 +471,10 @@ ca_info_t* get_ca_info(identification_t *name, chunk_t keyid) } -/* - * free the dynamic memory used by a ca_info record +/** + * Free the dynamic memory used by a ca_info record */ -static void -free_ca_info(ca_info_t* ca_info) +static void free_ca_info(ca_info_t* ca_info) { if (ca_info == NULL) { @@ -502,8 +504,8 @@ void free_ca_infos(void) } } -/* - * find a CA information record by name and optionally delete it +/** + * Find a CA information record by name and optionally delete it */ bool find_ca_info_by_name(const char *name, bool delete) { @@ -531,7 +533,7 @@ bool find_ca_info_by_name(const char *name, bool delete) return FALSE; } -/* +/** * Create an empty ca_info_t record */ ca_info_t* create_ca_info(void) @@ -659,8 +661,8 @@ void add_ca_info(const whack_message_t *msg) } } -/* - * list all ca_info records in the chained list +/** + * List all ca_info records in the chained list */ void list_ca_infos(bool utc) { diff --git a/src/pluto/ca.h b/src/pluto/ca.h index 77dfe33..3b9e4c9 100644 --- a/src/pluto/ca.h +++ b/src/pluto/ca.h @@ -17,8 +17,8 @@ #include #include +#include -#include "x509.h" #include "whack.h" #define MAX_CA_PATH_LEN 7 @@ -39,17 +39,21 @@ struct ca_info { bool strictcrlpolicy; }; +extern void ca_initialize(void); +extern void ca_finalize(void); +extern void ca_load_certs(char *type, char *path, x509_flag_t auth_flags); +extern void ca_list_certs(const char *caption, x509_flag_t auth_flags, bool utc); + +extern certificate_t* ca_add_cert(certificate_t *cert); + +extern certificate_t* ca_get_cert(identification_t *subject, chunk_t keyid, + x509_flag_t auth_flags) + extern bool trusted_ca(identification_t *a, identification_t *b, int *pathlen); extern bool match_requested_ca(linked_list_t *requested_ca, identification_t *our_ca, int *our_pathlen); -extern x509cert_t* get_authcert(identification_t *subject, chunk_t keyid, - x509_flag_t auth_flags); -extern void load_authcerts(char *type, char *path, x509_flag_t auth_flags); -extern x509cert_t* add_authcert(x509cert_t *cert, x509_flag_t auth_flags); -extern void free_authcerts(void); -extern void list_authcerts(const char *caption, x509_flag_t auth_flags, bool utc); -extern bool trust_authcert_candidate(const x509cert_t *cert, - const x509cert_t *alt_chain); +extern bool trust_authcert_candidate(certificate_t *cert, + linked_list_t *alt_chain); extern ca_info_t* get_ca_info(identification_t *name, chunk_t keyid); extern bool find_ca_info_by_name(const char *name, bool delete); extern void add_ca_info(const whack_message_t *msg); diff --git a/src/pluto/plutomain.c b/src/pluto/plutomain.c index 0471d26..a9e3aa0 100644 --- a/src/pluto/plutomain.c +++ b/src/pluto/plutomain.c @@ -675,6 +675,7 @@ int main(int argc, char **argv) init_adns(); init_myid(); init_fetch(); + ca_initialize(); ac_initialize(); /* drop unneeded capabilities and change UID/GID */ @@ -752,12 +753,12 @@ void exit_pluto(int status) delete_every_connection(); free_crl_fetch(); /* free chain of crl fetch requests */ free_ocsp_fetch(); /* free chain of ocsp fetch requests */ - free_authcerts(); /* free chain of X.509 authority certificates */ free_crls(); /* free chain of X.509 CRLs */ free_ca_infos(); /* free chain of X.509 CA information records */ free_ocsp(); /* free ocsp cache */ free_ifaces(); ac_finalize(); /* free X.509 attribute certificates */ + ca_finalize(); /* free X.509 authority certificates */ scx_finalize(); /* finalize and unload PKCS #11 module */ xauth_finalize(); /* finalize and unload XAUTH module */ stop_adns();