diff options
author | Sumit Bose <sbose@redhat.com> | 2010-11-22 14:24:23 +0100 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2010-12-06 09:30:13 -0500 |
commit | 32266b2c1c6b8bf95f3ba8fd7f3ff2ef63d8fb9a (patch) | |
tree | 726ed591038967e12d559ccebd6eece6cd2520cb | |
parent | 39875788b552ed157e68156e64e95dda5dc6aa43 (diff) | |
download | sssd-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.py | 2 | ||||
-rw-r--r-- | src/config/etc/sssd.api.d/sssd-ldap.conf | 2 | ||||
-rw-r--r-- | src/man/sssd-ldap.5.xml | 55 | ||||
-rw-r--r-- | src/providers/ipa/ipa_common.c | 4 | ||||
-rw-r--r-- | src/providers/ipa/ipa_common.h | 2 | ||||
-rw-r--r-- | src/providers/ldap/ldap_common.c | 4 | ||||
-rw-r--r-- | src/providers/ldap/ldap_init.c | 134 | ||||
-rw-r--r-- | src/providers/ldap/sdap.h | 2 | ||||
-rw-r--r-- | src/providers/ldap/sdap_access.c | 443 | ||||
-rw-r--r-- | src/providers/ldap/sdap_access.h | 15 |
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); |