On 08/19/2014 04:31 PM, Pavel Březina wrote:
On 08/19/2014 02:05 PM, Pavel Reichl wrote:

On 08/19/2014 12:43 PM, Pavel Březina wrote:
On 08/19/2014 10:37 AM, Pavel Reichl wrote:

On 08/18/2014 04:39 PM, Pavel Březina wrote:
On 08/18/2014 04:24 PM, Pavel Reichl wrote:

Thank you Pavel for comments, I was mostly able to fix patches as you
asked for but I have a little problem with default_dn. Could you
please
see my comments inline?


On 08/18/2014 12:13 PM, Pavel Březina wrote:
On 08/15/2014 03:13 PM, Pavel Reichl wrote:

On 08/11/2014 04:51 PM, Jakub Hrozek wrote:
On Mon, Aug 11, 2014 at 04:19:22PM +0200, Pavel Reichl wrote:
Subject: [PATCH 7/9] SDAP: new option - DN to ppolicy on LDAP
Do we need to make the DN configurable? When you install the
ppolicy,
can you actually set the DN?
I haven't tested setting different DN, but I believe it's
possible.
During weekend I was putting my notes for setting ppolicy on
OpenLDAP on
this wiki page
https://fedorahosted.org/sssd/wiki/openldap_ppolicy#Loadingppolicyoverlay




(this is work in progress and I'm not even sure we care to have
such a
how-to on our wiki), but I believe you can see from there that
DN is
configurable.
Any howto is good as long as it's up to date.

And from the howto it seems clear that olcPPolicyDefault is the DN
setting, so yes, this should be configurable.

Still we could use a default DN if user didn't set DN himself:

"cn=ppolicy,ou=policies,$search_base"
That sounds like a good idea.
_______________________________________________
sssd-devel mailing list
[email protected]
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hello,

please note that attached patches requires to be patches from "SDAP:
refactor sdap_access" thread to pushed first.

Patch 7:
Nack.

+    /* cached results of access control checks */
                     ^ cached_access contain only one result
     bool cached_access;
     const char *basedn;
  };

Please remove sdap_access_{filter|lockout}_decide_offline and use
only
sdap_access_decide_offline that will take bool instead of tevent_req
as parameter.

Patch 8:
Ack.

Patch 9:

+        case LDAP_ACCESS_LOCKOUT:
+            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;
+            }
+
+            state->ac_type = SDAP_ACCESS_CONTROL_PPOLICY_LOCK;
+
+ tevent_req_set_callback(subreq, sdap_access_done, req);
+            return EAGAIN;
+            break;
+

There is no need for ret = EOK and break.

+static char*
+get_default_ppolicy_dn(TALLOC_CTX *mem_ctx, struct sdap_options
*opts)
+{
+    char *search_base;
+
+ search_base = dp_opt_get_string(opts->basic, SDAP_SEARCH_BASE);
+
+    return talloc_asprintf(mem_ctx, "cn=ppolicy,ou=policies,%s",
search_base);
+}

ldap_search_base option may contain more than one search base and it may also contain filter. This option is parsed during initialization
of the provider and stored in sdap_domain->*search_base. But since
there may be more, you need to iterate over them.
Could you please elaborate a bit more about this? I'm afraid I don't
fully understand.

Would you like me to create an array of possible default DN's and
query
ldap server for every one? Or is there a way how to use multiple dn
in a
single ldap query?

Yes, you have to iterate over all of them. There is no way how to
search all of them in one query AFAIK.

However, I think it is sufficient to use only domain components to
create default dn, i.e. basedn of domain (dc=example,dc=com). This can
be obtained from sdap_domain structure. Then you wouldn't have to
iterate over all search bases.


However, I think it is sufficient to use only dc part here so you can
use basedn from sdap_domain structure.

Also, I think it would be better if this function is removed and the default value is set in ldap_get_options() with dp_opt_set_string if
the option is not set.



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

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

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

Hello, please see updated patches.

Thanks,

Pavel Reichl

