On 08/04/2014 02:37 PM, Pavel Březina wrote:
On 08/01/2014 07:48 PM, Pavel Reichl wrote:
Hello,

please see attached patches. Every patch is documented in its commit
message.

Generally speaking the first 6 patches are preparation for patch #8.

Thanks,

Pavel Reichl

PS: I also attached output of my testing to make it more obvious how the
patches are supposed to work.

------------------------------------------------------------------------
# john, people, example.com
dn: uid=john,ou=people,dc=example,dc=com
pwdAccountLockedTime: 000001010000Z

# max, people, example.com
dn: uid=max,ou=people,dc=example,dc=com

# dick, people, example.com
dn: uid=dick,ou=people,dc=example,dc=com
pwdAccountLockedTime: 20140801115742Z
--------------------------------------------------------------------------
$ ssh -l john@openldap `hostname`
Connection closed by UNKNOWN

$ ssh -l max@openldap `hostname`
Last login: Fri Aug  1 15:16:21 2014 from sssd.dev.work

$ ssh -l dick@openldap `hostname`
Last login: Fri Aug  1 12:57:33 2014

Patch #1:
Tentative ack, although you don't have to use tmp_ctx in sdap_save_user_cache_bool(). You can just allocate attrs on NULL since it is the only talloc context you use there.

Patch #2:
Nack. I see it is safe here but please avoid initializing ret to EOK. Set it to EOK at the end of the function instead.

Patch #3:
Ack.

Patch #4:
Typo in commit message "Also remove *sdap_access_filter_recv()* as it is replaced by *sdap_access_filter_recv()*."

But it's nack from me anyway.

Tevent naming convention seems to be broken all around this code and this patch actually makes it a little bit better. But you shouldn't use function named "sdap_access_recv" inside "sdap_access_done".

I see that sdap_access_send can invoke two different requests depending on the configuration. Ideally both should have custom _recv and you should use some kind of switch to determine which one should be called.

I would be willing to tentatively ack a patch with let's say "sdap_access_check_recv" or "sdap_access_subreq_recv" or similar used in sdap_access_done.

Patch #5:
Ack.

Patch #6:
Ack.

Patch #7:
Ack. But please swap this ticket with #8. The feature should be first implemented then documented.

Patch #8:
I believe the following comment is copy & paste? It shouldn't say filter request :)
+    /* Connection to LDAP succeeded
+     * Send filter request
+     */

+            if (strcasecmp(pwdAccountLockedTime,
+                           PERMANENTLY_LOCKED_ACCOUNT) == 0) {
+                locked = true;
+            } else {
+                DEBUG(SSSDBG_TRACE_FUNC,
+ "Account of: %s is beeing blocked by password policy, "
+                      "but value: [%s] value is ignored by SSSD.\n",
+                      state->username, pwdAccountLockedTime);
+            }

Why do we ignore the value? Isn't the value a time? Shouldn't we check if lock_time < machine_time?
Yes, it's time, but we care only if its the special value '000001010000Z' otherwise we grant the access.

Also the ticket says that this policy is only enabled if pwdLockout is true. Is it possible to acquire the attribute if pwdLockout is false? I see you don't check it.
Yes it's possible to acquire, I have updated the patches.

Those are questions that should be answered in the code if they are not bugs.

I haven't test the code yet - will it work also with 389ds or do you have an OpenLDAP server I could use handy?

I'll try to grant you access to my VM as I find configuring ppolicy as very painful experience.
_______________________________________________
sssd-devel mailing list
[email protected]
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel

I hope I have addressed all you concerns and didn't produce many new bugs. :-)


>From d30c2d26a132b07f438b44491a8d6059e21cbedf Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 09:15:59 +0100
Subject: [PATCH 1/9] SDAP: split sdap_access_filter_get_access_done

As a preparation for ticket #2364 separate code for storing user bool
values into sysdb to a new function sdap_save_user_cache_bool().
---
 src/providers/ldap/sdap_access.c | 59 ++++++++++++++++++++++++++--------------
 1 file changed, 39 insertions(+), 20 deletions(-)

diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 89d37e52fa27b14be6bf702ebf0bc18b3fe59da6..0702c565a466ba067350595233f9746443d0864d 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -40,6 +40,11 @@
 #include "providers/data_provider.h"
 #include "providers/dp_backend.h"
 
+static errno_t sdap_save_user_cache_bool(struct sss_domain_info *domain,
+                                         const char *username,
+                                         const char *attr_name,
+                                         bool value);
+
 static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
                                              struct tevent_context *ev,
                                              struct be_ctx *be_ctx,
@@ -856,7 +861,6 @@ static void sdap_access_filter_get_access_done(struct tevent_req *subreq)
     int ret, tret, dp_error;
     size_t num_results;
     bool found = false;
-    struct sysdb_attrs *attrs;
     struct sysdb_attrs **results;
     struct tevent_req *req =
             tevent_req_callback_data(subreq, struct tevent_req);
@@ -935,25 +939,8 @@ static void sdap_access_filter_get_access_done(struct tevent_req *subreq)
         ret = ERR_ACCESS_DENIED;
     }
 
