Path: blob/main/crypto/krb5/src/plugins/kdb/ldap/libkdb_ldap/lockout.c
39642 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* plugins/kdb/ldap/libkdb_ldap/lockout.c */2/*3* Copyright (C) 2009 by the Massachusetts Institute of Technology.4* All rights reserved.5*6* Export of this software from the United States of America may7* require a specific license from the United States Government.8* It is the responsibility of any person or organization contemplating9* export to obtain such a license before exporting.10*11* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and12* distribute this software and its documentation for any purpose and13* without fee is hereby granted, provided that the above copyright14* notice appear in all copies and that both that copyright notice and15* this permission notice appear in supporting documentation, and that16* the name of M.I.T. not be used in advertising or publicity pertaining17* to distribution of the software without specific, written prior18* permission. Furthermore if you modify this software you must label19* your software as modified software and not distribute it in such a20* fashion that it might be confused with the original M.I.T. software.21* M.I.T. makes no representations about the suitability of22* this software for any purpose. It is provided "as is" without express23* or implied warranty.24*/2526#include <k5-int.h>27#include <kadm5/admin.h>28#include <kdb.h>2930#include "kdb_ldap.h"31#include "princ_xdr.h"32#include "ldap_principal.h"33#include "ldap_pwd_policy.h"34#include "ldap_tkt_policy.h"3536static krb5_error_code37lookup_lockout_policy(krb5_context context,38krb5_db_entry *entry,39krb5_kvno *pw_max_fail,40krb5_deltat *pw_failcnt_interval,41krb5_deltat *pw_lockout_duration)42{43krb5_tl_data tl_data;44krb5_error_code code;45osa_princ_ent_rec adb;4647*pw_max_fail = 0;48*pw_failcnt_interval = 0;49*pw_lockout_duration = 0;5051tl_data.tl_data_type = KRB5_TL_KADM_DATA;5253code = krb5_dbe_lookup_tl_data(context, entry, &tl_data);54if (code != 0 || tl_data.tl_data_length == 0)55return code;5657memset(&adb, 0, sizeof(adb));5859code = krb5_lookup_tl_kadm_data(&tl_data, &adb);60if (code != 0)61return code;6263if (adb.policy != NULL) {64osa_policy_ent_t policy = NULL;6566code = krb5_ldap_get_password_policy(context, adb.policy, &policy);67if (code == 0) {68*pw_max_fail = policy->pw_max_fail;69*pw_failcnt_interval = policy->pw_failcnt_interval;70*pw_lockout_duration = policy->pw_lockout_duration;71}72krb5_db_free_policy(context, policy);73}7475ldap_osa_free_princ_ent(&adb);7677return 0;78}7980/* draft-behera-ldap-password-policy-10.txt 7.1 */81static krb5_boolean82locked_check_p(krb5_context context,83krb5_timestamp stamp,84krb5_kvno max_fail,85krb5_timestamp lockout_duration,86krb5_db_entry *entry)87{88krb5_timestamp unlock_time;8990/* If the entry was unlocked since the last failure, it's not locked. */91if (krb5_dbe_lookup_last_admin_unlock(context, entry, &unlock_time) == 0 &&92!ts_after(entry->last_failed, unlock_time))93return FALSE;9495if (max_fail == 0 || entry->fail_auth_count < max_fail)96return FALSE;9798if (lockout_duration == 0)99return TRUE; /* principal permanently locked */100101return ts_after(ts_incr(entry->last_failed, lockout_duration), stamp);102}103104krb5_error_code105krb5_ldap_lockout_check_policy(krb5_context context,106krb5_db_entry *entry,107krb5_timestamp stamp)108{109krb5_error_code code;110kdb5_dal_handle *dal_handle;111krb5_ldap_context *ldap_context;112krb5_kvno max_fail = 0;113krb5_deltat failcnt_interval = 0;114krb5_deltat lockout_duration = 0;115116SETUP_CONTEXT();117if (ldap_context->disable_lockout)118return 0;119120code = lookup_lockout_policy(context, entry, &max_fail,121&failcnt_interval,122&lockout_duration);123if (code != 0)124return code;125126if (locked_check_p(context, stamp, max_fail, lockout_duration, entry))127return KRB5KDC_ERR_CLIENT_REVOKED;128129return 0;130}131132krb5_error_code133krb5_ldap_lockout_audit(krb5_context context,134krb5_db_entry *entry,135krb5_timestamp stamp,136krb5_error_code status)137{138krb5_error_code code;139kdb5_dal_handle *dal_handle;140krb5_ldap_context *ldap_context;141krb5_kvno max_fail = 0;142krb5_deltat failcnt_interval = 0;143krb5_deltat lockout_duration = 0;144krb5_timestamp unlock_time;145146SETUP_CONTEXT();147148switch (status) {149case 0:150case KRB5KDC_ERR_PREAUTH_FAILED:151case KRB5KRB_AP_ERR_BAD_INTEGRITY:152break;153default:154return 0;155}156157if (entry == NULL)158return 0;159160if (!ldap_context->disable_lockout) {161code = lookup_lockout_policy(context, entry, &max_fail,162&failcnt_interval,163&lockout_duration);164if (code != 0)165return code;166}167168/*169* Don't continue to modify the DB for an already locked account.170* (In most cases, status will be KRB5KDC_ERR_CLIENT_REVOKED, and171* this check is unneeded, but in rare cases, we can fail with an172* integrity error or preauth failure before a policy check.)173*/174if (locked_check_p(context, stamp, max_fail, lockout_duration, entry))175return 0;176177entry->mask = 0;178179/* Only mark the authentication as successful if the entry180* required preauthentication, otherwise we have no idea. */181if (status == 0 && (entry->attributes & KRB5_KDB_REQUIRES_PRE_AUTH)) {182if (!ldap_context->disable_lockout && entry->fail_auth_count != 0) {183entry->fail_auth_count = 0;184entry->mask |= KADM5_FAIL_AUTH_COUNT;185}186if (!ldap_context->disable_last_success) {187entry->last_success = stamp;188entry->mask |= KADM5_LAST_SUCCESS;189}190} else if (!ldap_context->disable_lockout &&191(status == KRB5KDC_ERR_PREAUTH_FAILED ||192status == KRB5KRB_AP_ERR_BAD_INTEGRITY)) {193if (krb5_dbe_lookup_last_admin_unlock(context, entry,194&unlock_time) == 0 &&195!ts_after(entry->last_failed, unlock_time)) {196/* Reset fail_auth_count after administrative unlock. */197entry->fail_auth_count = 0;198entry->mask |= KADM5_FAIL_AUTH_COUNT;199}200201if (failcnt_interval != 0 &&202ts_after(stamp, ts_incr(entry->last_failed, failcnt_interval))) {203/* Reset fail_auth_count after failcnt_interval */204entry->fail_auth_count = 0;205entry->mask |= KADM5_FAIL_AUTH_COUNT;206}207208entry->last_failed = stamp;209entry->mask |= KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT_INCREMENT;210}211212if (entry->mask) {213code = krb5_ldap_put_principal(context, entry, NULL);214if (code != 0)215return code;216}217218return 0;219}220221222