Without this function the audit counters (krbLastFailedAuth,
krbLastSuccessfulAuth, krbLoginFailedCount) are not updated causing a
regression.

This function updates the counters unconditionally upon
successful/failed authentication (only if pre-auth is used which is the
default in FreeIPA).

A side effect of how this is implemented is that no other attributes are
updated when this happens so that replication is not kicked (because we
filter audit counters from replication to avoid replication storms), in
2.1.x updating these counters also ended up updating krbExtraData and
that caused replication to kick in.

Simo.

-- 
Simo Sorce * Red Hat, Inc * New York
>From f78034aaf3249532663962f38be21e9ef0f3c8cd Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Mon, 13 Feb 2012 12:15:07 -0500
Subject: [PATCH 3/5] ipa-kdb: add AS auditing support

Fixes: https://fedorahosted.org/freeipa/ticket/2334
---
 daemons/ipa-kdb/Makefile.am          |    1 +
 daemons/ipa-kdb/ipa_kdb.c            |    2 +-
 daemons/ipa-kdb/ipa_kdb.h            |   18 +++++-
 daemons/ipa-kdb/ipa_kdb_audit_as.c   |  120 ++++++++++++++++++++++++++++++++++
 daemons/ipa-kdb/ipa_kdb_passwords.c  |   79 ++--------------------
 daemons/ipa-kdb/ipa_kdb_principals.c |   36 ++++++-----
 daemons/ipa-kdb/ipa_kdb_pwdpolicy.c  |   89 +++++++++++++++++++++++++-
 util/ipa_pwd.h                       |    3 +
 8 files changed, 257 insertions(+), 91 deletions(-)
 create mode 100644 daemons/ipa-kdb/ipa_kdb_audit_as.c

diff --git a/daemons/ipa-kdb/Makefile.am b/daemons/ipa-kdb/Makefile.am
index 77b92e8db865eb4bcc8ced50e0ef0352b6ea334f..17c090418ec5a0e2a39d948dc385d509c5d05321 100644
--- a/daemons/ipa-kdb/Makefile.am
+++ b/daemons/ipa-kdb/Makefile.am
@@ -35,6 +35,7 @@ ipadb_la_SOURCES = 		\
 	ipa_kdb_pwdpolicy.c	\
 	ipa_kdb_mspac.c		\
 	ipa_kdb_delegation.c	\
+	ipa_kdb_audit_as.c	\
 	$(KRB5_UTIL_SRCS)	\
 	$(NULL)
 
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
index ca266d546b9529778eec1b27bc10e0150c952b1f..1dae4e6c1824c7936b0359d71dad809aed668d04 100644
--- a/daemons/ipa-kdb/ipa_kdb.c
+++ b/daemons/ipa-kdb/ipa_kdb.c
@@ -456,7 +456,7 @@ kdb_vftabl kdb_function_table = {
     NULL,                               /* check_transited_realms */
     NULL,                               /* check_policy_as */
     NULL,                               /* check_policy_tgs */
-    NULL,                               /* audit_as_req */
+    ipadb_audit_as_req,                 /* audit_as_req */
     NULL,                               /* refresh_config */
     ipadb_check_allowed_to_delegate     /* check_allowed_to_delegate */
 };
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
index 2531d03281e899152b9418b545dd7f1122cf61bb..22e28223c2c9c178c0041512a9d8b5621f590441 100644
--- a/daemons/ipa-kdb/ipa_kdb.h
+++ b/daemons/ipa-kdb/ipa_kdb.h
@@ -103,7 +103,8 @@ struct ipadb_e_data {
     time_t last_pwd_change;
     char *pw_policy_dn;
     char **pw_history;
-    struct ipapwd_policy pol;
+    struct ipapwd_policy *pol;
+    time_t last_admin_unlock;
 };
 
 struct ipadb_context *ipadb_get_context(krb5_context kcontext);
