credmgr: introduce a hook function to catch trust chain validation errors
This commit is contained in:
@@ -94,7 +94,12 @@ METHOD(cert_validator_t, validate, bool,
|
||||
if (subject->get_type(subject) == CERT_X509 &&
|
||||
issuer->get_type(issuer) == CERT_X509)
|
||||
{
|
||||
return check_addrblock((x509_t*)subject, (x509_t*)issuer);
|
||||
if (!check_addrblock((x509_t*)subject, (x509_t*)issuer))
|
||||
{
|
||||
lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION,
|
||||
subject);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -167,6 +167,8 @@ METHOD(cert_validator_t, validate, bool,
|
||||
{
|
||||
DBG1(DBG_CFG, "coupling new certificate '%Y' failed",
|
||||
subject->get_subject(subject));
|
||||
lib->credmgr->call_hook(lib->credmgr
|
||||
CRED_HOOK_POLICY_VIOLATION, subject);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -174,6 +176,8 @@ METHOD(cert_validator_t, validate, bool,
|
||||
DBG1(DBG_CFG, "coupling new certificate '%Y' failed, limit of %d "
|
||||
"couplings reached", subject->get_subject(subject),
|
||||
this->max_couplings);
|
||||
lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION,
|
||||
subject);
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
|
||||
@@ -53,6 +53,9 @@ struct cert_validator_t {
|
||||
/**
|
||||
* Validate a subject certificate in relation to its issuer.
|
||||
*
|
||||
* If FALSE is returned, the validator should call_hook() on the
|
||||
* credential manager with an appropriate type and the certificate.
|
||||
*
|
||||
* @param subject subject certificate to check
|
||||
* @param issuer issuer of subject
|
||||
* @param online whether to do online revocation checking
|
||||
|
||||
@@ -81,6 +81,16 @@ struct private_credential_manager_t {
|
||||
* mutex for cache queue
|
||||
*/
|
||||
mutex_t *queue_mutex;
|
||||
|
||||
/**
|
||||
* Registered hook to call on validation errors
|
||||
*/
|
||||
credential_hook_t hook;
|
||||
|
||||
/**
|
||||
* Registered data to pass to hook
|
||||
*/
|
||||
void *hook_data;
|
||||
};
|
||||
|
||||
/** data to pass to create_private_enumerator */
|
||||
@@ -126,6 +136,22 @@ typedef struct {
|
||||
enumerator_t *exclusive;
|
||||
} sets_enumerator_t;
|
||||
|
||||
METHOD(credential_manager_t, set_hook, void,
|
||||
private_credential_manager_t *this, credential_hook_t hook, void *data)
|
||||
{
|
||||
this->hook = hook;
|
||||
this->hook_data = data;
|
||||
}
|
||||
|
||||
METHOD(credential_manager_t, call_hook, void,
|
||||
private_credential_manager_t *this, credential_hook_type_t type,
|
||||
certificate_t *cert)
|
||||
{
|
||||
if (this->hook)
|
||||
{
|
||||
this->hook(this->hook_data, type, cert);
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(enumerator_t, sets_enumerate, bool,
|
||||
sets_enumerator_t *this, credential_set_t **set)
|
||||
@@ -553,15 +579,17 @@ static bool check_lifetime(private_credential_manager_t *this,
|
||||
{
|
||||
DBG1(DBG_CFG, "%s certificate invalid (valid from %T to %T)",
|
||||
label, ¬_before, FALSE, ¬_after, FALSE);
|
||||
return FALSE;
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
case SUCCESS:
|
||||
return TRUE;
|
||||
case FAILED:
|
||||
default:
|
||||
return FALSE;
|
||||
break;
|
||||
}
|
||||
call_hook(this, CRED_HOOK_EXPIRED, cert);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -722,9 +750,10 @@ static bool verify_trust_chain(private_credential_manager_t *this,
|
||||
{
|
||||
if (current->equals(current, issuer))
|
||||
{
|
||||
DBG1(DBG_CFG, " self-signed certificate \"%Y\" is not trusted",
|
||||
current->get_subject(current));
|
||||
DBG1(DBG_CFG, " self-signed certificate \"%Y\" is not "
|
||||
"trusted", current->get_subject(current));
|
||||
issuer->destroy(issuer);
|
||||
call_hook(this, CRED_HOOK_UNTRUSTED_ROOT, current);
|
||||
break;
|
||||
}
|
||||
auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer));
|
||||
@@ -736,6 +765,7 @@ static bool verify_trust_chain(private_credential_manager_t *this,
|
||||
{
|
||||
DBG1(DBG_CFG, "no issuer certificate found for \"%Y\"",
|
||||
current->get_subject(current));
|
||||
call_hook(this, CRED_HOOK_NO_ISSUER, current);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -754,8 +784,8 @@ static bool verify_trust_chain(private_credential_manager_t *this,
|
||||
current = issuer;
|
||||
if (trusted)
|
||||
{
|
||||
DBG1(DBG_CFG, " reached self-signed root ca with a path length of %d",
|
||||
pathlen);
|
||||
DBG1(DBG_CFG, " reached self-signed root ca with a "
|
||||
"path length of %d", pathlen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -763,6 +793,7 @@ static bool verify_trust_chain(private_credential_manager_t *this,
|
||||
if (pathlen > MAX_TRUST_PATH_LEN)
|
||||
{
|
||||
DBG1(DBG_CFG, "maximum path length of %d exceeded", MAX_TRUST_PATH_LEN);
|
||||
call_hook(this, CRED_HOOK_EXCEEDED_PATH_LEN, subject);
|
||||
}
|
||||
if (trusted)
|
||||
{
|
||||
@@ -1305,6 +1336,8 @@ credential_manager_t *credential_manager_create()
|
||||
.remove_local_set = _remove_local_set,
|
||||
.add_validator = _add_validator,
|
||||
.remove_validator = _remove_validator,
|
||||
.set_hook = _set_hook,
|
||||
.call_hook = _call_hook,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.sets = linked_list_create(),
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define CREDENTIAL_MANAGER_H_
|
||||
|
||||
typedef struct credential_manager_t credential_manager_t;
|
||||
typedef enum credential_hook_type_t credential_hook_type_t;
|
||||
|
||||
#include <utils/identification.h>
|
||||
#include <collections/enumerator.h>
|
||||
@@ -32,6 +33,37 @@ typedef struct credential_manager_t credential_manager_t;
|
||||
#include <credentials/certificates/certificate.h>
|
||||
#include <credentials/cert_validator.h>
|
||||
|
||||
/**
|
||||
* Type of a credential hook error/event.
|
||||
*/
|
||||
enum credential_hook_type_t {
|
||||
/** The certificate has expired (or is not yet valid) */
|
||||
CRED_HOOK_EXPIRED,
|
||||
/** The certificate has been revoked */
|
||||
CRED_HOOK_REVOKED,
|
||||
/** Checking certificate revocation failed. This does not necessarily mean
|
||||
* the certificate is rejected, just that revocation checking failed. */
|
||||
CRED_HOOK_VALIDATION_FAILED,
|
||||
/** No trusted issuer certificate has been found for this certificate */
|
||||
CRED_HOOK_NO_ISSUER,
|
||||
/** Encountered a self-signed (root) certificate, but it is not trusted */
|
||||
CRED_HOOK_UNTRUSTED_ROOT,
|
||||
/** Maximum trust chain length exceeded for certificate */
|
||||
CRED_HOOK_EXCEEDED_PATH_LEN,
|
||||
/** The certificate violates some other kind of policy and gets rejected */
|
||||
CRED_HOOK_POLICY_VIOLATION,
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook function to invoke on certificate validation errors.
|
||||
*
|
||||
* @param data user data supplied during hook registration
|
||||
* @param type type of validation error/event
|
||||
* @param cert associated certificate
|
||||
*/
|
||||
typedef void (*credential_hook_t)(void *data, credential_hook_type_t type,
|
||||
certificate_t *cert);
|
||||
|
||||
/**
|
||||
* Manages credentials using credential_sets.
|
||||
*
|
||||
@@ -262,6 +294,28 @@ struct credential_manager_t {
|
||||
*/
|
||||
void (*remove_validator)(credential_manager_t *this, cert_validator_t *vdtr);
|
||||
|
||||
/**
|
||||
* Set a hook to call on certain credential validation errors.
|
||||
*
|
||||
* @param hook hook to register, NULL to unregister
|
||||
* @param data data to pass to hook
|
||||
*/
|
||||
void (*set_hook)(credential_manager_t *this, credential_hook_t hook,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Call the registered credential hook, if any.
|
||||
*
|
||||
* While hooks are usually called by the credential manager itself, some
|
||||
* validator plugins might raise hooks as well if they consider certificates
|
||||
* invalid.
|
||||
*
|
||||
* @param type type of the event
|
||||
* @param cert associated certificate
|
||||
*/
|
||||
void (*call_hook)(credential_manager_t *this, credential_hook_type_t type,
|
||||
certificate_t *cert);
|
||||
|
||||
/**
|
||||
* Destroy a credential_manager instance.
|
||||
*/
|
||||
|
||||
@@ -533,20 +533,28 @@ METHOD(cert_validator_t, validate, bool,
|
||||
{
|
||||
if (!check_pathlen((x509_t*)issuer, pathlen))
|
||||
{
|
||||
lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_EXCEEDED_PATH_LEN,
|
||||
subject);
|
||||
return FALSE;
|
||||
}
|
||||
if (!check_name_constraints(subject, (x509_t*)issuer))
|
||||
{
|
||||
lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION,
|
||||
subject);
|
||||
return FALSE;
|
||||
}
|
||||
if (!check_policy((x509_t*)subject, (x509_t*)issuer, !pathlen, auth))
|
||||
{
|
||||
lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION,
|
||||
subject);
|
||||
return FALSE;
|
||||
}
|
||||
if (anchor)
|
||||
{
|
||||
if (!check_policy_constraints((x509_t*)issuer, pathlen, auth))
|
||||
{
|
||||
lib->credmgr->call_hook(lib->credmgr,
|
||||
CRED_HOOK_POLICY_VIOLATION, issuer);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,6 +691,8 @@ METHOD(cert_validator_t, validate, bool,
|
||||
case VALIDATION_REVOKED:
|
||||
case VALIDATION_ON_HOLD:
|
||||
/* has already been logged */
|
||||
lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_REVOKED,
|
||||
subject);
|
||||
return FALSE;
|
||||
case VALIDATION_SKIPPED:
|
||||
DBG2(DBG_CFG, "ocsp check skipped, no ocsp found");
|
||||
@@ -711,6 +713,8 @@ METHOD(cert_validator_t, validate, bool,
|
||||
case VALIDATION_REVOKED:
|
||||
case VALIDATION_ON_HOLD:
|
||||
/* has already been logged */
|
||||
lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_REVOKED,
|
||||
subject);
|
||||
return FALSE;
|
||||
case VALIDATION_FAILED:
|
||||
case VALIDATION_SKIPPED:
|
||||
@@ -720,6 +724,8 @@ METHOD(cert_validator_t, validate, bool,
|
||||
DBG1(DBG_CFG, "certificate status is unknown, crl is stale");
|
||||
break;
|
||||
}
|
||||
lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_VALIDATION_FAILED,
|
||||
subject);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user