[SSSD] [PATCH] Add new account expired rule to LDAP access provider
Sumit Bose
sbose at redhat.com
Thu Nov 25 13:25:22 UTC 2010
Hi,
these two patches aim to fix trac ticket #672. While the first patch
only makes a utility function public the second adds some new layers to
the LDAP access provider. I've tried to make the changes in a way to
make it easy to add new rules (#670) and new expire policies (#673,
#674, #690).
I would like to ask the reviewers to check if the new code is really as
flexible as I think and if it enough to evaluate the shadow expire
attribute here. TIA
bye,
Sumit
-------------- next part --------------
From 93be4c46f9ffaa2257598377f2750b06f44379be Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 23 Nov 2010 17:43:23 +0100
Subject: [PATCH 1/2] Make string_to_shadowpw_days() public
---
src/providers/ldap/ldap_auth.c | 33 ---------------------------------
src/providers/ldap/ldap_common.c | 34 ++++++++++++++++++++++++++++++++++
src/providers/ldap/ldap_common.h | 2 ++
3 files changed, 36 insertions(+), 33 deletions(-)
diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
index 57a549d..5a85fe9 100644
--- a/src/providers/ldap/ldap_auth.c
+++ b/src/providers/ldap/ldap_auth.c
@@ -232,39 +232,6 @@ static errno_t check_pwexpire_ldap(struct pam_data *pd,
return EOK;
}
-static errno_t string_to_shadowpw_days(const char *s, long *d)
-{
- long l;
- char *endptr;
-
- if (s == NULL || *s == '\0') {
- *d = -1;
- return EOK;
- }
-
- errno = 0;
- l = strtol(s, &endptr, 10);
- if (errno != 0) {
- DEBUG(1, ("strtol failed [%d][%s].\n", errno, strerror(errno)));
- return errno;
- }
-
- if (*endptr != '\0') {
- DEBUG(1, ("Input string [%s] is invalid.\n", s));
- return EINVAL;
- }
-
- if (*d < -1) {
- DEBUG(1, ("Input string contains not allowed negative value [%d].\n",
- *d));
- return EINVAL;
- }
-
- *d = l;
-
- return EOK;
-}
-
static errno_t find_password_expiration_attributes(TALLOC_CTX *mem_ctx,
const struct ldb_message *msg,
struct dp_option *opts,
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 5de3d55..14dae5f 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -739,3 +739,37 @@ done:
talloc_zfree(tmp_ctx);
return ret;
}
+
+errno_t string_to_shadowpw_days(const char *s, long *d)
+{
+ long l;
+ char *endptr;
+
+ if (s == NULL || *s == '\0') {
+ *d = -1;
+ return EOK;
+ }
+
+ errno = 0;
+ l = strtol(s, &endptr, 10);
+ if (errno != 0) {
+ DEBUG(1, ("strtol failed [%d][%s].\n", errno, strerror(errno)));
+ return errno;
+ }
+
+ if (*endptr != '\0') {
+ DEBUG(1, ("Input string [%s] is invalid.\n", s));
+ return EINVAL;
+ }
+
+ if (*d < -1) {
+ DEBUG(1, ("Input string contains not allowed negative value [%d].\n",
+ *d));
+ return EINVAL;
+ }
+
+ *d = l;
+
+ return EOK;
+}
+
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index 5c298d1..d24a983 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -143,4 +143,6 @@ int netgroup_get_recv(struct tevent_req *req, int *dp_error_out);
/* setup child logging */
int setup_child(struct sdap_id_ctx *ctx);
+
+errno_t string_to_shadowpw_days(const char *s, long *d);
#endif /* _LDAP_COMMON_H_ */
--
1.7.3.2
-------------- next part --------------
From 087fb87049eaaa7b6aeec4d772c4f46dbeb57277 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 22 Nov 2010 14:24:23 +0100
Subject: [PATCH 2/2] Add new account expired rule to LDAP access provider
Two new options are added to the LDAP access provider to allow a broader
range of access control rules to be evaluated.
'ldap_access_order' makes it possible to run more than one rule. To keep
compatibility with older versions the default is 'filter'. This patch
adds a new rule 'expire'.
'ldap_account_expire_policy' specifies which LDAP attribute should be
used to determine if an account is expired or not. Currently only
'shadow' is supported which evaluates the ldap_user_shadow_expire
attribute.
---
src/config/SSSDConfig.py | 2 +
src/config/etc/sssd.api.d/sssd-ldap.conf | 2 +
src/man/sssd-ldap.5.xml | 40 +++
src/providers/ipa/ipa_common.c | 4 +-
src/providers/ipa/ipa_common.h | 2 +-
src/providers/ldap/ldap_common.c | 4 +-
src/providers/ldap/ldap_init.c | 103 ++++++--
src/providers/ldap/sdap.h | 2 +
src/providers/ldap/sdap_access.c | 419 ++++++++++++++++++++++++++----
src/providers/ldap/sdap_access.h | 15 +-
10 files changed, 512 insertions(+), 81 deletions(-)
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index 7da1027..7136e76 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -161,6 +161,8 @@ option_strings = {
# [provider/ldap/access]
'ldap_access_filter' : _('LDAP filter to determine access privileges'),
+ 'ldap_account_expire_policy' : _('Which attributes shall be used to evaluate if an account is expired'),
+ 'ldap_access_order' : _('Which rules should be used to evaluate access control'),
# [provider/simple/access]
'simple_allow_users' : _('Comma separated list of allowed users'),
diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf
index 404f4d5..b7d2f9b 100644
--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
+++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
@@ -80,6 +80,8 @@ ldap_pwd_policy = str, None, false
[provider/ldap/access]
ldap_access_filter = str, None, false
+ldap_account_expire_policy = str, None, false
+ldap_access_order = str, None, false
[provider/ldap/chpass]
diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index a30b49d..d8cada3 100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -1021,6 +1021,46 @@ ldap_access_filter = memberOf=cn=allowedusers,ou=Groups,dc=example,dc=com
</varlistentry>
<varlistentry>
+ <term>ldap_account_expire_policy (string)</term>
+ <listitem>
+ <para>
+ With this option a client side evaluation of
+ access control attributes can be enabled.
+ </para>
+ <para>
+ Please note that it is always recommended to
+ use server side access control, i.e. the LDAP
+ server should deny the bind request with a
+ suitable error code even if the password is
+ correct.
+ </para>
+ <para>
+ Default: Empty
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>ldap_access_order (string)</term>
+ <listitem>
+ <para>
+ Comma separated list of access control options.
+ Allowed values are:
+ </para>
+ <para>
+ <emphasis>filter</emphasis>: use ldap_access_filter
+ </para>
+ <para>
+ <emphasis>expire</emphasis>: use
+ ldap_account_expire_policy
+ </para>
+ <para>
+ Default: filter
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>ldap_deref (string)</term>
<listitem>
<para>
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 439ada0..2efa815 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -77,7 +77,9 @@ struct dp_option ipa_def_ldap_opts[] = {
{ "ldap_access_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "ldap_netgroup_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "ldap_group_nesting_level", DP_OPT_NUMBER, { .number = 2 }, NULL_NUMBER },
- { "ldap_deref", DP_OPT_STRING, NULL_STRING, NULL_STRING }
+ { "ldap_deref", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_account_expire_policy", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_access_order", DP_OPT_STRING, NULL_STRING, NULL_STRING }
};
struct sdap_attr_map ipa_attr_map[] = {
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index ba8f85e..d457d7f 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -35,7 +35,7 @@ struct ipa_service {
/* the following defines are used to keep track of the options in the ldap
* module, so that if they change and ipa is not updated correspondingly
* this will trigger a runtime abort error */
-#define IPA_OPTS_BASIC_TEST 39
+#define IPA_OPTS_BASIC_TEST 41
/* the following define is used to keep track of the options in the krb5
* module, so that if they change and ipa is not updated correspondingly
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 14dae5f..eb0a20b 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -72,7 +72,9 @@ struct dp_option default_basic_opts[] = {
{ "ldap_access_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "ldap_netgroup_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "ldap_group_nesting_level", DP_OPT_NUMBER, { .number = 2 }, NULL_NUMBER },
- { "ldap_deref", DP_OPT_STRING, NULL_STRING, NULL_STRING }
+ { "ldap_deref", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_account_expire_policy", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_access_order", DP_OPT_STRING, { "filter" }, NULL_STRING }
};
struct sdap_attr_map generic_attr_map[] = {
diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
index b6176a9..70752da 100644
--- a/src/providers/ldap/ldap_init.c
+++ b/src/providers/ldap/ldap_init.c
@@ -225,6 +225,11 @@ int sssm_ldap_access_init(struct be_ctx *bectx,
int ret;
struct sdap_access_ctx *access_ctx;
const char *filter;
+ const char *order;
+ char **order_list;
+ int order_list_len;
+ size_t c;
+ const char *dummy;
access_ctx = talloc_zero(bectx, struct sdap_access_ctx);
if(access_ctx == NULL) {
@@ -238,32 +243,86 @@ int sssm_ldap_access_init(struct be_ctx *bectx,
goto done;
}
- filter = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic,
- SDAP_ACCESS_FILTER);
- if (filter == NULL) {
- /* It's okay if this is NULL. In that case we will simply act
- * like the 'deny' provider.
- */
- DEBUG(0, ("Warning: access_provider=ldap set, "
- "but no ldap_access_filter configured. "
- "All domain users will be denied access.\n"));
+ order = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic,
+ SDAP_ACCESS_ORDER);
+ if (order == NULL) {
+ DEBUG(1, ("ldap_access_order not given, using 'filter'.\n"));
+ order = "filter";
}
- else {
- if (filter[0] == '(') {
- /* This filter is wrapped in parentheses.
- * Pass it as-is to the openldap libraries.
- */
- access_ctx->filter = filter;
- }
- else {
- /* Add parentheses around the filter */
- access_ctx->filter = talloc_asprintf(access_ctx, "(%s)", filter);
- if (access_ctx->filter == NULL) {
- ret = ENOMEM;
- goto done;
+
+ ret = split_on_separator(access_ctx, order, ',', true, &order_list,
+ &order_list_len);
+ if (ret != EOK) {
+ DEBUG(1, ("split_on_separator failed.\n"));
+ goto done;
+ }
+
+ if (order_list_len -1 > LDAP_ACCESS_LAST) {
+ DEBUG(1, ("Currently only [%d] different access rules are supported.\n"));
+ ret = EINVAL;
+ goto done;
+ }
+
+ for (c = 0; order_list[c] != NULL; c++) {
+ if (strcasecmp(order_list[c], LDAP_ACCESS_FILTER_NAME) == 0) {
+ access_ctx->access_rule[c] = LDAP_ACCESS_FILTER;
+
+ filter = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic,
+ SDAP_ACCESS_FILTER);
+ if (filter == NULL) {
+ /* It's okay if this is NULL. In that case we will simply act
+ * like the 'deny' provider.
+ */
+ DEBUG(0, ("Warning: LDAP access rule 'filter' is set, "
+ "but no ldap_access_filter configured. "
+ "All domain users will be denied access.\n"));
}
+ else {
+ if (filter[0] == '(') {
+ /* This filter is wrapped in parentheses.
+ * Pass it as-is to the openldap libraries.
+ */
+ access_ctx->filter = filter;
+ }
+ else {
+ /* Add parentheses around the filter */
+ access_ctx->filter = talloc_asprintf(access_ctx, "(%s)", filter);
+ if (access_ctx->filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ } else if (strcasecmp(order_list[c], LDAP_ACCESS_EXPIRE_NAME) == 0) {
+ access_ctx->access_rule[c] = LDAP_ACCESS_EXPIRE;
+
+ dummy = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic,
+ SDAP_ACCOUNT_EXPIRE_POLICY);
+ if (dummy == NULL) {
+ DEBUG(0, ("Warning: LDAP access rule 'expire' is set, "
+ "but no ldap_account_expire_policy configured. "
+ "All domain users will be denied access.\n"));
+ } else {
+ if (strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_SHADOW) != 0) {
+ DEBUG(1, ("Unsupported LDAP account expire policy [%s].\n",
+ dummy));
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ } else {
+ DEBUG(1, ("Unexpected access rule name [%s].\n", order_list[c]));
+ ret = EINVAL;
+ goto done;
}
}
+ access_ctx->access_rule[c] = LDAP_ACCESS_EMPTY;
+ if (c == 0) {
+ DEBUG(0, ("Warning: access_provider=ldap set, "
+ "but ldap_access_order is empty. "
+ "All domain users will be denied access.\n"));
+ }
*ops = &sdap_access_ops;
*pvt_data = access_ctx;
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 945de37..4d52d5b 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -182,6 +182,8 @@ enum sdap_basic_opt {
SDAP_NETGROUP_SEARCH_BASE,
SDAP_NESTING_LEVEL,
SDAP_DEREF,
+ SDAP_ACCOUNT_EXPIRE_POLICY,
+ SDAP_ACCESS_ORDER,
SDAP_OPTS_BASIC /* opts counter */
};
diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 23c076f..d2b2705 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -57,6 +57,23 @@ static struct tevent_req *sdap_access_send(TALLOC_CTX *mem_ctx,
struct be_ctx *be_ctx,
struct sdap_access_ctx *access_ctx,
const char *username);
+
+static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct be_ctx *be_ctx,
+ struct sdap_access_ctx *access_ctx,
+ const char *username,
+ struct ldb_message *user_entry);
+static void sdap_access_filter_done(struct tevent_req *subreq);
+
+static struct tevent_req *sdap_account_expired_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct be_ctx *be_ctx,
+ struct sdap_access_ctx *access_ctx,
+ const char *username,
+ struct ldb_message *user_entry);
+static void sdap_account_expired_done(struct tevent_req *subreq);
+
static void sdap_access_done(struct tevent_req *req);
void sdap_pam_access_handler(struct be_req *breq)
{
@@ -86,23 +103,15 @@ void sdap_pam_access_handler(struct be_req *breq)
struct sdap_access_req_ctx {
const char *username;
- const char *filter;
struct tevent_context *ev;
struct sdap_access_ctx *access_ctx;
- struct sdap_id_ctx *sdap_ctx;
- struct sdap_id_op *sdap_op;
- struct sysdb_handle *handle;
struct be_ctx *be_ctx;
- const char **attrs;
int pam_status;
- bool cached_access;
- char *basedn;
+ struct ldb_message *user_entry;
+ size_t current_rule;
};
-static void sdap_access_decide_offline(struct tevent_req *req);
-static int sdap_access_retry(struct tevent_req *req);
-static void sdap_access_connect_done(struct tevent_req *subreq);
-static void sdap_access_get_access_done(struct tevent_req *req);
+static errno_t select_next_rule(struct tevent_req *req);
static struct tevent_req *sdap_access_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct be_ctx *be_ctx,
@@ -113,72 +122,340 @@ static struct tevent_req *sdap_access_send(TALLOC_CTX *mem_ctx,
struct sdap_access_req_ctx *state;
struct tevent_req *req;
struct ldb_result *res;
- const char *basedn;
- char *clean_username;
+ const char *attrs[] = { "*", NULL };
req = tevent_req_create(mem_ctx, &state, struct sdap_access_req_ctx);
if (req == NULL) {
+ DEBUG(1, ("tevent_req_create failed.\n"));
return NULL;
}
- if (access_ctx->filter == NULL || *access_ctx->filter == '\0') {
- /* If no filter is set, default to restrictive */
- DEBUG(6, ("No filter set. Access is denied.\n"));
- state->pam_status = PAM_PERM_DENIED;
- tevent_req_done(req);
- tevent_req_post(req, be_ctx->ev);
- return req;
- }
-
- state->filter = NULL;
state->be_ctx = be_ctx;
state->username = username;
state->pam_status = PAM_SYSTEM_ERR;
- state->sdap_ctx = access_ctx->id_ctx;
state->ev = ev;
state->access_ctx = access_ctx;
-
- state->attrs = talloc_array(state, const char *, 3);
- if (state->attrs == NULL) {
- DEBUG(1, ("Could not allocate attributes\n"));
- goto failed;
- }
-
- state->attrs[0] = SYSDB_ORIG_DN;
- state->attrs[1] = SYSDB_LDAP_ACCESS;
- state->attrs[2] = NULL;
+ state->current_rule = 0;
DEBUG(6, ("Performing access check for user [%s]\n", username));
+ if (access_ctx->access_rule[0] == LDAP_ACCESS_EMPTY) {
+ DEBUG(3, ("No access rules defined, access denied.\n"));
+ state->pam_status = PAM_PERM_DENIED;
+ ret = EOK;
+ goto done;
+ }
+
/* Get original user DN */
- ret = sysdb_get_user_attr(state, be_ctx->sysdb,
- be_ctx->domain, username,
- state->attrs,
+ ret = sysdb_get_user_attr(state, be_ctx->sysdb, be_ctx->domain,
+ username, attrs,
&res);
if (ret != EOK) {
if (ret == ENOENT) {
/* If we can't find the user, return permission denied */
state->pam_status = PAM_PERM_DENIED;
- goto finished;
+ ret = EOK;
+ goto done;
}
- goto failed;
+ goto done;
}
else {
if (res->count == 0) {
/* If we can't find the user, return permission denied */
state->pam_status = PAM_PERM_DENIED;
- goto finished;
+ ret = EOK;
+ goto done;
}
if (res->count != 1) {
DEBUG(1, ("Invalid response from sysdb_get_user_attr\n"));
- goto failed;
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ state->user_entry = res->msgs[0];
+
+ ret = select_next_rule(req);
+ if (ret != EOK) {
+ if (ret == EACCES) {
+ state->pam_status = PAM_PERM_DENIED;
+ ret = EOK;
+ goto done;
+ }
+ DEBUG(1, ("select_next_rule failed.\n"));
+ goto done;
+ }
+
+ return req;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t select_next_rule(struct tevent_req *req)
+{
+ struct sdap_access_req_ctx *state =
+ tevent_req_data(req, struct sdap_access_req_ctx);
+ struct tevent_req *subreq;
+
+ switch (state->access_ctx->access_rule[state->current_rule]) {
+ case LDAP_ACCESS_EMPTY:
+ return ENOENT;
+ break;
+ case LDAP_ACCESS_FILTER:
+ subreq = sdap_access_filter_send(state, state->ev, state->be_ctx,
+ state->access_ctx, state->username,
+ state->user_entry);
+ if (subreq == NULL) {
+ DEBUG(1, ("sdap_access_filter_send failed.\n"));
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, sdap_access_filter_done, req);
+ return EOK;
+ case LDAP_ACCESS_EXPIRE:
+ subreq = sdap_account_expired_send(state, state->ev, state->be_ctx,
+ state->access_ctx,
+ state->username,
+ state->user_entry);
+ if (subreq == NULL) {
+ DEBUG(1, ("sdap_account_expired_send failed.\n"));
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, sdap_account_expired_done, req);
+ return EOK;
+ default:
+ DEBUG(1, ("Unexpected access rule type. Access denied.\n"));
+ }
+
+ return EACCES;
+}
+
+static void next_access_rule(struct tevent_req *req)
+{
+ struct sdap_access_req_ctx *state =
+ tevent_req_data(req, struct sdap_access_req_ctx);
+ int ret;
+
+ if (state->pam_status == PAM_PERM_DENIED) {
+ tevent_req_done(req);
+ return;
+ }
+
+ state->current_rule++;
+
+ ret = select_next_rule(req);
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ state->pam_status = PAM_SUCCESS;
+ tevent_req_done(req);
+ return;
+ } else if (ret == EACCES) {
+ state->pam_status = PAM_PERM_DENIED;
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ }
+
+ return;
+}
+
+
+static errno_t sdap_account_expired_shadow(const char *username,
+ struct ldb_message *user_entry,
+ int *pam_status)
+{
+ int ret;
+ const char *val;
+ long sp_expire;
+ long today;
+
+ DEBUG(6, ("Performing access shadow check for user [%s]\n", username));
+
+ val = ldb_msg_find_attr_as_string(user_entry, SYSDB_SHADOWPW_EXPIRE, NULL);
+ if (val == NULL) {
+ DEBUG(1, ("Shadow expire attribute not found. Access denied.\n"));
+ *pam_status = PAM_PERM_DENIED;
+ return EOK;
+ }
+ ret = string_to_shadowpw_days(val, &sp_expire);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to retrieve shadow expire date.\n"));
+ return ret;
+ }
+
+ today = (long) (time(NULL) / (60 * 60 * 24));
+ if (today > sp_expire) {
+ *pam_status = PAM_ACCT_EXPIRED;
+ } else {
+ *pam_status = PAM_SUCCESS;
+ }
+
+ return EOK;
+}
+
+struct sdap_account_expired_req_ctx {
+ int pam_status;
+};
+
+static struct tevent_req *sdap_account_expired_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct be_ctx *be_ctx,
+ struct sdap_access_ctx *access_ctx,
+ const char *username,
+ struct ldb_message *user_entry)
+{
+ struct tevent_req *req;
+ struct sdap_account_expired_req_ctx *state;
+ int ret;
+ const char *expire;
+
+ req = tevent_req_create(mem_ctx, &state, struct sdap_account_expired_req_ctx);
+ if (req == NULL) {
+ DEBUG(1, ("tevent_req_create failed.\n"));
+ return NULL;
+ }
+
+ state->pam_status = PAM_SYSTEM_ERR;
+
+ expire = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic,
+ SDAP_ACCOUNT_EXPIRE_POLICY);
+ if (expire == NULL) {
+ DEBUG(1, ("Missing account expire policy. Access denied\n"));
+ state->pam_status = PAM_PERM_DENIED;
+ ret = EOK;
+ goto done;
+ } else {
+ if (strcasecmp(expire, LDAP_ACCOUNT_EXPIRE_SHADOW) == 0) {
+ ret = sdap_account_expired_shadow(username, user_entry,
+ &state->pam_status);
+ if (ret != EOK) {
+ DEBUG(1, ("sdap_account_expired_shadow failed.\n"));
+ goto done;
+ }
+ } else {
+ DEBUG(1, ("Unsupported LDAP account expire policy [%s]. "
+ "Access denied.\n", expire));
+ state->pam_status = PAM_PERM_DENIED;
+ ret = EOK;
+ goto done;
}
}
- /* Exactly one result returned */
- state->cached_access = ldb_msg_find_attr_as_bool(res->msgs[0],
- SYSDB_LDAP_ACCESS,
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+
+ return req;
+}
+
+static errno_t sdap_account_expired_recv(struct tevent_req *req, int *pam_status)
+{
+ struct sdap_account_expired_req_ctx *state =
+ tevent_req_data(req, struct sdap_account_expired_req_ctx);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *pam_status = state->pam_status;
+
+ return EOK;
+}
+
+static void sdap_account_expired_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct sdap_access_req_ctx *state =
+ tevent_req_data(req, struct sdap_access_req_ctx);
+
+ ret = sdap_account_expired_recv(subreq, &state->pam_status);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(1, ("Error retrieving access check result.\n"));
+ state->pam_status = PAM_SYSTEM_ERR;
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ next_access_rule(req);
+
+ return;
+}
+
+
+
+struct sdap_access_filter_req_ctx {
+ const char *username;
+ const char *filter;
+ struct tevent_context *ev;
+ struct sdap_access_ctx *access_ctx;
+ struct sdap_id_ctx *sdap_ctx;
+ struct sdap_id_op *sdap_op;
+ struct sysdb_handle *handle;
+ struct be_ctx *be_ctx;
+ int pam_status;
+ bool cached_access;
+ char *basedn;
+};
+
+static void sdap_access_decide_offline(struct tevent_req *req);
+static int sdap_access_retry(struct tevent_req *req);
+static void sdap_access_connect_done(struct tevent_req *subreq);
+static void sdap_access_get_access_done(struct tevent_req *req);
+static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct be_ctx *be_ctx,
+ struct sdap_access_ctx *access_ctx,
+ const char *username,
+ struct ldb_message *user_entry)
+{
+ errno_t ret;
+ struct sdap_access_filter_req_ctx *state;
+ struct tevent_req *req;
+ const char *basedn;
+ char *clean_username;
+
+ req = tevent_req_create(mem_ctx, &state, struct sdap_access_filter_req_ctx);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (access_ctx->filter == NULL || *access_ctx->filter == '\0') {
+ /* If no filter is set, default to restrictive */
+ DEBUG(6, ("No filter set. Access is denied.\n"));
+ state->pam_status = PAM_PERM_DENIED;
+ tevent_req_done(req);
+ tevent_req_post(req, be_ctx->ev);
+ return req;
+ }
+
+ state->filter = NULL;
+ state->be_ctx = be_ctx;
+ state->username = username;
+ state->pam_status = PAM_SYSTEM_ERR;
+ state->sdap_ctx = access_ctx->id_ctx;
+ state->ev = ev;
+ state->access_ctx = access_ctx;
+
+ DEBUG(6, ("Performing access filter check for user [%s]\n", username));
+
+ state->cached_access = ldb_msg_find_attr_as_bool(user_entry,
+ SYSDB_LDAP_ACCESS_FILTER,
false);
/* Ok, we have one result, check if we are online or offline */
if (be_is_offline(state->be_ctx)) {
@@ -188,7 +465,7 @@ static struct tevent_req *sdap_access_send(TALLOC_CTX *mem_ctx,
}
/* Perform online operation */
- basedn = ldb_msg_find_attr_as_string(res->msgs[0],
+ basedn = ldb_msg_find_attr_as_string(user_entry,
SYSDB_ORIG_DN,
NULL);
if(basedn == NULL) {
@@ -202,7 +479,6 @@ static struct tevent_req *sdap_access_send(TALLOC_CTX *mem_ctx,
DEBUG(1, ("Could not allocate memory for originalDN\n"));
goto failed;
}
- talloc_zfree(res);
/* Construct the filter */
@@ -251,8 +527,8 @@ finished:
static void sdap_access_decide_offline(struct tevent_req *req)
{
- struct sdap_access_req_ctx *state =
- tevent_req_data(req, struct sdap_access_req_ctx);
+ struct sdap_access_filter_req_ctx *state =
+ tevent_req_data(req, struct sdap_access_filter_req_ctx);
if (state->cached_access) {
DEBUG(6, ("Access granted by cached credentials\n"));
@@ -265,8 +541,8 @@ static void sdap_access_decide_offline(struct tevent_req *req)
static int sdap_access_retry(struct tevent_req *req)
{
- struct sdap_access_req_ctx *state =
- tevent_req_data(req, struct sdap_access_req_ctx);
+ struct sdap_access_filter_req_ctx *state =
+ tevent_req_data(req, struct sdap_access_filter_req_ctx);
struct tevent_req *subreq;
int ret;
@@ -284,8 +560,8 @@ static void sdap_access_connect_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
- struct sdap_access_req_ctx *state =
- tevent_req_data(req, struct sdap_access_req_ctx);
+ struct sdap_access_filter_req_ctx *state =
+ tevent_req_data(req, struct sdap_access_filter_req_ctx);
int ret, dp_error;
ret = sdap_id_op_connect_recv(subreq, &dp_error);
@@ -332,8 +608,8 @@ static void sdap_access_get_access_done(struct tevent_req *subreq)
struct sysdb_attrs **results;
struct tevent_req *req =
tevent_req_callback_data(subreq, struct tevent_req);
- struct sdap_access_req_ctx *state =
- tevent_req_data(req, struct sdap_access_req_ctx);
+ struct sdap_access_filter_req_ctx *state =
+ tevent_req_data(req, struct sdap_access_filter_req_ctx);
ret = sdap_get_generic_recv(subreq, state,
&num_results, &results);
talloc_zfree(subreq);
@@ -409,7 +685,7 @@ static void sdap_access_get_access_done(struct tevent_req *subreq)
goto done;
}
- ret = sysdb_attrs_add_bool(attrs, SYSDB_LDAP_ACCESS,
+ ret = sysdb_attrs_add_bool(attrs, SYSDB_LDAP_ACCESS_FILTER,
state->pam_status == PAM_SUCCESS ?
true :
false);
@@ -445,6 +721,39 @@ done:
}
}
+static errno_t sdap_access_filter_recv(struct tevent_req *req, int *pam_status)
+{
+ struct sdap_access_filter_req_ctx *state =
+ tevent_req_data(req, struct sdap_access_filter_req_ctx);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *pam_status = state->pam_status;
+
+ return EOK;
+}
+
+static void sdap_access_filter_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct sdap_access_req_ctx *state =
+ tevent_req_data(req, struct sdap_access_req_ctx);
+
+ ret = sdap_access_filter_recv(subreq, &state->pam_status);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(1, ("Error retrieving access check result.\n"));
+ state->pam_status = PAM_SYSTEM_ERR;
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ next_access_rule(req);
+
+ return;
+}
+
static errno_t sdap_access_recv(struct tevent_req *req, int *pam_status)
{
struct sdap_access_req_ctx *state =
diff --git a/src/providers/ldap/sdap_access.h b/src/providers/ldap/sdap_access.h
index 5dbe864..9b8e45b 100644
--- a/src/providers/ldap/sdap_access.h
+++ b/src/providers/ldap/sdap_access.h
@@ -27,11 +27,24 @@
#include "providers/dp_backend.h"
-#define SYSDB_LDAP_ACCESS "ldap_access_allow"
+#define SYSDB_LDAP_ACCESS_FILTER "ldap_access_filter_allow"
+
+#define LDAP_ACCESS_FILTER_NAME "filter"
+#define LDAP_ACCESS_EXPIRE_NAME "expire"
+
+#define LDAP_ACCOUNT_EXPIRE_SHADOW "shadow"
+
+enum ldap_access_rule {
+ LDAP_ACCESS_EMPTY = -1,
+ LDAP_ACCESS_FILTER = 0,
+ LDAP_ACCESS_EXPIRE,
+ LDAP_ACCESS_LAST
+};
struct sdap_access_ctx {
struct sdap_id_ctx *id_ctx;
const char *filter;
+ int access_rule[LDAP_ACCESS_LAST + 1];
};
void ldap_pam_access_handler(struct be_req *breq);
--
1.7.3.2
More information about the sssd-devel
mailing list