Hello,

please see attached patches resolving https://fedorahosted.org/sssd/ticket/2167

1st patch contains some minor refactoring useful for 2nd patch.

How to test:
On OpenLDAP following attributes should be set:
 * shadowMax - how many days after shadowLastChange will password expire
* shadowWarning - how many days before password expires should warning be displayed.

In sssd.conf domain section should contain something similar to:

access_provider = ldap
ldap_access_order = expire, expire_policy
ldap_account_expire_policy = shadow
ldap_pwd_policy = shadow

Thanks!
>From 0a7b3d94768967d4285a33506fa3bdfb322e9ed2 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <prei...@redhat.com>
Date: Mon, 16 Feb 2015 18:56:25 -0500
Subject: [PATCH 1/2] SDAP: refactor pwexpire policy

Move part of pwexpire policy code to a separate function.

Relates to:
https://fedorahosted.org/sssd/ticket/2167
---
 src/providers/ldap/ldap_auth.c | 76 ++++++++++++++++++++++++------------------
 src/providers/ldap/ldap_auth.h | 46 +++++++++++++++++++++++++
 2 files changed, 90 insertions(+), 32 deletions(-)
 create mode 100644 src/providers/ldap/ldap_auth.h

diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
index 5a40c1359f138c42eb915e873fe21a50ab038e81..1f1add53713201c83a13928ebb66f6bd130d7bac 100644
--- a/src/providers/ldap/ldap_auth.c
+++ b/src/providers/ldap/ldap_auth.c
@@ -46,16 +46,10 @@
 #include "providers/ldap/ldap_common.h"
 #include "providers/ldap/sdap_async.h"
 #include "providers/ldap/sdap_async_private.h"
+#include "providers/ldap/ldap_auth.h"
 
 #define LDAP_PWEXPIRE_WARNING_TIME 0
 
-enum pwexpire {
-    PWEXPIRE_NONE = 0,
-    PWEXPIRE_LDAP_PASSWORD_POLICY,
-    PWEXPIRE_KERBEROS,
-    PWEXPIRE_SHADOW
-};
-
 static errno_t add_expired_warning(struct pam_data *pd, long exp_time)
 {
     int ret;
@@ -248,10 +242,41 @@ done:
     return ret;
 }
 
-static errno_t find_password_expiration_attributes(TALLOC_CTX *mem_ctx,
-                                               const struct ldb_message *msg,
-                                               struct dp_option *opts,
-                                               enum pwexpire *type, void **data)
+errno_t check_pwexpire_policy(enum pwexpire pw_expire_type,
+                              void *pw_expire_data,
+                              struct pam_data *pd,
+                              int pwd_expiration_warning)
+{
+    errno_t ret;
+
+    switch (pw_expire_type) {
+    case PWEXPIRE_SHADOW:
+        ret = check_pwexpire_shadow(pw_expire_data, time(NULL), pd);
+        break;
+    case PWEXPIRE_KERBEROS:
+        ret = check_pwexpire_kerberos(pw_expire_data, time(NULL), pd,
+                                      pwd_expiration_warning);
+        break;
+    case PWEXPIRE_LDAP_PASSWORD_POLICY:
+        ret = check_pwexpire_ldap(pd, pw_expire_data,
+                                  pwd_expiration_warning);
+        break;
+    case PWEXPIRE_NONE:
+        ret = EOK;
+        break;
+    default:
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown password expiration type.\n");
+        ret = -1;
+    }
+
+    return ret;
+}
+
+static errno_t
+find_password_expiration_attributes(TALLOC_CTX *mem_ctx,
+                                    const struct ldb_message *msg,
+                                    struct dp_option *opts,
+                                    enum pwexpire *type, void **data)
 {
     const char *mark;
     const char *val;
@@ -492,7 +517,7 @@ static int get_user_dn_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req,
     return EOK;
 }
 
-static int get_user_dn(TALLOC_CTX *memctx,
+int get_user_dn(TALLOC_CTX *memctx,
                        struct sss_domain_info *domain,
                        struct sdap_options *opts,
                        const char *username,
@@ -998,7 +1023,7 @@ static void sdap_auth4chpass_done(struct tevent_req *req)
         case PWEXPIRE_NONE:
             break;
         default:
-            DEBUG(SSSDBG_CRIT_FAILURE, "Unknow pasword expiration type.\n");
+            DEBUG(SSSDBG_CRIT_FAILURE, "Unknown password expiration type.\n");
                 state->pd->pam_status = PAM_SYSTEM_ERR;
                 goto done;
         }
@@ -1247,25 +1272,12 @@ static void sdap_pam_auth_done(struct tevent_req *req)
     talloc_zfree(req);
 
     if (ret == EOK) {
-        switch (pw_expire_type) {
-        case PWEXPIRE_SHADOW:
-            ret = check_pwexpire_shadow(pw_expire_data, time(NULL), state->pd);
-            break;
-        case PWEXPIRE_KERBEROS:
-            ret = check_pwexpire_kerberos(pw_expire_data, time(NULL),
-                                          state->pd,
-                                          be_ctx->domain->pwd_expiration_warning);
-            break;
-        case PWEXPIRE_LDAP_PASSWORD_POLICY:
-            ret = check_pwexpire_ldap(state->pd, pw_expire_data,
-                                      be_ctx->domain->pwd_expiration_warning);
-            break;
-        case PWEXPIRE_NONE:
-            break;
-        default:
-            DEBUG(SSSDBG_CRIT_FAILURE, "Unknow pasword expiration type.\n");
-                state->pd->pam_status = PAM_SYSTEM_ERR;
-                goto done;
+        ret = check_pwexpire_policy(pw_expire_type, pw_expire_data, state->pd,
+                                    be_ctx->domain->pwd_expiration_warning);
+        if (ret == -1) {
+            /* Unknown password expiration type. */
+            state->pd->pam_status = PAM_SYSTEM_ERR;
+            goto done;
         }
     }
 
diff --git a/src/providers/ldap/ldap_auth.h b/src/providers/ldap/ldap_auth.h
new file mode 100644
index 0000000000000000000000000000000000000000..1038ddc7d136db925f2e9416c848e4505c45f4cf
--- /dev/null
+++ b/src/providers/ldap/ldap_auth.h
@@ -0,0 +1,46 @@
+/*
+    SSSD
+
+    Copyright (C) Pavel Reichl <prei...@redhat.com>
+
+    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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LDAP_AUTH_H_
+#define _LDAP_AUTH_H_
+
+#include "config.h"
+
+enum pwexpire {
+    PWEXPIRE_NONE = 0,
+    PWEXPIRE_LDAP_PASSWORD_POLICY,
+    PWEXPIRE_KERBEROS,
+    PWEXPIRE_SHADOW
+};
+
+int get_user_dn(TALLOC_CTX *memctx,
+                struct sss_domain_info *domain,
+                struct sdap_options *opts,
+                const char *username,
+                char **user_dn,
+                enum pwexpire *user_pw_expire_type,
+                void **user_pw_expire_data);
+
+errno_t check_pwexpire_policy(enum pwexpire pw_expire_type,
+                              void *pw_expire_data,
+                              struct pam_data *pd,
+                              errno_t checkb);
+
+
+#endif /* _LDAP_AUTH_H_ */
-- 
2.1.0

>From b845ae6587c6beeac68fc87d8936fadbad8dba3c Mon Sep 17 00:00:00 2001
From: Pavel Reichl <prei...@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

_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to