summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel McCallum <npmccallum@redhat.com>2014-02-06 10:56:46 -0500
committerPetr Viktorin <pviktori@redhat.com>2014-02-14 16:03:24 +0100
commitfd55da9a27f76611b01c38c2741c13652d6a3e60 (patch)
tree4808a6b12a668fef703213578256ecd2a8245d7c
parenta91c0972b992dbd15e78f2ba6982768ac958e4bd (diff)
downloadfreeipa.git-fd55da9a27f76611b01c38c2741c13652d6a3e60.tar.gz
freeipa.git-fd55da9a27f76611b01c38c2741c13652d6a3e60.tar.xz
freeipa.git-fd55da9a27f76611b01c38c2741c13652d6a3e60.zip
ipa-kdb: validate that an OTP user has tokens
This handles the case where a user is configured for OTP in ipaUserAuthType, but the user has not yet created any tokens. Until the user creates tokens, the user should still be able to log in via password. This logic already exists in LDAP, but ipa-kdb needs to perform the same validation to know what data to return to the KDC. https://fedorahosted.org/freeipa/ticket/4154 Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
-rw-r--r--daemons/ipa-kdb/ipa_kdb.c10
-rw-r--r--daemons/ipa-kdb/ipa_kdb.h6
-rw-r--r--daemons/ipa-kdb/ipa_kdb_principals.c144
3 files changed, 135 insertions, 25 deletions
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
index c807bbcf..0f3996cd 100644
--- a/daemons/ipa-kdb/ipa_kdb.c
+++ b/daemons/ipa-kdb/ipa_kdb.c
@@ -186,13 +186,13 @@ static const struct {
{ }
};
-void ipadb_get_user_auth(LDAP *lcontext, LDAPMessage *le,
- enum ipadb_user_auth *userauth)
+void ipadb_parse_user_auth(LDAP *lcontext, LDAPMessage *le,
+ enum ipadb_user_auth *userauth)
{
struct berval **vals;
int i, j;
- *userauth = IPADB_USER_AUTH_EMPTY;
+ *userauth = IPADB_USER_AUTH_NONE;
vals = ldap_get_values_len(lcontext, le, IPA_USER_AUTH_TYPE);
if (!vals)
return;
@@ -205,6 +205,8 @@ void ipadb_get_user_auth(LDAP *lcontext, LDAPMessage *le,
}
}
}
+
+ ldap_value_free_len(vals);
}
int ipadb_get_global_configs(struct ipadb_context *ipactx)
@@ -239,7 +241,7 @@ int ipadb_get_global_configs(struct ipadb_context *ipactx)
}
/* Check for permitted authentication types. */
- ipadb_get_user_auth(ipactx->lcontext, res, &ipactx->user_auth);
+ ipadb_parse_user_auth(ipactx->lcontext, res, &ipactx->user_auth);
vals = ldap_get_values_len(ipactx->lcontext, first,
"ipaConfigString");
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
index 5ad256b0..6c036e3b 100644
--- a/daemons/ipa-kdb/ipa_kdb.h
+++ b/daemons/ipa-kdb/ipa_kdb.h
@@ -80,7 +80,7 @@
struct ipadb_mspac;
enum ipadb_user_auth {
- IPADB_USER_AUTH_EMPTY = 0,
+ IPADB_USER_AUTH_NONE = 0,
IPADB_USER_AUTH_DISABLED = 1 << 0,
IPADB_USER_AUTH_PASSWORD = 1 << 1,
IPADB_USER_AUTH_RADIUS = 1 << 2,
@@ -275,5 +275,5 @@ void ipadb_audit_as_req(krb5_context kcontext,
krb5_error_code error_code);
/* AUTH METHODS */
-void ipadb_get_user_auth(LDAP *lcontext, LDAPMessage *le,
- enum ipadb_user_auth *user_auth);
+void ipadb_parse_user_auth(LDAP *lcontext, LDAPMessage *le,
+ enum ipadb_user_auth *user_auth);
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
index a5209522..8a8d67bb 100644
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
@@ -66,6 +66,7 @@ static char *std_principal_attrs[] = {
"passwordHistory",
IPA_KRB_AUTHZ_DATA_ATTR,
IPA_USER_AUTH_TYPE,
+ "ipatokenRadiusConfigLink",
"objectClass",
NULL
@@ -224,6 +225,122 @@ static int ipadb_ldap_attr_to_key_data(LDAP *lcontext, LDAPMessage *le,
return ret;
}
+static void ipadb_validate_otp(struct ipadb_context *ipactx,
+ LDAPMessage *lentry,
+ enum ipadb_user_auth *ua)
+{
+ static const char *attrs[] = { "dn", NULL };
+ static const char *dttmpl = "%Y%m%d%H%M%SZ";
+ static const char *ftmpl = "(&"
+ "(objectClass=ipaToken)(ipatokenOwner=%s)"
+ "(|(ipatokenNotBefore<=%s)(!(ipatokenNotBefore=*)))"
+ "(|(ipatokenNotAfter>=%s)(!(ipatokenNotAfter=*)))"
+ "(|(ipatokenDisabled=FALSE)(!(ipatokenDisabled=*)))"
+ ")";
+ krb5_error_code kerr = 0;
+ LDAPMessage *res = NULL;
+ char datetime[16] = {};
+ char *filter = NULL;
+ struct tm tm = {};
+ char *dn = NULL;
+ time_t now = 0;
+ int count = 0;
+
+ if (!(*ua & IPADB_USER_AUTH_OTP))
+ return;
+
+ /* Get the current time. */
+ if (time(&now) == (time_t) -1)
+ return;
+ if (gmtime_r(&now, &tm) == NULL)
+ return;
+
+ /* Make the current time string. */
+ if (strftime(datetime, sizeof(datetime), dttmpl, &tm) == 0)
+ return;
+
+ /* Make the filter. */
+ dn = ldap_get_dn(ipactx->lcontext, lentry);
+ if (dn == NULL)
+ return;
+ count = asprintf(&filter, ftmpl, dn, datetime, datetime);
+ ldap_memfree(dn);
+ if (count < 0)
+ return;
+
+ /* Fetch the active token list. */
+ kerr = ipadb_simple_search(ipactx, ipactx->base, LDAP_SCOPE_SUBTREE,
+ filter, (char**) attrs, &res);
+ free(filter);
+ if (kerr != 0 || res == NULL)
+ return;
+
+ /* Count the number of active tokens. */
+ count = ldap_count_entries(ipactx->lcontext, res);
+ ldap_msgfree(res);
+
+ /* If the user is configured for OTP, but has no active tokens, remove
+ * OTP from the list since the user obviously can't log in this way. */
+ if (count == 0)
+ *ua &= ~IPADB_USER_AUTH_OTP;
+}
+
+static void ipadb_validate_radius(struct ipadb_context *ipactx,
+ LDAPMessage *lentry,
+ enum ipadb_user_auth *ua)
+{
+ struct berval **vals;
+
+ if (!(*ua & IPADB_USER_AUTH_RADIUS))
+ return;
+
+ /* Ensure that the user has a link to a RADIUS config. */
+ vals = ldap_get_values_len(ipactx->lcontext, lentry,
+ "ipatokenRadiusConfigLink");
+ if (vals == NULL || vals[0] == NULL)
+ *ua &= ~IPADB_USER_AUTH_RADIUS;
+
+ if (vals != NULL)
+ 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;
+
+ /* If any other mechanism has passed validation, don't use password. */
+ else if (*ua & ~IPADB_USER_AUTH_PASSWORD)
+ *ua &= ~IPADB_USER_AUTH_PASSWORD;
+}
+
+static enum ipadb_user_auth ipadb_get_user_auth(struct ipadb_context *ipactx,
+ LDAPMessage *lentry)
+{
+ enum ipadb_user_auth ua = IPADB_USER_AUTH_NONE;
+
+ /* Get the user's user_auth settings. */
+ ipadb_parse_user_auth(ipactx->lcontext, lentry, &ua);
+
+ /* If the disabled flag is set, ignore everything else. */
+ if ((ua | ipactx->user_auth) & IPADB_USER_AUTH_DISABLED)
+ return IPADB_USER_AUTH_DISABLED;
+
+ /* Determine which user_auth policy is active: user or global. */
+ if (ua == IPADB_USER_AUTH_NONE)
+ ua = ipactx->user_auth;
+
+ /* Perform flag validation. */
+ ipadb_validate_otp(ipactx, lentry, &ua);
+ ipadb_validate_radius(ipactx, lentry, &ua);
+ ipadb_validate_password(ipactx, lentry, &ua);
+
+ return ua;
+}
+
static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
char *principal,
LDAPMessage *lentry,
@@ -231,9 +348,8 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
uint32_t *polmask)
{
krb5_octet otp_string[] = {'o', 't', 'p', 0, '[', ']', 0 };
- enum ipadb_user_auth user_ua = IPADB_USER_AUTH_EMPTY;
- enum ipadb_user_auth *active_ua = &user_ua;
struct ipadb_context *ipactx;
+ enum ipadb_user_auth ua;
LDAP *lcontext;
krb5_db_entry *entry;
struct ipadb_e_data *ied;
@@ -267,16 +383,8 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
entry->magic = KRB5_KDB_MAGIC_NUMBER;
entry->len = KRB5_KDB_V1_BASE_LENGTH;
- /* Get the user's user_auth settings. */
- ipadb_get_user_auth(ipactx->lcontext, lentry, &user_ua);
-
- /* TODO: Should we confirm the existence of ipatokenRadiusConfigLink in
- * the case of RADIUS? Existence of a token for OTP? */
-
- /* Determine which user_auth policy is active: user or global. */
- if ((ipactx->user_auth & IPADB_USER_AUTH_DISABLED)
- || user_ua == IPADB_USER_AUTH_EMPTY)
- active_ua = &ipactx->user_auth;
+ /* Get User Auth configuration. */
+ ua = ipadb_get_user_auth(ipactx, lentry);
/* ignore mask for now */
@@ -410,8 +518,7 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
switch (ret) {
case 0:
/* Only set a principal's key if password auth should be used. */
- if ((*active_ua & ~IPADB_USER_AUTH_DISABLED) != IPADB_USER_AUTH_EMPTY
- && !(*active_ua & IPADB_USER_AUTH_PASSWORD)) {
+ if (!(ua & IPADB_USER_AUTH_PASSWORD)) {
/* This is the same behavior as ENOENT below. */
ipa_krb5_free_key_data(res_key_data, result);
break;
@@ -551,10 +658,11 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
}
/* If enabled, set the otp user string, enabling otp. */
- if ((*active_ua & (IPADB_USER_AUTH_RADIUS | IPADB_USER_AUTH_OTP)) &&
- !(*active_ua & IPADB_USER_AUTH_DISABLED)) {
- ret = ipadb_set_tl_data(entry, KRB5_TL_STRING_ATTRS,
- sizeof(otp_string), otp_string);
+ if (ua & (IPADB_USER_AUTH_RADIUS | IPADB_USER_AUTH_OTP)) {
+ kerr = ipadb_set_tl_data(entry, KRB5_TL_STRING_ATTRS,
+ sizeof(otp_string), otp_string);
+ if (kerr)
+ goto done;
}
kerr = 0;