-    attrs = sysdb_new_attrs(state);
-    if (attrs == NULL) {
-        ret = ENOMEM;
-        DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up attrs\n");
-        goto done;
-    }
-
-    tret = sysdb_attrs_add_bool(attrs, SYSDB_LDAP_ACCESS_FILTER,
-                                ret == EOK ?  true : false);
-    if (tret != EOK) {
-        /* Failing to save to the cache is non-fatal.
-         * Just return the result.
-         */
-        DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up attrs\n");
-        goto done;
-    }
-
-    tret = sysdb_set_user_attr(state->domain, state->username, attrs,
-                               SYSDB_MOD_REP);
+    tret = sdap_save_user_cache_bool(state->domain, state->username,
+                                     SYSDB_LDAP_ACCESS_FILTER, found);
     if (tret != EOK) {
         /* Failing to save to the cache is non-fatal.
          * Just return the result.
@@ -1060,6 +1047,38 @@ static errno_t sdap_access_service(struct pam_data *pd,
     return ret;
 }
 
+static errno_t sdap_save_user_cache_bool(struct sss_domain_info *domain,
+                                         const char *username,
+                                         const char *attr_name,
+                                         bool value)
+{
+    errno_t ret;
+    struct sysdb_attrs *attrs;
+
+    attrs = sysdb_new_attrs(NULL);
+    if (attrs == NULL) {
+        ret = ENOMEM;
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up attrs\n");
+        goto done;
+    }
+
+    ret = sysdb_attrs_add_bool(attrs, attr_name, value);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up attrs\n");
+        goto done;
+    }
+
+    ret = sysdb_set_user_attr(domain, username, attrs, SYSDB_MOD_REP);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set user access attribute\n");
+        goto done;
+    }
+
+done:
+    talloc_free(attrs);
+    return ret;
+}
+
 static errno_t sdap_access_host(struct ldb_message *user_entry)
 {
     errno_t ret;
-- 
1.9.3

>From 96bb7442a9b9fc3f21e772e565fcf758b55c2dc9 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 11:00:48 +0100
Subject: [PATCH 2/9] SDAP: refactor sdap_access_filter_send

As preparation for ticket #2364 separate code for parsing user basedn
to a new function sdap_get_basedn_user_entry().
---
 src/providers/ldap/sdap_access.c | 52 ++++++++++++++++++++++++++++------------
 1 file changed, 37 insertions(+), 15 deletions(-)

diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 0702c565a466ba067350595233f9746443d0864d..ddf623616f662d92fd751ff7952f08527c60be45 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -45,6 +45,11 @@ static errno_t sdap_save_user_cache_bool(struct sss_domain_info *domain,
                                          const char *attr_name,
                                          bool value);
 
+static errno_t sdap_get_basedn_user_entry(TALLOC_CTX *mem_ctx,
+                                          struct ldb_message *user_entry,
+                                          const char *username,
+                                          char **_basedn);
+
 static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
                                              struct tevent_context *ev,
                                              struct be_ctx *be_ctx,
@@ -666,7 +671,6 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
 {
     struct sdap_access_filter_req_ctx *state;
     struct tevent_req *req;
-    const char *basedn;
     char *clean_username;
     errno_t ret = ERR_INTERNAL;
     char *name;
@@ -704,20 +708,9 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    /* Perform online operation */
-    basedn = ldb_msg_find_attr_as_string(user_entry, SYSDB_ORIG_DN, NULL);
-    if (basedn == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE,"Could not find originalDN for user [%s]\n",
-                 state->username);
-        ret = EINVAL;
-        goto done;
-    }
-
-    state->basedn = talloc_strdup(state, basedn);
-    if (state->basedn == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Could not allocate memory for originalDN\n");
-        ret = ENOMEM;
+    ret = sdap_get_basedn_user_entry(state, user_entry, state->username,
+                                     &state->basedn);
+    if (ret != EOK) {
         goto done;
     }
 
@@ -1141,3 +1134,32 @@ static errno_t sdap_access_host(struct ldb_message *user_entry)
 
     return ret;
 }
+
+static errno_t sdap_get_basedn_user_entry(TALLOC_CTX *mem_ctx,
+                                          struct ldb_message *user_entry,
+                                          const char *username,
+                                          char **_basedn)
+{
+    const char *basedn;
+    errno_t ret;
+
+    basedn = ldb_msg_find_attr_as_string(user_entry, SYSDB_ORIG_DN, NULL);
+    if (basedn == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,"Could not find originalDN for user [%s]\n",
+              username);
+        ret = EINVAL;
+        goto done;
+    }
+
+    *_basedn = talloc_strdup(mem_ctx, basedn);
+    if (*_basedn == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Could not allocate memory for originalDN\n");
+        ret = ENOMEM;
+        goto done;
+    }
+    ret = EOK;
+
+done:
+    return ret;
+}
-- 
1.9.3

>From 49bd5681a46f27472c6dd637762a0db4b85babd7 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 11:13:20 +0100
Subject: [PATCH 3/9] SDAP: nitpicks in sdap_access_filter_get_access_done

Fixed typo and replaced duplicated string by macro definition.
---
 src/providers/ldap/sdap_access.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index ddf623616f662d92fd751ff7952f08527c60be45..e54f4e0d5d13d72e8cb38bd1c827ea2f52e7dcef 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -40,6 +40,8 @@
 #include "providers/data_provider.h"
 #include "providers/dp_backend.h"
 
+#define MALFORMED_FILTER "Malformed access control filter [%s]\n"
+
 static errno_t sdap_save_user_cache_bool(struct sss_domain_info *domain,
                                          const char *username,
                                          const char *attr_name,
@@ -875,10 +877,8 @@ static void sdap_access_filter_get_access_done(struct tevent_req *subreq)
         } else if (dp_error == DP_ERR_OFFLINE) {
             ret = sdap_access_filter_decide_offline(req);
         } else if (ret == ERR_INVALID_FILTER) {
-            sss_log(SSS_LOG_ERR,
-                    "Malformed access control filter [%s]\n", state->filter);
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                "Malformed access control filter [%s]\n", state->filter);
+            sss_log(SSS_LOG_ERR, MALFORMED_FILTER, state->filter);
+            DEBUG(SSSDBG_CRIT_FAILURE, MALFORMED_FILTER, state->filter);
             ret = ERR_ACCESS_DENIED;
         } else {
             DEBUG(SSSDBG_CRIT_FAILURE,
@@ -918,9 +918,7 @@ static void sdap_access_filter_get_access_done(struct tevent_req *subreq)
     }
 
     if (found) {
-        /* Save "allow" to the cache for future offline
-         :q* access checks.
-         */
+        /* Save "allow" to the cache for future offline access checks. */
         DEBUG(SSSDBG_TRACE_FUNC, "Access granted by online lookup\n");
         ret = EOK;
     }
-- 
1.9.3

>From e510dbbb83b75982ce755cc27d8754875832aa3a Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 12:22:21 +0100
Subject: [PATCH 4/9] SDAP: refactor sdap_access_filter_done

As preparation for ticket #2364 move code from sdap_access_filter_done()
into sdap_access_done() to make its reuse possible and thus avoid code
duplication.
---
 src/providers/ldap/sdap_access.c | 41 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 37 insertions(+), 4 deletions(-)

diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index e54f4e0d5d13d72e8cb38bd1c827ea2f52e7dcef..68633643f7430c26885a78d2903e7a4dd636e1d5 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -60,8 +60,11 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
                                              struct sdap_id_conn_ctx *conn,
                                              const char *username,
                                              struct ldb_message *user_entry);
