Add lockout-related performance tuning variables
authorGreg Hudson <ghudson@mit.edu>
Mon, 10 May 2010 22:42:04 +0000 (22:42 +0000)
committerGreg Hudson <ghudson@mit.edu>
Mon, 10 May 2010 22:42:04 +0000 (22:42 +0000)
The account lockout feature of krb5 1.8 came at a cost in database
accesses for principals requiring preauth, even if lockout is not
used.  Add dbmodules variables disable_last_success and
disable_lockout for the DB2 and LDAP back ends, allowing the admin to
recover the lost performance at the cost of new functionality.

(Unrelated documentation fix: document database_name as a DB2-specific
dbmodules variable instead of the realm variable it used to be.)

ticket: 6719

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24003 dc483132-0cff-0310-8789-dd5450dbe970

doc/admin.texinfo
src/include/k5-int.h
src/plugins/kdb/db2/kdb_db2.c
src/plugins/kdb/db2/kdb_db2.h
src/plugins/kdb/db2/lockout.c
src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
src/plugins/kdb/ldap/libkdb_ldap/lockout.c

index 1ec4685183cc47bd9d5f2a563f38d6c36fc2df88..7d2e79718784ab03f33889d6aaa724760bf3f00d 100644 (file)
@@ -1051,6 +1051,23 @@ For each section, the following tags may be specified in the subsection:
 @itemx db_library
 This tag indicates the name of the loadable database library. The value should be @samp{db2} for DB2 database and @samp{kldap} for LDAP database.
 
+@itemx database_name
+This DB2-specific tag indicates the location of the database.  The
+default is @* @code{@value{DefaultDatabaseName}}.
+
+@itemx disable_last_success
+If set to @code{true}, suppresses KDC updates to the ``Last successful
+authentication'' field of principal entries requiring preauthentication.
+Setting this flag may improve performance.  (Principal entries which do
+not require preauthentication never update the ``Last successful
+authentication'' field.)
+
+@itemx disable_lockout
+If set to @code{true}, suppresses KDC updates to the ``Last failed
+authentication'' and ``Failed password attempts'' fields of principal
+entries requiring preauthentication.  Setting this flag may improve
+performance, but also disables account lockout.
+
 @itemx ldap_kerberos_container_dn 
 This LDAP specific tag indicates the DN of the container object where the realm objects will be located.
 
@@ -1481,10 +1498,6 @@ database.  The default is @code{@value{DefaultAclFile}}.
 daemons @code{kadmind4} and @code{v5passwdd} use to authenticate to
 the database.  The default is @code{@value{DefaultAdminKeytab}}.
 
-@itemx database_name
-(String.)  Location of the Kerberos database for this realm.  The
-default is @* @code{@value{DefaultDatabaseName}}.
-
 @itemx default_principal_expiration
 (Absolute time string.)  Specifies the default expiration date of
 principals created in this realm.  The default value for this tag is
index 9a23a7e681fd325dda68fa02fa70494a7e3bff99..fc39acdc795f56c2c789451f671fa88a4aa00cb0 100644 (file)
@@ -203,6 +203,8 @@ typedef INT64_TYPE krb5_int64;
 #define KRB5_CONF_DEFAULT_PRINCIPAL_EXPIRATION   "default_principal_expiration"
 #define KRB5_CONF_DEFAULT_PRINCIPAL_FLAGS        "default_principal_flags"
 #define KRB5_CONF_DICT_FILE                   "dict_file"
+#define KRB5_CONF_DISABLE_LAST_SUCCESS        "disable_last_success"
+#define KRB5_CONF_DISABLE_LOCKOUT             "disable_lockout"
 #define KRB5_CONF_DNS_LOOKUP_KDC              "dns_lookup_kdc"
 #define KRB5_CONF_DNS_LOOKUP_REALM            "dns_lookup_realm"
 #define KRB5_CONF_DNS_FALLBACK                "dns_fallback"
