>From b845ae6587c6beeac68fc87d8936fadbad8dba3c Mon Sep 17 00:00:00 2001
From: Pavel Reichl <preichl@redhat.com>
Date: Wed, 18 Feb 2015 01:03:40 -0500
Subject: [PATCH 2/2] SDAP: enable change phase of pw expire policy check

Implement new option which moves checking password policy from
authentication phase to accounting phase.

This allows SSSD to issue shadow expiration warning even if alternate
authentication method is used.

Resolves:
https://fedorahosted.org/sssd/ticket/2167
---
 src/man/sssd-ldap.5.xml          |  15 ++++++
 src/providers/data_provider_be.c |   9 ++++
 src/providers/dp_backend.h       |   2 +
 src/providers/ldap/ldap_auth.c   | 107 ++++++++++++++++++++++++++++++++++++++-
 src/providers/ldap/ldap_init.c   |   3 ++
 src/providers/ldap/sdap_access.c |  43 +++++++++++++++-
 src/providers/ldap/sdap_access.h |   2 +
 7 files changed, 179 insertions(+), 2 deletions(-)

diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index 9f2e9ac34add13e40d316374094024afdcc4ae31..e53156fdfcf99648f1a0d92450d3e3c38c188d7c 100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -1959,6 +1959,21 @@ ldap_access_filter = (employeeType=admin)
                             ldap_account_expire_policy
                         </para>
                         <para>
+                            <emphasis>expire_policy</emphasis>:
+                            password expiration policy is moved from authorization
+                            phase to accounting phase.
+                        </para>
+                        <para>
+                            This is useful if users are interested in
+                            seeing password expiration warning when authenticating
+                            using different method then passwords - for example
+                            SSH keys.
+                        </para>
+                        <para>
+                            Please note that 'access_provider = ldap' must
+                            be set for this feature to work.
+                        </para>
+                        <para>
                             <emphasis>authorized_service</emphasis>: use
                             the authorizedService attribute to determine
                             access
diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c
index 02749035cd12d0b81f613dbde7d72b5966cfc495..b770b83dfa24242ea0c5e2753e985a8d9fd09f29 100644
--- a/src/providers/data_provider_be.c
+++ b/src/providers/data_provider_be.c
@@ -2947,3 +2947,12 @@ static int data_provider_logrotate(struct sbus_request *dbus_req, void *data)
 
     return sbus_request_return_and_finish(dbus_req, DBUS_TYPE_INVALID);
 }
+
+const char *get_bet_mod_name(struct be_ctx *be_ctx, enum bet_type type)
+{
+    if (type > BET_NULL || type < BET_MAX) {
+        return be_ctx->bet_info[type].mod_name;
+    }
+
+    return NULL;
+}
diff --git a/src/providers/dp_backend.h b/src/providers/dp_backend.h
index e4e22ea343a5cbf4c75f176c3002dc579c2893fe..0291f3a7730022f44f8d80bc475f7544a957edb9 100644
--- a/src/providers/dp_backend.h
+++ b/src/providers/dp_backend.h
@@ -307,4 +307,6 @@ errno_t be_get_account_info_recv(struct tevent_req *req,
                                  int *_err_min,
                                  const char **_err_msg);
 
+const char *get_bet_mod_name(struct be_ctx *be_ctx, enum bet_type type);
+
 #endif /* __DP_BACKEND_H___ */
diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
index 1f1add53713201c83a13928ebb66f6bd130d7bac..b9d1f5846909011e3a23e9aac8d8b7cba9b71552 100644
--- a/src/providers/ldap/ldap_auth.c
+++ b/src/providers/ldap/ldap_auth.c
@@ -47,6 +47,7 @@
 #include "providers/ldap/sdap_async.h"
 #include "providers/ldap/sdap_async_private.h"
 #include "providers/ldap/ldap_auth.h"
+#include "providers/ldap/sdap_access.h"
 
 #define LDAP_PWEXPIRE_WARNING_TIME 0
 
@@ -1256,6 +1257,104 @@ done:
     be_req_terminate(breq, dp_err, pd->pam_status, NULL);
 }
 