+
 static errno_t sdap_access_filter_recv(struct tevent_req *req);
 
+static errno_t sdap_access_subreq_recv(struct tevent_req *req);
+
 static errno_t sdap_account_expired(struct sdap_access_ctx *access_ctx,
                                     struct pam_data *pd,
                                     struct ldb_message *user_entry);
@@ -219,14 +222,14 @@ static errno_t check_next_rule(struct sdap_access_req_ctx *state,
     return ret;
 }
 
-static void sdap_access_filter_done(struct tevent_req *subreq)
+static void sdap_access_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);
+    ret = sdap_access_subreq_recv(subreq);
     talloc_zfree(subreq);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Error retrieving access check result.\n");
@@ -249,6 +252,11 @@ static void sdap_access_filter_done(struct tevent_req *subreq)
     }
 }
 
+static void sdap_access_filter_done(struct tevent_req *subreq)
+{
+    sdap_access_done(subreq);
+}
+
 errno_t sdap_access_recv(struct tevent_req *req)
 {
     TEVENT_REQ_RETURN_ON_ERROR(req);
@@ -256,7 +264,6 @@ errno_t sdap_access_recv(struct tevent_req *req)
     return EOK;
 }
 
-
 #define SHADOW_EXPIRE_MSG "Account expired according to shadow attributes"
 
 static errno_t sdap_account_expired_shadow(struct pam_data *pd,
@@ -644,6 +651,10 @@ static errno_t sdap_account_expired(struct sdap_access_ctx *access_ctx,
     return ret;
 }
 
+enum sdap_access_control_type {
+    SDAP_ACCESS_CONTROL_FILTER,
+};
+
 struct sdap_access_filter_req_ctx {
     const char *username;
     const char *filter;
@@ -656,6 +667,7 @@ struct sdap_access_filter_req_ctx {
     struct sss_domain_info *domain;
     bool cached_access;
     char *basedn;
+    enum sdap_access_control_type ac_type;
 };
 
 static errno_t sdap_access_filter_decide_offline(struct tevent_req *req);
@@ -696,6 +708,7 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
     state->ev = ev;
     state->access_ctx = access_ctx;
     state->domain = domain;
+    state->ac_type = SDAP_ACCESS_CONTROL_FILTER;
 
     DEBUG(SSSDBG_TRACE_FUNC,
           "Performing access filter check for user [%s]\n", username);
@@ -772,6 +785,27 @@ done:
     return req;
 }
 
+static errno_t sdap_access_subreq_recv(struct tevent_req *req)
+{
+    struct sdap_access_filter_req_ctx *state;
+    errno_t ret;
+
+    state = tevent_req_data(req, struct sdap_access_filter_req_ctx);
+
+    switch(state->ac_type) {
+    case SDAP_ACCESS_CONTROL_FILTER:
+        ret = sdap_access_filter_recv(req);
+        break;
+    default:
+        ret = EINVAL;
+        DEBUG(SSSDBG_MINOR_FAILURE, "Unknown access control type: %d.",
+              state->ac_type);
+        break;
+    }
+
+    return ret;
+}
+
 static errno_t sdap_access_filter_decide_offline(struct tevent_req *req)
 {
     struct sdap_access_filter_req_ctx *state =
@@ -956,7 +990,6 @@ static errno_t sdap_access_filter_recv(struct tevent_req *req)
     return EOK;
 }
 
-
 #define AUTHR_SRV_MISSING_MSG "Authorized service attribute missing, " \
                               "access denied"
 #define AUTHR_SRV_DENY_MSG "Access denied by authorized service attribute"
-- 
1.9.3

>From 1477c50ead6f75f7e1e6c9b4f9fb418ab15cdd24 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 16:05:13 +0100
Subject: [PATCH 5/9] SDAP: don't log error on access denied

Don't log error if access is denied in function sdap_access_done().
---
 src/providers/ldap/sdap_access.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 68633643f7430c26885a78d2903e7a4dd636e1d5..82ba2bb59466eca07727adc730dfeeab6e118e84 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -232,7 +232,12 @@ static void sdap_access_done(struct tevent_req *subreq)
     ret = sdap_access_subreq_recv(subreq);
     talloc_zfree(subreq);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Error retrieving access check result.\n");
+        if (ret == ERR_ACCESS_DENIED) {
+            DEBUG(SSSDBG_TRACE_FUNC, "Access was denied.\n");
+        } else {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Error retrieving access check result.\n");
+        }
         tevent_req_error(req, ret);
         return;
     }
-- 
1.9.3

>From dd8e8b2d49b1d84907a8b3a47795acc721fd7864 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 16:13:08 +0100
Subject: [PATCH 6/9] SDAP: use versatile name for caching AC results

Use more versatile name for caching results of access control as this
may be used not just for filtering.
---
 src/providers/ldap/sdap_access.c | 15 ++++++++-------
 src/providers/ldap/sdap_access.h |  2 +-
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 82ba2bb59466eca07727adc730dfeeab6e118e84..248ffbad44d9aa4794b6042f9a825639277cc1e9 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -670,12 +670,13 @@ struct sdap_access_filter_req_ctx {
     struct sdap_id_op *sdap_op;
     struct sysdb_handle *handle;
     struct sss_domain_info *domain;
+    /* Used for both filter and locked use-cases. */
     bool cached_access;
     char *basedn;
     enum sdap_access_control_type ac_type;
 };
 
