From 651f9324735d0680c6a56246616932459e15b99d Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 13 Feb 2012 12:15:07 -0500 Subject: ipa-kdb: add AS auditing support Fixes: https://fedorahosted.org/freeipa/ticket/2334 --- daemons/ipa-kdb/Makefile.am | 1 + daemons/ipa-kdb/ipa_kdb.c | 2 +- daemons/ipa-kdb/ipa_kdb.h | 18 +++++- daemons/ipa-kdb/ipa_kdb_audit_as.c | 120 +++++++++++++++++++++++++++++++++++ daemons/ipa-kdb/ipa_kdb_passwords.c | 79 ++--------------------- daemons/ipa-kdb/ipa_kdb_principals.c | 36 ++++++----- daemons/ipa-kdb/ipa_kdb_pwdpolicy.c | 89 +++++++++++++++++++++++++- 7 files changed, 254 insertions(+), 91 deletions(-) create mode 100644 daemons/ipa-kdb/ipa_kdb_audit_as.c (limited to 'daemons/ipa-kdb') diff --git a/daemons/ipa-kdb/Makefile.am b/daemons/ipa-kdb/Makefile.am index 77b92e8db..17c090418 100644 --- a/daemons/ipa-kdb/Makefile.am +++ b/daemons/ipa-kdb/Makefile.am @@ -35,6 +35,7 @@ ipadb_la_SOURCES = \ ipa_kdb_pwdpolicy.c \ ipa_kdb_mspac.c \ ipa_kdb_delegation.c \ + ipa_kdb_audit_as.c \ $(KRB5_UTIL_SRCS) \ $(NULL) diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c index ca266d546..1dae4e6c1 100644 --- a/daemons/ipa-kdb/ipa_kdb.c +++ b/daemons/ipa-kdb/ipa_kdb.c @@ -456,7 +456,7 @@ kdb_vftabl kdb_function_table = { NULL, /* check_transited_realms */ NULL, /* check_policy_as */ NULL, /* check_policy_tgs */ - NULL, /* audit_as_req */ + ipadb_audit_as_req, /* audit_as_req */ NULL, /* refresh_config */ ipadb_check_allowed_to_delegate /* check_allowed_to_delegate */ }; diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h index 2531d0328..22e28223c 100644 --- a/daemons/ipa-kdb/ipa_kdb.h +++ b/daemons/ipa-kdb/ipa_kdb.h @@ -103,7 +103,8 @@ struct ipadb_e_data { time_t last_pwd_change; char *pw_policy_dn; char **pw_history; - struct ipapwd_policy pol; + struct ipapwd_policy *pol; + time_t last_admin_unlock; }; struct ipadb_context *ipadb_get_context(krb5_context kcontext); @@ -165,6 +166,11 @@ krb5_error_code ipadb_iterate(krb5_context kcontext, krb5_pointer func_arg); /* POLICY FUNCTIONS */ + +krb5_error_code ipadb_get_ipapwd_policy(struct ipadb_context *ipactx, + char *pw_policy_dn, + struct ipapwd_policy **pol); + krb5_error_code ipadb_create_pwd_policy(krb5_context kcontext, osa_policy_ent_t policy); krb5_error_code ipadb_get_pwd_policy(krb5_context kcontext, char *name, @@ -230,3 +236,13 @@ krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext, krb5_const_principal client, const krb5_db_entry *server, krb5_const_principal proxy); + +/* AS AUDIT */ + +void ipadb_audit_as_req(krb5_context kcontext, + krb5_kdc_req *request, + krb5_db_entry *client, + krb5_db_entry *server, + krb5_timestamp authtime, + krb5_error_code error_code); + diff --git a/daemons/ipa-kdb/ipa_kdb_audit_as.c b/daemons/ipa-kdb/ipa_kdb_audit_as.c new file mode 100644 index 000000000..c71568c38 --- /dev/null +++ b/daemons/ipa-kdb/ipa_kdb_audit_as.c @@ -0,0 +1,120 @@ +/* + * MIT Kerberos KDC database backend for FreeIPA + * + * Authors: Simo Sorce + * + * Copyright (C) 2012 Simo Sorce, Red Hat + * see file 'COPYING' for use and warranty information + * + * This program is free software you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ipa_kdb.h" +#include "ipa_pwd.h" + +void ipadb_audit_as_req(krb5_context kcontext, + krb5_kdc_req *request, + krb5_db_entry *client, + krb5_db_entry *server, + krb5_timestamp authtime, + krb5_error_code error_code) +{ + struct ipadb_context *ipactx; + struct ipadb_e_data *ied; + krb5_error_code kerr; + + if (!client) { + return; + } + + if (error_code != 0 && + error_code != KRB5KDC_ERR_PREAUTH_FAILED && + error_code != KRB5KRB_AP_ERR_BAD_INTEGRITY) { + return; + } + + ipactx = ipadb_get_context(kcontext); + if (!ipactx) { + return; + } + + ied = (struct ipadb_e_data *)client->e_data; + if (!ied) { + return; + } + + if (!ied->pol) { + kerr = ipadb_get_ipapwd_policy(ipactx, ied->pw_policy_dn, &ied->pol); + if (kerr != 0) { + return; + } + } + + client->mask = 0; + + switch (error_code) { + case 0: + /* Check if preauth flag is specified (default), otherwise we have + * no data to know if auth was successful or not */ + if (client->attributes & KRB5_KDB_REQUIRES_PRE_AUTH) { + if (client->fail_auth_count != 0) { + client->fail_auth_count = 0; + client->mask |= KMASK_FAIL_AUTH_COUNT; + } + client->last_success = authtime; + client->mask |= KMASK_LAST_SUCCESS; + } + break; + + case KRB5KDC_ERR_PREAUTH_FAILED: + case KRB5KRB_AP_ERR_BAD_INTEGRITY: + + if (client->last_failed <= ied->last_admin_unlock) { + /* Reset fail_auth_count, and admin unlocked the account */ + client->fail_auth_count = 0; + client->mask |= KMASK_FAIL_AUTH_COUNT; + } + if (ied->pol->lockout_duration != 0 && + ied->pol->failcnt_interval != 0 && + client->last_failed + ied->pol->failcnt_interval < authtime) { + /* Reset fail_auth_count, the interval's expired already */ + client->fail_auth_count = 0; + client->mask |= KMASK_FAIL_AUTH_COUNT; + } + + if (ied->pol->max_fail == 0 || + client->fail_auth_count < ied->pol->max_fail) { + /* let's increase the fail counter */ + client->fail_auth_count++; + client->mask |= KMASK_FAIL_AUTH_COUNT; + } + if (client->last_failed + ied->pol->lockout_duration > authtime) { + /* client already locked, nothing more to do */ + break; + } + client->last_failed = authtime; + client->mask |= KMASK_LAST_FAILED; + break; + default: + return; + } + + if (client->mask) { + kerr = ipadb_put_principal(kcontext, client, NULL); + if (kerr != 0) { + return; + } + } + client->mask = 0; +} diff --git a/daemons/ipa-kdb/ipa_kdb_passwords.c b/daemons/ipa-kdb/ipa_kdb_passwords.c index 0bb7fa724..c717e2031 100644 --- a/daemons/ipa-kdb/ipa_kdb_passwords.c +++ b/daemons/ipa-kdb/ipa_kdb_passwords.c @@ -24,73 +24,6 @@ #include "ipa_pwd.h" #include -static char *pw_policy_attrs[] = { - "krbmaxpwdlife", - "krbminpwdlife", - "krbpwdmindiffchars", - "krbpwdminlength", - "krbpwdhistorylength", - - NULL -}; - -static krb5_error_code ipadb_get_pw_policy(struct ipadb_context *ipactx, - char *pw_policy_dn, - struct ipapwd_policy *pol) -{ - krb5_error_code kerr; - LDAPMessage *res = NULL; - LDAPMessage *lentry; - uint32_t result; - int ret; - - kerr = ipadb_simple_search(ipactx, pw_policy_dn, LDAP_SCOPE_BASE, - "(objectClass=*)", pw_policy_attrs, &res); - if (kerr) { - goto done; - } - - lentry = ldap_first_entry(ipactx->lcontext, res); - if (!lentry) { - kerr = KRB5_KDB_INTERNAL_ERROR; - goto done; - } - - ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry, - "krbMinPwdLife", &result); - if (ret == 0) { - pol->min_pwd_life = result; - } - - ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry, - "krbMaxPwdLife", &result); - if (ret == 0) { - pol->max_pwd_life = result; - } - - ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry, - "krbPwdMinLength", &result); - if (ret == 0) { - pol->min_pwd_length = result; - } - - ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry, - "krbPwdHistoryLength", &result); - if (ret == 0) { - pol->history_length = result; - } - - ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry, - "krbPwdMinDiffChars", &result); - if (ret == 0) { - pol->min_complexity = result; - } - -done: - ldap_msgfree(res); - return kerr; -} - static krb5_error_code ipapwd_error_to_kerr(krb5_context context, enum ipapwd_error err) { @@ -149,13 +82,11 @@ static krb5_error_code ipadb_check_pw_policy(krb5_context context, return ENOMEM; } - ied->pol.max_pwd_life = IPAPWD_DEFAULT_PWDLIFE; - ied->pol.min_pwd_length = IPAPWD_DEFAULT_MINLEN; - kerr = ipadb_get_pw_policy(ipactx, ied->pw_policy_dn, &ied->pol); + kerr = ipadb_get_ipapwd_policy(ipactx, ied->pw_policy_dn, &ied->pol); if (kerr != 0) { return kerr; } - ret = ipapwd_check_policy(&ied->pol, passwd, time(NULL), + ret = ipapwd_check_policy(ied->pol, passwd, time(NULL), db_entry->expiration, db_entry->pw_expiration, ied->last_pwd_change, @@ -302,7 +233,11 @@ krb5_error_code ipadb_get_pwd_expiration(krb5_context context, } if (truexp) { - *expire_time = mod_time + ied->pol.max_pwd_life; + if (ied->pol) { + *expire_time = mod_time + ied->pol->max_pwd_life; + } else { + *expire_time = mod_time + IPAPWD_DEFAULT_PWDLIFE; + } } else { /* not 'self', so reset */ *expire_time = mod_time; diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c index 431fd27a4..3540f0eea 100644 --- a/daemons/ipa-kdb/ipa_kdb_principals.c +++ b/daemons/ipa-kdb/ipa_kdb_principals.c @@ -534,20 +534,6 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext, entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX; } - ret = ipadb_ldap_attr_to_time_t(lcontext, lentry, - "krbLastAdminUnlock", &restime); - if (ret == 0) { - krb5_int32 time32le = htole32((krb5_int32)restime); - - kerr = ipadb_set_tl_data(entry, - KRB5_TL_LAST_ADMIN_UNLOCK, - sizeof(time32le), - (krb5_octet *)&time32le); - if (kerr) { - goto done; - } - } - ied = calloc(1, sizeof(struct ipadb_e_data)); if (!ied) { kerr = ENOMEM; @@ -619,6 +605,22 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext, ied->last_pwd_change = restime; } + ret = ipadb_ldap_attr_to_time_t(lcontext, lentry, + "krbLastAdminUnlock", &restime); + if (ret == 0) { + krb5_int32 time32le = htole32((krb5_int32)restime); + + kerr = ipadb_set_tl_data(entry, + KRB5_TL_LAST_ADMIN_UNLOCK, + sizeof(time32le), + (krb5_octet *)&time32le); + if (kerr) { + goto done; + } + + ied->last_admin_unlock = restime; + } + kerr = 0; done: @@ -933,6 +935,7 @@ void ipadb_free_principal(krb5_context kcontext, krb5_db_entry *entry) free(ied->pw_history[i]); } free(ied->pw_history); + free(ied->pol); free(ied); } } @@ -1618,9 +1621,10 @@ static krb5_error_code ipadb_entry_to_mods(krb5_context kcontext, } } - if (ied->ipa_user && ied->passwd && ied->pol.history_length) { + if (ied->ipa_user && ied->passwd && + ied->pol && ied->pol->history_length) { ret = ipapwd_generate_new_history(ied->passwd, now, - ied->pol.history_length, + ied->pol->history_length, ied->pw_history, &new_history, &nh_len); if (ret) { diff --git a/daemons/ipa-kdb/ipa_kdb_pwdpolicy.c b/daemons/ipa-kdb/ipa_kdb_pwdpolicy.c index 46a051330..03948029f 100644 --- a/daemons/ipa-kdb/ipa_kdb_pwdpolicy.c +++ b/daemons/ipa-kdb/ipa_kdb_pwdpolicy.c @@ -21,10 +21,11 @@ */ #include "ipa_kdb.h" +#include "ipa_pwd.h" #define POLICY_SEARCH_FILTER "(&(objectClass=krbPwdPolicy)(cn=%s))" -static char *std_pwdpolicy_attrs[] = { +char *std_pwdpolicy_attrs[] = { "krbmaxpwdlife", "krbminpwdlife", "krbpwdmindiffchars", @@ -37,6 +38,92 @@ static char *std_pwdpolicy_attrs[] = { NULL }; +krb5_error_code ipadb_get_ipapwd_policy(struct ipadb_context *ipactx, + char *pw_policy_dn, + struct ipapwd_policy **_pol) +{ + struct ipapwd_policy *pol; + krb5_error_code kerr; + LDAPMessage *res = NULL; + LDAPMessage *lentry; + uint32_t result; + int ret; + + pol = calloc(1, sizeof(struct ipapwd_policy)); + if (!pol) { + return ENOMEM; + } + + pol->max_pwd_life = IPAPWD_DEFAULT_PWDLIFE; + pol->min_pwd_length = IPAPWD_DEFAULT_MINLEN; + + kerr = ipadb_simple_search(ipactx, pw_policy_dn, LDAP_SCOPE_BASE, + "(objectClass=*)", std_pwdpolicy_attrs, &res); + if (kerr) { + goto done; + } + + lentry = ldap_first_entry(ipactx->lcontext, res); + if (!lentry) { + kerr = KRB5_KDB_INTERNAL_ERROR; + goto done; + } + + ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry, + "krbMinPwdLife", &result); + if (ret == 0) { + pol->min_pwd_life = result; + } + + ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry, + "krbMaxPwdLife", &result); + if (ret == 0) { + pol->max_pwd_life = result; + } + + ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry, + "krbPwdMinLength", &result); + if (ret == 0) { + pol->min_pwd_length = result; + } + + ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry, + "krbPwdHistoryLength", &result); + if (ret == 0) { + pol->history_length = result; + } + + ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry, + "krbPwdMinDiffChars", &result); + if (ret == 0) { + pol->min_complexity = result; + } + + ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry, + "krbPwdMaxFailure", &result); + if (ret == 0) { + pol->max_fail = result; + } + + ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry, + "krbPwdFailureCountInterval", &result); + if (ret == 0) { + pol->failcnt_interval = result; + } + + ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry, + "krbPwdLockoutDuration", &result); + if (ret == 0) { + pol->lockout_duration = result; + } + + *_pol = pol; + +done: + ldap_msgfree(res); + return kerr; +} + krb5_error_code ipadb_create_pwd_policy(krb5_context kcontext, osa_policy_ent_t policy) { -- cgit