Path: blob/main/crypto/krb5/src/plugins/kdb/db2/lockout.c
34914 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* plugins/kdb/db2/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 "kdb.h"28#include <stdio.h>29#include <errno.h>30#include <kadm5/server_internal.h>31#include "kdb5.h"32#include "kdb_db2.h"3334/*35* Helper routines for databases that wish to use the default36* principal lockout functionality.37*/3839static krb5_error_code40lookup_lockout_policy(krb5_context context,41krb5_db_entry *entry,42krb5_kvno *pw_max_fail,43krb5_deltat *pw_failcnt_interval,44krb5_deltat *pw_lockout_duration)45{46krb5_tl_data tl_data;47krb5_error_code code;48osa_princ_ent_rec adb;49XDR xdrs;5051*pw_max_fail = 0;52*pw_failcnt_interval = 0;53*pw_lockout_duration = 0;5455tl_data.tl_data_type = KRB5_TL_KADM_DATA;5657code = krb5_dbe_lookup_tl_data(context, entry, &tl_data);58if (code != 0 || tl_data.tl_data_length == 0)59return code;6061memset(&adb, 0, sizeof(adb));62xdrmem_create(&xdrs, (char *)tl_data.tl_data_contents,63tl_data.tl_data_length, XDR_DECODE);64if (!xdr_osa_princ_ent_rec(&xdrs, &adb)) {65xdr_destroy(&xdrs);66return KADM5_XDR_FAILURE;67}6869if (adb.policy != NULL) {70osa_policy_ent_t policy = NULL;7172code = krb5_db2_get_policy(context, adb.policy, &policy);73if (code == 0) {74*pw_max_fail = policy->pw_max_fail;75*pw_failcnt_interval = policy->pw_failcnt_interval;76*pw_lockout_duration = policy->pw_lockout_duration;77krb5_db_free_policy(context, policy);78}79}8081xdr_destroy(&xdrs);8283xdrmem_create(&xdrs, NULL, 0, XDR_FREE);84xdr_osa_princ_ent_rec(&xdrs, &adb);85xdr_destroy(&xdrs);8687return 0;88}8990/* draft-behera-ldap-password-policy-10.txt 7.1 */91static krb5_boolean92locked_check_p(krb5_context context,93krb5_timestamp stamp,94krb5_kvno max_fail,95krb5_timestamp lockout_duration,96krb5_db_entry *entry)97{98krb5_timestamp unlock_time;99100/* If the entry was unlocked since the last failure, it's not locked. */101if (krb5_dbe_lookup_last_admin_unlock(context, entry, &unlock_time) == 0 &&102!ts_after(entry->last_failed, unlock_time))103return FALSE;104105if (max_fail == 0 || entry->fail_auth_count < max_fail)106return FALSE;107108if (lockout_duration == 0)109return TRUE; /* principal permanently locked */110111return ts_after(ts_incr(entry->last_failed, lockout_duration), stamp);112}113114krb5_error_code115krb5_db2_lockout_check_policy(krb5_context context,116krb5_db_entry *entry,117krb5_timestamp stamp)118{119krb5_error_code code;120krb5_kvno max_fail = 0;121krb5_deltat failcnt_interval = 0;122krb5_deltat lockout_duration = 0;123krb5_db2_context *db_ctx = context->dal_handle->db_context;124125if (db_ctx->disable_lockout)126return 0;127128code = lookup_lockout_policy(context, entry, &max_fail,129&failcnt_interval,130&lockout_duration);131if (code != 0)132return code;133134if (locked_check_p(context, stamp, max_fail, lockout_duration, entry))135return KRB5KDC_ERR_CLIENT_REVOKED;136137return 0;138}139140krb5_error_code141krb5_db2_lockout_audit(krb5_context context,142krb5_db_entry *entry,143krb5_timestamp stamp,144krb5_error_code status)145{146krb5_error_code code;147krb5_kvno max_fail = 0;148krb5_deltat failcnt_interval = 0;149krb5_deltat lockout_duration = 0;150krb5_db2_context *db_ctx = context->dal_handle->db_context;151krb5_boolean need_update = FALSE;152krb5_timestamp unlock_time;153154switch (status) {155case 0:156case KRB5KDC_ERR_PREAUTH_FAILED:157case KRB5KRB_AP_ERR_BAD_INTEGRITY:158break;159default:160return 0;161}162163if (entry == NULL)164return 0;165166if (!db_ctx->disable_lockout) {167code = lookup_lockout_policy(context, entry, &max_fail,168&failcnt_interval, &lockout_duration);169if (code != 0)170return code;171}172173/*174* Don't continue to modify the DB for an already locked account.175* (In most cases, status will be KRB5KDC_ERR_CLIENT_REVOKED, and176* this check is unneeded, but in rare cases, we can fail with an177* integrity error or preauth failure before a policy check.)178*/179if (locked_check_p(context, stamp, max_fail, lockout_duration, entry))180return 0;181182/* Only mark the authentication as successful if the entry183* required preauthentication, otherwise we have no idea. */184if (status == 0 && (entry->attributes & KRB5_KDB_REQUIRES_PRE_AUTH)) {185if (!db_ctx->disable_lockout && entry->fail_auth_count != 0) {186entry->fail_auth_count = 0;187need_update = TRUE;188}189if (!db_ctx->disable_last_success) {190entry->last_success = stamp;191need_update = TRUE;192}193} else if (!db_ctx->disable_lockout &&194(status == KRB5KDC_ERR_PREAUTH_FAILED ||195status == KRB5KRB_AP_ERR_BAD_INTEGRITY)) {196if (krb5_dbe_lookup_last_admin_unlock(context, entry,197&unlock_time) == 0 &&198!ts_after(entry->last_failed, unlock_time)) {199/* Reset fail_auth_count after administrative unlock. */200entry->fail_auth_count = 0;201}202203if (failcnt_interval != 0 &&204ts_after(stamp, ts_incr(entry->last_failed, failcnt_interval))) {205/* Reset fail_auth_count after failcnt_interval. */206entry->fail_auth_count = 0;207}208209entry->last_failed = stamp;210entry->fail_auth_count++;211need_update = TRUE;212}213214if (need_update) {215code = krb5_db2_put_principal(context, entry, NULL);216if (code != 0)217return code;218}219220return 0;221}222223224