index 713ed7aac84d6b6b4dcf4f2fde3a375863acab95..9c73c12dbcf2817a0e29f686ea31c521ee614688 100644 (file)
@@ -204,6 +204,7 @@ configure_context(krb5_context context, char *conf_section, char **db_args)
     krb5_db2_context *db_ctx;
     char **t_ptr, *opt = NULL, *val = NULL, *pval = NULL;
     profile_t profile = KRB5_DB_GET_PROFILE(context);
+    int bval;
 
     status = k5db2_init_context(context);
     if (status != 0)
@@ -252,6 +253,18 @@ configure_context(krb5_context context, char *conf_section, char **db_args)
         db_ctx->db_name = strdup(pval);
     }
 
+    status = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section,
+                                 KRB5_CONF_DISABLE_LAST_SUCCESS, FALSE, &bval);
+    if (status != 0)
+        goto cleanup;
+    db_ctx->disable_last_success = bval;
+
+    status = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section,
+                                 KRB5_CONF_DISABLE_LOCKOUT, FALSE, &bval);
+    if (status != 0)
+        goto cleanup;
+    db_ctx->disable_lockout = bval;
+
 cleanup:
     free(opt);
     free(val);
index 45958ee02c39317a5d60fa2ca8f15b2674a1ce18..7b4fcf405b17e6eb6bb481b61ed48015ca225e16 100644 (file)
@@ -46,7 +46,9 @@ typedef struct _krb5_db2_context {
     krb5_keyblock      *db_master_key; /* Master key of database */
     krb5_keylist_node *db_master_key_list;  /* Master key list of database */
     osa_adb_policy_t    policy_db;
-    krb5_boolean tempdb;
+    krb5_boolean        tempdb;
+    krb5_boolean        disable_last_success;
+    krb5_boolean        disable_lockout;
 } krb5_db2_context;
 
 #define KRB5_DB2_MAX_RETRY 5
index 498c0dea629768ab19b3ca7f70a9cff2b5ee28a0..ec22dce738d26f3c8f4da851183a80a6755976b3 100644 (file)
@@ -33,6 +33,7 @@
 #include <stdio.h>
 #include <errno.h>
 #include <kadm5/server_internal.h>
+#include "kdb5.h"
 #include "kdb_db2.h"
 
 /*
@@ -120,6 +121,10 @@ krb5_db2_lockout_check_policy(krb5_context context,
     krb5_kvno max_fail = 0;
     krb5_deltat failcnt_interval = 0;
     krb5_deltat lockout_duration = 0;
+    krb5_db2_context *db_ctx = context->dal_handle->db_context;
+
+    if (db_ctx->disable_lockout)
+        return 0;
 
     code = lookup_lockout_policy(context, entry, &max_fail,
                                  &failcnt_interval,
@@ -144,6 +149,8 @@ krb5_db2_lockout_audit(krb5_context context,
     krb5_deltat failcnt_interval = 0;
     krb5_deltat lockout_duration = 0;
     int nentries = 1;
+    krb5_db2_context *db_ctx = context->dal_handle->db_context;
+    krb5_boolean need_update = FALSE;
 
     switch (status) {
     case 0:
@@ -158,38 +165,43 @@ krb5_db2_lockout_audit(krb5_context context,
         return 0;
     }
 
-    code = lookup_lockout_policy(context, entry, &max_fail,
-                                 &failcnt_interval,
-                                 &lockout_duration);
-    if (code != 0)
-        return code;
-
-    assert (!locked_check_p(context, stamp, max_fail, lockout_duration, entry));
+    if (!db_ctx->disable_lockout) {
+        code = lookup_lockout_policy(context, entry, &max_fail,
+                                     &failcnt_interval, &lockout_duration);
+        if (code != 0)
+            return code;
+    }
 
+    /* Only mark the authentication as successful if the entry
+     * required preauthentication, otherwise we have no idea. */
     if (status == 0 && (entry->attributes & KRB5_KDB_REQUIRES_PRE_AUTH)) {
-        /*
-         * Only mark the authentication as successful if the entry
-         * required preauthentication, otherwise we have no idea.
-         */
-        entry->fail_auth_count = 0;
-        entry->last_success = stamp;
-    } else if (status == KRB5KDC_ERR_PREAUTH_FAILED ||
-               status == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+        if (!db_ctx->disable_lockout && entry->fail_auth_count != 0) {
+            entry->fail_auth_count = 0;
+            need_update = TRUE;
+        }
+        if (!db_ctx->disable_last_success) {
+            entry->last_success = stamp;
+            need_update = TRUE;
+        }
+    } else if (!db_ctx->disable_lockout &&
+               (status == KRB5KDC_ERR_PREAUTH_FAILED ||
+                status == KRB5KRB_AP_ERR_BAD_INTEGRITY)) {
         if (failcnt_interval != 0 &&
             stamp > entry->last_failed + failcnt_interval) {
-            /* Reset fail_auth_count after failcnt_interval */
+            /* Reset fail_auth_count after failcnt_interval. */
             entry->fail_auth_count = 0;
         }
 
         entry->last_failed = stamp;
         entry->fail_auth_count++;
-    } else
-        return 0; /* nothing to do */
+        need_update = TRUE;
+    }
 
