Implement user pre-authentication control with kdcpolicy plugin

We created a Kerberos kdcpolicy plugin to enforce user
pre-authentication policy for newly added pkinit and hardened policy.

In the past version of freeIPA, password enforcement exists but was done
by removing key data for a principal while parsing LDAP entry for it.
This hack is also removed and is now also enforced by kdcpolicy plugin
instead.

Resolves: https://pagure.io/freeipa/issue/8001
Signed-off-by: Changmin Teng <cteng@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Simo Sorce <ssorce@redhat.com>
Reviewed-By: Robbie Harwood <rharwood@redhat.com>
This commit is contained in:
Changmin Teng 2019-08-05 17:49:28 -04:00 committed by Alexander Bokovoy
parent 179c8f4009
commit 15ff9c8fec
3 changed files with 73 additions and 17 deletions

View File

@ -141,6 +141,7 @@ struct ipadb_e_data {
time_t last_admin_unlock;
char **authz_data;
bool has_tktpolaux;
enum ipadb_user_auth user_auth;
};
struct ipadb_context *ipadb_get_context(krb5_context kcontext);

View File

@ -18,12 +18,82 @@ ipa_kdcpolicy_check_as(krb5_context context, krb5_kdcpolicy_moddata moddata,
const char **status, krb5_deltat *lifetime_out,
krb5_deltat *renew_lifetime_out)
{
krb5_error_code kerr;
enum ipadb_user_auth ua;
struct ipadb_e_data *ied;
int valid_auth_indicators = 0;
*status = NULL;
*lifetime_out = 0;
*renew_lifetime_out = 0;
krb5_klog_syslog(LOG_INFO, "IPA kdcpolicy: checking AS-REQ.");
ied = (struct ipadb_e_data *)client->e_data;
if (ied == NULL || ied->magic != IPA_E_DATA_MAGIC) {
/* e-data is not availble, getting user auth from LDAP */
krb5_klog_syslog(LOG_INFO, "IPA kdcpolicy: client e_data not availble. Try fetching...");
kerr = ipadb_get_principal(context, request->client, KRB5_KDB_FLAG_ALIAS_OK, &client);
if (kerr != 0) {
krb5_klog_syslog(LOG_ERR, "IPA kdcpolicy: ipadb_find_principal failed.");
return kerr;
}
ied = (struct ipadb_e_data *)client->e_data;
if (ied == NULL && ied->magic != IPA_E_DATA_MAGIC) {
krb5_klog_syslog(LOG_ERR, "IPA kdcpolicy: client e_data fetching failed.");
return EINVAL;
}
}
ua = ied->user_auth;
/* If no mechanisms are set, allow every auth method */
if (ua == IPADB_USER_AUTH_NONE) {
return 0;
}
/* For each auth indicator, see if it is allowed for that user */
for (int i = 0; auth_indicators[i] != NULL; i++) {
const char *auth_indicator = auth_indicators[i];
if (strcmp(auth_indicator, "otp") == 0) {
valid_auth_indicators++;
if (!(ua & IPADB_USER_AUTH_OTP)) {
*status = "OTP pre-authentication not allowed for this user.";
return KRB5KDC_ERR_POLICY;
}
} else if (strcmp(auth_indicator, "radius") == 0) {
valid_auth_indicators++;
if (!(ua & IPADB_USER_AUTH_RADIUS)) {
*status = "OTP pre-authentication not allowed for this user.";
return KRB5KDC_ERR_POLICY;
}
} else if (strcmp(auth_indicator, "pkinit") == 0) {
valid_auth_indicators++;
if (!(ua & IPADB_USER_AUTH_PKINIT)) {
*status = "PKINIT pre-authentication not allowed for this user.";
return KRB5KDC_ERR_POLICY;
}
} else if (strcmp(auth_indicator, "hardened") == 0) {
valid_auth_indicators++;
/* Allow hardened even if only password pre-auth is allowed */
if (!(ua & (IPADB_USER_AUTH_HARDENED | IPADB_USER_AUTH_PASSWORD))) {
*status = "Password pre-authentication not not allowed for this user.";
return KRB5KDC_ERR_POLICY;
}
}
}
/* There is no auth indicator assigned for non-hardened password authentication
* so we assume password is used when no supported indicator exists */
if (!valid_auth_indicators) {
if (!(ua & IPADB_USER_AUTH_PASSWORD)) {
*status = "Non-hardened password authentication not allowed for this user.";
return KRB5KDC_ERR_POLICY;
}
}
return 0;
}

View File

@ -318,15 +318,6 @@ static void ipadb_validate_radius(struct ipadb_context *ipactx,
ldap_value_free_len(vals);
}
static void ipadb_validate_password(struct ipadb_context *ipactx,
LDAPMessage *lentry,
enum ipadb_user_auth *ua)
{
/* If no mechanisms are set, use password. */
if (*ua == IPADB_USER_AUTH_NONE)
*ua |= IPADB_USER_AUTH_PASSWORD;
}
static enum ipadb_user_auth ipadb_get_user_auth(struct ipadb_context *ipactx,
LDAPMessage *lentry)
{
@ -354,7 +345,6 @@ static enum ipadb_user_auth ipadb_get_user_auth(struct ipadb_context *ipactx,
/* Perform flag validation. */
ipadb_validate_otp(ipactx, lentry, &ua);
ipadb_validate_radius(ipactx, lentry, &ua);
ipadb_validate_password(ipactx, lentry, &ua);
return ua;
}
@ -708,13 +698,6 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
&res_key_data, &result, &mkvno);
switch (ret) {
case 0:
/* Only set a principal's key if password auth should be used. */
if (!(ua & IPADB_USER_AUTH_PASSWORD)) {
/* This is the same behavior as ENOENT below. */
ipa_krb5_free_key_data(res_key_data, result);
break;
}
entry->key_data = res_key_data;
entry->n_key_data = result;
if (mkvno) {
@ -851,6 +834,8 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
ied->authz_data = authz_data_list;
}
ied->user_auth = ua;
/* If enabled, set the otp user string, enabling otp. */
if (ua & IPADB_USER_AUTH_OTP) {
kerr = ipadb_set_tl_data(entry, KRB5_TL_STRING_ATTRS,