diff options
| author | Greg Hudson <ghudson@mit.edu> | 2009-10-25 16:55:12 +0000 |
|---|---|---|
| committer | Greg Hudson <ghudson@mit.edu> | 2009-10-25 16:55:12 +0000 |
| commit | 8d31a9d396f5bea88def4db395ad12dca2ac2e9f (patch) | |
| tree | 244f8f5b525432a2a2a280403f38d7b2fbdc0dfd /src/plugins/kdb/ldap | |
| parent | b82e46df9b6cbf663512985a99c6d79f2b0cb796 (diff) | |
Account lockout
Merge Luke's users/lhoward/lockout2 branch to trunk. Implements
account lockout policies for preauth-using principals using existing
principal metadata fields and new policy fields. The kadmin API
version is bumped from 2 to 3 to compatibly extend the policy_ent_rec
structure.
ticket: 6577
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23038 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/plugins/kdb/ldap')
| -rw-r--r-- | src/plugins/kdb/ldap/ldap_exp.c | 6 | ||||
| -rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/Makefile.in | 8 | ||||
| -rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/kdb_ext.c | 99 | ||||
| -rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c | 84 | ||||
| -rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h | 23 | ||||
| -rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c | 7 | ||||
| -rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/kerberos.ldif | 38 | ||||
| -rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/kerberos.schema | 30 | ||||
| -rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h | 9 | ||||
| -rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c | 60 | ||||
| -rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/ldap_pwd_policy.c | 24 | ||||
| -rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/ldap_service_rights.c | 12 | ||||
| -rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports | 1 | ||||
| -rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/lockout.c | 192 |
14 files changed, 558 insertions, 35 deletions
diff --git a/src/plugins/kdb/ldap/ldap_exp.c b/src/plugins/kdb/ldap/ldap_exp.c index 18a89fd61..742e3ce50 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 8479fb6fc..4306da102 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 000000000..0009b59d6 --- /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 4b8fcb2b5..0d86801d6 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 802ab0fc3..d14b48bf9 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 fdc5d10c7..db1f76a69 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 0bbdcf878..fd226b13d 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 9352cf1e4..9525e60d6 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 502e71ccd..abc27f114 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 03c3da48d..f81d39904 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 94d461b29..ed63e0812 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 23bb3dbeb..d670f59fb 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 a0d94f674..1ec9a3907 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 000000000..6b2d49e82 --- /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; +} |