-    code = krb5_db2_db_put_principal(context, entry,
-                                     &nentries, NULL);
-    if (code != 0)
-        return code;
+    if (need_update) {
+        code = krb5_db2_db_put_principal(context, entry, &nentries, NULL);
+        if (code != 0)
+            return code;
+    }
 
     return 0;
 }
index 2130f8bc0a5bc570a9d07d16dbd25bf2ae86f1f6..95909f6bebf65f182947543f998b2bab319dff4c 100644 (file)
@@ -223,6 +223,8 @@ typedef struct _krb5_ldap_context {
     k5_mutex_t                    hndl_lock;
     krb5_ldap_krbcontainer_params *krbcontainer;
     krb5_ldap_realm_params        *lrparams;
+    krb5_boolean                  disable_last_success;
+    krb5_boolean                  disable_lockout;
     krb5_context                  kcontext;   /* to set the error code and message */
 } krb5_ldap_context;
 
index 65ae887345d342900670c89922701d77ca190925..c3cb185d0e5d875db4cf15de40b8d8baee623f67 100644 (file)
@@ -105,6 +105,37 @@ prof_get_integer_def(krb5_context ctx, const char *conf_section,
     return 0;
 }
 
+/* Get integer or string values from the config section, falling back
+   to the default section, then to hard-coded values.  */
+static errcode_t
+prof_get_boolean_def(krb5_context ctx, const char *conf_section,
+                     const char *name, krb5_boolean dfl, krb5_boolean *out)
+{
+    errcode_t err;
+    int out_temp = 0;
+
+    err = profile_get_boolean(ctx->profile, KDB_MODULE_SECTION, conf_section,
+                              name, -1, &out_temp);
+    if (err) {
+        krb5_set_error_message(ctx, err, "Error reading '%s' attribute: %s",
+                               name, error_message(err));
+        return err;
+    }
+    if (out_temp != -1) {
+        *out = out_temp;
+        return 0;
+    }
+    err = profile_get_boolean(ctx->profile, KDB_MODULE_DEF_SECTION, name, 0,
+                              dfl, &out_temp);
+    if (err) {
+        krb5_set_error_message(ctx, err, "Error reading '%s' attribute: %s",
+                               name, error_message(err));
+        return err;
+    }
+    *out = out_temp;
+    return 0;
+}
+
 /* We don't have non-null defaults in any of our calls, so don't
    bother with the extra argument.  */
 static errcode_t
@@ -309,6 +340,16 @@ krb5_ldap_read_server_params(krb5_context context, char *conf_section,
         }
     }
 
+    if ((st = prof_get_boolean_def(context, conf_section,
+                                   KRB5_CONF_DISABLE_LAST_SUCCESS, FALSE,
+                                   &ldap_context->disable_last_success)))
+        goto cleanup;
+
+    if ((st = prof_get_boolean_def(context, conf_section,
+                                   KRB5_CONF_DISABLE_LOCKOUT, FALSE,
+                                   &ldap_context->disable_lockout)))
+        goto cleanup;
+
 cleanup:
     return(st);
 }