@@ -165,6 +166,11 @@ krb5_error_code ipadb_iterate(krb5_context kcontext,
                               krb5_pointer func_arg);
 
 /* POLICY FUNCTIONS */
+
+krb5_error_code ipadb_get_ipapwd_policy(struct ipadb_context *ipactx,
+                                        char *pw_policy_dn,
+                                        struct ipapwd_policy **pol);
+
 krb5_error_code ipadb_create_pwd_policy(krb5_context kcontext,
                                         osa_policy_ent_t policy);
 krb5_error_code ipadb_get_pwd_policy(krb5_context kcontext, char *name,
@@ -230,3 +236,13 @@ krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
                                                 krb5_const_principal client,
                                                 const krb5_db_entry *server,
                                                 krb5_const_principal proxy);
+
+/* AS AUDIT */
+
+void ipadb_audit_as_req(krb5_context kcontext,
+                        krb5_kdc_req *request,
+                        krb5_db_entry *client,
+                        krb5_db_entry *server,
+                        krb5_timestamp authtime,
+                        krb5_error_code error_code);
+
diff --git a/daemons/ipa-kdb/ipa_kdb_audit_as.c b/daemons/ipa-kdb/ipa_kdb_audit_as.c
new file mode 100644
index 0000000000000000000000000000000000000000..c71568c385595b3f6b9b595f4807ece8356349e0
--- /dev/null
+++ b/daemons/ipa-kdb/ipa_kdb_audit_as.c
@@ -0,0 +1,120 @@
+/*
+ * MIT Kerberos KDC database backend for FreeIPA
+ *
+ * Authors: Simo Sorce <sso...@redhat.com>
+ *
+ * Copyright (C) 2012  Simo Sorce, Red Hat
+ * see file 'COPYING' for use and warranty information
+ *
+ * This program is free software you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ipa_kdb.h"
+#include "ipa_pwd.h"
+
+void ipadb_audit_as_req(krb5_context kcontext,
+                        krb5_kdc_req *request,
+                        krb5_db_entry *client,
+                        krb5_db_entry *server,
+                        krb5_timestamp authtime,
+                        krb5_error_code error_code)
+{
+    struct ipadb_context *ipactx;
+    struct ipadb_e_data *ied;
+    krb5_error_code kerr;
+
+    if (!client) {
+        return;
+    }
+
+    if (error_code != 0 &&
+        error_code != KRB5KDC_ERR_PREAUTH_FAILED &&
+        error_code != KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+        return;
+    }
+
+    ipactx = ipadb_get_context(kcontext);
+    if (!ipactx) {
+        return;
+    }
+
+    ied = (struct ipadb_e_data *)client->e_data;
+    if (!ied) {
+        return;
+    }
+
+    if (!ied->pol) {
+        kerr = ipadb_get_ipapwd_policy(ipactx, ied->pw_policy_dn, &ied->pol);
+        if (kerr != 0) {
+            return;
+        }
+    }
+
+    client->mask = 0;
+
+    switch (error_code) {
+    case 0:
+        /* Check if preauth flag is specified (default), otherwise we have
+        * no data to know if auth was successful or not */
+        if (client->attributes & KRB5_KDB_REQUIRES_PRE_AUTH) {
+            if (client->fail_auth_count != 0) {
+                client->fail_auth_count = 0;
+                client->mask |= KMASK_FAIL_AUTH_COUNT;
+            }
+            client->last_success = authtime;
+            client->mask |= KMASK_LAST_SUCCESS;
+        }
+        break;
+
+    case KRB5KDC_ERR_PREAUTH_FAILED:
+    case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+
+        if (client->last_failed <= ied->last_admin_unlock) {
+            /* Reset fail_auth_count, and admin unlocked the account */
+            client->fail_auth_count = 0;
+            client->mask |= KMASK_FAIL_AUTH_COUNT;
+        }
+        if (ied->pol->lockout_duration != 0 &&
+            ied->pol->failcnt_interval != 0 &&
+            client->last_failed + ied->pol->failcnt_interval < authtime) {
+            /* Reset fail_auth_count, the interval's expired already */
+            client->fail_auth_count = 0;
+            client->mask |= KMASK_FAIL_AUTH_COUNT;
+        }
+
+        if (ied->pol->max_fail == 0 ||
+            client->fail_auth_count < ied->pol->max_fail) {
+            /* let's increase the fail counter */
+            client->fail_auth_count++;
+            client->mask |= KMASK_FAIL_AUTH_COUNT;
+        }
+        if (client->last_failed + ied->pol->lockout_duration > authtime) {
+            /* client already locked, nothing more to do */
+            break;
+        }
+        client->last_failed = authtime;
+        client->mask |= KMASK_LAST_FAILED;
+        break;
+    default:
+        return;
+    }
+
+    if (client->mask) {
+        kerr = ipadb_put_principal(kcontext, client, NULL);
+        if (kerr != 0) {
+            return;
+        }
+    }
+    client->mask = 0;
+}
diff --git a/daemons/ipa-kdb/ipa_kdb_passwords.c b/daemons/ipa-kdb/ipa_kdb_passwords.c
index 0bb7fa72496789241e27ecc852a1c6ede7f8e40a..c717e2031b6a8b5c19b54c42ac964edf9d5aad1a 100644
--- a/daemons/ipa-kdb/ipa_kdb_passwords.c
+++ b/daemons/ipa-kdb/ipa_kdb_passwords.c
@@ -24,73 +24,6 @@
 #include "ipa_pwd.h"
 #include <kadm5/kadm_err.h>
 
