summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2010-11-22 14:24:23 +0100
committerStephen Gallagher <sgallagh@redhat.com>2010-12-06 09:30:13 -0500
commit32266b2c1c6b8bf95f3ba8fd7f3ff2ef63d8fb9a (patch)
tree726ed591038967e12d559ccebd6eece6cd2520cb
parent39875788b552ed157e68156e64e95dda5dc6aa43 (diff)
downloadsssd-32266b2c1c6b8bf95f3ba8fd7f3ff2ef63d8fb9a.tar.gz
sssd-32266b2c1c6b8bf95f3ba8fd7f3ff2ef63d8fb9a.tar.bz2
sssd-32266b2c1c6b8bf95f3ba8fd7f3ff2ef63d8fb9a.zip
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.
-rw-r--r--src/config/SSSDConfig.py2
-rw-r--r--src/config/etc/sssd.api.d/sssd-ldap.conf2
-rw-r--r--src/man/sssd-ldap.5.xml55
-rw-r--r--src/providers/ipa/ipa_common.c4
-rw-r--r--src/providers/ipa/ipa_common.h2
-rw-r--r--src/providers/ldap/ldap_common.c4
-rw-r--r--src/providers/ldap/ldap_init.c134
-rw-r--r--src/providers/ldap/sdap.h2
-rw-r--r--src/providers/ldap/sdap_access.c443
-rw-r--r--src/providers/ldap/sdap_access.h15
10 files changed, 570 insertions, 93 deletions
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index edfc823e..e48e83ab 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -160,6 +160,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 404f4d59..b7d2f9b2 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 cf6747e7..8936882c 100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -370,7 +370,8 @@
<term>ldap_user_shadow_expire (string)</term>
<listitem>
<para>
- When using ldap_pwd_policy=shadow, this parameter
+ When using ldap_pwd_policy=shadow or
+ ldap_account_expire_policy=shadow, this parameter
contains the name of an LDAP attribute corresponding
to its
<citerefentry>
@@ -1026,6 +1027,58 @@ 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>
+ The following values are allowed:
+ </para>
+ <para>
+ <emphasis>shadow</emphasis>: use the value of
+ ldap_user_shadow_expire to determine if the account
+ is expired.
+ </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>
+ <para>
+ Please note that it is a configuration error if a
+ value is used more than once.
+ </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 786be833..32058772 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 f0d4ad8c..0082b065 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 d9d44c89..dc012262 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 b6176a9f..d6407c41 100644
--- a/src/providers/ldap/ldap_init.c
+++ b/src/providers/ldap/ldap_init.c
@@ -54,6 +54,31 @@ struct bet_ops sdap_access_ops = {
.finalize = sdap_shutdown
};
+/* Please use this only for short lists */
+errno_t check_order_list_for_duplicates(char **list, size_t len,
+ bool case_sensitive)
+{
+ size_t c;
+ size_t d;
+ int cmp;
+
+ for (c = 0; list[c] != NULL; c++) {
+ for (d = c + 1; list[d] != NULL; d++) {
+ if (case_sensitive) {
+ cmp = strcmp(list[c], list[d]);
+ } else {
+ cmp = strcasecmp(list[c], list[d]);
+ }
+ if (cmp == 0) {
+ DEBUG(1, ("Duplicate string [%s] found.\n", list[c]));
+ return EINVAL;
+ }
+ }
+ }
+
+ return EOK;
+}
+
int sssm_ldap_id_init(struct be_ctx *bectx,
struct bet_ops **ops,
void **pvt_data)
@@ -225,6 +250,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 +268,92 @@ 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;
+ }
+
+ ret = check_order_list_for_duplicates(order_list, order_list_len, false);
+ if (ret != EOK) {
+ DEBUG(1, ("check_order_list_for_duplicates 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 945de374..4d52d5b3 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 23c076f1..4ebd7276 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,82 +122,352 @@ 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 ||
+ state->pam_status == PAM_ACCT_EXPIRED) {
+ 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(3, ("Shadow expire attribute not found. "
+ "Access will be granted.\n"));
+ *pam_status = PAM_SUCCESS;
+ 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 (sp_expire > 0 && 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_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 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)) {
/* Ok, we're offline. Return from the cache */
- sdap_access_decide_offline(req);
+ sdap_access_filter_decide_offline(req);
goto finished;
}
/* 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 +481,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 */
@@ -232,7 +510,7 @@ static struct tevent_req *sdap_access_send(TALLOC_CTX *mem_ctx,
goto failed;
}
- ret = sdap_access_retry(req);
+ ret = sdap_access_filter_retry(req);
if (ret != EOK) {
goto failed;
}
@@ -249,10 +527,10 @@ finished:
return req;
}
-static void sdap_access_decide_offline(struct tevent_req *req)
+static void sdap_access_filter_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"));
@@ -263,10 +541,10 @@ static void sdap_access_decide_offline(struct tevent_req *req)
}
}
-static int sdap_access_retry(struct tevent_req *req)
+static int sdap_access_filter_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;
@@ -276,16 +554,16 @@ static int sdap_access_retry(struct tevent_req *req)
return ret;
}
- tevent_req_set_callback(subreq, sdap_access_connect_done, req);
+ tevent_req_set_callback(subreq, sdap_access_filter_connect_done, req);
return EOK;
}
-static void sdap_access_connect_done(struct tevent_req *subreq)
+static void sdap_access_filter_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);
@@ -293,7 +571,7 @@ static void sdap_access_connect_done(struct tevent_req *subreq)
if (ret != EOK) {
if (dp_error == DP_ERR_OFFLINE) {
- sdap_access_decide_offline(req);
+ sdap_access_filter_decide_offline(req);
tevent_req_done(req);
return;
}
@@ -320,10 +598,10 @@ static void sdap_access_connect_done(struct tevent_req *subreq)
return;
}
- tevent_req_set_callback(subreq, sdap_access_get_access_done, req);
+ tevent_req_set_callback(subreq, sdap_access_filter_get_access_done, req);
}
-static void sdap_access_get_access_done(struct tevent_req *subreq)
+static void sdap_access_filter_get_access_done(struct tevent_req *subreq)
{
int ret, dp_error;
size_t num_results;
@@ -332,8 +610,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);
@@ -342,13 +620,13 @@ static void sdap_access_get_access_done(struct tevent_req *subreq)
if (ret != EOK) {
if (dp_error == DP_ERR_OK) {
/* retry */
- ret = sdap_access_retry(req);
+ ret = sdap_access_filter_retry(req);
if (ret == EOK) {
return;
}
state->pam_status = PAM_SYSTEM_ERR;
} else if (dp_error == DP_ERR_OFFLINE) {
- sdap_access_decide_offline(req);
+ sdap_access_filter_decide_offline(req);
} else {
DEBUG(1, ("sdap_get_generic_send() returned error [%d][%s]\n",
ret, strerror(ret)));
@@ -409,7 +687,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 +723,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 5dbe8646..9b8e45bd 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);