index 020c77a945ca38087a09b6cc312a24410ae04347..323963e8dd01bfa0944a34e3e5d9c96343680153 100644 (file)
@@ -113,10 +113,16 @@ krb5_ldap_lockout_check_policy(krb5_context context,
                                krb5_timestamp stamp)
 {
     krb5_error_code code;
+    kdb5_dal_handle *dal_handle;
+    krb5_ldap_context *ldap_context;
     krb5_kvno max_fail = 0;
     krb5_deltat failcnt_interval = 0;
     krb5_deltat lockout_duration = 0;
 
+    SETUP_CONTEXT();
+    if (ldap_context->disable_lockout)
+        return 0;
+
     code = lookup_lockout_policy(context, entry, &max_fail,
                                  &failcnt_interval,
                                  &lockout_duration);
@@ -136,11 +142,15 @@ krb5_ldap_lockout_audit(krb5_context context,
                         krb5_error_code status)
 {
     krb5_error_code code;
+    kdb5_dal_handle *dal_handle;
+    krb5_ldap_context *ldap_context;
     krb5_kvno max_fail = 0;
     krb5_deltat failcnt_interval = 0;
     krb5_deltat lockout_duration = 0;
     int nentries = 1;
 
+    SETUP_CONTEXT();
+
     switch (status) {
     case 0:
     case KRB5KDC_ERR_PREAUTH_FAILED:
@@ -150,26 +160,32 @@ krb5_ldap_lockout_audit(krb5_context context,
         return 0;
     }
 
-    code = lookup_lockout_policy(context, entry, &max_fail,
-                                 &failcnt_interval,
-                                 &lockout_duration);
-    if (code != 0)
-        return code;
+    if (!ldap_context->disable_lockout) {
+        code = lookup_lockout_policy(context, entry, &max_fail,
+                                     &failcnt_interval,
+                                     &lockout_duration);
+        if (code != 0)
+            return code;
+    }
 
     entry->mask = 0;
 
     assert (!locked_check_p(context, stamp, max_fail, lockout_duration, entry));
 
+    /* Only mark the authentication as successful if the entry
+     * required preauthentication, otherwise we have no idea. */
     if (status == 0 && (entry->attributes & KRB5_KDB_REQUIRES_PRE_AUTH)) {
-        /*
-         * Only mark the authentication as successful if the entry
-         * required preauthentication, otherwise we have no idea.
-         */
-        entry->fail_auth_count = 0;
-        entry->last_success = stamp;
-        entry->mask |= KADM5_FAIL_AUTH_COUNT | KADM5_LAST_SUCCESS;
-    } else if (status == KRB5KDC_ERR_PREAUTH_FAILED ||
-               status == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+        if (!ldap_context->disable_lockout && entry->fail_auth_count != 0) {
+            entry->fail_auth_count = 0;
+            entry->mask |= KADM5_FAIL_AUTH_COUNT;
+        }
+        if (!ldap_context->disable_last_success) {
+            entry->last_success = stamp;
+            entry->mask |= KADM5_LAST_SUCCESS;
+        }
+    } else if (!ldap_context->disable_lockout &&
+               (status == KRB5KDC_ERR_PREAUTH_FAILED ||
+                status == KRB5KRB_AP_ERR_BAD_INTEGRITY)) {
         if (failcnt_interval != 0 &&
             stamp > entry->last_failed + failcnt_interval) {
             /* Reset fail_auth_count after failcnt_interval */
@@ -182,8 +198,7 @@ krb5_ldap_lockout_audit(krb5_context context,
     }
 
     if (entry->mask) {
-        code = krb5_ldap_put_principal(context, entry,
-                                       &nentries, NULL);
+        code = krb5_ldap_put_principal(context, entry, &nentries, NULL);
         if (code != 0)
             return code;
     }