On 08/07/2014 02:22 PM, Pavel Březina wrote:
On 08/06/2014 06:36 PM, Pavel Reichl wrote:
On 08/04/2014 02:37 PM, Pavel Březina wrote:
On 08/01/2014 07:48 PM, Pavel Reichl wrote:
Hello,
please see attached patches. Every patch is documented in its commit
message.
Generally speaking the first 6 patches are preparation for patch #8.
Thanks,
Pavel Reichl
PS: I also attached output of my testing to make it more obvious
how the
patches are supposed to work.
------------------------------------------------------------------------
# john, people, example.com
dn: uid=john,ou=people,dc=example,dc=com
pwdAccountLockedTime: 000001010000Z
# max, people, example.com
dn: uid=max,ou=people,dc=example,dc=com
# dick, people, example.com
dn: uid=dick,ou=people,dc=example,dc=com
pwdAccountLockedTime: 20140801115742Z
--------------------------------------------------------------------------
$ ssh -l john@openldap `hostname`
Connection closed by UNKNOWN
$ ssh -l max@openldap `hostname`
Last login: Fri Aug 1 15:16:21 2014 from sssd.dev.work
$ ssh -l dick@openldap `hostname`
Last login: Fri Aug 1 12:57:33 2014
Patch #1:
Tentative ack, although you don't have to use tmp_ctx in
sdap_save_user_cache_bool(). You can just allocate attrs on NULL since
it is the only talloc context you use there.
Patch #2:
Nack. I see it is safe here but please avoid initializing ret to EOK.
Set it to EOK at the end of the function instead.
Patch #3:
Ack.
Patch #4:
Typo in commit message "Also remove *sdap_access_filter_recv()* as it
is replaced by *sdap_access_filter_recv()*."
But it's nack from me anyway.
Tevent naming convention seems to be broken all around this code and
this patch actually makes it a little bit better. But you shouldn't
use function named "sdap_access_recv" inside "sdap_access_done".
I see that sdap_access_send can invoke two different requests
depending on the configuration. Ideally both should have custom _recv
and you should use some kind of switch to determine which one should
be called.
I would be willing to tentatively ack a patch with let's say
"sdap_access_check_recv" or "sdap_access_subreq_recv" or similar used
in sdap_access_done.
Patch #5:
Ack.
Patch #6:
Ack.
Patch #7:
Ack. But please swap this ticket with #8. The feature should be first
implemented then documented.
Patch #8:
I believe the following comment is copy & paste? It shouldn't say
filter request :)
+ /* Connection to LDAP succeeded
+ * Send filter request
+ */
+ if (strcasecmp(pwdAccountLockedTime,
+ PERMANENTLY_LOCKED_ACCOUNT) == 0) {
+ locked = true;
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Account of: %s is beeing blocked by password
policy, "
+ "but value: [%s] value is ignored by SSSD.\n",
+ state->username, pwdAccountLockedTime);
+ }
Why do we ignore the value? Isn't the value a time? Shouldn't we check
if lock_time < machine_time?
Yes, it's time, but we care only if its the special value
'000001010000Z' otherwise we grant the access.
Also the ticket says that this policy is only enabled if pwdLockout is
true. Is it possible to acquire the attribute if pwdLockout is false?
I see you don't check it.
Yes it's possible to acquire, I have updated the patches.
Those are questions that should be answered in the code if they are
not bugs.
I haven't test the code yet - will it work also with 389ds or do you
have an OpenLDAP server I could use handy?
I'll try to grant you access to my VM as I find configuring ppolicy as
very painful experience.
_______________________________________________
sssd-devel mailing list
[email protected]
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I hope I have addressed all you concerns and didn't produce many new
bugs. :-)
Patch 1: ACK
Patch 2: ACK
Patch 3: ACK
Patch 4: ACK
Patch 5: ACK
Patch 6:
+ /* Used for both filter and locked use-cases. */
nitpick: use case doesn't have a hyphen inside
But more importantly, you changed a sysdb attribute name here. It will
require sysdb update to convert the old attribute name to the new one.
Maybe it would be better to keep the existing attribute and also
create a new one for lock? Opinions?
Patch 7:
The option is suppose to configure dn of an object that contains
pwdLockout attribute so I believe it should not be named
ldap_ppolicy_search_base - you do not treat it as a search base
anyway, the usage is different from other similar options.
I would suggest ldap_pwdlockout_dn?
If I understand it [1] correctly the attribute may be defined in any
object of class pwdPolicy and dn of the policy is configured in
OpenLDAP configuration. Different users may have different policy
configured but we don't have to deal with this now IMHO so the current
approach is fine.
Patch 8:
sdap_access_lock_send() initializing ret to an error is certainly
better then EOK but please, try to avoid it unless you take some
advantage from it. It is completely unused here. We want to let gcc to
catch cases where 'ret' remains uninitialized.
Tevent style:
- please, keep _send, _done and _recv function together (i.e. move
lock_recv after lock_done)
- _send function should create a new tevent_req with a new state, so
either discard or rename sdap_access_locked_time_send
Patch 9:
LGTM besides the option name. Can a native speaker check it please?
In general, please add more comments (mostly about the questions
asked) around patches #7 and #8 (either in the code directly or in the
commit message). I think this code contains a lot of premisses that
are not known to us and need to be understood so we can effectively
maintain the code.
[1] http://www.zytrax.com/books/ldap/ch6/ppolicy.html
_______________________________________________
sssd-devel mailing list
[email protected]
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Thanks Pavel,
please see updated patches.
Stephen,
I took the liberty of CCing you because I was wondering if you would
mind to review a man page change (patch #9).
Regards,
Pavel Reichl
>From d30c2d26a132b07f438b44491a8d6059e21cbedf Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 09:15:59 +0100
Subject: [PATCH 1/9] SDAP: split sdap_access_filter_get_access_done
As a preparation for ticket #2364 separate code for storing user bool
values into sysdb to a new function sdap_save_user_cache_bool().
---
src/providers/ldap/sdap_access.c | 59 ++++++++++++++++++++++++++--------------
1 file changed, 39 insertions(+), 20 deletions(-)
diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 89d37e52fa27b14be6bf702ebf0bc18b3fe59da6..0702c565a466ba067350595233f9746443d0864d 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -40,6 +40,11 @@
#include "providers/data_provider.h"
#include "providers/dp_backend.h"
+static errno_t sdap_save_user_cache_bool(struct sss_domain_info *domain,
+ const char *username,
+ const char *attr_name,
+ bool value);
+
static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct be_ctx *be_ctx,
@@ -856,7 +861,6 @@ static void sdap_access_filter_get_access_done(struct tevent_req *subreq)
int ret, tret, dp_error;
size_t num_results;
bool found = false;
- struct sysdb_attrs *attrs;
struct sysdb_attrs **results;
struct tevent_req *req =
tevent_req_callback_data(subreq, struct tevent_req);
@@ -935,25 +939,8 @@ static void sdap_access_filter_get_access_done(struct tevent_req *subreq)
ret = ERR_ACCESS_DENIED;
}
- attrs = sysdb_new_attrs(state);
- if (attrs == NULL) {
- ret = ENOMEM;
- DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up attrs\n");
- goto done;
- }
-
- tret = sysdb_attrs_add_bool(attrs, SYSDB_LDAP_ACCESS_FILTER,
- ret == EOK ? true : false);
- if (tret != EOK) {
- /* Failing to save to the cache is non-fatal.
- * Just return the result.
- */
- DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up attrs\n");
- goto done;
- }
-
- tret = sysdb_set_user_attr(state->domain, state->username, attrs,
- SYSDB_MOD_REP);
+ tret = sdap_save_user_cache_bool(state->domain, state->username,
+ SYSDB_LDAP_ACCESS_FILTER, found);
if (tret != EOK) {
/* Failing to save to the cache is non-fatal.
* Just return the result.
@@ -1060,6 +1047,38 @@ static errno_t sdap_access_service(struct pam_data *pd,
return ret;
}
+static errno_t sdap_save_user_cache_bool(struct sss_domain_info *domain,
+ const char *username,
+ const char *attr_name,
+ bool value)
+{
+ errno_t ret;
+ struct sysdb_attrs *attrs;
+
+ attrs = sysdb_new_attrs(NULL);
+ if (attrs == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up attrs\n");
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_bool(attrs, attr_name, value);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up attrs\n");
+ goto done;
+ }
+
+ ret = sysdb_set_user_attr(domain, username, attrs, SYSDB_MOD_REP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set user access attribute\n");
+ goto done;
+ }
+
+done:
+ talloc_free(attrs);
+ return ret;
+}
+
static errno_t sdap_access_host(struct ldb_message *user_entry)
{
errno_t ret;
--
1.9.3
>From 96bb7442a9b9fc3f21e772e565fcf758b55c2dc9 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 11:00:48 +0100
Subject: [PATCH 2/9] SDAP: refactor sdap_access_filter_send
As preparation for ticket #2364 separate code for parsing user basedn
to a new function sdap_get_basedn_user_entry().
---
src/providers/ldap/sdap_access.c | 52 ++++++++++++++++++++++++++++------------
1 file changed, 37 insertions(+), 15 deletions(-)
diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 0702c565a466ba067350595233f9746443d0864d..ddf623616f662d92fd751ff7952f08527c60be45 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -45,6 +45,11 @@ static errno_t sdap_save_user_cache_bool(struct sss_domain_info *domain,
const char *attr_name,
bool value);
+static errno_t sdap_get_basedn_user_entry(TALLOC_CTX *mem_ctx,
+ struct ldb_message *user_entry,
+ const char *username,
+ char **_basedn);
+
static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct be_ctx *be_ctx,
@@ -666,7 +671,6 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
{
struct sdap_access_filter_req_ctx *state;
struct tevent_req *req;
- const char *basedn;
char *clean_username;
errno_t ret = ERR_INTERNAL;
char *name;
@@ -704,20 +708,9 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
goto done;
}
- /* Perform online operation */
- basedn = ldb_msg_find_attr_as_string(user_entry, SYSDB_ORIG_DN, NULL);
- if (basedn == NULL) {
- DEBUG(SSSDBG_CRIT_FAILURE,"Could not find originalDN for user [%s]\n",
- state->username);
- ret = EINVAL;
- goto done;
- }
-
- state->basedn = talloc_strdup(state, basedn);
- if (state->basedn == NULL) {
- DEBUG(SSSDBG_CRIT_FAILURE,
- "Could not allocate memory for originalDN\n");
- ret = ENOMEM;
+ ret = sdap_get_basedn_user_entry(state, user_entry, state->username,
+ &state->basedn);
+ if (ret != EOK) {
goto done;
}
@@ -1141,3 +1134,32 @@ static errno_t sdap_access_host(struct ldb_message *user_entry)
return ret;
}
+
+static errno_t sdap_get_basedn_user_entry(TALLOC_CTX *mem_ctx,
+ struct ldb_message *user_entry,
+ const char *username,
+ char **_basedn)
+{
+ const char *basedn;
+ errno_t ret;
+
+ basedn = ldb_msg_find_attr_as_string(user_entry, SYSDB_ORIG_DN, NULL);
+ if (basedn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,"Could not find originalDN for user [%s]\n",
+ username);
+ ret = EINVAL;
+ goto done;
+ }
+
+ *_basedn = talloc_strdup(mem_ctx, basedn);
+ if (*_basedn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for originalDN\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = EOK;
+
+done:
+ return ret;
+}
--
1.9.3
>From 49bd5681a46f27472c6dd637762a0db4b85babd7 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 11:13:20 +0100
Subject: [PATCH 3/9] SDAP: nitpicks in sdap_access_filter_get_access_done
Fixed typo and replaced duplicated string by macro definition.
---
src/providers/ldap/sdap_access.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index ddf623616f662d92fd751ff7952f08527c60be45..e54f4e0d5d13d72e8cb38bd1c827ea2f52e7dcef 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -40,6 +40,8 @@
#include "providers/data_provider.h"
#include "providers/dp_backend.h"
+#define MALFORMED_FILTER "Malformed access control filter [%s]\n"
+
static errno_t sdap_save_user_cache_bool(struct sss_domain_info *domain,
const char *username,
const char *attr_name,
@@ -875,10 +877,8 @@ static void sdap_access_filter_get_access_done(struct tevent_req *subreq)
} else if (dp_error == DP_ERR_OFFLINE) {
ret = sdap_access_filter_decide_offline(req);
} else if (ret == ERR_INVALID_FILTER) {
- sss_log(SSS_LOG_ERR,
- "Malformed access control filter [%s]\n", state->filter);
- DEBUG(SSSDBG_CRIT_FAILURE,
- "Malformed access control filter [%s]\n", state->filter);
+ sss_log(SSS_LOG_ERR, MALFORMED_FILTER, state->filter);
+ DEBUG(SSSDBG_CRIT_FAILURE, MALFORMED_FILTER, state->filter);
ret = ERR_ACCESS_DENIED;
} else {
DEBUG(SSSDBG_CRIT_FAILURE,
@@ -918,9 +918,7 @@ static void sdap_access_filter_get_access_done(struct tevent_req *subreq)
}
if (found) {
- /* Save "allow" to the cache for future offline
- :q* access checks.
- */
+ /* Save "allow" to the cache for future offline access checks. */
DEBUG(SSSDBG_TRACE_FUNC, "Access granted by online lookup\n");
ret = EOK;
}
--
1.9.3
>From e510dbbb83b75982ce755cc27d8754875832aa3a Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 12:22:21 +0100
Subject: [PATCH 4/9] SDAP: refactor sdap_access_filter_done
As preparation for ticket #2364 move code from sdap_access_filter_done()
into sdap_access_done() to make its reuse possible and thus avoid code
duplication.
---
src/providers/ldap/sdap_access.c | 41 ++++++++++++++++++++++++++++++++++++----
1 file changed, 37 insertions(+), 4 deletions(-)
diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index e54f4e0d5d13d72e8cb38bd1c827ea2f52e7dcef..68633643f7430c26885a78d2903e7a4dd636e1d5 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -60,8 +60,11 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
struct sdap_id_conn_ctx *conn,
const char *username,
struct ldb_message *user_entry);
+
static errno_t sdap_access_filter_recv(struct tevent_req *req);
+static errno_t sdap_access_subreq_recv(struct tevent_req *req);
+
static errno_t sdap_account_expired(struct sdap_access_ctx *access_ctx,
struct pam_data *pd,
struct ldb_message *user_entry);
@@ -219,14 +222,14 @@ static errno_t check_next_rule(struct sdap_access_req_ctx *state,
return ret;
}
-static void sdap_access_filter_done(struct tevent_req *subreq)
+static void sdap_access_done(struct tevent_req *subreq)
{
errno_t ret;
struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
struct sdap_access_req_ctx *state =
tevent_req_data(req, struct sdap_access_req_ctx);
- ret = sdap_access_filter_recv(subreq);
+ ret = sdap_access_subreq_recv(subreq);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Error retrieving access check result.\n");
@@ -249,6 +252,11 @@ static void sdap_access_filter_done(struct tevent_req *subreq)
}
}
+static void sdap_access_filter_done(struct tevent_req *subreq)
+{
+ sdap_access_done(subreq);
+}
+
errno_t sdap_access_recv(struct tevent_req *req)
{
TEVENT_REQ_RETURN_ON_ERROR(req);
@@ -256,7 +264,6 @@ errno_t sdap_access_recv(struct tevent_req *req)
return EOK;
}
-
#define SHADOW_EXPIRE_MSG "Account expired according to shadow attributes"
static errno_t sdap_account_expired_shadow(struct pam_data *pd,
@@ -644,6 +651,10 @@ static errno_t sdap_account_expired(struct sdap_access_ctx *access_ctx,
return ret;
}
+enum sdap_access_control_type {
+ SDAP_ACCESS_CONTROL_FILTER,
+};
+
struct sdap_access_filter_req_ctx {
const char *username;
const char *filter;
@@ -656,6 +667,7 @@ struct sdap_access_filter_req_ctx {
struct sss_domain_info *domain;
bool cached_access;
char *basedn;
+ enum sdap_access_control_type ac_type;
};
static errno_t sdap_access_filter_decide_offline(struct tevent_req *req);
@@ -696,6 +708,7 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
state->ev = ev;
state->access_ctx = access_ctx;
state->domain = domain;
+ state->ac_type = SDAP_ACCESS_CONTROL_FILTER;
DEBUG(SSSDBG_TRACE_FUNC,
"Performing access filter check for user [%s]\n", username);
@@ -772,6 +785,27 @@ done:
return req;
}
+static errno_t sdap_access_subreq_recv(struct tevent_req *req)
+{
+ struct sdap_access_filter_req_ctx *state;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct sdap_access_filter_req_ctx);
+
+ switch(state->ac_type) {
+ case SDAP_ACCESS_CONTROL_FILTER:
+ ret = sdap_access_filter_recv(req);
+ break;
+ default:
+ ret = EINVAL;
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unknown access control type: %d.",
+ state->ac_type);
+ break;
+ }
+
+ return ret;
+}
+
static errno_t sdap_access_filter_decide_offline(struct tevent_req *req)
{
struct sdap_access_filter_req_ctx *state =
@@ -956,7 +990,6 @@ static errno_t sdap_access_filter_recv(struct tevent_req *req)
return EOK;
}
-
#define AUTHR_SRV_MISSING_MSG "Authorized service attribute missing, " \
"access denied"
#define AUTHR_SRV_DENY_MSG "Access denied by authorized service attribute"
--
1.9.3
>From 1477c50ead6f75f7e1e6c9b4f9fb418ab15cdd24 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 16:05:13 +0100
Subject: [PATCH 5/9] SDAP: don't log error on access denied
Don't log error if access is denied in function sdap_access_done().
---
src/providers/ldap/sdap_access.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 68633643f7430c26885a78d2903e7a4dd636e1d5..82ba2bb59466eca07727adc730dfeeab6e118e84 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -232,7 +232,12 @@ static void sdap_access_done(struct tevent_req *subreq)
ret = sdap_access_subreq_recv(subreq);
talloc_zfree(subreq);
if (ret != EOK) {
- DEBUG(SSSDBG_CRIT_FAILURE, "Error retrieving access check result.\n");
+ if (ret == ERR_ACCESS_DENIED) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Access was denied.\n");
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Error retrieving access check result.\n");
+ }
tevent_req_error(req, ret);
return;
}
--
1.9.3
>From cb27bec69a7b45b30241e662abdb6d1d03723ad3 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <[email protected]>
Date: Fri, 1 Aug 2014 16:13:08 +0100
Subject: [PATCH 6/9] SDAP: use versatile name for caching AC results
Prepare code for other access control result values to store.
---
src/providers/ldap/sdap_access.c | 30 +++++++++++++++++++++---------
1 file changed, 21 insertions(+), 9 deletions(-)
diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 82ba2bb59466eca07727adc730dfeeab6e118e84..75672d8c5a79f3bd40829b2f8d058cb4c07c01b6 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -670,8 +670,10 @@ struct sdap_access_filter_req_ctx {
struct sdap_id_op *sdap_op;
struct sysdb_handle *handle;
struct sss_domain_info *domain;
- bool cached_access;
char *basedn;
+ /* cached results of access control checks */
+ bool cached_access_filter;
+
enum sdap_access_control_type ac_type;
};
@@ -718,9 +720,9 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
DEBUG(SSSDBG_TRACE_FUNC,
"Performing access filter check for user [%s]\n", username);
- state->cached_access = ldb_msg_find_attr_as_bool(user_entry,
- SYSDB_LDAP_ACCESS_FILTER,
- false);
+ state->cached_access_filter = 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 */
@@ -811,12 +813,13 @@ static errno_t sdap_access_subreq_recv(struct tevent_req *req)
return ret;
}
-static errno_t sdap_access_filter_decide_offline(struct tevent_req *req)
+/* Helper function,
+ * cached_ac => access granted
+ * !cached_ac => access denied
+ */
+static errno_t sdap_access_decide_offline_impl(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 {
@@ -825,6 +828,15 @@ static errno_t sdap_access_filter_decide_offline(struct tevent_req *req)
}
}
+static errno_t sdap_access_filter_decide_offline(struct tevent_req *req)
+{
+ struct sdap_access_filter_req_ctx *state;
+
+ state = tevent_req_data(req, struct sdap_access_filter_req_ctx);
+
+ return sdap_access_decide_offline_impl(state->cached_access_filter);
+}
+
static int sdap_access_filter_retry(struct tevent_req *req)
{
struct sdap_access_filter_req_ctx *state =
--
1.9.3
>From 97e6062da4125d5287d839d212a63aa9e48c7f2b 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 c3482b3a4df6d0fd39b2fa93c19ee5316dde0d94..4c835d744e90928d6042c8f623ba8101cdefbcf1 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -323,6 +323,7 @@ option_strings = {
'ldap_use_tokengroups' : _('Whether to use Token-Groups'),
'ldap_min_id' : _('Set lower boundary for allowed IDs from the LDAP server'),
'ldap_max_id' : _('Set upper boundary for allowed IDs from the LDAP server'),
+ 'ldap_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 93d869c67f96afbea2e39d061daf578602b2f9a9..b9f01bc840da953957943c09c406c433cc065007 100644
--- a/src/config/etc/sssd.api.d/sssd-ad.conf
+++ b/src/config/etc/sssd.api.d/sssd-ad.conf
@@ -112,6 +112,7 @@ ldap_groups_use_matching_rule_in_chain = bool, None, false
ldap_initgroups_use_matching_rule_in_chain = bool, None, false
ldap_use_tokengroups = bool, None, false
ldap_rfc2307_fallback_to_local_users = bool, None, false
+ldap_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 f3b9cb0637bd3f827dd198acb74c933b4f953a81..92d8aa082e35b5f56c7d2452a8e32f0e98fc8b34 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 a4802a1ea190fbdf57ec13d031bb74ad46e08a47..29276bfd74b9fcc67042a138006959896c34fbae 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 a3ade012a7c1efb705a011697af36c28e39b1a9b..a82f7a9e08b41b300970b4f8b90f8850fb11c80c 100644
--- a/src/providers/ad/ad_opts.h
+++ b/src/providers/ad/ad_opts.h
@@ -134,6 +134,7 @@ struct dp_option ad_def_ldap_opts[] = {
{ "ldap_disable_range_retrieval", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
{ "ldap_min_id", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
{ "ldap_max_id", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
+ { "ldap_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 c7197beb1ecae2935255c46559287ad1186f760e..1c14cfdcbf7a9c343f57121e891d0e6da6a3ea10 100644
--- a/src/providers/ipa/ipa_opts.h
+++ b/src/providers/ipa/ipa_opts.h
@@ -155,6 +155,7 @@ struct dp_option ipa_def_ldap_opts[] = {
{ "ldap_disable_range_retrieval", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
{ "ldap_min_id", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
{ "ldap_max_id", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
+ { "ldap_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 13a84ec1e979864e06d5690ec29967548b21aac1..4d5b71f43d78553c8ddd05d88e202e62ed7c9d52 100644
--- a/src/providers/ldap/ldap_opts.h
+++ b/src/providers/ldap/ldap_opts.h
@@ -121,6 +121,7 @@ struct dp_option default_basic_opts[] = {
{ "ldap_disable_range_retrieval", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
{ "ldap_min_id", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
{ "ldap_max_id", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
+ { "ldap_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 e5d9729014265c8ad2d72f0e5c4d321b9621cbc9..4c44487fa6fa02295921236694b3561514308816 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -232,6 +232,7 @@ enum sdap_basic_opt {
SDAP_DISABLE_RANGE_RETRIEVAL,
SDAP_MIN_ID,
SDAP_MAX_ID,
+ SDAP_PWDLOCKOUT_DN,
SDAP_OPTS_BASIC /* opts counter */
};
--
1.9.3
>From 17955dd41db26cd979d6417ad8802682822c43e8 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".
------------------------------------------------------------------------
Resolves:
https://fedorahosted.org/sssd/ticket/2364
---
src/providers/ldap/ldap_init.c | 2 +
src/providers/ldap/sdap_access.c | 458 +++++++++++++++++++++++++++++++++++++++
src/providers/ldap/sdap_access.h | 9 +
3 files changed, 469 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 75672d8c5a79f3bd40829b2f8d058cb4c07c01b6..77975a545f7ac7bcfbcfedf760ed1049ef44267e 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -40,6 +40,7 @@
#include "providers/data_provider.h"
#include "providers/dp_backend.h"
+#define PERMANENTLY_LOCKED_ACCOUNT "000001010000Z"
#define MALFORMED_FILTER "Malformed access control filter [%s]\n"
static errno_t sdap_save_user_cache_bool(struct sss_domain_info *domain,
@@ -52,6 +53,18 @@ static errno_t sdap_get_basedn_user_entry(TALLOC_CTX *mem_ctx,
const char *username,
char **_basedn);
+static struct tevent_req *
+sdap_access_lock_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct be_ctx *be_ctx,
+ struct sss_domain_info *domain,
+ struct sdap_access_ctx *access_ctx,
+ struct sdap_id_conn_ctx *conn,
+ const char *username,
+ struct ldb_message *user_entry);
+
+static void sdap_access_lock_done(struct tevent_req *subreq);
+
static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct be_ctx *be_ctx,
@@ -63,6 +76,8 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
static errno_t sdap_access_filter_recv(struct tevent_req *req);
+static errno_t sdap_access_lock_recv(struct tevent_req *req);
+
static errno_t sdap_access_subreq_recv(struct tevent_req *req);
static errno_t sdap_account_expired(struct sdap_access_ctx *access_ctx,
@@ -182,6 +197,24 @@ static errno_t check_next_rule(struct sdap_access_req_ctx *state,
/* we are done with no errors */
return EOK;
+ case LDAP_ACCESS_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;
+ }
+
+ tevent_req_set_callback(subreq, sdap_access_lock_done, req);
+ return EAGAIN;
+ break;
+
case LDAP_ACCESS_FILTER:
subreq = sdap_access_filter_send(state, state->ev, state->be_ctx,
state->domain,
@@ -658,6 +691,7 @@ static errno_t sdap_account_expired(struct sdap_access_ctx *access_ctx,
enum sdap_access_control_type {
SDAP_ACCESS_CONTROL_FILTER,
+ SDAP_ACCESS_CONTROL_LOCK,
};
struct sdap_access_filter_req_ctx {
@@ -673,14 +707,18 @@ struct sdap_access_filter_req_ctx {
char *basedn;
/* cached results of access control checks */
bool cached_access_filter;
+ bool cached_access_lockout;
enum sdap_access_control_type ac_type;
};
+static errno_t sdap_access_lockout_decide_offline(struct tevent_req *req);
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_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,
@@ -803,6 +841,9 @@ static errno_t sdap_access_subreq_recv(struct tevent_req *req)
case SDAP_ACCESS_CONTROL_FILTER:
ret = sdap_access_filter_recv(req);
break;
+ case SDAP_ACCESS_CONTROL_LOCK:
+ ret = sdap_access_lock_recv(req);
+ break;
default:
ret = EINVAL;
DEBUG(SSSDBG_MINOR_FAILURE, "Unknown access control type: %d.",
@@ -828,6 +869,15 @@ static errno_t sdap_access_decide_offline_impl(bool cached_ac)
}
}
+static errno_t sdap_access_lockout_decide_offline(struct tevent_req *req)
+{
+ struct sdap_access_filter_req_ctx *state;
+
+ state = tevent_req_data(req, struct sdap_access_filter_req_ctx);
+
+ return sdap_access_decide_offline_impl(state->cached_access_lockout);
+}
+
static errno_t sdap_access_filter_decide_offline(struct tevent_req *req)
{
struct sdap_access_filter_req_ctx *state;
@@ -1183,6 +1233,414 @@ static errno_t sdap_access_host(struct ldb_message *user_entry)
return ret;
}
+static void sdap_access_lock_get_lockout_done(struct tevent_req *subreq);
+static int sdap_access_lock_retry(struct tevent_req *req);
+static struct tevent_req*
+sdap_access_locked_time_query(struct tevent_req *req);
+static void sdap_access_locked_time_query_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sdap_access_lock_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct be_ctx *be_ctx,
+ struct sss_domain_info *domain,
+ struct sdap_access_ctx *access_ctx,
+ struct sdap_id_conn_ctx *conn,
+ const char *username,
+ struct ldb_message *user_entry)
+{
+ struct sdap_access_filter_req_ctx *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx,
+ &state, struct sdap_access_filter_req_ctx);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->filter = NULL;
+ state->username = username;
+ state->opts = access_ctx->id_ctx->opts;
+ state->conn = conn;
+ state->ev = ev;
+ state->access_ctx = access_ctx;
+ state->domain = domain;
+ state->ac_type = SDAP_ACCESS_CONTROL_LOCK;
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Performing access lock check for user [%s]\n", username);
+
+ state->cached_access_lockout = 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_lockout_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 void sdap_access_lock_done(struct tevent_req *subreq)
+{
+ sdap_access_done(subreq);
+}
+
+static errno_t sdap_access_lock_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+static int sdap_access_lock_retry(struct tevent_req *req)
+{
+ struct sdap_access_filter_req_ctx *state;
+ struct tevent_req *subreq;
+ int ret;
+
+ state = tevent_req_data(req, struct sdap_access_filter_req_ctx);
+ subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
+ if (!subreq) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sdap_id_op_connect_send failed: %d (%s)\n", ret, strerror(ret));
+ return ret;
+ }
+
+ tevent_req_set_callback(subreq, sdap_access_lock_connect_done, req);
+ return EOK;
+}
+
+static void sdap_access_lock_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct sdap_access_filter_req_ctx *state;
+ int ret, dp_error;
+ const char *attrs[] = { SYSDB_LDAP_ACCESS_LOCKOUT, NULL };
+ const char *ppolicy_dn;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sdap_access_filter_req_ctx);
+
+ ret = sdap_id_op_connect_recv(subreq, &dp_error);
+ talloc_zfree(subreq);
+
+ if (ret != EOK) {
+ if (dp_error == DP_ERR_OFFLINE) {
+ ret = sdap_access_lockout_decide_offline(req);
+ if (ret == EOK) {
+ tevent_req_done(req);
+ return;
+ }
+ }
+
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ppolicy_dn = dp_opt_get_string(state->opts->basic,
+ SDAP_PWDLOCKOUT_DN);
+ /* We can't find out if ppolicy is enabled, so we deny access */
+ if (ppolicy_dn == NULL) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "ldap_pwdlockout_dn was not defined in configuration file."
+ " Access is denied!\n");
+ tevent_req_error(req, ERR_ACCESS_DENIED);
+ return;
+ }
+
+ /* Connection to LDAP succeeded
+ * Send 'pwdLockout' request
+ */
+ subreq = sdap_get_generic_send(state,
+ state->ev,
+ state->opts,
+ sdap_id_op_handle(state->sdap_op),
+ ppolicy_dn,
+ LDAP_SCOPE_BASE,
+ NULL, attrs,
+ NULL, 0,
+ dp_opt_get_int(state->opts->basic,
+ SDAP_SEARCH_TIMEOUT),
+ false);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not start LDAP communication\n");
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ tevent_req_set_callback(subreq, sdap_access_lock_get_lockout_done, req);
+}
+
+static void sdap_access_lock_get_lockout_done(struct tevent_req *subreq)
+{
+ int ret, tret, dp_error;
+ size_t num_results;
+ bool pwdLockout = false;
+ struct sysdb_attrs **results;
+ struct tevent_req *req;
+ struct sdap_access_filter_req_ctx *state;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sdap_access_filter_req_ctx);
+
+ ret = sdap_get_generic_recv(subreq, state, &num_results, &results);
+ talloc_zfree(subreq);
+
+ /* Check the number of responses we got
+ * If it's exactly 1, we passed the check
+ * If it's < 1, we failed the check
+ * Anything else is an error
+ */
+ if (num_results < 1) {
+ // todo doplnit base
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "[%s] was not found."
+ "Granting access.\n", SYSDB_LDAP_ACCESS_LOCKOUT);
+ } else if (results == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "num_results > 0, but results is NULL\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ } else if (num_results > 1) {
+ /* It should not be possible to get more than one reply
+ * here, since we're doing a base-scoped search
+ */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Received multiple replies\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ } else { /* Ok, we got a single reply */
+ ret = sysdb_attrs_get_bool(results[0], SYSDB_LDAP_ACCESS_LOCKOUT,
+ &pwdLockout);
+ }
+
+ if (pwdLockout) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Password policy is enabled on LDAP server.\n");
+
+ /* ppolicy is enabled => findout if account is locked */
+ subreq = sdap_access_locked_time_query(req);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sdap_access_lock_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sdap_access_locked_time_query_done,
+ req);
+ ret = EAGAIN;
+ goto done;
+
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Password policy is disabled on LDAP server "
+ "- storing 'access granted' in sysdb.\n");
+ tret = sdap_save_user_cache_bool(state->domain, state->username,
+ SYSDB_LDAP_ACCESS_CACHED_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);
+ }
+ }
+}
+
+static struct tevent_req*
+sdap_access_locked_time_query(struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ struct sdap_access_filter_req_ctx *state;
+ const char *attrs[] = { SYSDB_LDAP_ACCESS_LOCKED_TIME, NULL };
+
+ state = tevent_req_data(req, struct sdap_access_filter_req_ctx);
+
+ subreq = sdap_get_generic_send(state,
+ state->ev,
+ state->opts,
+ sdap_id_op_handle(state->sdap_op),
+ state->basedn,
+ LDAP_SCOPE_BASE,
+ NULL, attrs,
+ NULL, 0,
+ dp_opt_get_int(state->opts->basic,
+ SDAP_SEARCH_TIMEOUT),
+ false);
+ return subreq;
+}
+
+static void sdap_access_locked_time_query_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_lockout_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) {
+ /* 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_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 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 14297ffaa600449eb0643d545528fa35ec1edb3d 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 | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index e8bcfd0d13cb39b7e60fc8f3aa4338b53d2921e2..b0cc48cd9e0c9108b37f4397f86be00a40424faf 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>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>
@@ -1937,6 +1944,23 @@ 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>
+ </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