-static char *pw_policy_attrs[] = {
-    "krbmaxpwdlife",
-    "krbminpwdlife",
-    "krbpwdmindiffchars",
-    "krbpwdminlength",
-    "krbpwdhistorylength",
-
-    NULL
-};
-
-static krb5_error_code ipadb_get_pw_policy(struct ipadb_context *ipactx,
-                                           char *pw_policy_dn,
-                                           struct ipapwd_policy *pol)
-{
-    krb5_error_code kerr;
-    LDAPMessage *res = NULL;
-    LDAPMessage *lentry;
-    uint32_t result;
-    int ret;
-
-    kerr = ipadb_simple_search(ipactx, pw_policy_dn, LDAP_SCOPE_BASE,
-                               "(objectClass=*)", pw_policy_attrs, &res);
-    if (kerr) {
-        goto done;
-    }
-
-    lentry = ldap_first_entry(ipactx->lcontext, res);
-    if (!lentry) {
-        kerr = KRB5_KDB_INTERNAL_ERROR;
-        goto done;
-    }
-
-    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
-                                    "krbMinPwdLife", &result);
-    if (ret == 0) {
-        pol->min_pwd_life = result;
-    }
-
-    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
-                                    "krbMaxPwdLife", &result);
-    if (ret == 0) {
-        pol->max_pwd_life = result;
-    }
-
-    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
-                                    "krbPwdMinLength", &result);
-    if (ret == 0) {
-        pol->min_pwd_length = result;
-    }
-
-    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
-                                    "krbPwdHistoryLength", &result);
-    if (ret == 0) {
-        pol->history_length = result;
-    }
-
-    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
-                                    "krbPwdMinDiffChars", &result);
-    if (ret == 0) {
-        pol->min_complexity = result;
-    }
-
-done:
-    ldap_msgfree(res);
-    return kerr;
-}
-
 static krb5_error_code ipapwd_error_to_kerr(krb5_context context,
                                             enum ipapwd_error err)
 {
@@ -149,13 +82,11 @@ static krb5_error_code ipadb_check_pw_policy(krb5_context context,
         return ENOMEM;
     }
 
-    ied->pol.max_pwd_life = IPAPWD_DEFAULT_PWDLIFE;
-    ied->pol.min_pwd_length = IPAPWD_DEFAULT_MINLEN;
-    kerr = ipadb_get_pw_policy(ipactx, ied->pw_policy_dn, &ied->pol);
+    kerr = ipadb_get_ipapwd_policy(ipactx, ied->pw_policy_dn, &ied->pol);
     if (kerr != 0) {
         return kerr;
     }
-    ret = ipapwd_check_policy(&ied->pol, passwd, time(NULL),
+    ret = ipapwd_check_policy(ied->pol, passwd, time(NULL),
                               db_entry->expiration,
                               db_entry->pw_expiration,
                               ied->last_pwd_change,
@@ -302,7 +233,11 @@ krb5_error_code ipadb_get_pwd_expiration(krb5_context context,
     }
 
     if (truexp) {
-        *expire_time = mod_time + ied->pol.max_pwd_life;
+        if (ied->pol) {
+            *expire_time = mod_time + ied->pol->max_pwd_life;
+        } else {
+            *expire_time = mod_time + IPAPWD_DEFAULT_PWDLIFE;
+        }
     } else {
         /* not 'self', so reset */
         *expire_time = mod_time;
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
index 431fd27a4e187d818f2238954df45b09a645fa56..3540f0eea2ac5f9c36a7312f3676939f8611f6a9 100644
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
@@ -534,20 +534,6 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
         entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
     }
 
-    ret = ipadb_ldap_attr_to_time_t(lcontext, lentry,
-                                    "krbLastAdminUnlock", &restime);
-    if (ret == 0) {
-        krb5_int32 time32le = htole32((krb5_int32)restime);
-
-        kerr = ipadb_set_tl_data(entry,
-                                 KRB5_TL_LAST_ADMIN_UNLOCK,
-                                 sizeof(time32le),
-                                 (krb5_octet *)&time32le);
-        if (kerr) {
-            goto done;
-        }
-    }
-
     ied = calloc(1, sizeof(struct ipadb_e_data));
     if (!ied) {
         kerr = ENOMEM;
@@ -619,6 +605,22 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
         ied->last_pwd_change = restime;
     }
 
+    ret = ipadb_ldap_attr_to_time_t(lcontext, lentry,
+                                    "krbLastAdminUnlock", &restime);
+    if (ret == 0) {
+        krb5_int32 time32le = htole32((krb5_int32)restime);
+
+        kerr = ipadb_set_tl_data(entry,
+                                 KRB5_TL_LAST_ADMIN_UNLOCK,
+                                 sizeof(time32le),
+                                 (krb5_octet *)&time32le);
+        if (kerr) {
+            goto done;
+        }
+
+        ied->last_admin_unlock = restime;
+    }
+
     kerr = 0;
 
 done:
@@ -933,6 +935,7 @@ void ipadb_free_principal(krb5_context kcontext, krb5_db_entry *entry)
                     free(ied->pw_history[i]);
                 }
                 free(ied->pw_history);
+                free(ied->pol);
                 free(ied);
             }
         }
