Merge branch 'stroke-purge-on-reread'

Remove all previously loaded certificates during "ipsec reread", finally
allowing the removal of CA certificates from a running daemon.

Fixes #842, #700, #305.
This commit is contained in:
Martin Willi
2015-03-03 13:52:35 +01:00
6 changed files with 314 additions and 128 deletions
+9 -6
View File
@@ -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"
+79 -2
View File
@@ -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;
}
+190 -118
View File
@@ -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",
+5 -2
View File
@@ -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);
@@ -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,
@@ -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.
*