summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/kdb/db2/Makefile.in12
-rw-r--r--src/plugins/kdb/db2/db2_exp.c10
-rw-r--r--src/plugins/kdb/db2/kdb_db2.c241
-rw-r--r--src/plugins/kdb/db2/kdb_db2.h22
-rw-r--r--src/plugins/kdb/db2/kdb_ext.c99
-rw-r--r--src/plugins/kdb/db2/lockout.c196
-rw-r--r--src/plugins/kdb/db2/pol_xdr.c26
-rw-r--r--src/plugins/kdb/db2/policy_db.h1
-rw-r--r--src/plugins/kdb/ldap/ldap_exp.c6
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/Makefile.in8
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/kdb_ext.c99
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c84
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h23
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c7
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/kerberos.ldif38
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/kerberos.schema30
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h9
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c60
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_pwd_policy.c24
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_service_rights.c12
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports1
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/lockout.c192
22 files changed, 1122 insertions, 78 deletions
diff --git a/src/plugins/kdb/db2/Makefile.in b/src/plugins/kdb/db2/Makefile.in
index dd3045c168..b2532a4fd0 100644
--- a/src/plugins/kdb/db2/Makefile.in
+++ b/src/plugins/kdb/db2/Makefile.in
@@ -25,7 +25,7 @@ DB_DEPS-redirect = $(BUILDTOP)/include/db.h
DB_LIB = @DB_LIB@
KDB5_DB_LIB = @KDB5_DB_LIB@
DB_DEPLIB = $(DB_DEPLIB-@DB_VERSION@)
-DB_DEPLIB-k5 = $(TOPLIBD)/libdb$(DEPLIBEXT)
+DB_DEPLIB-k5 = $(TOPLIBD)/libdb$(DEPLIBEXT) $(KADMSRV_DEPLIBS)
DB_DEPLIB-sys =
LIBBASE=db2
@@ -39,7 +39,7 @@ SHLIB_EXPDEPS = \
$(GSSRPC_DEPLIBS) \
$(TOPLIBD)/libk5crypto$(SHLIBEXT) \
$(TOPLIBD)/libkrb5$(SHLIBEXT)
-SHLIB_EXPLIBS= $(GSSRPC_LIBS) -lkrb5 -lcom_err -lk5crypto $(KDB5_DB_LIB) $(SUPPORT_LIB) $(LIBS) @DB_EXTRA_LIBS@
+SHLIB_EXPLIBS= $(GSSRPC_LIBS) -lkrb5 -lcom_err -lk5crypto $(KDB5_DB_LIB) $(KADMSRV_LIBS) $(SUPPORT_LIB) $(LIBS) @DB_EXTRA_LIBS@
SHLIB_DIRS=-L$(TOPLIBD)
SHLIB_RDIRS=$(KRB5_LIBDIR)
@@ -56,8 +56,10 @@ SRCS= \
$(srcdir)/adb_openclose.c \
$(srcdir)/adb_policy.c \
$(srcdir)/kdb_db2.c \
+ $(srcdir)/kdb_ext.c \
$(srcdir)/pol_xdr.c \
- $(srcdir)/db2_exp.c
+ $(srcdir)/db2_exp.c \
+ $(srcdir)/lockout.c
STOBJLISTS=OBJS.ST $(DBOBJLISTS)
STLIBOBJS= \
@@ -65,8 +67,10 @@ STLIBOBJS= \
adb_openclose.o \
adb_policy.o \
kdb_db2.o \
+ kdb_ext.o \
pol_xdr.o \
- db2_exp.o
+ db2_exp.o \
+ lockout.o
all-unix:: all-liblinks
install-unix:: install-libs
diff --git a/src/plugins/kdb/db2/db2_exp.c b/src/plugins/kdb/db2/db2_exp.c
index 85864ac6fe..2e1f3b5198 100644
--- a/src/plugins/kdb/db2/db2_exp.c
+++ b/src/plugins/kdb/db2/db2_exp.c
@@ -197,6 +197,13 @@ WRAP_K (krb5_db2_promote_db,
( krb5_context kcontext, char *conf_section, char **db_args ),
(kcontext, conf_section, db_args));
+WRAP_K (krb5_db2_invoke,
+ (krb5_context kcontext,
+ unsigned int method,
+ const krb5_data *request,
+ krb5_data *response),
+ (kcontext, method, request, response));
+
static krb5_error_code
hack_init ()
{
@@ -256,5 +263,6 @@ kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_db2, kdb_function_table) = {
/* get_master_key_list */ wrap_krb5_db2_db_get_mkey_list,
/* blah blah blah */ 0,0,0,0,0,0,0,0,
/* promote_db */ wrap_krb5_db2_promote_db,
- 0,0,0,
+ 0, 0,
+ /* invoke */ wrap_krb5_db2_invoke
};
diff --git a/src/plugins/kdb/db2/kdb_db2.c b/src/plugins/kdb/db2/kdb_db2.c
index a947f2b037..b987039d15 100644
--- a/src/plugins/kdb/db2/kdb_db2.c
+++ b/src/plugins/kdb/db2/kdb_db2.c
@@ -1,7 +1,7 @@
/*
* lib/kdb/kdb_db2.c
*
- * Copyright 1997,2006,2007,2008 by the Massachusetts Institute of Technology.
+ * Copyright 1997,2006,2007-2009 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
@@ -68,8 +68,6 @@
#define KDB_DB2_DATABASE_NAME "database_name"
-#include "kdb_db2.h"
-
static char *gen_dbsuffix(char *, char *);
static krb5_error_code krb5_db2_db_start_update(krb5_context);
@@ -774,7 +772,7 @@ destroy_file_suffix(char *dbname, char *suffix)
char *filename;
struct stat statb;
int nb, fd;
- unsigned int j;
+ int j;
off_t pos;
char buf[BUFSIZ];
char zbuf[BUFSIZ];
@@ -1315,6 +1313,9 @@ krb5_db2_open(krb5_context kcontext,
else if (!opt && !strcmp(val, "temporary") ) {
tempdb = 1;
}
+ else if (!opt && !strcmp(val, "merge_nra")) {
+ ;
+ }
/* ignore hash argument. Might have been passed from create */
else if (!opt || strcmp(opt, "hash")) {
krb5_set_error_message(kcontext, EINVAL,
@@ -1394,8 +1395,9 @@ krb5_db2_create(krb5_context kcontext, char *conf_section, char **db_args)
}
else if (!opt && !strcmp(val, "temporary")) {
tempdb = 1;
- }
- else if (opt && !strcmp(opt, "hash")) {
+ } else if (!opt && !strcmp(val, "merge_nra")) {
+ ;
+ } else if (opt && !strcmp(opt, "hash")) {
flags = KRB5_KDB_CREATE_HASH;
} else {
krb5_set_error_message(kcontext, EINVAL,
@@ -1563,7 +1565,7 @@ krb5_db2_db_set_option(krb5_context kcontext, int option, void *value)
krb5_db2_context *db_ctx;
kdb5_dal_handle *dal_handle;
- if (!k5db2_inited(kcontext))
+ if (!k5db2_inited(kcontext))
return KRB5_KDB_DBNOTINITED;
dal_handle = kcontext->dal_handle;
@@ -1679,6 +1681,8 @@ krb5_db2_promote_db(krb5_context kcontext, char *conf_section, char **db_args)
krb5_error_code status = 0;
char *db_name = NULL;
char *temp_db_name = NULL;
+ char **db_argp;
+ int merge_nra = 0;
krb5_clear_error_message (kcontext);
@@ -1699,7 +1703,14 @@ krb5_db2_promote_db(krb5_context kcontext, char *conf_section, char **db_args)
goto clean_n_exit;
}
- status = krb5_db2_db_rename (kcontext, temp_db_name, db_name);
+ for (db_argp = db_args; *db_argp; db_argp++) {
+ if (!strcmp(*db_argp, "merge_nra")) {
+ merge_nra++;
+ break;
+ }
+ }
+
+ status = krb5_db2_db_rename (kcontext, temp_db_name, db_name, merge_nra);
if (status)
goto clean_n_exit;
@@ -1711,6 +1722,154 @@ clean_n_exit:
return status;
}
+/*
+ * Merge non-replicated attributes from src into dst, setting
+ * changed to non-zero if dst was changed.
+ *
+ * Non-replicated attributes are: last_success, last_failed,
+ * fail_auth_count, and any negative TL data values.
+ */
+static krb5_error_code
+krb5_db2_merge_principal(krb5_context kcontext,
+ krb5_db_entry *src,
+ krb5_db_entry *dst,
+ int *changed)
+{
+ *changed = 0;
+
+ if (dst->last_success != src->last_success) {
+ dst->last_success = src->last_success;
+ (*changed)++;
+ }
+
+ if (dst->last_failed != src->last_failed) {
+ dst->last_failed = src->last_failed;
+ (*changed)++;
+ }
+
+ if (dst->fail_auth_count != src->fail_auth_count) {
+ dst->fail_auth_count = src->fail_auth_count;
+ (*changed)++;
+ }
+
+ return 0;
+}
+
+struct nra_context {
+ krb5_context kcontext;
+ krb5_db2_context *db_context;
+};
+
+/*
+ * Iteration callback merges non-replicated attributes from
+ * old database.
+ */
+static krb5_error_code
+krb5_db2_merge_nra_iterator(krb5_pointer ptr, krb5_db_entry *entry)
+{
+ struct nra_context *nra = (struct nra_context *)ptr;
+ kdb5_dal_handle *dal_handle = nra->kcontext->dal_handle;
+ krb5_error_code retval;
+ int n_entries = 0, changed;
+ krb5_db_entry s_entry;
+ krb5_boolean more;
+ krb5_db2_context *dst_db;
+
+ memset(&s_entry, 0, sizeof(s_entry));
+
+ dst_db = dal_handle->db_context;
+ dal_handle->db_context = nra->db_context;
+
+ /* look up the new principal in the old DB */
+ retval = krb5_db2_db_get_principal(nra->kcontext,
+ entry->princ,
+ &s_entry,
+ &n_entries,
+ &more);
+ if (retval != 0 || n_entries == 0) {
+ /* principal may be newly created, so ignore */
+ dal_handle->db_context = dst_db;
+ return 0;
+ }
+
+ /* merge non-replicated attributes from the old entry in */
+ krb5_db2_merge_principal(nra->kcontext, &s_entry, entry, &changed);
+
+ dal_handle->db_context = dst_db;
+
+ /* if necessary, commit the modified new entry to the new DB */
+ if (changed) {
+ retval = krb5_db2_db_put_principal(nra->kcontext,
+ entry,
+ &n_entries,
+ NULL);
+ } else {
+ retval = 0;
+ }
+
+ return retval;
+}
+
+/*
+ * Merge non-replicated attributes (that is, lockout-related
+ * attributes and negative TL data types) from the old database
+ * into the new one.
+ *
+ * Note: src_db is locked on success.
+ */
+static krb5_error_code
+krb5_db2_begin_nra_merge(krb5_context kcontext,
+ krb5_db2_context *src_db,
+ krb5_db2_context *dst_db)
+{
+ krb5_error_code retval;
+ kdb5_dal_handle *dal_handle = kcontext->dal_handle;
+ struct nra_context nra;
+
+ nra.kcontext = kcontext;
+ nra.db_context = dst_db;
+
+ assert(dal_handle->db_context == dst_db);
+ dal_handle->db_context = src_db;
+
+ retval = krb5_db2_db_lock(kcontext, KRB5_LOCKMODE_EXCLUSIVE);
+ if (retval) {
+ dal_handle->db_context = dst_db;
+ return retval;
+ }
+
+ retval = krb5_db2_db_iterate_ext(kcontext,
+ krb5_db2_merge_nra_iterator,
+ &nra,
+ 0,
+ 0);
+ if (retval != 0)
+ (void) krb5_db2_db_unlock(kcontext);
+
+ dal_handle->db_context = dst_db;
+
+ return retval;
+}
+
+/*
+ * Finish merge of non-replicated attributes by unlocking
+ * src_db.
+ */
+static krb5_error_code
+krb5_db2_end_nra_merge(krb5_context kcontext,
+ krb5_db2_context *src_db,
+ krb5_db2_context *dst_db)
+{
+ krb5_error_code retval;
+ kdb5_dal_handle *dal_handle = kcontext->dal_handle;
+
+ dal_handle->db_context = src_db;
+ retval = krb5_db2_db_unlock(kcontext);
+ dal_handle->db_context = dst_db;
+
+ return retval;
+}
+
/* Retrieved from pre-DAL code base. */
/*
* "Atomically" rename the database in a way that locks out read
@@ -1723,12 +1882,12 @@ clean_n_exit:
* have to go through the same stuff that we went through up in db_destroy.
*/
krb5_error_code
-krb5_db2_db_rename(context, from, to)
+krb5_db2_db_rename(context, from, to, merge_nra)
krb5_context context;
char *from;
char *to;
+ int merge_nra;
{
- DB *db;
char *fromok;
krb5_error_code retval;
krb5_db2_context *s_context, *db_ctx;
@@ -1745,13 +1904,10 @@ krb5_db2_db_rename(context, from, to)
* files must exist because krb5_db2_db_lock, called below,
* will fail otherwise.
*/
- db = k5db2_dbopen(db_ctx, to, O_RDWR|O_CREAT, 0600, 0);
- if (db == NULL) {
- retval = errno;
+ retval = krb5_db2_db_create(context, to, 0);
+ if (retval != 0 && retval != EEXIST)
goto errout;
- }
- else
- (*db->close)(db);
+
/*
* Set the database to the target, so that other processes sharing
* the target will stop their activity, and notice the new database.
@@ -1764,25 +1920,6 @@ krb5_db2_db_rename(context, from, to)
if (retval)
goto errout;
- {
- /* Ugly brute force hack.
-
- Should be going through nice friendly helper routines for
- this, but it's a mess of jumbled so-called interfaces right
- now. */
- char policy[2048], new_policy[2048];
- assert (strlen(db_ctx->db_name) < 2000);
- snprintf(policy, sizeof(policy), "%s.kadm5", db_ctx->db_name);
- snprintf(new_policy, sizeof(new_policy),
- "%s~.kadm5", db_ctx->db_name);
- if (0 != rename(new_policy, policy)) {
- retval = errno;
- goto errout;
- }
- strlcat(new_policy, ".lock",sizeof(new_policy));
- (void) unlink(new_policy);
- }
-
db_ctx->db_lf_name = gen_dbsuffix(db_ctx->db_name, KDB2_LOCK_EXT);
if (db_ctx->db_lf_name == NULL) {
retval = ENOMEM;
@@ -1813,6 +1950,11 @@ krb5_db2_db_rename(context, from, to)
if ((retval = krb5_db2_db_start_update(context)))
goto errfromok;
+ if (merge_nra) {
+ if ((retval = krb5_db2_begin_nra_merge(context, s_context, db_ctx)))
+ goto errfromok;
+ }
+
if (rename(from, to)) {
retval = errno;
goto errfromok;
@@ -1821,7 +1963,35 @@ krb5_db2_db_rename(context, from, to)
retval = errno;
goto errfromok;
}
+
+ if (merge_nra) {
+ krb5_db2_end_nra_merge(context, s_context, db_ctx);
+ }
+
retval = krb5_db2_db_end_update(context);
+ if (retval)
+ goto errfromok;
+
+ {
+ /* XXX moved so that NRA merge works */
+ /* Ugly brute force hack.
+
+ Should be going through nice friendly helper routines for
+ this, but it's a mess of jumbled so-called interfaces right
+ now. */
+ char policy[2048], new_policy[2048];
+ assert (strlen(db_ctx->db_name) < 2000);
+ snprintf(policy, sizeof(policy), "%s.kadm5", db_ctx->db_name);
+ snprintf(new_policy, sizeof(new_policy),
+ "%s~.kadm5", db_ctx->db_name);
+ if (0 != rename(new_policy, policy)) {
+ retval = errno;
+ goto errfromok;
+ }
+ strlcat(new_policy, ".lock",sizeof(new_policy));
+ (void) unlink(new_policy);
+ }
+
errfromok:
free_dbsuffix(fromok);
errout:
@@ -1839,3 +2009,4 @@ errout:
return retval;
}
+
diff --git a/src/plugins/kdb/db2/kdb_db2.h b/src/plugins/kdb/db2/kdb_db2.h
index 640c4d62d3..cef7b648a8 100644
--- a/src/plugins/kdb/db2/kdb_db2.h
+++ b/src/plugins/kdb/db2/kdb_db2.h
@@ -71,7 +71,8 @@ krb5_error_code krb5_db2_db_destroy
krb5_error_code krb5_db2_db_rename
(krb5_context,
char *,
- char * );
+ char *,
+ int );
krb5_error_code krb5_db2_db_get_principal
(krb5_context,
krb5_const_principal,
@@ -219,4 +220,23 @@ void krb5_db2_free_policy( krb5_context kcontext,
/* Thread-safety wrapper slapped on top of original implementation. */
extern k5_mutex_t *krb5_db2_mutex;
+/* lockout */
+krb5_error_code
+krb5_db2_lockout_check_policy(krb5_context context,
+ krb5_db_entry *entry,
+ krb5_timestamp stamp);
+
+krb5_error_code
+krb5_db2_lockout_audit(krb5_context context,
+ krb5_db_entry *entry,
+ krb5_timestamp stamp,
+ krb5_error_code status);
+
+/* methods */
+krb5_error_code
+krb5_db2_invoke(krb5_context context,
+ unsigned int method,
+ const krb5_data *req,
+ krb5_data *rep);
+
#endif /* KRB5_KDB_DB2_H */
diff --git a/src/plugins/kdb/db2/kdb_ext.c b/src/plugins/kdb/db2/kdb_ext.c
new file mode 100644
index 0000000000..9d73966b4d
--- /dev/null
+++ b/src/plugins/kdb/db2/kdb_ext.c
@@ -0,0 +1,99 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * plugins/kdb/db2/kdb_ext.c
+ *
+ * Copyright (C) 2009 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ *
+ */
+
+#include "k5-int.h"
+#include "kdb.h"
+#include <stdio.h>
+#include <errno.h>
+#include <kdb_ext.h>
+#include "kdb_db2.h"
+
+static krb5_error_code
+krb5_db2_check_policy_as(krb5_context context,
+ unsigned int method,
+ const krb5_data *request,
+ krb5_data *response)
+{
+ const kdb_check_policy_as_req *req;
+ kdb_check_policy_as_rep *rep;
+ krb5_error_code code;
+
+ req = (const kdb_check_policy_as_req *)request->data;
+ rep = (kdb_check_policy_as_rep *)response->data;
+
+ rep->status = NULL;
+
+ code = krb5_db2_lockout_check_policy(context, req->client,
+ req->kdc_time);
+ if (code == KRB5KDC_ERR_CLIENT_REVOKED)
+ rep->status = "LOCKED_OUT";
+
+ return code;
+}
+
+static krb5_error_code
+krb5_db2_audit_as(krb5_context context,
+ unsigned int method,
+ const krb5_data *request,
+ krb5_data *response)
+{
+ const kdb_audit_as_req *req;
+ krb5_error_code code;
+
+ req = (const kdb_audit_as_req *)request->data;
+
+ code = krb5_db2_lockout_audit(context, req->client,
+ req->authtime, req->error_code);
+
+ return code;
+}
+
+krb5_error_code
+krb5_db2_invoke(krb5_context context,
+ unsigned int method,
+ const krb5_data *req,
+ krb5_data *rep)
+{
+ krb5_error_code code = KRB5_KDB_DBTYPE_NOSUP;
+
+ switch (method) {
+ case KRB5_KDB_METHOD_CHECK_POLICY_AS:
+ code = krb5_db2_check_policy_as(context, method, req, rep);
+ break;
+ case KRB5_KDB_METHOD_AUDIT_AS:
+ code = krb5_db2_audit_as(context, method, req, rep);
+ break;
+ default:
+ break;
+ }
+
+ return code;
+}
+
diff --git a/src/plugins/kdb/db2/lockout.c b/src/plugins/kdb/db2/lockout.c
new file mode 100644
index 0000000000..1e6602dcca
--- /dev/null
+++ b/src/plugins/kdb/db2/lockout.c
@@ -0,0 +1,196 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * plugins/kdb/db2/lockout.c
+ *
+ * Copyright (C) 2009 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ *
+ */
+
+#include "k5-int.h"
+#include "kdb.h"
+#include <stdio.h>
+#include <errno.h>
+#include <kadm5/server_internal.h>
+#include "kdb_db2.h"
+
+/*
+ * Helper routines for databases that wish to use the default
+ * principal lockout functionality.
+ */
+
+static krb5_error_code
+lookup_lockout_policy(krb5_context context,
+ krb5_db_entry *entry,
+ krb5_kvno *pw_max_fail,
+ krb5_deltat *pw_failcnt_interval,
+ krb5_deltat *pw_lockout_duration)
+{
+ krb5_tl_data tl_data;
+ krb5_error_code code;
+ osa_princ_ent_rec adb;
+ XDR xdrs;
+
+ *pw_max_fail = 0;
+ *pw_failcnt_interval = 0;
+ *pw_lockout_duration = 0;
+
+ tl_data.tl_data_type = KRB5_TL_KADM_DATA;
+
+ code = krb5_dbe_lookup_tl_data(context, entry, &tl_data);
+ if (code != 0 || tl_data.tl_data_length == 0)
+ return code;
+
+ memset(&adb, 0, sizeof(adb));
+ xdrmem_create(&xdrs, (char *)tl_data.tl_data_contents,
+ tl_data.tl_data_length, XDR_DECODE);
+ if (!xdr_osa_princ_ent_rec(&xdrs, &adb)) {
+ xdr_destroy(&xdrs);
+ return KADM5_XDR_FAILURE;
+ }
+
+ if (adb.policy != NULL) {
+ osa_policy_ent_t policy = NULL;
+ int count = 0;
+
+ code = krb5_db2_get_policy(context, adb.policy,
+ &policy, &count);
+ if (code == 0 && count == 1) {
+ *pw_max_fail = policy->pw_max_fail;
+ *pw_failcnt_interval = policy->pw_failcnt_interval;
+ *pw_lockout_duration = policy->pw_lockout_duration;
+ }
+ if (policy != NULL)
+ krb5_db2_free_policy(context, policy);
+ }
+
+ xdr_destroy(&xdrs);
+
+ xdrmem_create(&xdrs, NULL, 0, XDR_FREE);
+ xdr_osa_princ_ent_rec(&xdrs, &adb);
+ xdr_destroy(&xdrs);
+
+ return 0;
+}
+
+/* draft-behera-ldap-password-policy-10.txt 7.1 */
+static krb5_boolean
+locked_check_p(krb5_context context,
+ krb5_timestamp stamp,
+ krb5_kvno max_fail,
+ krb5_timestamp lockout_duration,
+ krb5_db_entry *entry)
+{
+ if (max_fail == 0 || entry->fail_auth_count < max_fail)
+ return FALSE;
+
+ if (lockout_duration == 0)
+ return TRUE; /* principal permanently locked */
+
+ return (stamp < entry->last_failed + lockout_duration);
+}
+
+krb5_error_code
+krb5_db2_lockout_check_policy(krb5_context context,
+ krb5_db_entry *entry,
+ krb5_timestamp stamp)
+{
+ krb5_error_code code;
+ krb5_kvno max_fail = 0;
+ krb5_deltat failcnt_interval = 0;
+ krb5_deltat lockout_duration = 0;
+
+ code = lookup_lockout_policy(context, entry, &max_fail,
+ &failcnt_interval,
+ &lockout_duration);
+ if (code != 0)
+ return code;
+
+ if (locked_check_p(context, stamp, max_fail, lockout_duration, entry))
+ return KRB5KDC_ERR_CLIENT_REVOKED;
+
+ return 0;
+}
+
+krb5_error_code
+krb5_db2_lockout_audit(krb5_context context,
+ krb5_db_entry *entry,
+ krb5_timestamp stamp,
+ krb5_error_code status)
+{
+ krb5_error_code code;
+ krb5_kvno max_fail = 0;
+ krb5_deltat failcnt_interval = 0;
+ krb5_deltat lockout_duration = 0;
+ int nentries = 1;
+
+ switch (status) {
+ case 0:
+ case KRB5KDC_ERR_PREAUTH_FAILED:
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ break;
+#if 0
+ case KRB5KDC_ERR_CLIENT_REVOKED:
+ break;
+#endif
+ default:
+ 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 (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 (failcnt_interval != 0 &&
+ stamp > entry->last_failed + 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 */
+
+ code = krb5_db2_db_put_principal(context, entry,
+ &nentries, NULL);
+ if (code != 0)
+ return code;
+
+ return 0;
+}
+
diff --git a/src/plugins/kdb/db2/pol_xdr.c b/src/plugins/kdb/db2/pol_xdr.c
index 82e29b8046..31856fbd2a 100644
--- a/src/plugins/kdb/db2/pol_xdr.c
+++ b/src/plugins/kdb/db2/pol_xdr.c
@@ -51,14 +51,27 @@ bool_t xdr_nullstring(XDR *xdrs, char **objp)
return FALSE;
}
+static int
+osa_policy_min_vers(osa_policy_ent_t objp)
+{
+ int vers;
+
+ if (objp->pw_max_fail ||
+ objp->pw_failcnt_interval ||
+ objp->pw_lockout_duration)
+ vers = OSA_ADB_POLICY_VERSION_2;
+ else
+ vers = OSA_ADB_POLICY_VERSION_1;
+ return vers;
+}
bool_t
xdr_osa_policy_ent_rec(XDR *xdrs, osa_policy_ent_t objp)
{
switch (xdrs->x_op) {
case XDR_ENCODE:
- objp->version = OSA_ADB_POLICY_VERSION_1;
+ objp->version = osa_policy_min_vers(objp);
/* fall through */
case XDR_FREE:
if (!xdr_int(xdrs, &objp->version))
@@ -67,7 +80,8 @@ xdr_osa_policy_ent_rec(XDR *xdrs, osa_policy_ent_t objp)
case XDR_DECODE:
if (!xdr_int(xdrs, &objp->version))
return FALSE;
- if (objp->version != OSA_ADB_POLICY_VERSION_1)
+ if (objp->version != OSA_ADB_POLICY_VERSION_1 &&
+ objp->version != OSA_ADB_POLICY_VERSION_2)
return FALSE;
break;
}
@@ -86,5 +100,13 @@ xdr_osa_policy_ent_rec(XDR *xdrs, osa_policy_ent_t objp)
return (FALSE);
if (!xdr_u_int32(xdrs, &objp->policy_refcnt))
return (FALSE);
+ if (objp->version > OSA_ADB_POLICY_VERSION_1) {
+ if (!xdr_u_int32(xdrs, &objp->pw_max_fail))
+ return (FALSE);
+ if (!xdr_u_int32(xdrs, &objp->pw_failcnt_interval))
+ return (FALSE);
+ if (!xdr_u_int32(xdrs, &objp->pw_lockout_duration))
+ return (FALSE);
+ }
return (TRUE);
}
diff --git a/src/plugins/kdb/db2/policy_db.h b/src/plugins/kdb/db2/policy_db.h
index 11fece3df2..d841d7376f 100644
--- a/src/plugins/kdb/db2/policy_db.h
+++ b/src/plugins/kdb/db2/policy_db.h
@@ -39,6 +39,7 @@ typedef long osa_adb_ret_t;
#define OSA_ADB_POLICY_VERSION_MASK 0x12345D00
#define OSA_ADB_POLICY_VERSION_1 0x12345D01
+#define OSA_ADB_POLICY_VERSION_2 0x12345D02
diff --git a/src/plugins/kdb/ldap/ldap_exp.c b/src/plugins/kdb/ldap/ldap_exp.c
index 18a89fd619..742e3ce507 100644
--- a/src/plugins/kdb/ldap/ldap_exp.c
+++ b/src/plugins/kdb/ldap/ldap_exp.c
@@ -87,6 +87,10 @@ kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_ldap, kdb_function_table) = {
/* fetch_master_key_list */ NULL,
/* store_master_key_list */ NULL,
/* Search enc type */ NULL,
- /* Change pwd */ NULL
+ /* Change pwd */ NULL,
+ /* promote_db */ NULL,
+ /* dbekd_decrypt_key_data */ NULL,
+ /* dbekd_encrypt_key_data */ NULL,
+ /* db_invoke */ krb5_ldap_invoke,
};
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/Makefile.in b/src/plugins/kdb/ldap/libkdb_ldap/Makefile.in
index 8479fb6fcb..4306da102d 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/Makefile.in
+++ b/src/plugins/kdb/ldap/libkdb_ldap/Makefile.in
@@ -54,8 +54,10 @@ SRCS= $(srcdir)/kdb_ldap.c \
$(srcdir)/princ_xdr.c \
$(srcdir)/ldap_fetch_mkey.c \
$(srcdir)/ldap_service_stash.c \
+ $(srcdir)/kdb_ext.c \
$(srcdir)/kdb_xdr.c \
- $(srcdir)/ldap_err.c
+ $(srcdir)/ldap_err.c \
+ $(srcdir)/lockout.c \
STOBJLISTS=OBJS.ST
STLIBOBJS= kdb_ldap.o \
@@ -74,8 +76,10 @@ STLIBOBJS= kdb_ldap.o \
princ_xdr.o \
ldap_fetch_mkey.o \
ldap_service_stash.o \
+ kdb_ext.o \
kdb_xdr.o \
- ldap_err.o
+ ldap_err.o \
+ lockout.o
all-unix:: all-liblinks
install-unix:: install-libs
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ext.c b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ext.c
new file mode 100644
index 0000000000..0009b59d68
--- /dev/null
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ext.c
@@ -0,0 +1,99 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * plugins/kdb/ldap/kdb_ext.c
+ *
+ * Copyright (C) 2009 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ *
+ */
+
+#include "k5-int.h"
+#include "kdb.h"
+#include <stdio.h>
+#include <errno.h>
+#include <kdb_ext.h>
+#include "kdb_ldap.h"
+
+static krb5_error_code
+krb5_ldap_check_policy_as(krb5_context context,
+ unsigned int method,
+ const krb5_data *request,
+ krb5_data *response)
+{
+ const kdb_check_policy_as_req *req;
+ kdb_check_policy_as_rep *rep;
+ krb5_error_code code;
+
+ req = (const kdb_check_policy_as_req *)request->data;
+ rep = (kdb_check_policy_as_rep *)response->data;
+
+ rep->status = NULL;
+
+ code = krb5_ldap_lockout_check_policy(context, req->client,
+ req->kdc_time);
+ if (code == KRB5KDC_ERR_CLIENT_REVOKED)
+ rep->status = "LOCKED_OUT";
+
+ return code;
+}
+
+static krb5_error_code
+krb5_ldap_audit_as(krb5_context context,
+ unsigned int method,
+ const krb5_data *request,
+ krb5_data *response)
+{
+ const kdb_audit_as_req *req;
+ krb5_error_code code;
+
+ req = (const kdb_audit_as_req *)request->data;
+
+ code = krb5_ldap_lockout_audit(context, req->client,
+ req->authtime, req->error_code);
+
+ return code;
+}
+
+krb5_error_code
+krb5_ldap_invoke(krb5_context context,
+ unsigned int method,
+ const krb5_data *req,
+ krb5_data *rep)
+{
+ krb5_error_code code = KRB5_KDB_DBTYPE_NOSUP;
+
+ switch (method) {
+ case KRB5_KDB_METHOD_CHECK_POLICY_AS:
+ code = krb5_ldap_check_policy_as(context, method, req, rep);
+ break;
+ case KRB5_KDB_METHOD_AUDIT_AS:
+ code = krb5_ldap_audit_as(context, method, req, rep);
+ break;
+ default:
+ break;
+ }
+
+ return code;
+}
+
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c
index 4b8fcb2b5d..0d86801d64 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c
@@ -162,69 +162,68 @@ cleanup:
}
-/* Function to check if a LDAP server supports the SASL external mechanism
- *Return values:
- * 0 => supports
- * 1 => does not support
- * 2 => don't know
+/*
+ * Interrogate the root DSE (zero length DN) for an attribute
+ * value assertion.
*/
-#define ERR_MSG1 "Unable to check if SASL EXTERNAL mechanism is supported by LDAP server. Proceeding anyway ..."
-#define ERR_MSG2 "SASL EXTERNAL mechanism not supported by LDAP server. Can't perform certificate-based bind."
-
-int
-has_sasl_external_mech(context, ldap_server)
+static int
+has_rootdse_ava(context, ldap_server, attribute, value)
krb5_context context;
char *ldap_server;
+ char *attribute;
+ char *value;
{
int i=0, flag=0, ret=0, retval=0;
- char *attrs[]={"supportedSASLMechanisms", NULL}, **values=NULL;
+ char *attrs[2], **values=NULL;
LDAP *ld=NULL;
LDAPMessage *msg=NULL, *res=NULL;
+ struct berval cred;
+
+ attrs[0] = attribute;
+ attrs[1] = NULL;
retval = ldap_initialize(&ld, ldap_server);
if (retval != LDAP_SUCCESS) {
- krb5_set_error_message(context, 2, "%s", ERR_MSG1);
ret = 2; /* Don't know */
goto cleanup;
}
+ cred.bv_val = "";
+ cred.bv_len = 0;
+
/* Anonymous bind */
- retval = ldap_sasl_bind_s(ld, NULL, NULL, NULL, NULL, NULL, NULL);
+ retval = ldap_sasl_bind_s(ld, "", NULL, &cred, NULL, NULL, NULL);
if (retval != LDAP_SUCCESS) {
- krb5_set_error_message(context, 2, "%s", ERR_MSG1);
ret = 2; /* Don't know */
goto cleanup;
}
retval = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, NULL, attrs, 0, NULL, NULL, NULL, 0, &res);
if (retval != LDAP_SUCCESS) {
- krb5_set_error_message(context, 2, "%s", ERR_MSG1);
ret = 2; /* Don't know */
goto cleanup;
}
msg = ldap_first_message(ld, res);
if (msg == NULL) {
- krb5_set_error_message(context, 2, "%s", ERR_MSG1);
ret = 2; /* Don't know */
goto cleanup;
}
- values = ldap_get_values(ld, msg, "supportedSASLMechanisms");
+ values = ldap_get_values(ld, msg, attribute);
if (values == NULL) {
- krb5_set_error_message(context, 1, "%s", ERR_MSG2);
ret = 1; /* Not supported */
goto cleanup;
}
for (i = 0; values[i] != NULL; i++) {
- if (strcmp(values[i], "EXTERNAL"))
- continue;
- flag = 1;
+ if (strcmp(values[i], value) == 0) {
+ flag = 1;
+ break;
+ }
}
if (flag != 1) {
- krb5_set_error_message(context, 1, "%s", ERR_MSG2);
ret = 1; /* Not supported */
goto cleanup;
}
@@ -243,6 +242,47 @@ cleanup:
return ret;
}
+#define ERR_MSG1 "Unable to check if SASL EXTERNAL mechanism is supported by LDAP server. Proceeding anyway ..."
+#define ERR_MSG2 "SASL EXTERNAL mechanism not supported by LDAP server. Can't perform certificate-based bind."
+
+/* Function to check if a LDAP server supports the SASL external mechanism
+ *Return values:
+ * 0 => supports
+ * 1 => does not support
+ * 2 => don't know
+ */
+int
+has_sasl_external_mech(context, ldap_server)
+ krb5_context context;
+ char *ldap_server;
+{
+ int ret;
+
+ ret = has_rootdse_ava(context, ldap_server,
+ "supportedSASLMechanisms", "EXTERNAL");
+ switch (ret) {
+ case 1: /* not supported */
+ krb5_set_error_message(context, 1, "%s", ERR_MSG2);
+ break;
+ case 2: /* don't know */
+ krb5_set_error_message(context, 1, "%s", ERR_MSG1);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int
+has_modify_increment(context, ldap_server)
+ krb5_context context;
+ char *ldap_server;
+{
+ return has_rootdse_ava(context, ldap_server,
+ "supportedFeatures", "1.3.6.1.1.14");
+}
+
void * krb5_ldap_alloc(krb5_context context, void *ptr, size_t size)
{
return realloc(ptr, size);
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
index 802ab0fc36..d14b48bf97 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
@@ -197,6 +197,7 @@ struct _krb5_ldap_server_info {
#ifdef HAVE_EDIRECTORY
char *root_certificate_file;
#endif
+ int modify_increment;
struct _krb5_ldap_server_info *next;
};
@@ -291,6 +292,9 @@ krb5_ldap_read_startup_information(krb5_context );
int
has_sasl_external_mech(krb5_context, char *);
+int
+has_modify_increment(krb5_context, char *);
+
krb5_error_code
krb5_ldap_free_server_context_params(krb5_ldap_context *ldap_context);
@@ -328,4 +332,23 @@ int
ldap_unbind_ext_s(LDAP *, LDAPControl **, LDAPControl **);
#endif
+/* lockout.c */
+krb5_error_code
+krb5_ldap_lockout_check_policy(krb5_context context,
+ krb5_db_entry *entry,
+ krb5_timestamp stamp);
+
+krb5_error_code
+krb5_ldap_lockout_audit(krb5_context context,
+ krb5_db_entry *entry,
+ krb5_timestamp stamp,
+ krb5_error_code status);
+
+/* kdb_ext.c */
+krb5_error_code
+krb5_ldap_invoke(krb5_context context,
+ unsigned int method,
+ const krb5_data *req,
+ krb5_data *rep);
+
#endif
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c
index fdc5d10c77..db1f76a69e 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c
@@ -229,6 +229,13 @@ krb5_ldap_db_init(krb5_context context, krb5_ldap_context *ldap_context)
krb5_clear_error_message(context);
+#ifdef LDAP_MOD_INCREMENT
+ server_info->modify_increment =
+ (has_modify_increment(context, server_info->server_name) == 0);
+#else
+ server_info->modify_increment = 0;
+#endif /* LDAP_MOD_INCREMENT */
+
for (conns=0; conns < ldap_context->max_server_conns; ++conns) {
if ((st=krb5_ldap_initialize(ldap_context, server_info)) != 0)
break;
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kerberos.ldif b/src/plugins/kdb/ldap/libkdb_ldap/kerberos.ldif
index 0bbdcf8783..fd226b13d3 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/kerberos.ldif
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kerberos.ldif
@@ -337,6 +337,42 @@ attributetypes: ( 2.16.840.1.113719.1.301.4.34.1
SINGLE-VALUE)
+##### Number of consecutive pre-authentication failures before lockout
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributetypes: ( 1.3.6.1.4.1.5322.21.2.1
+ NAME 'krbPwdMaxFailure'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE-VALUE)
+
+
+##### Period after which bad preauthentication count will be reset
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributetypes: ( 1.3.6.1.4.1.5322.21.2.2
+ NAME 'krbPwdFailureCountInterval'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE-VALUE)
+
+
+##### Period in which lockout is enforced
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributetypes: ( 1.3.6.1.4.1.5322.21.2.3
+ NAME 'krbPwdLockoutDuration'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE-VALUE)
+
+
##### FDN pointing to a Kerberos Password Policy object
dn: cn=schema
@@ -760,7 +796,7 @@ objectClasses: ( 2.16.840.1.113719.1.301.6.14.1
NAME 'krbPwdPolicy'
SUP top
MUST ( cn )
- MAY ( krbMaxPwdLife $ krbMinPwdLife $ krbPwdMinDiffChars $ krbPwdMinLength $ krbPwdHistoryLength ) )
+ MAY ( krbMaxPwdLife $ krbMinPwdLife $ krbPwdMinDiffChars $ krbPwdMinLength $ krbPwdHistoryLength $ krbPwdMaxFailure $ krbPwdFailureCountInterval $ krbPwdLockoutDuration ) )
##### The krbTicketPolicyAux holds Kerberos ticket policy attributes.
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kerberos.schema b/src/plugins/kdb/ldap/libkdb_ldap/kerberos.schema
index 9352cf1e46..9525e60d62 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/kerberos.schema
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kerberos.schema
@@ -270,6 +270,33 @@ attributetype ( 2.16.840.1.113719.1.301.4.34.1
SINGLE-VALUE)
+##### Number of consecutive pre-authentication failures before lockout
+
+attributetype ( 1.3.6.1.4.1.5322.21.2.1
+ NAME 'krbPwdMaxFailure'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE-VALUE)
+
+
+##### Period after which bad preauthentication count will be reset
+
+attributetype ( 1.3.6.1.4.1.5322.21.2.2
+ NAME 'krbPwdFailureCountInterval'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE-VALUE)
+
+
+##### Period in which lockout is enforced
+
+attributetype ( 1.3.6.1.4.1.5322.21.2.3
+ NAME 'krbPwdLockoutDuration'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE-VALUE)
+
+
##### FDN pointing to a Kerberos Password Policy object
attributetype ( 2.16.840.1.113719.1.301.4.36.1
@@ -506,7 +533,6 @@ attributetype ( 2.16.840.1.113719.1.301.4.53.1
EQUALITY distinguishedNameMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.12)
-
########################################################################
########################################################################
# Object Class Definitions #
@@ -616,7 +642,7 @@ objectclass ( 2.16.840.1.113719.1.301.6.14.1
NAME 'krbPwdPolicy'
SUP top
MUST ( cn )
- MAY ( krbMaxPwdLife $ krbMinPwdLife $ krbPwdMinDiffChars $ krbPwdMinLength $ krbPwdHistoryLength ) )
+ MAY ( krbMaxPwdLife $ krbMinPwdLife $ krbPwdMinDiffChars $ krbPwdMinLength $ krbPwdHistoryLength $ krbPwdMaxFailure $ krbPwdFailureCountInterval $ krbPwdLockoutDuration ) )
##### The krbTicketPolicyAux holds Kerberos ticket policy attributes.
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h
index 502e71ccd5..abc27f1148 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h
@@ -81,6 +81,15 @@
#define KDB_LAST_SUCCESS_ATTR 0x000800
#define KDB_LAST_FAILED_ATTR 0x001000
#define KDB_FAIL_AUTH_COUNT_ATTR 0x002000
+#define KDB_LOCKED_TIME_ATTR 0x004000
+
+/*
+ * This is a private contract between krb5_ldap_lockout_audit()
+ * and krb5_ldap_put_principal(). If present, it means that the
+ * krbPwdMaxFailure attribute should be incremented by one.
+ */
+#define KADM5_FAIL_AUTH_COUNT_INCREMENT 0x080000 /* KADM5_CPW_FUNCTION */
+
extern struct timeval timeout;
extern char *policyclass[];
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
index 03c3da48d7..f81d399041 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
@@ -863,7 +863,7 @@ krb5_ldap_put_principal(context, entries, nentries, db_args)
establish_links = TRUE;
}
- if ((entries->last_success)!=0) {
+ if (entries->mask & KADM5_LAST_SUCCESS) {
memset(strval, 0, sizeof(strval));
if ((strval[0]=getstringtime(entries->last_success)) == NULL)
goto cleanup;
@@ -874,7 +874,7 @@ krb5_ldap_put_principal(context, entries, nentries, db_args)
free (strval[0]);
}
- if (entries->last_failed!=0) {
+ if (entries->mask & KADM5_LAST_FAILED) {
memset(strval, 0, sizeof(strval));
if ((strval[0]=getstringtime(entries->last_failed)) == NULL)
goto cleanup;
@@ -885,9 +885,58 @@ krb5_ldap_put_principal(context, entries, nentries, db_args)
free(strval[0]);
}
- if (entries->fail_auth_count!=0) {
- if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount", LDAP_MOD_REPLACE, entries->fail_auth_count)) !=0)
+ if (entries->mask & KADM5_FAIL_AUTH_COUNT) {
+ krb5_kvno fail_auth_count;
+
+ fail_auth_count = entries->fail_auth_count;
+ if (entries->mask & KADM5_FAIL_AUTH_COUNT_INCREMENT)
+ fail_auth_count++;
+
+ st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount",
+ LDAP_MOD_REPLACE,
+ fail_auth_count);
+ if (st != 0)
goto cleanup;
+ } else if (entries->mask & KADM5_FAIL_AUTH_COUNT_INCREMENT) {
+ /*
+ * If the client library and server supports RFC 4525,
+ * then use it to increment by one the value of the
+ * krbLoginFailedCount attribute. Otherwise, assert the
+ * (provided) old value by deleting it before adding.
+ */
+#ifdef LDAP_MOD_INCREMENT
+ if (ldap_server_handle->server_info->modify_increment) {
+ st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount",
+ LDAP_MOD_INCREMENT, 1);
+ if (st != 0)
+ goto cleanup;
+ } else
+#endif /* LDAP_MOD_INCREMENT */
+ if (entries->fail_auth_count == 0) {
+ /*
+ * Unfortunately we have no way of distinguishing between
+ * an absent and a zero-valued attribute by the time we are
+ * called here. So, although this creates a race condition,
+ * it appears impossible to assert the old value as that
+ * would fail were the attribute absent.
+ */
+ st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount",
+ LDAP_MOD_REPLACE, 1);
+ if (st != 0)
+ goto cleanup;
+ } else {
+ st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount",
+ LDAP_MOD_DELETE,
+ entries->fail_auth_count);
+ if (st != 0)
+ goto cleanup;
+
+ st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount",
+ LDAP_MOD_ADD,
+ entries->fail_auth_count + 1);
+ if (st != 0)
+ goto cleanup;
+ }
}
if (entries->mask & KADM5_MAX_LIFE) {
@@ -1180,6 +1229,9 @@ krb5_ldap_put_principal(context, entries, nentries, db_args)
krb5_set_error_message(context, st, "%s", errbuf);
goto cleanup;
}
+
+ if (entries->mask & KADM5_FAIL_AUTH_COUNT_INCREMENT)
+ entries->fail_auth_count++;
}
}
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_pwd_policy.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_pwd_policy.c
index 94d461b29f..ed63e0812e 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_pwd_policy.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_pwd_policy.c
@@ -39,7 +39,9 @@
static char *password_policy_attributes[] = { "cn", "krbmaxpwdlife", "krbminpwdlife",
"krbpwdmindiffchars", "krbpwdminlength",
- "krbpwdhistorylength", NULL };
+ "krbpwdhistorylength", "krbpwdmaxfailure",
+ "krbpwdfailurecountinterval",
+ "krbpwdlockoutduration", NULL };
/*
* Function to create password policy object.
@@ -97,7 +99,13 @@ krb5_ldap_create_password_policy (context, policy)
|| ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdminlength", LDAP_MOD_ADD,
(signed) policy->pw_min_length)) != 0)
|| ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdhistorylength", LDAP_MOD_ADD,
- (signed) policy->pw_history_num)) != 0))
+ (signed) policy->pw_history_num)) != 0)
+ || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdmaxfailure", LDAP_MOD_ADD,
+ (signed) policy->pw_max_fail)) != 0)
+ || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdfailurecountinterval", LDAP_MOD_ADD,
+ (signed) policy->pw_failcnt_interval)) != 0)
+ || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdlockoutduration", LDAP_MOD_ADD,
+ (signed) policy->pw_lockout_duration)) != 0))
goto cleanup;
/* password policy object creation */
@@ -157,7 +165,13 @@ krb5_ldap_put_password_policy (context, policy)
|| ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdminlength", LDAP_MOD_REPLACE,
(signed) policy->pw_min_length)) != 0)
|| ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdhistorylength", LDAP_MOD_REPLACE,
- (signed) policy->pw_history_num)) != 0))
+ (signed) policy->pw_history_num)) != 0)
+ || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdmaxfailure", LDAP_MOD_REPLACE,
+ (signed) policy->pw_max_fail)) != 0)
+ || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdfailurecountinterval", LDAP_MOD_REPLACE,
+ (signed) policy->pw_failcnt_interval)) != 0)
+ || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdlockoutduration", LDAP_MOD_REPLACE,
+ (signed) policy->pw_lockout_duration)) != 0))
goto cleanup;
/* modify the password policy object. */
@@ -199,6 +213,10 @@ populate_policy(krb5_context context,
krb5_ldap_get_value(ld, ent, "krbpwdminlength", &(pol_entry->pw_min_length));
krb5_ldap_get_value(ld, ent, "krbpwdhistorylength", &(pol_entry->pw_history_num));
+ krb5_ldap_get_value(ld, ent, "krbpwdmaxfailure", &(pol_entry->pw_max_fail));
+ krb5_ldap_get_value(ld, ent, "krbpwdfailurecountinterval", &(pol_entry->pw_failcnt_interval));
+ krb5_ldap_get_value(ld, ent, "krbpwdlockoutduration", &(pol_entry->pw_lockout_duration));
+
/* Get the reference count */
pol_dn = ldap_get_dn(ld, ent);
st = krb5_ldap_get_reference_count (context, pol_dn, "krbPwdPolicyReference",
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_service_rights.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_service_rights.c
index 23bb3dbeb2..d670f59fb7 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_service_rights.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_service_rights.c
@@ -87,6 +87,9 @@ static char *adminrights_subtree[][2]={
{"2#subtree#","#krbLastFailedAuth"},
{"2#subtree#","#krbLoginFailedCount"},
{"2#subtree#","#krbLastSuccessfulAuth"},
+ {"6#subtree#","#krbPwdMaxFailure"},
+ {"6#subtree#","#krbPwdFailureCountInterval"},
+ {"6#subtree#","#krbPwdLockoutDuration"},
{ "","" }
};
@@ -116,6 +119,9 @@ static char *pwdrights_subtree[][2] = {
{"2#subtree#","#krbLastFailedAuth"},
{"2#subtree#","#krbLoginFailedCount"},
{"2#subtree#","#krbLastSuccessfulAuth"},
+ {"2#subtree#","#krbPwdMaxFailure"},
+ {"2#subtree#","#krbPwdFailureCountInterval"},
+ {"2#subtree#","#krbPwdLockoutDuration"},
{ "", "" }
};
@@ -187,6 +193,9 @@ static char *adminrights_realmcontainer[][2]={
{"2#subtree#","#krbLastFailedAuth"},
{"2#subtree#","#krbLoginFailedCount"},
{"2#subtree#","#krbLastSuccessfulAuth"},
+ {"6#subtree#","#krbPwdMaxFailure"},
+ {"6#subtree#","#krbPwdFailureCountInterval"},
+ {"6#subtree#","#krbPwdLockoutDuration"},
{ "","" }
};
@@ -225,6 +234,9 @@ static char *pwdrights_realmcontainer[][2]={
{"2#subtree#","#krbLastFailedAuth"},
{"2#subtree#","#krbLoginFailedCount"},
{"2#subtree#","#krbLastSuccessfulAuth"},
+ {"2#subtree#","#krbPwdMaxFailure"},
+ {"2#subtree#","#krbPwdFailureCountInterval"},
+ {"2#subtree#","#krbPwdLockoutDuration"},
{ "", "" }
};
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports b/src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports
index a0d94f6740..1ec9a39070 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports
+++ b/src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports
@@ -51,3 +51,4 @@ krb5_ldap_release_errcode_string
krb5_ldap_create
krb5_ldap_set_mkey_list
krb5_ldap_get_mkey_list
+krb5_ldap_invoke
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/lockout.c b/src/plugins/kdb/ldap/libkdb_ldap/lockout.c
new file mode 100644
index 0000000000..6b2d49e82b
--- /dev/null
+++ b/src/plugins/kdb/ldap/libkdb_ldap/lockout.c
@@ -0,0 +1,192 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * plugins/kdb/ldap/lockout.c
+ *
+ * Copyright (C) 2009 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <k5-int.h>
+#include <kadm5/admin.h>
+#include <kdb.h>
+
+#include "kdb_ldap.h"
+#include "princ_xdr.h"
+#include "ldap_principal.h"
+#include "ldap_pwd_policy.h"
+#include "ldap_tkt_policy.h"
+
+static krb5_error_code
+lookup_lockout_policy(krb5_context context,
+ krb5_db_entry *entry,
+ krb5_kvno *pw_max_fail,
+ krb5_deltat *pw_failcnt_interval,
+ krb5_deltat *pw_lockout_duration)
+{
+ krb5_tl_data tl_data;
+ krb5_error_code code;
+ osa_princ_ent_rec adb;
+ XDR xdrs;
+
+ *pw_max_fail = 0;
+ *pw_failcnt_interval = 0;
+ *pw_lockout_duration = 0;
+
+ tl_data.tl_data_type = KRB5_TL_KADM_DATA;
+
+ code = krb5_dbe_lookup_tl_data(context, entry, &tl_data);
+ if (code != 0 || tl_data.tl_data_length == 0)
+ return code;
+
+ memset(&adb, 0, sizeof(adb));
+
+ code = krb5_lookup_tl_kadm_data(&tl_data, &adb);
+ if (code != 0)
+ return code;
+
+ if (adb.policy != NULL) {
+ osa_policy_ent_t policy = NULL;
+ int count = 0;
+
+ code = krb5_ldap_get_password_policy(context, adb.policy,
+ &policy, &count);
+ if (code == 0 && count == 1) {
+ *pw_max_fail = policy->pw_max_fail;
+ *pw_failcnt_interval = policy->pw_failcnt_interval;
+ *pw_lockout_duration = policy->pw_lockout_duration;
+ }
+ krb5_ldap_free_password_policy(context, policy);
+ }
+
+ xdrmem_create(&xdrs, NULL, 0, XDR_FREE);
+ ldap_xdr_osa_princ_ent_rec(&xdrs, &adb);
+ xdr_destroy(&xdrs);
+
+ return 0;
+}
+
+/* draft-behera-ldap-password-policy-10.txt 7.1 */
+static krb5_boolean
+locked_check_p(krb5_context context,
+ krb5_timestamp stamp,
+ krb5_kvno max_fail,
+ krb5_timestamp lockout_duration,
+ krb5_db_entry *entry)
+{
+ if (max_fail == 0 || entry->fail_auth_count < max_fail)
+ return FALSE;
+
+ if (lockout_duration == 0)
+ return TRUE; /* principal permanently locked */
+
+ return (stamp < entry->last_failed + lockout_duration);
+}
+
+krb5_error_code
+krb5_ldap_lockout_check_policy(krb5_context context,
+ krb5_db_entry *entry,
+ krb5_timestamp stamp)
+{
+ krb5_error_code code;
+ krb5_kvno max_fail = 0;
+ krb5_deltat failcnt_interval = 0;
+ krb5_deltat lockout_duration = 0;
+
+ code = lookup_lockout_policy(context, entry, &max_fail,
+ &failcnt_interval,
+ &lockout_duration);
+ if (code != 0 || failcnt_interval == 0)
+ return code;
+
+ if (locked_check_p(context, stamp, max_fail, lockout_duration, entry))
+ return KRB5KDC_ERR_CLIENT_REVOKED;
+
+ return 0;
+}
+
+krb5_error_code
+krb5_ldap_lockout_audit(krb5_context context,
+ krb5_db_entry *entry,
+ krb5_timestamp stamp,
+ krb5_error_code status)
+{
+ krb5_error_code code;
+ krb5_kvno max_fail = 0;
+ krb5_deltat failcnt_interval = 0;
+ krb5_deltat lockout_duration = 0;
+ int nentries = 1;
+
+ switch (status) {
+ case 0:
+ case KRB5KDC_ERR_PREAUTH_FAILED:
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ break;
+ default:
+ return 0;
+ }
+
+ 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));
+
+ 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 (failcnt_interval != 0 &&
+ stamp > entry->last_failed + failcnt_interval) {
+ /* Reset fail_auth_count after failcnt_interval */
+ entry->fail_auth_count = 0;
+ entry->mask |= KADM5_FAIL_AUTH_COUNT;
+ }
+
+ entry->last_failed = stamp;
+ entry->mask |= KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT_INCREMENT;
+ }
+
+ if (entry->mask) {
+ code = krb5_ldap_put_principal(context, entry,
+ &nentries, NULL);
+ if (code != 0)
+ return code;
+ }
+
+ return 0;
+}