@@ -1618,9 +1621,10 @@ static krb5_error_code ipadb_entry_to_mods(krb5_context kcontext,
             }
         }
 
-        if (ied->ipa_user && ied->passwd && ied->pol.history_length) {
+        if (ied->ipa_user && ied->passwd &&
+            ied->pol && ied->pol->history_length) {
             ret = ipapwd_generate_new_history(ied->passwd, now,
-                                              ied->pol.history_length,
+                                              ied->pol->history_length,
                                               ied->pw_history,
                                               &new_history, &nh_len);
             if (ret) {
diff --git a/daemons/ipa-kdb/ipa_kdb_pwdpolicy.c b/daemons/ipa-kdb/ipa_kdb_pwdpolicy.c
index 46a0513307c859ff2cfef7ad58442edb1b9cc78d..03948029f1c69d9c554b6ca7a50f581f9ef374c7 100644
--- a/daemons/ipa-kdb/ipa_kdb_pwdpolicy.c
+++ b/daemons/ipa-kdb/ipa_kdb_pwdpolicy.c
@@ -21,10 +21,11 @@
  */
 
 #include "ipa_kdb.h"
+#include "ipa_pwd.h"
 
 #define POLICY_SEARCH_FILTER "(&(objectClass=krbPwdPolicy)(cn=%s))"
 
-static char *std_pwdpolicy_attrs[] = {
+char *std_pwdpolicy_attrs[] = {
     "krbmaxpwdlife",
     "krbminpwdlife",
     "krbpwdmindiffchars",
@@ -37,6 +38,92 @@ static char *std_pwdpolicy_attrs[] = {
     NULL
 };
 
+krb5_error_code ipadb_get_ipapwd_policy(struct ipadb_context *ipactx,
+                                        char *pw_policy_dn,
+                                        struct ipapwd_policy **_pol)
+{
+    struct ipapwd_policy *pol;
+    krb5_error_code kerr;
+    LDAPMessage *res = NULL;
+    LDAPMessage *lentry;
+    uint32_t result;
+    int ret;
+
+    pol = calloc(1, sizeof(struct ipapwd_policy));
+    if (!pol) {
+        return ENOMEM;
+    }
+
+    pol->max_pwd_life = IPAPWD_DEFAULT_PWDLIFE;
+    pol->min_pwd_length = IPAPWD_DEFAULT_MINLEN;
+
+    kerr = ipadb_simple_search(ipactx, pw_policy_dn, LDAP_SCOPE_BASE,
+                               "(objectClass=*)", std_pwdpolicy_attrs, &res);
+    if (kerr) {
+        goto done;
+    }
+
+    lentry = ldap_first_entry(ipactx->lcontext, res);
+    if (!lentry) {
+        kerr = KRB5_KDB_INTERNAL_ERROR;
+        goto done;
+    }
+
+    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
+                                    "krbMinPwdLife", &result);
+    if (ret == 0) {
+        pol->min_pwd_life = result;
+    }
+
+    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
+                                    "krbMaxPwdLife", &result);
+    if (ret == 0) {
+        pol->max_pwd_life = result;
+    }
+
+    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
+                                    "krbPwdMinLength", &result);
+    if (ret == 0) {
+        pol->min_pwd_length = result;
+    }
+
+    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
+                                    "krbPwdHistoryLength", &result);
+    if (ret == 0) {
+        pol->history_length = result;
+    }
+
+    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
+                                    "krbPwdMinDiffChars", &result);
+    if (ret == 0) {
+        pol->min_complexity = result;
+    }
+
+    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
+                                    "krbPwdMaxFailure", &result);
+    if (ret == 0) {
+        pol->max_fail = result;
+    }
+
+    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
+                                    "krbPwdFailureCountInterval", &result);
+    if (ret == 0) {
+        pol->failcnt_interval = result;
+    }
+
+    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
+                                    "krbPwdLockoutDuration", &result);
+    if (ret == 0) {
+        pol->lockout_duration = result;
+    }
+
+    *_pol = pol;
+
+done:
+    ldap_msgfree(res);
+    return kerr;
+}
+
 krb5_error_code ipadb_create_pwd_policy(krb5_context kcontext,
                                         osa_policy_ent_t policy)
 {
diff --git a/util/ipa_pwd.h b/util/ipa_pwd.h
index ecb82108485b9592e5c9adb4df10b272ef8837f8..2e537753e4b6b72cab19f3ce62c04f453e237664 100644
--- a/util/ipa_pwd.h
+++ b/util/ipa_pwd.h
@@ -47,6 +47,9 @@ struct ipapwd_policy {
     int min_pwd_length;
     int history_length;
     int min_complexity;
+    int max_fail;
+    int failcnt_interval;
+    int lockout_duration;
 };
 
 time_t ipapwd_gentime_to_time_t(char *timestr);
-- 
1.7.7.6

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to