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

>From bd6148d96eb972d55d5aacf9f3d64acac3da84dd Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 09:15:59 +0100
Subject: [PATCH 1/8] 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 | 67 ++++++++++++++++++++++++++++------------
 1 file changed, 47 insertions(+), 20 deletions(-)

diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 89d37e52fa27b14be6bf702ebf0bc18b3fe59da6..6e612950326c42914a98b1436a9b36936cce3133 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,46 @@ 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;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    attrs = sysdb_new_attrs(tmp_ctx);
+    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(tmp_ctx);
+    return ret;
+}
+
 static errno_t sdap_access_host(struct ldb_message *user_entry)
 {
     errno_t ret;
-- 
1.9.3

>From bce1150187d4526b2593167409852b4eee829b6e Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 11:00:48 +0100
Subject: [PATCH 2/8] 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 | 51 ++++++++++++++++++++++++++++------------
 1 file changed, 36 insertions(+), 15 deletions(-)

diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 6e612950326c42914a98b1436a9b36936cce3133..894d105fadfb837482e8ef2734b5b1c227bad3fa 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;
     }
 
@@ -1149,3 +1142,31 @@ 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 = EOK;
+
+    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;
+    }
+
+done:
+    return ret;
+}
-- 
1.9.3

>From f038ef40859d7ec7a67c5d48e80eaf5729bf4fad Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 11:13:20 +0100
Subject: [PATCH 3/8] 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 894d105fadfb837482e8ef2734b5b1c227bad3fa..c18f69d43dfa74c787fa91963ca26d97d17ad1d4 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 17a06664368ad8d3d8bda09426eb82c85e80ebd3 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 12:22:21 +0100
Subject: [PATCH 4/8] 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.

Also remove sdap_access_filter_recv() as it is replaced by
sdap_access_filter_recv().
---
 src/providers/ldap/sdap_access.c | 18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)

diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index c18f69d43dfa74c787fa91963ca26d97d17ad1d4..8c0abadae07b33fa7ff880304906b5923d79ad28 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -60,7 +60,6 @@ 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_account_expired(struct sdap_access_ctx *access_ctx,
                                     struct pam_data *pd,
@@ -219,14 +218,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_recv(subreq);
     talloc_zfree(subreq);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Error retrieving access check result.\n");
@@ -249,6 +248,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);
@@ -949,14 +953,6 @@ done:
     }
 }
 
-static errno_t sdap_access_filter_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"
-- 
1.9.3

>From c16de8e6810270035ce9ff6d2a0d0a90d9dc9cfe Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 16:05:13 +0100
Subject: [PATCH 5/8] 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 8c0abadae07b33fa7ff880304906b5923d79ad28..ecde3ecc8ddfa38aa9b2025e83e8ad94238d9bdc 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -228,7 +228,12 @@ static void sdap_access_done(struct tevent_req *subreq)
     ret = sdap_access_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 c98f84f6ddea58a7deb6f69b9b8a6cd5f7d14382 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 16:13:08 +0100
Subject: [PATCH 6/8] 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 ecde3ecc8ddfa38aa9b2025e83e8ad94238d9bdc..6eed59ab855693f2d5299ef14eb2f3fb743107b4 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -663,11 +663,12 @@ 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;
 };
 
-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);
@@ -710,12 +711,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;
     }
 
@@ -781,7 +782,7 @@ done:
     return req;
 }
 
-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);
@@ -826,7 +827,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;
@@ -884,7 +885,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);
@@ -940,7 +941,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 220e8ab33015acdbed04de306eb796ffd53ce2e0 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 17:04:55 +0100
Subject: [PATCH 7/8] MAN: document new option 'lock' of ldap_access_order

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

diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index e8bcfd0d13cb39b7e60fc8f3aa4338b53d2921e2..d9e8af544249f80a90c0ef779cca0e80bcf0aa35 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'.
+                        </para>
+
+                        <para>
                             <emphasis>expire</emphasis>: use
                             ldap_account_expire_policy
                         </para>
-- 
1.9.3

>From da565c407e616b2b5c43c301f8212bc8cdc736fd Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 17:44:24 +0100
Subject: [PATCH 8/8] 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 | 294 ++++++++++++++++++++++++++++++++++++++-
 src/providers/ldap/sdap_access.h |   3 +
 3 files changed, 298 insertions(+), 1 deletion(-)

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 6eed59ab855693f2d5299ef14eb2f3fb743107b4..cb4b4dbd9508bc9d75d5d285009967d2bf5e3ecb 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,
@@ -178,6 +191,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,
@@ -265,7 +296,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,
@@ -670,8 +700,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,
@@ -1143,6 +1175,266 @@ static errno_t sdap_access_host(struct ldb_message *user_entry)
     return ret;
 }
 
+static void sdap_access_lock_get_access_done(struct tevent_req *subreq);
+static int sdap_access_lock_retry(struct tevent_req *req);
+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;
+
+    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;
+    static const char *attrs[] = { SYSDB_LDAP_ACCESS_LOCKED_TIME, NULL };
+
+    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;
+    }
+
+    /* Connection to LDAP succeeded
+     * Send filter request
+     */
+    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);
+    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_access_done, req);
+}
+
+static void sdap_access_lock_get_access_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..9ffea9196974958ccb1be03a6f8346b6c0192436 100644
--- a/src/providers/ldap/sdap_access.h
+++ b/src/providers/ldap/sdap_access.h
@@ -29,11 +29,13 @@
 #include "providers/ldap/ldap_common.h"
 
 #define SYSDB_LDAP_ACCESS_CACHED "ldap_access_cached"
+#define SYSDB_LDAP_ACCESS_LOCKED_TIME "pwdAccountLockedTime"
 
 #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 +50,7 @@ enum ldap_access_rule {
     LDAP_ACCESS_EXPIRE,
     LDAP_ACCESS_SERVICE,
     LDAP_ACCESS_HOST,
+    LDAP_ACCESS_LOCK,
     LDAP_ACCESS_LAST
 };
 
-- 
1.9.3

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

Reply via email to