Hi,
I have just last few nitpicks.

+    /* option was configured */
+    if (ppolicy_dn != NULL) {
+        state->ppolicy_dns = talloc_array(state, const char*, 2);
+        if (state->ppolicy_dns == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Could not allocate
ppolicy_dns.\n");
+            tevent_req_error(req, ENOMEM);
+            return;
+        }
+
+        state->ppolicy_dns[0] = ppolicy_dn;
+        state->ppolicy_dns[1] = NULL;
+
+    } else {
+        /* try to determine default value */
+        DEBUG(SSSDBG_CONF_SETTINGS,
+              "ldap_pwdlockout_dn was not defined in configuration
file.\n");
+
+        state->ppolicy_dns = get_default_ppolicy_dns(state,
state->opts->sdom);
+        if (state->ppolicy_dns == NULL) {
+            tevent_req_error(req, ERR_ACCESS_DENIED);
+            return;
+        }
+    }

Why do you return ENOMEM on the top and ERR_ACCESS_DENIED on the
bottom? Shouldn't it be the same?

errno_t sdap_access_lock_step(struct tevent_req *req)

When we iterate over an array, the convention is that the step
function will return EOK if we are at the end, EAGAIN if it created
another tevent_req and error on error.

Also, I would prefer to use an index to access state->ppolicy_dns
instead of dereferencing it and using pointer arithmetic.



OK, I hope that attached patches address all your concerns!

Ack.

I just squash into the 3rd patch the following change:

-                                   *state->ppolicy_dns,
+ state->ppolicy_dns[state->ppolicy_dns_index],






_______________________________________________
sssd-devel mailing list
[email protected]
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Rebased patches for 1.11 (includes Pavel's mini patch)
>From 8338d7ff98a198c8595d7196a43423d758233d9e 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
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

As a preparation for ticket #2364 separate code for storing user bool
values into sysdb to a new function sdap_save_user_cache_bool().

Reviewed-by: Pavel Březina <[email protected]>
---
 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 3a25a92656dfe9f041042d6048cb78770c8dfe09..b0df5dee29966ef277822a07d1fb181393b9c6b1 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,
@@ -857,7 +862,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);
@@ -936,25 +940,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->sysdb, 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.
@@ -1061,6 +1048,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->sysdb, 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 c6e1b4f35361bf615caad182b359ee65bdfe2ad2 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
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

As preparation for ticket #2364 separate code for parsing user basedn
to a new function sdap_get_basedn_user_entry().

We actually do not need to call strdup on basedn, instead we can just point to address in user_entry as it's allocated on parent memory context.

Reviewed-by: Pavel Březina <[email protected]>
---
 src/providers/ldap/sdap_access.c | 46 ++++++++++++++++++++++++++--------------
 1 file changed, 30 insertions(+), 16 deletions(-)

diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index b0df5dee29966ef277822a07d1fb181393b9c6b1..ce0fcbfdf32316993b78e36f82111849cfbbbbda 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -45,6 +45,10 @@ 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(struct ldb_message *user_entry,
+                                          const char *username,
+                                          const char **_basedn);
+
 static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
                                              struct tevent_context *ev,
                                              struct be_ctx *be_ctx,
@@ -649,7 +653,7 @@ struct sdap_access_filter_req_ctx {
     struct sysdb_handle *handle;
     struct sss_domain_info *domain;
     bool cached_access;
-    char *basedn;
+    const char *basedn;
 };
 
 static errno_t sdap_access_filter_decide_offline(struct tevent_req *req);
@@ -667,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;
@@ -705,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(user_entry, state->username,
+                                     &state->basedn);
+    if (ret != EOK) {
         goto done;
     }
 
@@ -1142,3 +1134,25 @@ static errno_t sdap_access_host(struct ldb_message *user_entry)
 
     return ret;
 }
+
+static errno_t sdap_get_basedn_user_entry(struct ldb_message *user_entry,
+                                          const char *username,
+                                          const 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 = basedn;
+    ret = EOK;
+
+done:
+    return ret;
+}
-- 
1.9.3