-static errno_t sdap_access_filter_decide_offline(struct tevent_req *req);
+static errno_t sdap_access_decide_offline(struct tevent_req *req);
 static int sdap_access_filter_retry(struct tevent_req *req);
 static void sdap_access_filter_connect_done(struct tevent_req *subreq);
 static void sdap_access_filter_get_access_done(struct tevent_req *req);
@@ -719,12 +720,12 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
           "Performing access filter check for user [%s]\n", username);
 
     state->cached_access = ldb_msg_find_attr_as_bool(user_entry,
-                                                     SYSDB_LDAP_ACCESS_FILTER,
+                                                     SYSDB_LDAP_ACCESS_CACHED,
                                                      false);
     /* Ok, we have one result, check if we are online or offline */
     if (be_is_offline(be_ctx)) {
         /* Ok, we're offline. Return from the cache */
-        ret = sdap_access_filter_decide_offline(req);
+        ret = sdap_access_decide_offline(req);
         goto done;
     }
 
@@ -811,7 +812,7 @@ static errno_t sdap_access_subreq_recv(struct tevent_req *req)
     return ret;
 }
 
-static errno_t sdap_access_filter_decide_offline(struct tevent_req *req)
+static errno_t sdap_access_decide_offline(struct tevent_req *req)
 {
     struct sdap_access_filter_req_ctx *state =
             tevent_req_data(req, struct sdap_access_filter_req_ctx);
@@ -856,7 +857,7 @@ static void sdap_access_filter_connect_done(struct tevent_req *subreq)
 
     if (ret != EOK) {
         if (dp_error == DP_ERR_OFFLINE) {
-            ret = sdap_access_filter_decide_offline(req);
+            ret = sdap_access_decide_offline(req);
             if (ret == EOK) {
                 tevent_req_done(req);
                 return;
@@ -914,7 +915,7 @@ static void sdap_access_filter_get_access_done(struct tevent_req *subreq)
                 return;
             }
         } else if (dp_error == DP_ERR_OFFLINE) {
-            ret = sdap_access_filter_decide_offline(req);
+            ret = sdap_access_decide_offline(req);
         } else if (ret == ERR_INVALID_FILTER) {
             sss_log(SSS_LOG_ERR, MALFORMED_FILTER, state->filter);
             DEBUG(SSSDBG_CRIT_FAILURE, MALFORMED_FILTER, state->filter);
@@ -970,7 +971,7 @@ static void sdap_access_filter_get_access_done(struct tevent_req *subreq)
     }
 
     tret = sdap_save_user_cache_bool(state->domain, state->username,
-                                     SYSDB_LDAP_ACCESS_FILTER, found);
+                                     SYSDB_LDAP_ACCESS_CACHED, found);
     if (tret != EOK) {
         /* Failing to save to the cache is non-fatal.
          * Just return the result.
diff --git a/src/providers/ldap/sdap_access.h b/src/providers/ldap/sdap_access.h
index 30097e21f59abe19fc14d052edb0a6343c67f892..890ea48bd906bd2ab7efa4f0720c01058389226d 100644
--- a/src/providers/ldap/sdap_access.h
+++ b/src/providers/ldap/sdap_access.h
@@ -28,7 +28,7 @@
 #include "providers/dp_backend.h"
 #include "providers/ldap/ldap_common.h"
 
-#define SYSDB_LDAP_ACCESS_FILTER "ldap_access_filter_allow"
+#define SYSDB_LDAP_ACCESS_CACHED "ldap_access_cached"
 
 #define LDAP_ACCESS_FILTER_NAME "filter"
 #define LDAP_ACCESS_EXPIRE_NAME "expire"
-- 
1.9.3

>From 7f612f108f158d942beee6740ee10293c259dbfc Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Wed, 6 Aug 2014 16:05:53 +0100
Subject: [PATCH 7/9] SDAP: new option - DN to ppolicy on LDAP

To check value of attribute pwdLockout on LDAP server, DN of ppolicy
must be set.

Resolves:
https://fedorahosted.org/sssd/ticket/2364
---
 src/config/SSSDConfig/__init__.py.in     | 1 +
 src/config/etc/sssd.api.d/sssd-ad.conf   | 1 +
 src/config/etc/sssd.api.d/sssd-ipa.conf  | 1 +
 src/config/etc/sssd.api.d/sssd-ldap.conf | 1 +
 src/providers/ad/ad_opts.h               | 1 +
 src/providers/ipa/ipa_opts.h             | 1 +
 src/providers/ldap/ldap_opts.h           | 1 +
 src/providers/ldap/sdap.h                | 1 +
 8 files changed, 8 insertions(+)

diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index c3482b3a4df6d0fd39b2fa93c19ee5316dde0d94..bf039e3daf3d05313d7d594e27d31029cc12622a 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -323,6 +323,7 @@ option_strings = {
     'ldap_use_tokengroups' : _('Whether to use Token-Groups'),
     'ldap_min_id' : _('Set lower boundary for allowed IDs from the LDAP server'),
     'ldap_max_id' : _('Set upper boundary for allowed IDs from the LDAP server'),
+    'ldap_ppolicy_search_base' : _('DN for ppolicy queries'),
 
     # [provider/ldap/auth]
     'ldap_pwd_policy' : _('Policy to evaluate the password expiration'),
diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf b/src/config/etc/sssd.api.d/sssd-ad.conf
index 93d869c67f96afbea2e39d061daf578602b2f9a9..7f850f96bf59d97b33ece2b35b7ea0c2c60689bc 100644
--- a/src/config/etc/sssd.api.d/sssd-ad.conf
+++ b/src/config/etc/sssd.api.d/sssd-ad.conf
@@ -112,6 +112,7 @@ ldap_groups_use_matching_rule_in_chain = bool, None, false
 ldap_initgroups_use_matching_rule_in_chain = bool, None, false
 ldap_use_tokengroups = bool, None, false
 ldap_rfc2307_fallback_to_local_users = bool, None, false
+ldap_ppolicy_search_base = str, None, false
 
 [provider/ad/auth]
 krb5_ccachedir = str, None, false
diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
index f3b9cb0637bd3f827dd198acb74c933b4f953a81..1f92b2deede060f48c918f22e635a247dba8f308 100644
--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
@@ -130,6 +130,7 @@ ldap_initgroups_use_matching_rule_in_chain = bool, None, false
 ldap_use_tokengroups = bool, None, false
 ldap_rfc2307_fallback_to_local_users = bool, None, false
 ipa_server_mode = bool, None, false
+ldap_ppolicy_search_base = str, None, false
 
 [provider/ipa/auth]
 krb5_ccachedir = str, None, false
diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf
index a4802a1ea190fbdf57ec13d031bb74ad46e08a47..7bc437ff68c8ec654433678be3c58a9aaedee18b 100644
--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
+++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
@@ -119,6 +119,7 @@ ldap_use_tokengroups = bool, None, false
 ldap_rfc2307_fallback_to_local_users = bool, None, false
 ldap_min_id = int, None, false
 ldap_max_id = int, None, false
+ldap_ppolicy_search_base = str, None, false
 
 [provider/ldap/auth]
 ldap_pwd_policy = str, None, false
diff --git a/src/providers/ad/ad_opts.h b/src/providers/ad/ad_opts.h
index a3ade012a7c1efb705a011697af36c28e39b1a9b..77e05d74f1373b8155ab66a602c00e8849dc9716 100644
--- a/src/providers/ad/ad_opts.h
+++ b/src/providers/ad/ad_opts.h
@@ -134,6 +134,7 @@ struct dp_option ad_def_ldap_opts[] = {
     { "ldap_disable_range_retrieval", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_min_id", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
     { "ldap_max_id", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
+    { "ldap_ppolicy_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
index c7197beb1ecae2935255c46559287ad1186f760e..9f000ca1e2f9a4726b80af473fa2ed8984fbbac7 100644
--- a/src/providers/ipa/ipa_opts.h
+++ b/src/providers/ipa/ipa_opts.h
@@ -155,6 +155,7 @@ struct dp_option ipa_def_ldap_opts[] = {
     { "ldap_disable_range_retrieval", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_min_id", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
     { "ldap_max_id", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
+    { "ldap_ppolicy_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/ldap/ldap_opts.h b/src/providers/ldap/ldap_opts.h
index 13a84ec1e979864e06d5690ec29967548b21aac1..231d337b47812a512693c9a907d1a0ed69442274 100644
--- a/src/providers/ldap/ldap_opts.h
+++ b/src/providers/ldap/ldap_opts.h
@@ -121,6 +121,7 @@ struct dp_option default_basic_opts[] = {
     { "ldap_disable_range_retrieval", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_min_id", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
     { "ldap_max_id", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
+    { "ldap_ppolicy_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index e5d9729014265c8ad2d72f0e5c4d321b9621cbc9..0008b5b03299b7a82694d8450d9910aab3e7ac6b 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -232,6 +232,7 @@ enum sdap_basic_opt {
     SDAP_DISABLE_RANGE_RETRIEVAL,
     SDAP_MIN_ID,
     SDAP_MAX_ID,
+    SDAP_PPOLICY_SEARCH_BASE,
 
     SDAP_OPTS_BASIC /* opts counter */
 };
-- 
1.9.3

>From f6cdc0ce09645cae4254c4e6cb6dfc24de0545e1 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 17:44:24 +0100
Subject: [PATCH 8/9] SDAP: account lock to restrict access via ssh key

Be able to configure sssd to honor openldap account lock to restrict
access via ssh key. Introduce new ldap_access_order value ('lock')
for enabling/disabling this feature.

Resolves:
https://fedorahosted.org/sssd/ticket/2364
---
 src/providers/ldap/ldap_init.c   |   2 +
 src/providers/ldap/sdap_access.c | 439 +++++++++++++++++++++++++++++++++++++++
 src/providers/ldap/sdap_access.h |   4 +
 3 files changed, 445 insertions(+)

diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
index 9960fd3ab7a6e446e5cdc1e8fa4984aab812d980..85d9acef1d2dd55361cba2ae67376673fe67d021 100644
--- a/src/providers/ldap/ldap_init.c
+++ b/src/providers/ldap/ldap_init.c
@@ -421,6 +421,8 @@ int sssm_ldap_access_init(struct be_ctx *bectx,
             access_ctx->access_rule[c] = LDAP_ACCESS_SERVICE;
         } else if (strcasecmp(order_list[c], LDAP_ACCESS_HOST_NAME) == 0) {
             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_LOCK;
         } 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 248ffbad44d9aa4794b6042f9a825639277cc1e9..e1bc018835f98661683bb8971ff2c8be6b3bcf99 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -40,6 +40,7 @@
 #include "providers/data_provider.h"
 #include "providers/dp_backend.h"
 
+#define PERMANENTLY_LOCKED_ACCOUNT "000001010000Z"
 #define MALFORMED_FILTER "Malformed access control filter [%s]\n"
 
 static errno_t sdap_save_user_cache_bool(struct sss_domain_info *domain,
@@ -52,6 +53,18 @@ static errno_t sdap_get_basedn_user_entry(TALLOC_CTX *mem_ctx,
                                           const char *username,
                                           char **_basedn);
 
+static struct tevent_req *
+sdap_access_lock_send(TALLOC_CTX *mem_ctx,
+                      struct tevent_context *ev,
+                      struct be_ctx *be_ctx,
+                      struct sss_domain_info *domain,
+                      struct sdap_access_ctx *access_ctx,
+                      struct sdap_id_conn_ctx *conn,
+                      const char *username,
+                      struct ldb_message *user_entry);
+
+static void sdap_access_lock_done(struct tevent_req *subreq);
+
 static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
                                              struct tevent_context *ev,
                                              struct be_ctx *be_ctx,
@@ -63,6 +76,8 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
 
 static errno_t sdap_access_filter_recv(struct tevent_req *req);
 
+static errno_t sdap_access_lock_recv(struct tevent_req *req);
+
 static errno_t sdap_access_subreq_recv(struct tevent_req *req);
 
 static errno_t sdap_account_expired(struct sdap_access_ctx *access_ctx,
@@ -182,6 +197,24 @@ static errno_t check_next_rule(struct sdap_access_req_ctx *state,
             /* we are done with no errors */
             return EOK;
 
+        case LDAP_ACCESS_LOCK:
+            ret = EOK;
+
+            subreq = sdap_access_lock_send(state, state->ev, state->be_ctx,
+                                           state->domain,
+                                           state->access_ctx,
+                                           state->conn,
+                                           state->pd->user,
+                                           state->user_entry);
+            if (subreq == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE, "sdap_access_lock_send failed.\n");
+                return ENOMEM;
+            }
+
+            tevent_req_set_callback(subreq, sdap_access_lock_done, req);
+            return EAGAIN;
+            break;
+
         case LDAP_ACCESS_FILTER:
             subreq = sdap_access_filter_send(state, state->ev, state->be_ctx,
                                              state->domain,
@@ -658,6 +691,7 @@ static errno_t sdap_account_expired(struct sdap_access_ctx *access_ctx,
 
 enum sdap_access_control_type {
     SDAP_ACCESS_CONTROL_FILTER,
+    SDAP_ACCESS_CONTROL_LOCK,
 };
 
 struct sdap_access_filter_req_ctx {
@@ -678,8 +712,10 @@ struct sdap_access_filter_req_ctx {
 
 static errno_t sdap_access_decide_offline(struct tevent_req *req);
 static int sdap_access_filter_retry(struct tevent_req *req);
+static void sdap_access_lock_connect_done(struct tevent_req *subreq);
 static void sdap_access_filter_connect_done(struct tevent_req *subreq);
 static void sdap_access_filter_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,
@@ -802,6 +838,9 @@ static errno_t sdap_access_subreq_recv(struct tevent_req *req)
     case SDAP_ACCESS_CONTROL_FILTER:
         ret = sdap_access_filter_recv(req);
         break;
+    case SDAP_ACCESS_CONTROL_LOCK:
+        ret = sdap_access_lock_recv(req);
+        break;
     default:
         ret = EINVAL;
         DEBUG(SSSDBG_MINOR_FAILURE, "Unknown access control type: %d.",
@@ -996,6 +1035,13 @@ static errno_t sdap_access_filter_recv(struct tevent_req *req)
     return EOK;
 }
 
+static errno_t sdap_access_lock_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
 #define AUTHR_SRV_MISSING_MSG "Authorized service attribute missing, " \
                               "access denied"
 #define AUTHR_SRV_DENY_MSG "Access denied by authorized service attribute"
@@ -1172,6 +1218,399 @@ static errno_t sdap_access_host(struct ldb_message *user_entry)
     return ret;
 }
 
+static void sdap_access_lock_get_lockout_done(struct tevent_req *subreq);
+static int sdap_access_lock_retry(struct tevent_req *req);
+static struct tevent_req*
+sdap_access_locked_time_send(struct tevent_req *req);
+static void sdap_access_locked_time_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sdap_access_lock_send(TALLOC_CTX *mem_ctx,
+                      struct tevent_context *ev,
+                      struct be_ctx *be_ctx,
+                      struct sss_domain_info *domain,
+                      struct sdap_access_ctx *access_ctx,
+                      struct sdap_id_conn_ctx *conn,
+                      const char *username,
+                      struct ldb_message *user_entry)
+{
+    struct sdap_access_filter_req_ctx *state;
+    struct tevent_req *req;
+    errno_t ret = ERR_INTERNAL;
+
+    req = tevent_req_create(mem_ctx,
+                            &state, struct sdap_access_filter_req_ctx);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    state->filter = NULL;
+    state->username = username;
+    state->opts = access_ctx->id_ctx->opts;
+    state->conn = conn;
+    state->ev = ev;
+    state->access_ctx = access_ctx;
+    state->domain = domain;
+    state->ac_type = SDAP_ACCESS_CONTROL_LOCK;
+
+    DEBUG(SSSDBG_TRACE_FUNC,
+          "Performing access lock check for user [%s]\n", username);
+
+    state->cached_access = ldb_msg_find_attr_as_bool(user_entry,
+                                                     SYSDB_LDAP_ACCESS_CACHED,
+                                                     false);
+
+    /* Ok, we have one result, check if we are online or offline */
+    if (be_is_offline(be_ctx)) {
+        /* Ok, we're offline. Return from the cache */
+        ret = sdap_access_decide_offline(req);
+        goto done;
+    }
+
+    ret = sdap_get_basedn_user_entry(state, user_entry, state->username,
+                                     &state->basedn);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Checking lock against LDAP\n");
+
+    state->sdap_op = sdap_id_op_create(state,
+                                       state->conn->conn_cache);
+    if (!state->sdap_op) {
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sdap_access_lock_retry(req);
+    if (ret != EOK) {
+        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 int sdap_access_lock_retry(struct tevent_req *req)
+{
+    struct sdap_access_filter_req_ctx *state;
+    struct tevent_req *subreq;
+    int ret;
+
+    state = tevent_req_data(req, struct sdap_access_filter_req_ctx);
+    subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
+    if (!subreq) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "sdap_id_op_connect_send failed: %d (%s)\n", ret, strerror(ret));
+        return ret;
+    }
+
+    tevent_req_set_callback(subreq, sdap_access_lock_connect_done, req);
+    return EOK;
+}
+
+static void sdap_access_lock_connect_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct sdap_access_filter_req_ctx *state;
+    int ret, dp_error;
+    const char *attrs[] = { SYSDB_LDAP_ACCESS_LOCKOUT, NULL };
+    const char *ppolicy_dn;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_access_filter_req_ctx);
+
+    ret = sdap_id_op_connect_recv(subreq, &dp_error);
+    talloc_zfree(subreq);
+
+    if (ret != EOK) {
+        if (dp_error == DP_ERR_OFFLINE) {
+            ret = sdap_access_decide_offline(req);
+            if (ret == EOK) {
+                tevent_req_done(req);
+                return;
+            }
+        }
+
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    ppolicy_dn = dp_opt_get_string(state->opts->basic,
+                                   SDAP_PPOLICY_SEARCH_BASE);
+    /* We can't find out if ppolicy is enabled, so we deny access */
+    if (ppolicy_dn == NULL) {
+        DEBUG(SSSDBG_CONF_SETTINGS,
+              "ldap_ppolicy_search_base was not defined in configuration file."
+              " Access is denied!\n");
+        tevent_req_error(req, ERR_ACCESS_DENIED);
+        return;
+    }
+
+    /* Connection to LDAP succeeded
+     * Send 'pwdLockout' request
+     */
+    subreq = sdap_get_generic_send(state,
+                                   state->ev,
+                                   state->opts,
+                                   sdap_id_op_handle(state->sdap_op),
+                                   ppolicy_dn,
+                                   LDAP_SCOPE_BASE,
+                                   NULL, attrs,
+                                   NULL, 0,
+                                   dp_opt_get_int(state->opts->basic,
+                                                  SDAP_SEARCH_TIMEOUT),
+                                   false);
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not start LDAP communication\n");
+        tevent_req_error(req, EIO);
+        return;
+    }
+
+    tevent_req_set_callback(subreq, sdap_access_lock_get_lockout_done, req);
+}
+
+static void sdap_access_lock_get_lockout_done(struct tevent_req *subreq)
+{
+    int ret, tret, dp_error;
+    size_t num_results;
+    bool pwdLockout = false;
+    struct sysdb_attrs **results;
+    struct tevent_req *req;
+    struct sdap_access_filter_req_ctx *state;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_access_filter_req_ctx);
+
+    ret = sdap_get_generic_recv(subreq, state, &num_results, &results);
+    talloc_zfree(subreq);
+
+    /* Check the number of responses we got
+     * If it's exactly 1, we passed the check
+     * If it's < 1, we failed the check
+     * Anything else is an error
+     */
+    if (num_results < 1) {
+        // todo doplnit base
+        DEBUG(SSSDBG_CONF_SETTINGS,
+              "[%s] was not found."
+                  "Granting access.\n", SYSDB_LDAP_ACCESS_LOCKOUT);
+    } else if (results == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "num_results > 0, but results is NULL\n");
+        ret = ERR_INTERNAL;
+        goto done;
+    } else if (num_results > 1) {
+        /* It should not be possible to get more than one reply
+         * here, since we're doing a base-scoped search
+         */
+        DEBUG(SSSDBG_CRIT_FAILURE, "Received multiple replies\n");
+        ret = ERR_INTERNAL;
+        goto done;
+    } else { /* Ok, we got a single reply */
+        ret = sysdb_attrs_get_bool(results[0], SYSDB_LDAP_ACCESS_LOCKOUT,
+                                   &pwdLockout);
+    }
+
+    if (pwdLockout) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Password policy is enabled on LDAP server.\n");
+
+        subreq = sdap_access_locked_time_send(req);
+        if (subreq == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "sdap_access_lock_send failed.\n");
+            ret = ENOMEM;
+            goto done;
+        }
+
+        tevent_req_set_callback(subreq, sdap_access_locked_time_done, req);
+        ret = EAGAIN;
+        goto done;
+
+    } else {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Password policy is disabled on LDAP server "
+              "- storing 'access granted' in sysdb.\n");
+        tret = sdap_save_user_cache_bool(state->domain, state->username,
+                                         SYSDB_LDAP_ACCESS_CACHED, true);
+        if (tret != EOK) {
+            /* Failing to save to the cache is non-fatal.
+             * Just return the result.
+             */
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Failed to set user locked attribute\n");
+            goto done;
+        }
+
+        ret = EOK;
+        goto done;
+    }
+
+done:
+    if (ret != EAGAIN) {
+        /* release connection */
+        tret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
+        if (tret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "sdap_get_generic_send() returned error [%d][%s]\n",
+                  ret, sss_strerror(ret));
+        }
+
+        if (ret == EOK) {
+            tevent_req_done(req);
+        } else {
+            tevent_req_error(req, ret);
+        }
+    }
+}
+
+static struct tevent_req*
+sdap_access_locked_time_send(struct tevent_req *req)
+{
+    struct tevent_req *subreq;
+    struct sdap_access_filter_req_ctx *state;
+    const char *attrs[] = { SYSDB_LDAP_ACCESS_LOCKED_TIME, NULL };
+
+    state = tevent_req_data(req, struct sdap_access_filter_req_ctx);
+
+    subreq = sdap_get_generic_send(state,
+                                   state->ev,
+                                   state->opts,
+                                   sdap_id_op_handle(state->sdap_op),
+                                   state->basedn,
+                                   LDAP_SCOPE_BASE,
+                                   NULL, attrs,
+                                   NULL, 0,
+                                   dp_opt_get_int(state->opts->basic,
+                                                  SDAP_SEARCH_TIMEOUT),
+                                   false);
+    return subreq;
+}
+
+static void sdap_access_locked_time_done(struct tevent_req *subreq)
+{
+    int ret, tret, dp_error;
+    size_t num_results;
+    bool locked = false;
+    const char *pwdAccountLockedTime;
+    struct sysdb_attrs **results;
+    struct tevent_req *req;
+    struct sdap_access_filter_req_ctx *state;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_access_filter_req_ctx);
+
+    ret = sdap_get_generic_recv(subreq, state, &num_results, &results);
+    talloc_zfree(subreq);
+
+    ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
+    if (ret != EOK) {
+        if (dp_error == DP_ERR_OK) {
+            /* retry */
+            tret = sdap_access_lock_retry(req);
+            if (tret == EOK) {
+                return;
+            }
+        } else if (dp_error == DP_ERR_OFFLINE) {
+            ret = sdap_access_decide_offline(req);
+        } else {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "sdap_get_generic_send() returned error [%d][%s]\n",
+                  ret, sss_strerror(ret));
+        }
+
+        goto done;
+    }
+
+    /* Check the number of responses we got
+     * If it's exactly 1, we passed the check
+     * If it's < 1, we failed the check
+     * Anything else is an error
+     */
+    if (num_results < 1) {
+        DEBUG(SSSDBG_CONF_SETTINGS,
+              "User [%s] was not found with the specified filter. "
+                  "Denying access.\n", state->username);
+    } else if (results == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "num_results > 0, but results is NULL\n");
+        ret = ERR_INTERNAL;
+        goto done;
+    } else if (num_results > 1) {
+        /* It should not be possible to get more than one reply
+         * here, since we're doing a base-scoped search
+         */
+        DEBUG(SSSDBG_CRIT_FAILURE, "Received multiple replies\n");
+        ret = ERR_INTERNAL;
+        goto done;
+    } else { /* Ok, we got a single reply */
+        ret = sysdb_attrs_get_string(results[0], SYSDB_LDAP_ACCESS_LOCKED_TIME,
+                                     &pwdAccountLockedTime);
+        if (ret == EOK) {
+            if (strcasecmp(pwdAccountLockedTime,
+                           PERMANENTLY_LOCKED_ACCOUNT) == 0) {
+                locked = true;
+            } else {
+                DEBUG(SSSDBG_TRACE_FUNC,
+                      "Account of: %s is beeing blocked by password policy, "
+                      "but value: [%s] value is ignored by SSSD.\n",
+                      state->username, pwdAccountLockedTime);
+            }
+        } else {
+            /* Attribute SYSDB_LDAP_ACCESS_LOCKED_TIME in not be present unless
+             * user's account is blocked by password policy.
+             */
+            DEBUG(SSSDBG_TRACE_INTERNAL,
+                  "Attribute %s failed to be obtained - [%d][%s].\n",
+                  SYSDB_LDAP_ACCESS_LOCKED_TIME, ret, strerror(ret));
+        }
+    }
+
+    if (locked) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Access denied by online lookup - account is locked.\n");
+        ret = ERR_ACCESS_DENIED;
+    } else {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Access granted by online lookup - account is not locked.\n");
+        ret = EOK;
+    }
+
+    /* Save '!locked' to the cache for future offline access checks.
+     * Locked == true => access denied,
+     * Locked == false => access granted
+     */
+    tret = sdap_save_user_cache_bool(state->domain, state->username,
+                                     SYSDB_LDAP_ACCESS_CACHED, !locked);
+
+    if (tret != EOK) {
+        /* Failing to save to the cache is non-fatal.
+         * Just return the result.
+         */
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set user locked attribute\n");
+        goto done;
+    }
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+}
+
+static void sdap_access_lock_done(struct tevent_req *subreq)
+{
+    sdap_access_done(subreq);
+}
+
 static errno_t sdap_get_basedn_user_entry(TALLOC_CTX *mem_ctx,
                                           struct ldb_message *user_entry,
                                           const char *username,
diff --git a/src/providers/ldap/sdap_access.h b/src/providers/ldap/sdap_access.h
index 890ea48bd906bd2ab7efa4f0720c01058389226d..49f71af78808ffefc6e063e9847797d472e63970 100644
--- a/src/providers/ldap/sdap_access.h
+++ b/src/providers/ldap/sdap_access.h
@@ -29,11 +29,14 @@
 #include "providers/ldap/ldap_common.h"
 
 #define SYSDB_LDAP_ACCESS_CACHED "ldap_access_cached"
+#define SYSDB_LDAP_ACCESS_LOCKED_TIME "pwdAccountLockedTime"
+#define SYSDB_LDAP_ACCESS_LOCKOUT "pwdLockout"
 
 #define LDAP_ACCESS_FILTER_NAME "filter"
 #define LDAP_ACCESS_EXPIRE_NAME "expire"
 #define LDAP_ACCESS_SERVICE_NAME "authorized_service"
 #define LDAP_ACCESS_HOST_NAME "host"
+#define LDAP_ACCESS_LOCK_NAME "lock"
 
 #define LDAP_ACCOUNT_EXPIRE_SHADOW "shadow"
 #define LDAP_ACCOUNT_EXPIRE_AD "ad"
@@ -48,6 +51,7 @@ enum ldap_access_rule {
     LDAP_ACCESS_EXPIRE,
     LDAP_ACCESS_SERVICE,
     LDAP_ACCESS_HOST,
+    LDAP_ACCESS_LOCK,
     LDAP_ACCESS_LAST
 };
 
-- 
1.9.3

>From b1169e94e9a1418947bbb93ef1f1bcac14ad0636 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 17:04:55 +0100
Subject: [PATCH 9/9] MAN: options 'lock' and 'ldap_ppolicy_search_base'

Resolves:
https://fedorahosted.org/sssd/ticket/2364
---
 src/man/sssd-ldap.5.xml | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index e8bcfd0d13cb39b7e60fc8f3aa4338b53d2921e2..cae536d6d8a74c352e4d27e50f308231b9ff4c97 100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -1914,6 +1914,13 @@ ldap_access_filter = (employeeType=admin)
                             <emphasis>filter</emphasis>: use ldap_access_filter
                         </para>
                         <para>
+                            <emphasis>lock</emphasis>: use account locking.
+                            If set, this option denies access in case that ldap
+                            attribute 'pwdAccountLockedTime' is present and has
+                            value of '000001010000Z'. Please see the option
+                            ldap_ppolicy_search_base.
+                        </para>
+                        <para>
                             <emphasis>expire</emphasis>: use
                             ldap_account_expire_policy
                         </para>
@@ -1937,6 +1944,23 @@ ldap_access_filter = (employeeType=admin)
                 </varlistentry>
 
                 <varlistentry>
+                    <term>ldap_ppolicy_search_base (string)</term>
+                    <listitem>
+                        <para>
+                           This option specifies the DN of password policy entry
+                           on LDAP server. Please note that absence of this
+                           option in sssd.conf in case of enabled account
+                           lockout checking will yield access denied as
+                           ppolicy attributes on LDAP server cannot be checked
+                           properly.
+                        </para>
+                        <para>
+                          Example: cn=ppolicy,ou=policies,dc=example,dc=com
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
                     <term>ldap_deref (string)</term>
                     <listitem>
                         <para>
-- 
1.9.3

_______________________________________________
sssd-devel mailing list
[email protected]
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to