From bf8cce77a35cb0a3cdb0d21fb9c39b7b6372bc11 Mon Sep 17 00:00:00 2001 From: Jan Zeleny Date: Tue, 1 May 2012 03:36:37 -0400 Subject: Modify behavior of pam_pwd_expiration_warning New option pwd_expiration_warning is introduced which can be set per domain and can override the value specified by the original pam_pwd_expiration_warning. If the value of expiration warning is set to zero, the filter isn't apllied at all - if backend server returns the warning, it will be automatically displayed. Default value for Kerberos: 7 days Default value for LDAP: don't apply the filter Technical note: default value when creating the domain is -1. This is important so we can distinguish between "no value set" and 0. Without this possibility it would be impossible to set different values for LDAP and Kerberos provider. --- src/confdb/confdb.c | 18 ++++++++++++++++++ src/confdb/confdb.h | 3 +++ src/config/etc/sssd.api.conf | 1 + src/man/sssd.conf.5.xml | 35 ++++++++++++++++++++++++++++++++++- src/providers/krb5/krb5_auth.c | 31 +++++++++++++++++++++++++++---- src/providers/ldap/ldap_auth.c | 42 ++++++++++++++++++++++++++++++------------ src/responder/pam/pamsrv_cmd.c | 35 ----------------------------------- src/util/domain_info_utils.c | 1 + src/util/sss_krb5.h | 5 +++++ 9 files changed, 119 insertions(+), 52 deletions(-) diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c index d938d4f61..427101e9b 100644 --- a/src/confdb/confdb.c +++ b/src/confdb/confdb.c @@ -922,6 +922,24 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, goto done; } + /* Set the PAM warning time, if specified */ + val = ldb_msg_find_attr_as_int(res->msgs[0], + CONFDB_DOMAIN_PWD_EXPIRATION_WARNING, + -1); + if (val > 0) { + /* The value is in days, transform it to seconds */ + val *= 24 * 3600; + } else { + ret = confdb_get_int(cdb, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_PWD_EXPIRATION_WARNING, + -1, &val); + if (ret != EOK) { + DEBUG(1, ("Failed to read PAM expiration warning, not fatal.\n")); + val = -1; + } + } + domain->pwd_expiration_warning = val; + ret = get_entry_as_uint32(res->msgs[0], &domain->override_gid, CONFDB_DOMAIN_OVERRIDE_GID, 0); if (ret != EOK) { diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h index b90db303a..5ec9f721c 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -152,6 +152,7 @@ #define CONFDB_DOMAIN_NETGROUP_CACHE_TIMEOUT "entry_cache_netgroup_timeout" #define CONFDB_DOMAIN_SERVICE_CACHE_TIMEOUT "entry_cache_service_timeout" #define CONFDB_DOMAIN_AUTOFS_CACHE_TIMEOUT "entry_cache_autofs_timeout" +#define CONFDB_DOMAIN_PWD_EXPIRATION_WARNING "pwd_expiration_warning" /* Local Provider */ #define CONFDB_LOCAL_DEFAULT_SHELL "default_shell" @@ -199,6 +200,8 @@ struct sss_domain_info { uint32_t service_timeout; uint32_t autofsmap_timeout; + int pwd_expiration_warning; + struct sysdb_ctx *sysdb; struct sss_domain_info **subdomains; diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf index a7bece99a..a5fdbffb6 100644 --- a/src/config/etc/sssd.api.conf +++ b/src/config/etc/sssd.api.conf @@ -89,6 +89,7 @@ use_fully_qualified_names = bool, None, false entry_cache_timeout = int, None, false lookup_family_order = str, None, false account_cache_expiration = int, None, false +pwd_expiration_warning = int, None, false filter_users = list, str, false filter_groups = list, str, false dns_resolver_timeout = int, None, false diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml index ef7490d94..a7691edcc 100644 --- a/src/man/sssd.conf.5.xml +++ b/src/man/sssd.conf.5.xml @@ -632,7 +632,17 @@ warning. - Default: 7 + If zero is set, then this filter is not applied, + i.e. if the expiration warning was received from + backend server, it will automatically be displayed. + + + This setting can be overridden by setting + pwd_expiration_warning + for a particular domain. + + + Default: 0 @@ -922,6 +932,29 @@ + + pwd_expiration_warning (integer) + + + Display a warning N days before the password expires. + + + If zero is set, then this filter is not applied, + i.e. if the expiration warning was received from + backend server, it will automatically be displayed. + + + Please note that the backend server has to provide + information about the expiration time of the password. + If this information is missing, sssd cannot display a + warning. Also an auth provider has to be configured for + the backend. + + + Default: 7 (Kerberos), 0 (LDAP) + + + id_provider (string) diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c index 0306426cc..986e449fd 100644 --- a/src/providers/krb5/krb5_auth.c +++ b/src/providers/krb5/krb5_auth.c @@ -734,8 +734,16 @@ static void krb5_child_done(struct tevent_req *subreq) int32_t msg_len; int64_t time_data; struct tgt_times tgtt; + int pwd_exp_warning; + uint32_t *expiration; + uint32_t *msg_subtype; + bool skip; memset(&tgtt, 0, sizeof(tgtt)); + pwd_exp_warning = state->be_ctx->domain->pwd_expiration_warning; + if (pwd_exp_warning < 0) { + pwd_exp_warning = KERBEROS_PWEXPIRE_WARNING_TIME; + } ret = handle_child_recv(subreq, pd, &buf, &len); talloc_zfree(subreq); @@ -771,6 +779,7 @@ static void krb5_child_done(struct tevent_req *subreq) SAFEALIGN_COPY_INT32(&msg_status, buf+p, &p); while (p < len) { + skip = false; SAFEALIGN_COPY_INT32(&msg_type, buf+p, &p); SAFEALIGN_COPY_INT32(&msg_len, buf+p, &p); @@ -813,10 +822,24 @@ static void krb5_child_done(struct tevent_req *subreq) tgtt.starttime, tgtt.endtime, tgtt.renew_till)); } - ret = pam_add_response(pd, msg_type, msg_len, &buf[p]); - if (ret != EOK) { - /* This is not a fatal error */ - DEBUG(1, ("pam_add_response failed.\n")); + if (msg_type == SSS_PAM_USER_INFO) { + msg_subtype = (uint32_t *)&buf[p]; + if (*msg_subtype == SSS_PAM_USER_INFO_EXPIRE_WARN) + { + expiration = (uint32_t *)&buf[p+sizeof(uint32_t)]; + if (pwd_exp_warning > 0 && + difftime(pwd_exp_warning, *expiration) < 0.0) { + skip = true; + } + } + } + + if (!skip) { + ret = pam_add_response(pd, msg_type, msg_len, &buf[p]); + if (ret != EOK) { + /* This is not a fatal error */ + DEBUG(1, ("pam_add_response failed.\n")); + } } p += msg_len; diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c index 734249ced..da64f249e 100644 --- a/src/providers/ldap/ldap_auth.c +++ b/src/providers/ldap/ldap_auth.c @@ -47,11 +47,7 @@ #include "providers/ldap/sdap_async.h" #include "providers/ldap/sdap_async_private.h" -/* MIT Kerberos has the same hardcoded warning interval of 7 days. Due to the - * fact that using the expiration time of a Kerberos password with LDAP - * authentication is presumably a rare case a separate config option is not - * necessary. */ -#define KERBEROS_PWEXPIRE_WARNING_TIME (7 * 24 * 60 * 60) +#define LDAP_PWEXPIRE_WARNING_TIME 0 enum pwexpire { PWEXPIRE_NONE = 0, @@ -90,11 +86,13 @@ static errno_t add_expired_warning(struct pam_data *pd, long exp_time) static errno_t check_pwexpire_kerberos(const char *expire_date, time_t now, struct pam_data *pd, - enum sdap_result *result) + enum sdap_result *result, + int pwd_exp_warning) { char *end; struct tm tm; time_t expire_time; + int expiration_warning; int ret; memset(&tm, 0, sizeof(tm)); @@ -130,8 +128,14 @@ static errno_t check_pwexpire_kerberos(const char *expire_date, time_t now, } else { *result = SDAP_AUTH_SUCCESS; + if (pwd_exp_warning >= 0) { + expiration_warning = pwd_exp_warning; + } else { + expiration_warning = KERBEROS_PWEXPIRE_WARNING_TIME; + } if (pd != NULL && - difftime(now + KERBEROS_PWEXPIRE_WARNING_TIME, expire_time) > 0.0) { + (difftime(now + expiration_warning, expire_time) > 0.0 || + expiration_warning == 0)) { ret = add_expired_warning(pd, (long) difftime(expire_time, now)); if (ret != EOK) { DEBUG(1, ("add_expired_warning failed.\n")); @@ -202,13 +206,19 @@ static errno_t check_pwexpire_shadow(struct spwd *spwd, time_t now, static errno_t check_pwexpire_ldap(struct pam_data *pd, struct sdap_ppolicy_data *ppolicy, - enum sdap_result *result) + enum sdap_result *result, + int pwd_exp_warning) { if (ppolicy->grace > 0 || ppolicy->expire > 0) { uint32_t *data; uint32_t *ptr; + time_t now = time(NULL); int ret; + if (pwd_exp_warning < 0) { + pwd_exp_warning = 0; + } + data = talloc_size(pd, 2* sizeof(uint32_t)); if (data == NULL) { DEBUG(1, ("talloc_size failed.\n")); @@ -221,6 +231,10 @@ static errno_t check_pwexpire_ldap(struct pam_data *pd, ptr++; *ptr = ppolicy->grace; } else if (ppolicy->expire > 0) { + if (pwd_exp_warning == 0 || + difftime(now + pwd_exp_warning, ppolicy->expire) > 0.0) { + goto done; + } *ptr = SSS_PAM_USER_INFO_EXPIRE_WARN; ptr++; *ptr = ppolicy->expire; @@ -234,6 +248,7 @@ static errno_t check_pwexpire_ldap(struct pam_data *pd, } } +done: *result = SDAP_AUTH_SUCCESS; return EOK; } @@ -830,8 +845,8 @@ static void sdap_auth4chpass_done(struct tevent_req *req) } break; case PWEXPIRE_KERBEROS: - ret = check_pwexpire_kerberos(pw_expire_data, time(NULL), NULL, - &result); + ret = check_pwexpire_kerberos(pw_expire_data, time(NULL), NULL, &result, + state->breq->domain->pwd_expiration_warning); if (ret != EOK) { DEBUG(1, ("check_pwexpire_kerberos failed.\n")); state->pd->pam_status = PAM_SYSTEM_ERR; @@ -1064,6 +1079,7 @@ static void sdap_pam_auth_done(struct tevent_req *req) tevent_req_callback_data(req, struct sdap_pam_auth_state); enum sdap_result result; enum pwexpire pw_expire_type; + struct be_ctx *be_ctx = state->breq->be_ctx; void *pw_expire_data; int dp_err = DP_ERR_OK; int ret; @@ -1091,7 +1107,8 @@ static void sdap_pam_auth_done(struct tevent_req *req) break; case PWEXPIRE_KERBEROS: ret = check_pwexpire_kerberos(pw_expire_data, time(NULL), - state->pd, &result); + state->pd, &result, + be_ctx->domain->pwd_expiration_warning); if (ret != EOK) { DEBUG(1, ("check_pwexpire_kerberos failed.\n")); state->pd->pam_status = PAM_SYSTEM_ERR; @@ -1099,7 +1116,8 @@ static void sdap_pam_auth_done(struct tevent_req *req) } break; case PWEXPIRE_LDAP_PASSWORD_POLICY: - ret = check_pwexpire_ldap(state->pd, pw_expire_data, &result); + ret = check_pwexpire_ldap(state->pd, pw_expire_data, &result, + be_ctx->domain->pwd_expiration_warning); if (ret != EOK) { DEBUG(1, ("check_pwexpire_ldap failed.\n")); state->pd->pam_status = PAM_SYSTEM_ERR; diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c index c3e027e3c..608a38d4f 100644 --- a/src/responder/pam/pamsrv_cmd.c +++ b/src/responder/pam/pamsrv_cmd.c @@ -42,7 +42,6 @@ enum pam_verbosity { }; #define DEFAULT_PAM_VERBOSITY PAM_VERBOSITY_IMPORTANT -#define DEFAULT_PAM_PWD_EXPIRATION_WARNING 7 static void pam_reply(struct pam_auth_req *preq); @@ -515,16 +514,7 @@ static errno_t filter_responses(struct confdb_ctx *cdb, struct response_data *resp; uint32_t user_info_type; int64_t expire_date; - uint32_t expire_warn; - TALLOC_CTX *tmp_ctx; int pam_verbosity; - int pam_expiration_warning; - - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - DEBUG(1, ("talloc_new failed.\n")); - return ENOMEM; - } ret = confdb_get_int(cdb, CONFDB_PAM_CONF_ENTRY, CONFDB_PAM_VERBOSITY, DEFAULT_PAM_VERBOSITY, @@ -534,20 +524,7 @@ static errno_t filter_responses(struct confdb_ctx *cdb, pam_verbosity = DEFAULT_PAM_VERBOSITY; } - - ret = confdb_get_int(cdb, CONFDB_PAM_CONF_ENTRY, - CONFDB_PAM_PWD_EXPIRATION_WARNING, - DEFAULT_PAM_PWD_EXPIRATION_WARNING, - &pam_expiration_warning); - if (ret != EOK) { - DEBUG(1, ("Failed to read PAM expiration warning, not fatal.\n")); - pam_expiration_warning = DEFAULT_PAM_PWD_EXPIRATION_WARNING; - } - - talloc_free(tmp_ctx); - resp = resp_list; - while(resp != NULL) { if (resp->type == SSS_PAM_USER_INFO) { if (resp->len < sizeof(uint32_t)) { @@ -580,18 +557,6 @@ static errno_t filter_responses(struct confdb_ctx *cdb, resp->do_not_send_to_client = true; } - break; - case SSS_PAM_USER_INFO_EXPIRE_WARN: - if (resp->len != 2 * sizeof(uint32_t)) { - DEBUG(1, ("User info expire warning entry is " - "too short.\n")); - return EINVAL; - } - memcpy(&expire_warn, resp->data + sizeof(uint32_t), - sizeof(uint32_t)); - if(expire_warn > pam_expiration_warning * (60 * 60 * 24)) { - resp->do_not_send_to_client = true; - } break; default: DEBUG(7, ("User info type [%d] not filtered.\n")); diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c index d9f320d80..45f98d858 100644 --- a/src/util/domain_info_utils.c +++ b/src/util/domain_info_utils.c @@ -77,6 +77,7 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx, /* FIXME: get ranges from the server */ dom->id_min = 0; dom->id_max = 0xffffffff; + dom->pwd_expiration_warning = parent->pwd_expiration_warning; dom->cache_credentials = parent->cache_credentials; dom->case_sensitive = parent->case_sensitive; dom->user_timeout = parent->user_timeout; diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h index 50c4b696f..6ad80806c 100644 --- a/src/util/sss_krb5.h +++ b/src/util/sss_krb5.h @@ -34,6 +34,11 @@ #include "util/util.h" +/* MIT Kerberos has the same hardcoded warning interval of 7 days. Due to the + * fact that using the expiration time of a Kerberos password with LDAP + * authentication is presumably a rare case a separate config option is not + * necessary. */ +#define KERBEROS_PWEXPIRE_WARNING_TIME (7 * 24 * 60 * 60) #define KEYTAB_CLEAN_NAME keytab_name ? keytab_name : "default" const char * KRB5_CALLCONV sss_krb5_get_error_message (krb5_context, -- cgit