>From e17c4c790737b04ac8819381f73ee15b1e7282d1 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
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fixed typo and replaced duplicated string by macro definition.

Reviewed-by: Pavel Březina <[email protected]>
---
 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 ce0fcbfdf32316993b78e36f82111849cfbbbbda..147f5a645a1978e328b9b9ae4f57efd79f274622 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 f064282553d456c4e97d98b04f8b6ae970fa60d7 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
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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.

Rename check_next_rule() to sdap_access_check_next_rule().

Update definition order of tevent-using functions by time of execution.

Reviewed-by: Pavel Březina <[email protected]>
---
 src/providers/ldap/sdap_access.c | 55 +++++++++++++++++++++++++++-------------
 1 file changed, 37 insertions(+), 18 deletions(-)

diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 147f5a645a1978e328b9b9ae4f57efd79f274622..f3afdc7d0bd7949ae598743f1a0afeb3b7634523 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -59,6 +59,7 @@ 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,
@@ -70,6 +71,10 @@ static  errno_t sdap_access_service(struct pam_data *pd,
 
 static errno_t sdap_access_host(struct ldb_message *user_entry);
 
+enum sdap_access_control_type {
+    SDAP_ACCESS_CONTROL_FILTER,
+};
+
 struct sdap_access_req_ctx {
     struct pam_data *pd;
     struct tevent_context *ev;
@@ -79,11 +84,12 @@ struct sdap_access_req_ctx {
     struct sss_domain_info *domain;
     struct ldb_message *user_entry;
     size_t current_rule;
+    enum sdap_access_control_type ac_type;
 };
 
-static errno_t check_next_rule(struct sdap_access_req_ctx *state,
-                               struct tevent_req *req);
-static void sdap_access_filter_done(struct tevent_req *subreq);
+static errno_t sdap_access_check_next_rule(struct sdap_access_req_ctx *state,
+                                           struct tevent_req *req);
+static void sdap_access_done(struct tevent_req *subreq);
 
 struct tevent_req *
 sdap_access_send(TALLOC_CTX *mem_ctx,
@@ -152,7 +158,7 @@ sdap_access_send(TALLOC_CTX *mem_ctx,
 
     state->user_entry = res->msgs[0];
 
-    ret = check_next_rule(state, req);
+    ret = sdap_access_check_next_rule(state, req);
     if (ret == EAGAIN) {
         return req;
     }
@@ -167,8 +173,8 @@ done:
     return req;
 }
 
-static errno_t check_next_rule(struct sdap_access_req_ctx *state,
-                               struct tevent_req *req)
+static errno_t sdap_access_check_next_rule(struct sdap_access_req_ctx *state,
+                                           struct tevent_req *req)
 {
     struct tevent_req *subreq;
     int ret = EOK;
@@ -191,7 +197,9 @@ static errno_t check_next_rule(struct sdap_access_req_ctx *state,
                 return ENOMEM;
             }
 
-            tevent_req_set_callback(subreq, sdap_access_filter_done, req);
+            state->ac_type = SDAP_ACCESS_CONTROL_FILTER;
+
+            tevent_req_set_callback(subreq, sdap_access_done, req);
             return EAGAIN;
 
         case LDAP_ACCESS_EXPIRE:
@@ -219,14 +227,27 @@ 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);
+    struct tevent_req *req;
+    struct sdap_access_req_ctx *state;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_access_req_ctx);
+
+    /* process subrequest */
+    switch(state->ac_type) {
+    case SDAP_ACCESS_CONTROL_FILTER:
+        ret = sdap_access_filter_recv(subreq);
+        break;
+    default:
+        ret = EINVAL;
+        DEBUG(SSSDBG_MINOR_FAILURE, "Unknown access control type: %d.",
+              state->ac_type);
+        break;
+    }
 
-    ret = sdap_access_filter_recv(subreq);
     talloc_zfree(subreq);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Error retrieving access check result.\n");
@@ -236,7 +257,7 @@ static void sdap_access_filter_done(struct tevent_req *subreq)
 
     state->current_rule++;
 
-    ret = check_next_rule(state, req);
+    ret = sdap_access_check_next_rule(state, req);
     switch (ret) {
     case EAGAIN:
         return;
@@ -256,7 +277,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,
@@ -661,7 +681,7 @@ struct sdap_access_filter_req_ctx {
 static errno_t sdap_access_filter_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);
+static void sdap_access_filter_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,
@@ -848,10 +868,10 @@ static void sdap_access_filter_connect_done(struct tevent_req *subreq)
         return;
     }
 
-    tevent_req_set_callback(subreq, sdap_access_filter_get_access_done, req);
+    tevent_req_set_callback(subreq, sdap_access_filter_done, req);
 }
 
-static void sdap_access_filter_get_access_done(struct tevent_req *subreq)
+static void sdap_access_filter_done(struct tevent_req *subreq)
 {
     int ret, tret, dp_error;
     size_t num_results;
@@ -956,7 +976,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 bde211d20e8b3521e5f5aff7e1c5ed7a3cd40eca 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
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Don't log error if access is denied in function sdap_access_done().

Reviewed-by: Pavel Březina <[email protected]>
---
 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 f3afdc7d0bd7949ae598743f1a0afeb3b7634523..f060b32888d21557a142bdf57f6069581d6f5a82 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -250,7 +250,12 @@ static void sdap_access_done(struct tevent_req *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 28af1b708cb16bfa944eab99b9b781bbe2302a40 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: refactor AC offline checks

Prepare code for other access control checks.
---
 src/providers/ldap/sdap_access.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index f060b32888d21557a142bdf57f6069581d6f5a82..fd1163e5efd7c1925b5984b4a1e3f528768afe86 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -679,11 +679,12 @@ struct sdap_access_filter_req_ctx {
     struct sdap_id_op *sdap_op;
     struct sysdb_handle *handle;
     struct sss_domain_info *domain;
+    /* cached result of access control checks */
     bool cached_access;
     const char *basedn;
 };
 
-static errno_t sdap_access_filter_decide_offline(struct tevent_req *req);
+static errno_t sdap_access_decide_offline(bool cached_ac);
 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_done(struct tevent_req *req);
@@ -728,10 +729,11 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
     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(be_ctx)) {
         /* Ok, we're offline. Return from the cache */
-        ret = sdap_access_filter_decide_offline(req);
+        ret = sdap_access_decide_offline(state->cached_access);
         goto done;
     }
 
@@ -797,12 +799,13 @@ done:
     return req;
 }
 
-static errno_t sdap_access_filter_decide_offline(struct tevent_req *req)
+/* Helper function,
+ * cached_ac => access granted
+ * !cached_ac => access denied
+ */
+static errno_t sdap_access_decide_offline(bool cached_ac)
 {
-    struct sdap_access_filter_req_ctx *state =
-            tevent_req_data(req, struct sdap_access_filter_req_ctx);
-
-    if (state->cached_access) {
+    if (cached_ac) {
         DEBUG(SSSDBG_TRACE_FUNC, "Access granted by cached credentials\n");
         return EOK;
     } else {
@@ -842,7 +845,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(state->cached_access);
             if (ret == EOK) {
                 tevent_req_done(req);
                 return;
@@ -900,7 +903,7 @@ static void sdap_access_filter_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(state->cached_access);
         } else if (ret == ERR_INVALID_FILTER) {
             sss_log(SSS_LOG_ERR, MALFORMED_FILTER, state->filter);
             DEBUG(SSSDBG_CRIT_FAILURE, MALFORMED_FILTER, state->filter);
-- 
1.9.3

>From 50825da34bdcaf0ab8cc620faa535686de516523 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 pwdLockout attribute 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 48db1eb8f3ec12e6f46dbccd66dc0dbf72879b42..4242df5078fe53c209db95c0854ff1f95f6735dd 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -318,6 +318,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_pwdlockout_dn' : _('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 77f7eac71b9c591ed7c12add1a26ec3225cf85d1..2d3c236a04d9f2202e8f1d05ee345b7abeff6927 100644
--- a/src/config/etc/sssd.api.d/sssd-ad.conf
+++ b/src/config/etc/sssd.api.d/sssd-ad.conf
@@ -110,6 +110,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_pwdlockout_dn = 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 e1ebf0888356288efb0c9cb34ed569cc47a0825a..ed28698fc04ba1c60cc21321ac0923cf38ca0d39 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_pwdlockout_dn = 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 4e3f0e9ea95d0d22bec5e6d40e25251da3aee298..b71e46130488f5626fe0c60fcb4792ed340fd4b2 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_pwdlockout_dn = 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 19ad14d62b5e74fae54fbac71807fed35198aaf1..31703fd7fd702f0a5dd711e601706df2f63b2934 100644
--- a/src/providers/ad/ad_opts.h
+++ b/src/providers/ad/ad_opts.h
@@ -130,6 +130,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_pwdlockout_dn", 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 52c85779f79aee4d000b6101d61f6cc27ea45c38..15357481c2f052f71176faacbfaaa144a36b6a1e 100644
--- a/src/providers/ipa/ipa_opts.h
+++ b/src/providers/ipa/ipa_opts.h
@@ -153,6 +153,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_pwdlockout_dn", 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 803bd3f19c32691eb10a5eec9cd333224af4f7a0..cc40ad1cbcd343f17bc1c47b88d5ce790661aae4 100644
--- a/src/providers/ldap/ldap_opts.h
+++ b/src/providers/ldap/ldap_opts.h
@@ -119,6 +119,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_pwdlockout_dn", 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 567cf42a36c457764da2e7cf53ff5ceb72ede9e0..77b1a3b063508813bececa1e21f73ef44d7066dd 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -231,6 +231,7 @@ enum sdap_basic_opt {
     SDAP_DISABLE_RANGE_RETRIEVAL,
     SDAP_MIN_ID,
     SDAP_MAX_ID,
+    SDAP_PWDLOCKOUT_DN,
 
     SDAP_OPTS_BASIC /* opts counter */
 };
-- 
1.9.3

>From ae0fb545356b41b8be89bd4d9fd0784218c82bd5 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 lockout 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.

Account is considered locked if pwdAccountLockedTime attribut has value
of 000001010000Z.

------------------------------------------------------------------------
Quotation from man slapo-ppolicy:

pwdAccountLockedTime

This attribute contains the time that the user's account was locked. If
the account has been locked, the password may no longer be used to
authenticate the user to the  directory. If pwdAccountLockedTime is set
to 000001010000Z, the user's account has been permanently locked and
may only be unlocked by an administrator. Note that account locking
only takes effect when the pwdLockout password policy attribute is set
to "TRUE".
------------------------------------------------------------------------

Also set default value for sdap_pwdlockout_dn to
cn=ppolicy,ou=policies,${search_base}

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

diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
index 9960fd3ab7a6e446e5cdc1e8fa4984aab812d980..44333a9a3a45de16aaaf83fecaea4817cebc90d4 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_LOCKOUT;
         } 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 fd1163e5efd7c1925b5984b4a1e3f528768afe86..880735ebb96ed52dbd5e7235e0933c97e20d0c8e 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,
@@ -51,6 +52,16 @@ static errno_t sdap_get_basedn_user_entry(struct ldb_message *user_entry,
                                           const char *username,
                                           const 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 struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
                                              struct tevent_context *ev,
                                              struct be_ctx *be_ctx,
@@ -62,6 +73,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_account_expired(struct sdap_access_ctx *access_ctx,
                                     struct pam_data *pd,
                                     struct ldb_message *user_entry);
@@ -73,6 +86,7 @@ static errno_t sdap_access_host(struct ldb_message *user_entry);
 
 enum sdap_access_control_type {
     SDAP_ACCESS_CONTROL_FILTER,
+    SDAP_ACCESS_CONTROL_PPOLICY_LOCK,
 };
 
 struct sdap_access_req_ctx {
@@ -185,6 +199,23 @@ static errno_t sdap_access_check_next_rule(struct sdap_access_req_ctx *state,
             /* we are done with no errors */
             return EOK;
 
+        case LDAP_ACCESS_LOCKOUT:
+            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;
+            }
+
+            state->ac_type = SDAP_ACCESS_CONTROL_PPOLICY_LOCK;
+
+            tevent_req_set_callback(subreq, sdap_access_done, req);
+            return EAGAIN;
+
         case LDAP_ACCESS_FILTER:
             subreq = sdap_access_filter_send(state, state->ev, state->be_ctx,
                                              state->domain,
@@ -241,6 +272,9 @@ static void sdap_access_done(struct tevent_req *subreq)
     case SDAP_ACCESS_CONTROL_FILTER:
         ret = sdap_access_filter_recv(subreq);
         break;
+    case SDAP_ACCESS_CONTROL_PPOLICY_LOCK:
+        ret = sdap_access_lock_recv(subreq);
+        break;
     default:
         ret = EINVAL;
         DEBUG(SSSDBG_MINOR_FAILURE, "Unknown access control type: %d.",
@@ -686,6 +720,8 @@ struct sdap_access_filter_req_ctx {
 
 static errno_t sdap_access_decide_offline(bool cached_ac);
 static int sdap_access_filter_retry(struct tevent_req *req);
+static void sdap_access_lock_connect_done(struct tevent_req *subreq);
+static errno_t sdap_access_lock_get_lockout_step(struct tevent_req *req);
 static void sdap_access_filter_connect_done(struct tevent_req *subreq);
 static void sdap_access_filter_done(struct tevent_req *req);
 static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
@@ -1160,6 +1196,527 @@ 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 errno_t sdap_access_lock_step(struct tevent_req *req);
+static void sdap_access_lock_step_done(struct tevent_req *subreq);
+
+struct sdap_access_lock_req_ctx {
+    const char *username;
+    const char *filter;
+    struct tevent_context *ev;
+    struct sdap_access_ctx *access_ctx;
+    struct sdap_options *opts;
+    struct sdap_id_conn_ctx *conn;
+    struct sdap_id_op *sdap_op;
+    struct sysdb_handle *handle;
+    struct sss_domain_info *domain;
+    /* cached results of access control checks */
+    bool cached_access;
+    const char *basedn;
+    /* default DNs to ppolicy */
+    const char **ppolicy_dns;
+    unsigned int ppolicy_dns_index;
+};
+
+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_lock_req_ctx *state;
+    struct tevent_req *req;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx,
+                            &state, struct sdap_access_lock_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->ppolicy_dns_index = 0;
+
+    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_LOCKOUT, 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(state->cached_access);
+        goto done;
+    }
+
+    ret = sdap_get_basedn_user_entry(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_lock_req_ctx *state;
+    struct tevent_req *subreq;
+    int ret;
+
+    state = tevent_req_data(req, struct sdap_access_lock_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 const char**
+get_default_ppolicy_dns(TALLOC_CTX *mem_ctx, struct sdap_domain *sdom)
+{
+    const char **ppolicy_dns;
+    int count = 0;
+    int i;
+
+    while(sdom->search_bases[count] != NULL) {
+        count++;
+    }
+
+    /* +1 to have space for final NULL */
+    ppolicy_dns = talloc_array(mem_ctx, const char*, count + 1);
+
+    for(i = 0; i < count; i++) {
+        ppolicy_dns[i] = talloc_asprintf(mem_ctx, "cn=ppolicy,ou=policies,%s",
+                                         sdom->search_bases[i]->basedn);
+    }
+
+    ppolicy_dns[count] = NULL;
+    return ppolicy_dns;
+}
+
+static void sdap_access_lock_connect_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct sdap_access_lock_req_ctx *state;
+    int ret, dp_error;
+    const char *ppolicy_dn;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_access_lock_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(state->cached_access);
+            if (ret == EOK) {
+                tevent_req_done(req);
+                return;
+            }
+        }
+
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    ppolicy_dn = dp_opt_get_string(state->opts->basic,
+                                   SDAP_PWDLOCKOUT_DN);
+
+    /* option was configured */
+    if (ppolicy_dn != NULL) {
+        state->ppolicy_dns = talloc_array(state, const char*, 2);
+        if (state->ppolicy_dns == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Could not allocate ppolicy_dns.\n");
+            tevent_req_error(req, ERR_ACCESS_DENIED);
+            return;
+        }
+
+        state->ppolicy_dns[0] = ppolicy_dn;
+        state->ppolicy_dns[1] = NULL;
+
+    } else {
+        /* try to determine default value */
+        DEBUG(SSSDBG_CONF_SETTINGS,
+              "ldap_pwdlockout_dn was not defined in configuration file.\n");
+
+        state->ppolicy_dns = get_default_ppolicy_dns(state, state->opts->sdom);
+        if (state->ppolicy_dns == NULL) {
+            tevent_req_error(req, ERR_ACCESS_DENIED);
+            return;
+        }
+    }
+
+    /* Connection to LDAP succeeded
+     * Send 'pwdLockout' request
+     */
+    ret = sdap_access_lock_get_lockout_step(req);
+    if (ret != EOK && ret != EAGAIN) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "sdap_access_lock_get_lockout_step failed: [%d][%s]\n",
+              ret, strerror(ret));
+        tevent_req_error(req, ERR_ACCESS_DENIED);
+        return;
+    }
+}
+
+static errno_t
+sdap_access_lock_get_lockout_step(struct tevent_req *req)
+{
+    const char *attrs[] = { SYSDB_LDAP_ACCESS_LOCKOUT, NULL };
+    struct sdap_access_lock_req_ctx *state;
+    struct tevent_req *subreq;
+    errno_t ret;
+
+    state = tevent_req_data(req, struct sdap_access_lock_req_ctx);
+
+    /* no more DNs to try */
+    if (state->ppolicy_dns[state->ppolicy_dns_index] == NULL) {
+        ret = EOK;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_CONF_SETTINGS,
+          "Trying to find out if ppolicy is enabled using the DN: %s\n",
+          state->ppolicy_dns[state->ppolicy_dns_index]);
+
+    subreq = sdap_get_generic_send(state,
+                                   state->ev,
+                                   state->opts,
+                                   sdap_id_op_handle(state->sdap_op),
+                                   state->ppolicy_dns[state->ppolicy_dns_index],
+                                   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);
+        ret = EIO;
+        goto done;
+    }
+
+    /* try next basedn */
+    state->ppolicy_dns_index++;
+    tevent_req_set_callback(subreq, sdap_access_lock_get_lockout_done, req);
+
+    ret = EAGAIN;
+
+done:
+    return ret;
+}
+
+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_lock_req_ctx *state;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_access_lock_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
+     */
+    /* Didn't find ppolicy attribute */
+    if (num_results < 1) {
+        /* Try using next $search_base */
+        ret = sdap_access_lock_get_lockout_step(req);
+        if (ret == EOK) {
+            /* No more search bases to try */
+            DEBUG(SSSDBG_CONF_SETTINGS,
+                  "[%s] was not found. Granting access.\n",
+                  SYSDB_LDAP_ACCESS_LOCKOUT);
+        } else {
+            if (ret != EAGAIN) {
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                      "sdap_access_lock_get_lockout_step failed: [%d][%s]\n",
+                      ret, strerror(ret));
+            }
+            goto done;
+        }
+    } 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 (ret != EOK) {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  "Error reading %s: [%s]\n", SYSDB_LDAP_ACCESS_LOCKOUT,
+                  strerror(ret));
+            ret = ERR_INTERNAL;
+            goto done;
+        }
+    }
+
+    if (pwdLockout) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Password policy is enabled on LDAP server.\n");
+
+        /* ppolicy is enabled => find out if account is locked */
+        ret = sdap_access_lock_step(req);
+        if (ret != EOK && ret != EAGAIN) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "sdap_access_lock_step failed: [%d][%s].\n",
+                  ret, strerror(ret));
+        }
+        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_LOCKOUT,
+                                         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);
+        }
+    }
+}
+
+errno_t sdap_access_lock_step(struct tevent_req *req)
+{
+    errno_t ret;
+    struct tevent_req *subreq;
+    struct sdap_access_lock_req_ctx *state;
+    const char *attrs[] = { SYSDB_LDAP_ACCESS_LOCKED_TIME, NULL };
+
+    state = tevent_req_data(req, struct sdap_access_lock_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);
+
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "sdap_access_lock_send failed.\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    tevent_req_set_callback(subreq, sdap_access_lock_step_done, req);
+    ret = EAGAIN;
+
+done:
+    return ret;
+}
+
+static void sdap_access_lock_step_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_lock_req_ctx *state;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_access_lock_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(state->cached_access);
+        } 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) {
+            /* We do *not* care about exact value of account locked time, we
+             * only *do* care if the value is equal to
+             * PERMANENTLY_LOCKED_ACCOUNT, which means that account is locked
+             * permanently.
+             */
+            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_LOCKOUT,
+                                     !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 errno_t sdap_access_lock_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
 static errno_t sdap_get_basedn_user_entry(struct ldb_message *user_entry,
                                           const char *username,
                                           const char **_basedn)
diff --git a/src/providers/ldap/sdap_access.h b/src/providers/ldap/sdap_access.h
index 30097e21f59abe19fc14d052edb0a6343c67f892..f085e619961198b887d65ed5ee0bc5cdd90d1b20 100644
--- a/src/providers/ldap/sdap_access.h
+++ b/src/providers/ldap/sdap_access.h
@@ -28,12 +28,20 @@
 #include "providers/dp_backend.h"
 #include "providers/ldap/ldap_common.h"
 
+/* Attributes in sysdb, used for caching last values of lockout or filter
+ * access control checks.
+ */
 #define SYSDB_LDAP_ACCESS_FILTER "ldap_access_filter_allow"
+#define SYSDB_LDAP_ACCESS_CACHED_LOCKOUT "ldap_access_lockout_allow"
+/* names of ppolicy attributes */
+#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 "lockout"
 
 #define LDAP_ACCOUNT_EXPIRE_SHADOW "shadow"
 #define LDAP_ACCOUNT_EXPIRE_AD "ad"
@@ -48,6 +56,7 @@ enum ldap_access_rule {
     LDAP_ACCESS_EXPIRE,
     LDAP_ACCESS_SERVICE,
     LDAP_ACCESS_HOST,
+    LDAP_ACCESS_LOCKOUT,
     LDAP_ACCESS_LAST
 };
 
-- 
1.9.3

>From ea7c70ac48cc2a2c34c988afcf4e23761061fcb6 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 'lockout' and 'ldap_pwdlockout_dn'

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

diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index c808033a1237a0dd9544f6bc7cb77666264889a6..ecbf2f54c189ab91e797c2ddf05b209cab43c6c3 100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -1907,6 +1907,13 @@ ldap_access_filter = (employeeType=admin)
                             <emphasis>filter</emphasis>: use ldap_access_filter
                         </para>
                         <para>
+                            <emphasis>lockout</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_pwdlockout_dn.
+                        </para>
+                        <para>
                             <emphasis>expire</emphasis>: use
                             ldap_account_expire_policy
                         </para>
@@ -1930,6 +1937,26 @@ ldap_access_filter = (employeeType=admin)
                 </varlistentry>
 
                 <varlistentry>
+                    <term>ldap_pwdlockout_dn (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>
+                        <para>
+                          Default: cn=ppolicy,ou=policies,$ldap_search_base
+                        </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