[SSSD] [PATCH] Add new account expired rule to LDAP access provider

Sumit Bose sbose at redhat.com
Thu Nov 25 13:25:22 UTC 2010


Hi,

these two patches aim to fix trac ticket #672. While the first patch
only makes a utility function public the second adds some new layers to
the LDAP access provider. I've tried to make the changes in a way to
make it easy to add new rules (#670) and new expire policies (#673,
#674, #690).

I would like to ask the reviewers to check if the new code is really as
flexible as I think and if it enough to evaluate the shadow expire
attribute here. TIA

bye,
Sumit
-------------- next part --------------
From 93be4c46f9ffaa2257598377f2750b06f44379be Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 23 Nov 2010 17:43:23 +0100
Subject: [PATCH 1/2] Make string_to_shadowpw_days() public

---
 src/providers/ldap/ldap_auth.c   |   33 ---------------------------------
 src/providers/ldap/ldap_common.c |   34 ++++++++++++++++++++++++++++++++++
 src/providers/ldap/ldap_common.h |    2 ++
 3 files changed, 36 insertions(+), 33 deletions(-)

diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
index 57a549d..5a85fe9 100644
--- a/src/providers/ldap/ldap_auth.c
+++ b/src/providers/ldap/ldap_auth.c
@@ -232,39 +232,6 @@ static errno_t check_pwexpire_ldap(struct pam_data *pd,
     return EOK;
 }
 
-static errno_t string_to_shadowpw_days(const char *s, long *d)
-{
-    long l;
-    char *endptr;
-
-    if (s == NULL || *s == '\0') {
-        *d = -1;
-        return EOK;
-    }
-
-    errno = 0;
-    l = strtol(s, &endptr, 10);
-    if (errno != 0) {
-        DEBUG(1, ("strtol failed [%d][%s].\n", errno, strerror(errno)));
-        return errno;
-    }
-
-    if (*endptr != '\0') {
-        DEBUG(1, ("Input string [%s] is invalid.\n", s));
-        return EINVAL;
-    }
-
-    if (*d < -1) {
-        DEBUG(1, ("Input string contains not allowed negative value [%d].\n",
-                  *d));
-        return EINVAL;
-    }
-
-    *d = l;
-
-    return EOK;
-}
-
 static errno_t find_password_expiration_attributes(TALLOC_CTX *mem_ctx,
                                                const struct ldb_message *msg,
                                                struct dp_option *opts,
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 5de3d55..14dae5f 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -739,3 +739,37 @@ done:
     talloc_zfree(tmp_ctx);
     return ret;
 }
+
+errno_t string_to_shadowpw_days(const char *s, long *d)
+{
+    long l;
+    char *endptr;
+
+    if (s == NULL || *s == '\0') {
+        *d = -1;
+        return EOK;
+    }
+
+    errno = 0;
+    l = strtol(s, &endptr, 10);
+    if (errno != 0) {
+        DEBUG(1, ("strtol failed [%d][%s].\n", errno, strerror(errno)));
+        return errno;
+    }
+
+    if (*endptr != '\0') {
+        DEBUG(1, ("Input string [%s] is invalid.\n", s));
+        return EINVAL;
+    }
+
+    if (*d < -1) {
+        DEBUG(1, ("Input string contains not allowed negative value [%d].\n",
+                  *d));
+        return EINVAL;
+    }
+
+    *d = l;
+
+    return EOK;
+}
+
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index 5c298d1..d24a983 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -143,4 +143,6 @@ int netgroup_get_recv(struct tevent_req *req, int *dp_error_out);
 /* setup child logging */
 int setup_child(struct sdap_id_ctx *ctx);
 
+
+errno_t string_to_shadowpw_days(const char *s, long *d);
 #endif /* _LDAP_COMMON_H_ */
-- 
1.7.3.2

-------------- next part --------------
From 087fb87049eaaa7b6aeec4d772c4f46dbeb57277 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 22 Nov 2010 14:24:23 +0100
Subject: [PATCH 2/2] Add new account expired rule to LDAP access provider

Two new options are added to the LDAP access provider to allow a broader
range of access control rules to be evaluated.

'ldap_access_order' makes it possible to run more than one rule. To keep
compatibility with older versions the default is 'filter'. This patch
adds a new rule 'expire'.

'ldap_account_expire_policy' specifies which LDAP attribute should be
used to determine if an account is expired or not. Currently only
'shadow' is supported which evaluates the ldap_user_shadow_expire
attribute.
---
 src/config/SSSDConfig.py                 |    2 +
 src/config/etc/sssd.api.d/sssd-ldap.conf |    2 +
 src/man/sssd-ldap.5.xml                  |   40 +++
 src/providers/ipa/ipa_common.c           |    4 +-
 src/providers/ipa/ipa_common.h           |    2 +-
 src/providers/ldap/ldap_common.c         |    4 +-
 src/providers/ldap/ldap_init.c           |  103 ++++++--
 src/providers/ldap/sdap.h                |    2 +
 src/providers/ldap/sdap_access.c         |  419 ++++++++++++++++++++++++++----
 src/providers/ldap/sdap_access.h         |   15 +-
 10 files changed, 512 insertions(+), 81 deletions(-)

diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index 7da1027..7136e76 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -161,6 +161,8 @@ option_strings = {
 
     # [provider/ldap/access]
     'ldap_access_filter' : _('LDAP filter to determine access privileges'),
+    'ldap_account_expire_policy' : _('Which attributes shall be used to evaluate if an account is expired'),
+    'ldap_access_order' : _('Which rules should be used to evaluate access control'),
 
     # [provider/simple/access]
     'simple_allow_users' : _('Comma separated list of allowed users'),
diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf
index 404f4d5..b7d2f9b 100644
--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
+++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
@@ -80,6 +80,8 @@ ldap_pwd_policy = str, None, false
 
 [provider/ldap/access]
 ldap_access_filter = str, None, false
+ldap_account_expire_policy = str, None, false
+ldap_access_order = str, None, false
 
 [provider/ldap/chpass]
 
diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index a30b49d..d8cada3 100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -1021,6 +1021,46 @@ ldap_access_filter = memberOf=cn=allowedusers,ou=Groups,dc=example,dc=com
                 </varlistentry>
 
                 <varlistentry>
+                    <term>ldap_account_expire_policy (string)</term>
+                    <listitem>
+                        <para>
+                            With this option a client side evaluation of
+                            access control attributes can be enabled.
+                        </para>
+                        <para>
+                            Please note that it is always recommended to
+                            use server side access control, i.e. the LDAP
+                            server should deny the bind request with a
+                            suitable error code even if the password is
+                            correct.
+                        </para>
+                        <para>
+                            Default: Empty
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                    <term>ldap_access_order (string)</term>
+                    <listitem>
+                        <para>
+                            Comma separated list of access control options.
+                            Allowed values are:
+                        </para>
+                        <para>
+                            <emphasis>filter</emphasis>: use ldap_access_filter
+                        </para>
+                        <para>
+                            <emphasis>expire</emphasis>: use
+                            ldap_account_expire_policy
+                        </para>
+                        <para>
+                            Default: filter
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
                     <term>ldap_deref (string)</term>
                     <listitem>
                         <para>
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 439ada0..2efa815 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -77,7 +77,9 @@ struct dp_option ipa_def_ldap_opts[] = {
     { "ldap_access_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_netgroup_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_group_nesting_level", DP_OPT_NUMBER, { .number = 2 }, NULL_NUMBER },
-    { "ldap_deref", DP_OPT_STRING, NULL_STRING, NULL_STRING }
+    { "ldap_deref", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "ldap_account_expire_policy", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "ldap_access_order", DP_OPT_STRING, NULL_STRING, NULL_STRING }
 };
 
 struct sdap_attr_map ipa_attr_map[] = {
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index ba8f85e..d457d7f 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -35,7 +35,7 @@ struct ipa_service {
 /* the following defines are used to keep track of the options in the ldap
  * module, so that if they change and ipa is not updated correspondingly
  * this will trigger a runtime abort error */
-#define IPA_OPTS_BASIC_TEST 39
+#define IPA_OPTS_BASIC_TEST 41
 
 /* the following define is used to keep track of the options in the krb5
  * module, so that if they change and ipa is not updated correspondingly
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 14dae5f..eb0a20b 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -72,7 +72,9 @@ struct dp_option default_basic_opts[] = {
     { "ldap_access_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_netgroup_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_group_nesting_level", DP_OPT_NUMBER, { .number = 2 }, NULL_NUMBER },
-    { "ldap_deref", DP_OPT_STRING, NULL_STRING, NULL_STRING }
+    { "ldap_deref", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "ldap_account_expire_policy", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "ldap_access_order", DP_OPT_STRING, { "filter" }, NULL_STRING }
 };
 
 struct sdap_attr_map generic_attr_map[] = {
diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
index b6176a9..70752da 100644
--- a/src/providers/ldap/ldap_init.c
+++ b/src/providers/ldap/ldap_init.c
@@ -225,6 +225,11 @@ int sssm_ldap_access_init(struct be_ctx *bectx,
     int ret;
     struct sdap_access_ctx *access_ctx;
     const char *filter;
+    const char *order;
+    char **order_list;
+    int order_list_len;
+    size_t c;
+    const char *dummy;
 
     access_ctx = talloc_zero(bectx, struct sdap_access_ctx);
     if(access_ctx == NULL) {
@@ -238,32 +243,86 @@ int sssm_ldap_access_init(struct be_ctx *bectx,
         goto done;
     }
 
-    filter = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic,
-                                            SDAP_ACCESS_FILTER);
-    if (filter == NULL) {
-        /* It's okay if this is NULL. In that case we will simply act
-         * like the 'deny' provider.
-         */
-        DEBUG(0, ("Warning: access_provider=ldap set, "
-                  "but no ldap_access_filter configured. "
-                  "All domain users will be denied access.\n"));
+    order = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic,
+                               SDAP_ACCESS_ORDER);
+    if (order == NULL) {
+        DEBUG(1, ("ldap_access_order not given, using 'filter'.\n"));
+        order = "filter";
     }
-    else {
-        if (filter[0] == '(') {
-            /* This filter is wrapped in parentheses.
-             * Pass it as-is to the openldap libraries.
-             */
-            access_ctx->filter = filter;
-        }
-        else {
-            /* Add parentheses around the filter */
-            access_ctx->filter = talloc_asprintf(access_ctx, "(%s)", filter);
-            if (access_ctx->filter == NULL) {
-                ret = ENOMEM;
-                goto done;
+
+    ret = split_on_separator(access_ctx, order, ',', true, &order_list,
+                             &order_list_len);
+    if (ret != EOK) {
+        DEBUG(1, ("split_on_separator failed.\n"));
+        goto done;
+    }
+
+    if (order_list_len -1 > LDAP_ACCESS_LAST) {
+        DEBUG(1, ("Currently only [%d] different access rules are supported.\n"));
+        ret = EINVAL;
+        goto done;
+    }
+
+    for (c = 0; order_list[c] != NULL; c++) {
+        if (strcasecmp(order_list[c], LDAP_ACCESS_FILTER_NAME) == 0) {
+            access_ctx->access_rule[c] = LDAP_ACCESS_FILTER;
+
+            filter = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic,
+                                                    SDAP_ACCESS_FILTER);
+            if (filter == NULL) {
+                /* It's okay if this is NULL. In that case we will simply act
+                 * like the 'deny' provider.
+                 */
+                DEBUG(0, ("Warning: LDAP access rule 'filter' is set, "
+                          "but no ldap_access_filter configured. "
+                          "All domain users will be denied access.\n"));
             }
+            else {
+                if (filter[0] == '(') {
+                    /* This filter is wrapped in parentheses.
+                     * Pass it as-is to the openldap libraries.
+                     */
+                    access_ctx->filter = filter;
+                }
+                else {
+                    /* Add parentheses around the filter */
+                    access_ctx->filter = talloc_asprintf(access_ctx, "(%s)", filter);
+                    if (access_ctx->filter == NULL) {
+                        ret = ENOMEM;
+                        goto done;
+                    }
+                }
+            }
+
+        } else if (strcasecmp(order_list[c], LDAP_ACCESS_EXPIRE_NAME) == 0) {
+            access_ctx->access_rule[c] = LDAP_ACCESS_EXPIRE;
+
+            dummy = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic,
+                                       SDAP_ACCOUNT_EXPIRE_POLICY);
+            if (dummy == NULL) {
+                DEBUG(0, ("Warning: LDAP access rule 'expire' is set, "
+                          "but no ldap_account_expire_policy configured. "
+                          "All domain users will be denied access.\n"));
+            } else {
+                if (strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_SHADOW) != 0) {
+                    DEBUG(1, ("Unsupported LDAP account expire policy [%s].\n",
+                              dummy));
+                    ret = EINVAL;
+                    goto done;
+                }
+            }
+        } else {
+            DEBUG(1, ("Unexpected access rule name [%s].\n", order_list[c]));
+            ret = EINVAL;
+            goto done;
         }
     }
+    access_ctx->access_rule[c] = LDAP_ACCESS_EMPTY;
+    if (c == 0) {
+        DEBUG(0, ("Warning: access_provider=ldap set, "
+                  "but ldap_access_order is empty. "
+                  "All domain users will be denied access.\n"));
+    }
 
     *ops = &sdap_access_ops;
     *pvt_data = access_ctx;
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 945de37..4d52d5b 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -182,6 +182,8 @@ enum sdap_basic_opt {
     SDAP_NETGROUP_SEARCH_BASE,
     SDAP_NESTING_LEVEL,
     SDAP_DEREF,
+    SDAP_ACCOUNT_EXPIRE_POLICY,
+    SDAP_ACCESS_ORDER,
 
     SDAP_OPTS_BASIC /* opts counter */
 };
diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 23c076f..d2b2705 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -57,6 +57,23 @@ static struct tevent_req *sdap_access_send(TALLOC_CTX *mem_ctx,
                                            struct be_ctx *be_ctx,
                                            struct sdap_access_ctx *access_ctx,
                                            const char *username);
+
+static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct be_ctx *be_ctx,
+                                             struct sdap_access_ctx *access_ctx,
+                                             const char *username,
+                                             struct ldb_message *user_entry);
+static void sdap_access_filter_done(struct tevent_req *subreq);
+
+static struct tevent_req *sdap_account_expired_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct be_ctx *be_ctx,
+                                             struct sdap_access_ctx *access_ctx,
+                                             const char *username,
+                                             struct ldb_message *user_entry);
+static void sdap_account_expired_done(struct tevent_req *subreq);
+
 static void sdap_access_done(struct tevent_req *req);
 void sdap_pam_access_handler(struct be_req *breq)
 {
@@ -86,23 +103,15 @@ void sdap_pam_access_handler(struct be_req *breq)
 
 struct sdap_access_req_ctx {
     const char *username;
-    const char *filter;
     struct tevent_context *ev;
     struct sdap_access_ctx *access_ctx;
-    struct sdap_id_ctx *sdap_ctx;
-    struct sdap_id_op *sdap_op;
-    struct sysdb_handle *handle;
     struct be_ctx *be_ctx;
-    const char **attrs;
     int pam_status;
-    bool cached_access;
-    char *basedn;
+    struct ldb_message *user_entry;
+    size_t current_rule;
 };
 
-static void sdap_access_decide_offline(struct tevent_req *req);
-static int sdap_access_retry(struct tevent_req *req);
-static void sdap_access_connect_done(struct tevent_req *subreq);
-static void sdap_access_get_access_done(struct tevent_req *req);
+static errno_t select_next_rule(struct tevent_req *req);
 static struct tevent_req *sdap_access_send(TALLOC_CTX *mem_ctx,
                                            struct tevent_context *ev,
                                            struct be_ctx *be_ctx,
@@ -113,72 +122,340 @@ static struct tevent_req *sdap_access_send(TALLOC_CTX *mem_ctx,
     struct sdap_access_req_ctx *state;
     struct tevent_req *req;
     struct ldb_result *res;
-    const char *basedn;
-    char *clean_username;
+    const char *attrs[] = { "*", NULL };
 
     req = tevent_req_create(mem_ctx, &state, struct sdap_access_req_ctx);
     if (req == NULL) {
+        DEBUG(1, ("tevent_req_create failed.\n"));
         return NULL;
     }
 
-    if (access_ctx->filter == NULL || *access_ctx->filter == '\0') {
-        /* If no filter is set, default to restrictive */
-        DEBUG(6, ("No filter set. Access is denied.\n"));
-        state->pam_status = PAM_PERM_DENIED;
-        tevent_req_done(req);
-        tevent_req_post(req, be_ctx->ev);
-        return req;
-    }
-
-    state->filter = NULL;
     state->be_ctx = be_ctx;
     state->username = username;
     state->pam_status = PAM_SYSTEM_ERR;
-    state->sdap_ctx = access_ctx->id_ctx;
     state->ev = ev;
     state->access_ctx = access_ctx;
-
-    state->attrs = talloc_array(state, const char *, 3);
-    if (state->attrs == NULL) {
-        DEBUG(1, ("Could not allocate attributes\n"));
-        goto failed;
-    }
-
-    state->attrs[0] = SYSDB_ORIG_DN;
-    state->attrs[1] = SYSDB_LDAP_ACCESS;
-    state->attrs[2] = NULL;
+    state->current_rule = 0;
 
     DEBUG(6, ("Performing access check for user [%s]\n", username));
 
+    if (access_ctx->access_rule[0] == LDAP_ACCESS_EMPTY) {
+        DEBUG(3, ("No access rules defined, access denied.\n"));
+        state->pam_status = PAM_PERM_DENIED;
+        ret = EOK;
+        goto done;
+    }
+
     /* Get original user DN */
-    ret = sysdb_get_user_attr(state, be_ctx->sysdb,
-                              be_ctx->domain, username,
-                              state->attrs,
+    ret = sysdb_get_user_attr(state, be_ctx->sysdb, be_ctx->domain,
+                              username, attrs,
                               &res);
     if (ret != EOK) {
         if (ret == ENOENT) {
             /* If we can't find the user, return permission denied */
             state->pam_status = PAM_PERM_DENIED;
-            goto finished;
+            ret = EOK;
+            goto done;
         }
-        goto failed;
+        goto done;
     }
     else {
         if (res->count == 0) {
             /* If we can't find the user, return permission denied */
             state->pam_status = PAM_PERM_DENIED;
-            goto finished;
+            ret = EOK;
+            goto done;
         }
 
         if (res->count != 1) {
             DEBUG(1, ("Invalid response from sysdb_get_user_attr\n"));
-            goto failed;
+            ret = EINVAL;
+            goto done;
+        }
+    }
+
+    state->user_entry = res->msgs[0];
+
+    ret = select_next_rule(req);
+    if (ret != EOK) {
+        if (ret == EACCES) {
+            state->pam_status = PAM_PERM_DENIED;
+            ret = EOK;
+            goto done;
+        }
+        DEBUG(1, ("select_next_rule failed.\n"));
+        goto done;
+    }
+
+    return req;
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t select_next_rule(struct tevent_req *req)
+{
+    struct sdap_access_req_ctx *state =
+            tevent_req_data(req, struct sdap_access_req_ctx);
+    struct tevent_req *subreq;
+
+    switch (state->access_ctx->access_rule[state->current_rule]) {
+        case LDAP_ACCESS_EMPTY:
+            return ENOENT;
+            break;
+        case LDAP_ACCESS_FILTER:
+            subreq = sdap_access_filter_send(state, state->ev, state->be_ctx,
+                                             state->access_ctx, state->username,
+                                             state->user_entry);
+            if (subreq == NULL) {
+                DEBUG(1, ("sdap_access_filter_send failed.\n"));
+                return ENOMEM;
+            }
+
+            tevent_req_set_callback(subreq, sdap_access_filter_done, req);
+            return EOK;
+        case LDAP_ACCESS_EXPIRE:
+            subreq = sdap_account_expired_send(state, state->ev, state->be_ctx,
+                                               state->access_ctx,
+                                               state->username,
+                                               state->user_entry);
+            if (subreq == NULL) {
+                DEBUG(1, ("sdap_account_expired_send failed.\n"));
+                return ENOMEM;
+            }
+
+            tevent_req_set_callback(subreq, sdap_account_expired_done, req);
+            return EOK;
+        default:
+            DEBUG(1, ("Unexpected access rule type. Access denied.\n"));
+    }
+
+    return EACCES;
+}
+
+static void next_access_rule(struct tevent_req *req)
+{
+    struct sdap_access_req_ctx *state =
+            tevent_req_data(req, struct sdap_access_req_ctx);
+    int ret;
+
+    if (state->pam_status == PAM_PERM_DENIED) {
+        tevent_req_done(req);
+        return;
+    }
+
+    state->current_rule++;
+
+    ret = select_next_rule(req);
+    if (ret != EOK) {
+        if (ret == ENOENT) {
+            state->pam_status = PAM_SUCCESS;
+            tevent_req_done(req);
+            return;
+        } else if (ret == EACCES) {
+            state->pam_status = PAM_PERM_DENIED;
+            tevent_req_done(req);
+        } else {
+            tevent_req_error(req, ret);
+        }
+    }
+
+    return;
+}
+
+
+static errno_t sdap_account_expired_shadow(const char *username,
+                                           struct ldb_message *user_entry,
+                                           int *pam_status)
+{
+    int ret;
+    const char *val;
+    long sp_expire;
+    long today;
+
+    DEBUG(6, ("Performing access shadow check for user [%s]\n", username));
+
+    val = ldb_msg_find_attr_as_string(user_entry, SYSDB_SHADOWPW_EXPIRE, NULL);
+    if (val == NULL) {
+        DEBUG(1, ("Shadow expire attribute not found. Access denied.\n"));
+        *pam_status = PAM_PERM_DENIED;
+        return EOK;
+    }
+    ret = string_to_shadowpw_days(val, &sp_expire);
+    if (ret != EOK) {
+        DEBUG(1, ("Failed to retrieve shadow expire date.\n"));
+        return ret;
+    }
+
+    today = (long) (time(NULL) / (60 * 60 * 24));
+    if (today > sp_expire) {
+        *pam_status = PAM_ACCT_EXPIRED;
+    } else {
+        *pam_status = PAM_SUCCESS;
+    }
+
+    return EOK;
+}
+
+struct sdap_account_expired_req_ctx {
+    int pam_status;
+};
+
+static struct tevent_req *sdap_account_expired_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct be_ctx *be_ctx,
+                                             struct sdap_access_ctx *access_ctx,
+                                             const char *username,
+                                             struct ldb_message *user_entry)
+{
+    struct tevent_req *req;
+    struct sdap_account_expired_req_ctx *state;
+    int ret;
+    const char *expire;
+
+    req = tevent_req_create(mem_ctx, &state, struct sdap_account_expired_req_ctx);
+    if (req == NULL) {
+        DEBUG(1, ("tevent_req_create failed.\n"));
+        return NULL;
+    }
+
+    state->pam_status = PAM_SYSTEM_ERR;
+
+    expire = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic,
+                                SDAP_ACCOUNT_EXPIRE_POLICY);
+    if (expire == NULL) {
+        DEBUG(1, ("Missing account expire policy. Access denied\n"));
+        state->pam_status = PAM_PERM_DENIED;
+        ret = EOK;
+        goto done;
+    } else {
+        if (strcasecmp(expire, LDAP_ACCOUNT_EXPIRE_SHADOW) == 0) {
+            ret = sdap_account_expired_shadow(username, user_entry,
+                                              &state->pam_status);
+            if (ret != EOK) {
+                DEBUG(1, ("sdap_account_expired_shadow failed.\n"));
+                goto done;
+            }
+        } else {
+            DEBUG(1, ("Unsupported LDAP account expire policy [%s]. "
+                      "Access denied.\n", expire));
+            state->pam_status = PAM_PERM_DENIED;
+            ret = EOK;
+            goto done;
         }
     }
 
-    /* Exactly one result returned */
-    state->cached_access = ldb_msg_find_attr_as_bool(res->msgs[0],
-                                                     SYSDB_LDAP_ACCESS,
+    ret = EOK;
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+
+    return req;
+}
+
+static errno_t sdap_account_expired_recv(struct tevent_req *req, int *pam_status)
+{
+    struct sdap_account_expired_req_ctx *state =
+            tevent_req_data(req, struct sdap_account_expired_req_ctx);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *pam_status = state->pam_status;
+
+    return EOK;
+}
+
+static void sdap_account_expired_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+    struct sdap_access_req_ctx *state =
+            tevent_req_data(req, struct sdap_access_req_ctx);
+
+    ret = sdap_account_expired_recv(subreq, &state->pam_status);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(1, ("Error retrieving access check result.\n"));
+        state->pam_status = PAM_SYSTEM_ERR;
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    next_access_rule(req);
+
+    return;
+}
+
+
+
+struct sdap_access_filter_req_ctx {
+    const char *username;
+    const char *filter;
+    struct tevent_context *ev;
+    struct sdap_access_ctx *access_ctx;
+    struct sdap_id_ctx *sdap_ctx;
+    struct sdap_id_op *sdap_op;
+    struct sysdb_handle *handle;
+    struct be_ctx *be_ctx;
+    int pam_status;
+    bool cached_access;
+    char *basedn;
+};
+
+static void sdap_access_decide_offline(struct tevent_req *req);
+static int sdap_access_retry(struct tevent_req *req);
+static void sdap_access_connect_done(struct tevent_req *subreq);
+static void sdap_access_get_access_done(struct tevent_req *req);
+static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct be_ctx *be_ctx,
+                                             struct sdap_access_ctx *access_ctx,
+                                             const char *username,
+                                             struct ldb_message *user_entry)
+{
+    errno_t ret;
+    struct sdap_access_filter_req_ctx *state;
+    struct tevent_req *req;
+    const char *basedn;
+    char *clean_username;
+
+    req = tevent_req_create(mem_ctx, &state, struct sdap_access_filter_req_ctx);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    if (access_ctx->filter == NULL || *access_ctx->filter == '\0') {
+        /* If no filter is set, default to restrictive */
+        DEBUG(6, ("No filter set. Access is denied.\n"));
+        state->pam_status = PAM_PERM_DENIED;
+        tevent_req_done(req);
+        tevent_req_post(req, be_ctx->ev);
+        return req;
+    }
+
+    state->filter = NULL;
+    state->be_ctx = be_ctx;
+    state->username = username;
+    state->pam_status = PAM_SYSTEM_ERR;
+    state->sdap_ctx = access_ctx->id_ctx;
+    state->ev = ev;
+    state->access_ctx = access_ctx;
+
+    DEBUG(6, ("Performing access filter check for user [%s]\n", username));
+
+    state->cached_access = ldb_msg_find_attr_as_bool(user_entry,
+                                                     SYSDB_LDAP_ACCESS_FILTER,
                                                      false);
     /* Ok, we have one result, check if we are online or offline */
     if (be_is_offline(state->be_ctx)) {
@@ -188,7 +465,7 @@ static struct tevent_req *sdap_access_send(TALLOC_CTX *mem_ctx,
     }
 
     /* Perform online operation */
-    basedn = ldb_msg_find_attr_as_string(res->msgs[0],
+    basedn = ldb_msg_find_attr_as_string(user_entry,
                                          SYSDB_ORIG_DN,
                                          NULL);
     if(basedn == NULL) {
@@ -202,7 +479,6 @@ static struct tevent_req *sdap_access_send(TALLOC_CTX *mem_ctx,
         DEBUG(1, ("Could not allocate memory for originalDN\n"));
         goto failed;
     }
-    talloc_zfree(res);
 
     /* Construct the filter */
 
@@ -251,8 +527,8 @@ finished:
 
 static void sdap_access_decide_offline(struct tevent_req *req)
 {
-    struct sdap_access_req_ctx *state =
-            tevent_req_data(req, struct sdap_access_req_ctx);
+    struct sdap_access_filter_req_ctx *state =
+            tevent_req_data(req, struct sdap_access_filter_req_ctx);
 
     if (state->cached_access) {
         DEBUG(6, ("Access granted by cached credentials\n"));
@@ -265,8 +541,8 @@ static void sdap_access_decide_offline(struct tevent_req *req)
 
 static int sdap_access_retry(struct tevent_req *req)
 {
-    struct sdap_access_req_ctx *state =
-            tevent_req_data(req, struct sdap_access_req_ctx);
+    struct sdap_access_filter_req_ctx *state =
+            tevent_req_data(req, struct sdap_access_filter_req_ctx);
     struct tevent_req *subreq;
     int ret;
 
@@ -284,8 +560,8 @@ static void sdap_access_connect_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
-    struct sdap_access_req_ctx *state =
-            tevent_req_data(req, struct sdap_access_req_ctx);
+    struct sdap_access_filter_req_ctx *state =
+            tevent_req_data(req, struct sdap_access_filter_req_ctx);
     int ret, dp_error;
 
     ret = sdap_id_op_connect_recv(subreq, &dp_error);
@@ -332,8 +608,8 @@ static void sdap_access_get_access_done(struct tevent_req *subreq)
     struct sysdb_attrs **results;
     struct tevent_req *req =
             tevent_req_callback_data(subreq, struct tevent_req);
-    struct sdap_access_req_ctx *state =
-            tevent_req_data(req, struct sdap_access_req_ctx);
+    struct sdap_access_filter_req_ctx *state =
+            tevent_req_data(req, struct sdap_access_filter_req_ctx);
     ret = sdap_get_generic_recv(subreq, state,
                                 &num_results, &results);
     talloc_zfree(subreq);
@@ -409,7 +685,7 @@ static void sdap_access_get_access_done(struct tevent_req *subreq)
         goto done;
     }
 
-    ret = sysdb_attrs_add_bool(attrs, SYSDB_LDAP_ACCESS,
+    ret = sysdb_attrs_add_bool(attrs, SYSDB_LDAP_ACCESS_FILTER,
                                state->pam_status == PAM_SUCCESS ?
                                                     true :
                                                     false);
@@ -445,6 +721,39 @@ done:
     }
 }
 
+static errno_t sdap_access_filter_recv(struct tevent_req *req, int *pam_status)
+{
+    struct sdap_access_filter_req_ctx *state =
+            tevent_req_data(req, struct sdap_access_filter_req_ctx);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *pam_status = state->pam_status;
+
+    return EOK;
+}
+
+static void sdap_access_filter_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+    struct sdap_access_req_ctx *state =
+            tevent_req_data(req, struct sdap_access_req_ctx);
+
+    ret = sdap_access_filter_recv(subreq, &state->pam_status);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(1, ("Error retrieving access check result.\n"));
+        state->pam_status = PAM_SYSTEM_ERR;
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    next_access_rule(req);
+
+    return;
+}
+
 static errno_t sdap_access_recv(struct tevent_req *req, int *pam_status)
 {
     struct sdap_access_req_ctx *state =
diff --git a/src/providers/ldap/sdap_access.h b/src/providers/ldap/sdap_access.h
index 5dbe864..9b8e45b 100644
--- a/src/providers/ldap/sdap_access.h
+++ b/src/providers/ldap/sdap_access.h
@@ -27,11 +27,24 @@
 
 #include "providers/dp_backend.h"
 
-#define SYSDB_LDAP_ACCESS "ldap_access_allow"
+#define SYSDB_LDAP_ACCESS_FILTER "ldap_access_filter_allow"
+
+#define LDAP_ACCESS_FILTER_NAME "filter"
+#define LDAP_ACCESS_EXPIRE_NAME "expire"
+
+#define LDAP_ACCOUNT_EXPIRE_SHADOW "shadow"
+
+enum ldap_access_rule {
+    LDAP_ACCESS_EMPTY = -1,
+    LDAP_ACCESS_FILTER = 0,
+    LDAP_ACCESS_EXPIRE,
+    LDAP_ACCESS_LAST
+};
 
 struct sdap_access_ctx {
     struct sdap_id_ctx *id_ctx;
     const char *filter;
+    int access_rule[LDAP_ACCESS_LAST + 1];
 };
 
 void ldap_pam_access_handler(struct be_req *breq);
-- 
1.7.3.2



More information about the sssd-devel mailing list