+static errno_t is_expire_policy_in_access_order(const char *access_order,
+                                                bool *_result)
+{
+    TALLOC_CTX *tmp_ctx;
+    int order_list_len;
+    char **order_list;
+    errno_t ret;
+    bool result;
+    int c;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = split_on_separator(tmp_ctx, access_order, ',', true, true,
+                             &order_list, &order_list_len);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "split_on_separator failed.\n");
+        goto done;
+    }
+
+    for (c = 0; order_list[c] != NULL; c++) {
+        if (strcasecmp(order_list[c],
+                       LDAP_ACCESS_EXPIRE_POLICY_NAME) == 0) {
+            result = true;
+            goto done;
+        }
+    }
+    result = false;
+
+done:
+    talloc_free(tmp_ctx);
+    if (ret == EOK) {
+        *_result = result;
+    }
+    return ret;
+}
+
+static errno_t postpone_check_pwexpire_policy(struct be_ctx *be_ctx,
+                                              const char *access_order,
+                                              bool *_result)
+{
+    const char* access_provider_name;
+    errno_t ret;
+    bool result;
+
+    access_provider_name = get_bet_mod_name(be_ctx, BET_ACCESS);
+    if (access_provider_name != NULL &&
+        strcmp(access_provider_name, "ldap") == 0) {
+
+        ret = is_expire_policy_in_access_order(access_order, &result);
+        goto done;
+    }
+    result = false;
+    ret = EOK;
+
+done:
+    if (ret == EOK) {
+        *_result = result;
+    }
+    return ret;
+}
+
+static bool postpone_check_pwexpire_policy_cached(struct be_ctx *be_ctx,
+                                                  const char *access_order)
+{
+    static bool is_result_cached = false;
+    static bool cached_result;
+    errno_t ret;
+
+    if (is_result_cached) {
+        goto done;
+    }
+
+    /* Result cannot change unless configuration is changed and provider
+     * restarted. */
+    ret = postpone_check_pwexpire_policy(be_ctx, access_order, &cached_result);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "postpone_check_pwexpire_policy failed: %d:[%s].\n",
+              ret, sss_strerror(ret));
+        /* in case of failure do not postpone password expire policy check */
+        cached_result = false;
+    }
+    is_result_cached = true;
+
+done:
+    return cached_result;
+}
+
+static const char *get_access_order(struct tevent_req *req)
+{
+    struct auth_state *state = tevent_req_data(req, struct auth_state);
+    return dp_opt_get_cstring(state->ctx->opts->basic, SDAP_ACCESS_ORDER);
+}
+
 static void sdap_pam_auth_done(struct tevent_req *req)
 {
     struct sdap_pam_auth_state *state =
@@ -1264,14 +1363,20 @@ static void sdap_pam_auth_done(struct tevent_req *req)
     enum pwexpire pw_expire_type;
     void *pw_expire_data;
     const char *password;
+    const char *access_order;
     int dp_err = DP_ERR_OK;
     int ret;
 
     ret = auth_recv(req, state, NULL, NULL,
                     &pw_expire_type, &pw_expire_data);
+    access_order = get_access_order(req);
     talloc_zfree(req);
 
-    if (ret == EOK) {
+    /* if access_provider is ldap and ldap_access_order contains expire_policy
+     * then password expiration policy is postponed until accounting phase
+     * takes place.
+     */
+    if (!postpone_check_pwexpire_policy_cached(be_ctx, access_order)) {
         ret = check_pwexpire_policy(pw_expire_type, pw_expire_data, state->pd,
                                     be_ctx->domain->pwd_expiration_warning);
         if (ret == -1) {
diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
index 44333a9a3a45de16aaaf83fecaea4817cebc90d4..3b8db83300c43e5e5397cdf0c880a1d82effb4c2 100644
--- a/src/providers/ldap/ldap_init.c
+++ b/src/providers/ldap/ldap_init.c
@@ -423,6 +423,9 @@ int sssm_ldap_access_init(struct be_ctx *bectx,
             access_ctx->access_rule[c] = LDAP_ACCESS_HOST;
         } else if (strcasecmp(order_list[c], LDAP_ACCESS_LOCK_NAME) == 0) {
             access_ctx->access_rule[c] = LDAP_ACCESS_LOCKOUT;
+        } else if (strcasecmp(order_list[c],
+                              LDAP_ACCESS_EXPIRE_POLICY_NAME) == 0) {
+            access_ctx->access_rule[c] = LDAP_ACCESS_EXPIRE_POLICY;
         } else {
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Unexpected access rule name [%s].\n", order_list[c]);
diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index a6c882cae634f080b200fe75f51867e39192bcd9..84ed05a516b9a4996d5693f0991f73f4410057ab 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -39,10 +39,16 @@
 #include "providers/ldap/sdap_async.h"
 #include "providers/data_provider.h"
 #include "providers/dp_backend.h"
+#include "providers/ldap/ldap_auth.h"
 
 #define PERMANENTLY_LOCKED_ACCOUNT "000001010000Z"
 #define MALFORMED_FILTER "Malformed access control filter [%s]\n"
 
+static errno_t perform_pwexpire_policy(TALLOC_CTX *mem_ctx,
+                                       struct sss_domain_info *domain,
+                                       struct pam_data *pd,
+                                       struct sdap_options *opts);
+
 static errno_t sdap_save_user_cache_bool(struct sss_domain_info *domain,
                                          const char *username,
                                          const char *attr_name,
@@ -237,6 +243,11 @@ static errno_t sdap_access_check_next_rule(struct sdap_access_req_ctx *state,
                                        state->pd, state->user_entry);
             break;
 
+        case LDAP_ACCESS_EXPIRE_POLICY:
+            ret = perform_pwexpire_policy(state, state->domain, state->pd,
+                                          state->access_ctx->id_ctx->opts);
+            break;
+
         case LDAP_ACCESS_SERVICE:
             ret = sdap_access_service( state->pd, state->user_entry);
             break;
@@ -651,7 +662,6 @@ static errno_t sdap_account_expired_nds(struct pam_data *pd,
     return EOK;
 }
 
-
 static errno_t sdap_account_expired(struct sdap_access_ctx *access_ctx,
                                     struct pam_data *pd,
                                     struct ldb_message *user_entry)
@@ -702,6 +712,37 @@ static errno_t sdap_account_expired(struct sdap_access_ctx *access_ctx,
     return ret;
 }
 
+static errno_t perform_pwexpire_policy(TALLOC_CTX *mem_ctx,
+                                       struct sss_domain_info *domain,
+                                       struct pam_data *pd,
+                                       struct sdap_options *opts)
+{
+    enum pwexpire pw_expire_type;
+    void *pw_expire_data;
+    errno_t ret;
+    char *dn;
+
+    ret = get_user_dn(mem_ctx, domain, opts, pd->user, &dn, &pw_expire_type,
+                      &pw_expire_data);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "get_user_dn returned %d:[%s]\n.",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = check_pwexpire_policy(pw_expire_type, pw_expire_data, pd,
+                                domain->pwd_expiration_warning);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "check_pwexpire_policy returned %d:[%s]\n.",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+done:
+    return ret;
+}
+
 struct sdap_access_filter_req_ctx {
     const char *username;
     const char *filter;
diff --git a/src/providers/ldap/sdap_access.h b/src/providers/ldap/sdap_access.h
index f085e619961198b887d65ed5ee0bc5cdd90d1b20..3fc419a684e8336480cf5cfb7e368da1112a60e9 100644
--- a/src/providers/ldap/sdap_access.h
+++ b/src/providers/ldap/sdap_access.h
@@ -39,6 +39,7 @@
 
 #define LDAP_ACCESS_FILTER_NAME "filter"
 #define LDAP_ACCESS_EXPIRE_NAME "expire"
+#define LDAP_ACCESS_EXPIRE_POLICY_NAME "expire_policy"
 #define LDAP_ACCESS_SERVICE_NAME "authorized_service"
 #define LDAP_ACCESS_HOST_NAME "host"
 #define LDAP_ACCESS_LOCK_NAME "lockout"
@@ -57,6 +58,7 @@ enum ldap_access_rule {
     LDAP_ACCESS_SERVICE,
     LDAP_ACCESS_HOST,
     LDAP_ACCESS_LOCKOUT,
+    LDAP_ACCESS_EXPIRE_POLICY,
     LDAP_ACCESS_LAST
 };
 
-- 
2.1.0

