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;
}
{
DBG1(DBG_CFG, "coupling new certificate '%Y' failed",
subject->get_subject(subject));
+ lib->credmgr->call_hook(lib->credmgr
+ CRED_HOOK_POLICY_VIOLATION, subject);
}
}
else
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);
}
/**
* 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
* 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 */
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)
{
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;
}
/**
{
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));
{
DBG1(DBG_CFG, "no issuer certificate found for \"%Y\"",
current->get_subject(current));
+ call_hook(this, CRED_HOOK_NO_ISSUER, current);
break;
}
}
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;
}
}
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)
{
.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(),
#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>
#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.
*
* The credential manager is the entry point of the credential framework. It
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.
*/
void (*destroy)(credential_manager_t *this);
{
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;
}
}
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");
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:
DBG1(DBG_CFG, "certificate status is unknown, crl is stale");
break;
}
+ lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_VALIDATION_FAILED,
+ subject);
}
return TRUE;
}