summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorStephen Gallagher <sgallagh@redhat.com>2010-05-06 10:09:41 -0400
committerStephen Gallagher <sgallagh@redhat.com>2010-05-27 14:44:14 -0400
commit35480afaefafb77b28d35b29039989ab888aafe9 (patch)
tree60789844987297b64c9dc237bdd4501e4b5df86f /src
parent8bb6aa3fd81a3c195b92270ddf189296abae65eb (diff)
downloadsssd-35480afaefafb77b28d35b29039989ab888aafe9.tar.gz
sssd-35480afaefafb77b28d35b29039989ab888aafe9.tar.xz
sssd-35480afaefafb77b28d35b29039989ab888aafe9.zip
Add ldap_access_filter option
This option (applicable to access_provider=ldap) allows the admin to set an additional LDAP search filter that must match in order for a user to be granted access to the system. Common examples for this would be limiting access to users by in a particular group, for example: ldap_access_filter = memberOf=cn=access_group,ou=Groups,dc=example,dc=com
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/config/SSSDConfig.py3
-rwxr-xr-xsrc/config/SSSDConfigTest.py2
-rw-r--r--src/config/etc/sssd.api.d/sssd-ldap.conf3
-rw-r--r--src/db/sysdb.c10
-rw-r--r--src/db/sysdb.h2
-rw-r--r--src/man/sssd-ldap.5.xml39
-rw-r--r--src/providers/ipa/ipa_common.c3
-rw-r--r--src/providers/ipa/ipa_common.h2
-rw-r--r--src/providers/ldap/ldap_common.c3
-rw-r--r--src/providers/ldap/ldap_common.h3
-rw-r--r--src/providers/ldap/ldap_init.c56
-rw-r--r--src/providers/ldap/sdap.h1
-rw-r--r--src/providers/ldap/sdap_access.c457
-rw-r--r--src/providers/ldap/sdap_access.h39
15 files changed, 621 insertions, 4 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ac1746191..b83523255 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -349,6 +349,7 @@ dist_noinst_HEADERS = \
providers/krb5/krb5_utils.h \
providers/ldap/ldap_common.h \
providers/ldap/sdap.h \
+ providers/ldap/sdap_access.h \
providers/ldap/sdap_async.h \
providers/ldap/sdap_async_private.h \
providers/ipa/ipa_common.h \
@@ -729,6 +730,7 @@ libsss_ldap_la_SOURCES = \
providers/ldap/ldap_id.c \
providers/ldap/ldap_id_enum.c \
providers/ldap/ldap_id_cleanup.c \
+ providers/ldap/sdap_access.c \
providers/ldap/ldap_auth.c \
providers/ldap/ldap_init.c \
providers/ldap/ldap_common.c \
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index 6b759d83c..7b9d96c9e 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -151,6 +151,9 @@ option_strings = {
# [provider/ldap/auth]
'ldap_pwd_policy' : _('Policy to evaluate the password expiration'),
+ # [provider/ldap/access]
+ 'ldap_access_filter' : _('LDAP filter to determine access privileges'),
+
# [provider/simple/access]
'simple_allow_users' : _('Comma separated list of allowed users'),
'simple_deny_users' : _('Comma separated list of prohibited users'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index 61c2f9497..04d438e06 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -687,7 +687,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
control_provider_dict = {
'ipa': ['id', 'auth', 'access', 'chpass'],
'local': ['id', 'auth', 'chpass'],
- 'ldap': ['id', 'auth', 'chpass'],
+ 'ldap': ['id', 'auth', 'access', 'chpass'],
'krb5': ['auth', 'chpass'],
'proxy': ['id', 'auth'],
'simple': ['access'],
diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf
index abcb5199d..7f0c36069 100644
--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
+++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
@@ -67,5 +67,8 @@ ldap_force_upper_case_realm = bool, None, false
[provider/ldap/auth]
ldap_pwd_policy = str, None, false
+[provider/ldap/access]
+ldap_access_filter = str, None, false
+
[provider/ldap/chpass]
diff --git a/src/db/sysdb.c b/src/db/sysdb.c
index 2d4a38d79..bfad77d8a 100644
--- a/src/db/sysdb.c
+++ b/src/db/sysdb.c
@@ -166,6 +166,16 @@ int sysdb_attrs_add_string(struct sysdb_attrs *attrs,
return sysdb_attrs_add_val(attrs, name, &v);
}
+int sysdb_attrs_add_bool(struct sysdb_attrs *attrs,
+ const char *name, bool value)
+{
+ if(value) {
+ return sysdb_attrs_add_string(attrs, name, "TRUE");
+ }
+
+ return sysdb_attrs_add_string(attrs, name, "FALSE");
+}
+
int sysdb_attrs_steal_string(struct sysdb_attrs *attrs,
const char *name, char *str)
{
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 23560ecd9..a5413a25c 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -161,6 +161,8 @@ int sysdb_attrs_add_val(struct sysdb_attrs *attrs,
const char *name, const struct ldb_val *val);
int sysdb_attrs_add_string(struct sysdb_attrs *attrs,
const char *name, const char *str);
+int sysdb_attrs_add_bool(struct sysdb_attrs *attrs,
+ const char *name, bool value);
int sysdb_attrs_add_long(struct sysdb_attrs *attrs,
const char *name, long value);
int sysdb_attrs_add_uint32(struct sysdb_attrs *attrs,
diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index 9b1f14b65..89437d97f 100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -657,6 +657,45 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>ldap_access_filter (string)</term>
+ <listitem>
+ <para>
+ If using access_provider = ldap, this option is
+ mandatory. It specifies an LDAP search filter
+ criteria that must be met for the user to be
+ granted access on this host. If
+ access_provider = ldap and this option is
+ not set, it will result in all users being
+ denied access. Use access_provider = allow to
+ change this default behavior.
+ </para>
+ <para>
+ Example:
+ </para>
+ <programlisting>
+access_provider = ldap
+ldap_access_filter = memberOf=cn=allowedusers,ou=Groups,dc=example,dc=com
+ </programlisting>
+ <para>
+ This example means that access to this host is
+ restricted to members of the "allowedusers" group
+ in ldap.
+ </para>
+ <para>
+ Offline caching for this feature is limited to
+ determining whether the user's last online login
+ was granted access permission. If they were
+ granted access during their last login, they will
+ continue to be granted access while offline and
+ vice-versa.
+ </para>
+ <para>
+ Default: Empty
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
</refsect1>
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index a65ed2795..580b66f40 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -71,7 +71,8 @@ struct dp_option ipa_def_ldap_opts[] = {
{ "ldap_referrals", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
{ "account_cache_expiration", DP_OPT_NUMBER, { .number = 0 }, NULL_NUMBER },
{ "ldap_dns_service_name", DP_OPT_STRING, { SSS_LDAP_SRV_NAME }, NULL_STRING },
- { "ldap_krb5_ticket_lifetime", DP_OPT_NUMBER, { .number = (24 * 60 * 60) }, NULL_NUMBER }
+ { "ldap_krb5_ticket_lifetime", DP_OPT_NUMBER, { .number = (24 * 60 * 60) }, NULL_NUMBER },
+ { "ldap_access_filter", 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 9678e0cea..fe9ca5418 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 34
+#define IPA_OPTS_BASIC_TEST 35
/* 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 1d62b7504..f97267148 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -65,7 +65,8 @@ struct dp_option default_basic_opts[] = {
{ "ldap_referrals", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
{ "account_cache_expiration", DP_OPT_NUMBER, { .number = 0 }, NULL_NUMBER },
{ "ldap_dns_service_name", DP_OPT_STRING, { SSS_LDAP_SRV_NAME }, NULL_STRING },
- { "ldap_krb5_ticket_lifetime", DP_OPT_NUMBER, { .number = (24 * 60 * 60) }, NULL_NUMBER }
+ { "ldap_krb5_ticket_lifetime", DP_OPT_NUMBER, { .number = (24 * 60 * 60) }, NULL_NUMBER },
+ { "ldap_access_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING }
};
struct sdap_attr_map generic_attr_map[] = {
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index 53ffd6fe8..1ee7378cb 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -73,6 +73,9 @@ void sdap_pam_auth_handler(struct be_req *breq);
/* chpass */
void sdap_pam_chpass_handler(struct be_req *breq);
+/* access */
+void sdap_pam_access_handler(struct be_req *breq);
+
void sdap_handler_done(struct be_req *req, int dp_err,
diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
index df2956ec6..5c6f4b790 100644
--- a/src/providers/ldap/ldap_init.c
+++ b/src/providers/ldap/ldap_init.c
@@ -25,6 +25,7 @@
#include "providers/child_common.h"
#include "providers/ldap/ldap_common.h"
#include "providers/ldap/sdap_async_private.h"
+#include "providers/ldap/sdap_access.h"
static void sdap_shutdown(struct be_req *req);
@@ -46,6 +47,12 @@ struct bet_ops sdap_chpass_ops = {
.finalize = sdap_shutdown
};
+/* Access Handler */
+struct bet_ops sdap_access_ops = {
+ .handler = sdap_pam_access_handler,
+ .finalize = sdap_shutdown
+};
+
int sssm_ldap_id_init(struct be_ctx *bectx,
struct bet_ops **ops,
void **pvt_data)
@@ -55,6 +62,15 @@ int sssm_ldap_id_init(struct be_ctx *bectx,
const char *dns_service_name;
int ret;
+ /* If we're already set up, just return that */
+ if(bectx->bet_info[BET_ID].mod_name &&
+ strcmp("LDAP", bectx->bet_info[BET_ID].mod_name)) {
+ DEBUG(8, ("Re-using sdap_id_ctx for this provider\n"));
+ *ops = bectx->bet_info[BET_ID].bet_ops;
+ *pvt_data = bectx->bet_info[BET_ID].pvt_bet_data;
+ return EOK;
+ }
+
ctx = talloc_zero(bectx, struct sdap_id_ctx);
if (!ctx) return ENOMEM;
@@ -186,6 +202,46 @@ int sssm_ldap_chpass_init(struct be_ctx *bectx,
return ret;
}
+int sssm_ldap_access_init(struct be_ctx *bectx,
+ struct bet_ops **ops,
+ void **pvt_data)
+{
+ int ret;
+ struct sdap_access_ctx *access_ctx;
+
+ access_ctx = talloc_zero(bectx, struct sdap_access_ctx);
+ if(access_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sssm_ldap_id_init(bectx, ops, (void **)&access_ctx->id_ctx);
+ if (ret != EOK) {
+ DEBUG(1, ("sssm_ldap_id_init failed.\n"));
+ goto done;
+ }
+
+ access_ctx->filter = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic,
+ SDAP_ACCESS_FILTER);
+ if (access_ctx->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"));
+ }
+
+ *ops = &sdap_access_ops;
+ *pvt_data = access_ctx;
+
+done:
+ if (ret != EOK) {
+ talloc_free(access_ctx);
+ }
+ return ret;
+}
+
static void sdap_shutdown(struct be_req *req)
{
/* TODO: Clean up any internal data */
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 917e60107..d698b55bc 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -152,6 +152,7 @@ enum sdap_basic_opt {
SDAP_ACCOUNT_CACHE_EXPIRATION,
SDAP_DNS_SERVICE_NAME,
SDAP_KRB5_TICKET_LIFETIME,
+ SDAP_ACCESS_FILTER,
SDAP_OPTS_BASIC /* opts counter */
};
diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
new file mode 100644
index 000000000..fd3deb783
--- /dev/null
+++ b/src/providers/ldap/sdap_access.c
@@ -0,0 +1,457 @@
+/*
+ SSSD
+
+ sdap_access.c
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2010 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/param.h>
+#include <security/pam_modules.h>
+#include <talloc.h>
+#include <tevent.h>
+#include <errno.h>
+
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/ldap/sdap.h"
+#include "providers/ldap/sdap_access.h"
+#include "providers/ldap/sdap_async.h"
+#include "providers/data_provider.h"
+#include "providers/dp_backend.h"
+
+static void sdap_access_reply(struct be_req *be_req, int pam_status)
+{
+ struct pam_data *pd;
+ pd = talloc_get_type(be_req->req_data, struct pam_data);
+ pd->pam_status = pam_status;
+
+ if (pam_status == PAM_SUCCESS || pam_status == PAM_PERM_DENIED) {
+ be_req->fn(be_req, DP_ERR_OK, pam_status, NULL);
+ }
+
+ else {
+ be_req->fn(be_req, DP_ERR_FATAL, pam_status, NULL);
+ }
+}
+
+static struct tevent_req *sdap_access_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct be_ctx *be_ctx,
+ struct sdap_access_ctx *access_ctx,
+ const char *username);
+static void sdap_access_done(struct tevent_req *req);
+void sdap_pam_access_handler(struct be_req *breq)
+{
+ struct pam_data *pd;
+ struct tevent_req *req;
+ struct sdap_access_ctx *access_ctx;
+
+ pd = talloc_get_type(breq->req_data, struct pam_data);
+
+ access_ctx =
+ talloc_get_type(breq->be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
+ struct sdap_access_ctx);
+
+ req = sdap_access_send(breq,
+ breq->be_ctx->ev,
+ breq->be_ctx,
+ access_ctx,
+ pd->user);
+ if (req == NULL) {
+ DEBUG(1, ("Unable to start sdap_access request\n"));
+ sdap_access_reply(breq, PAM_SYSTEM_ERR);
+ return;
+ }
+
+ tevent_req_set_callback(req, sdap_access_done, 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 sysdb_handle *handle;
+ struct be_ctx *be_ctx;
+ const char **attrs;
+ int pam_status;
+ bool cached_access;
+ char *basedn;
+};
+
+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_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct be_ctx *be_ctx,
+ struct sdap_access_ctx *access_ctx,
+ const char *username)
+{
+ errno_t ret;
+ struct sdap_access_req_ctx *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct ldb_result *res;
+ const char *basedn;
+
+ req = tevent_req_create(mem_ctx, &state, struct sdap_access_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;
+
+ 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;
+
+ DEBUG(6, ("Performing access check for user [%s]\n", username));
+
+ /* Get original user DN */
+ ret = sysdb_get_user_attr(state, be_ctx->sysdb,
+ be_ctx->domain, username,
+ state->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;
+ }
+ goto failed;
+ }
+ else {
+ if (res->count == 0) {
+ /* If we can't find the user, return permission denied */
+ state->pam_status = PAM_PERM_DENIED;
+ goto finished;
+ }
+
+ if (res->count != 1) {
+ DEBUG(1, ("Invalid response from sysdb_get_user_attr\n"));
+ goto failed;
+ }
+ }
+
+ /* Exactly one result returned */
+ state->cached_access = ldb_msg_find_attr_as_bool(res->msgs[0],
+ SYSDB_LDAP_ACCESS,
+ 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 */
+ if (state->cached_access) {
+ DEBUG(6, ("Access granted by cached credentials\n"));
+ ret = EOK;
+ state->pam_status = PAM_SUCCESS;
+ goto finished;
+ }
+
+ /* Access denied */
+ DEBUG(6, ("Access denied by cached credentials\n"));
+ ret = EOK;
+ state->pam_status = PAM_PERM_DENIED;
+ goto finished;
+ }
+
+ /* Perform online operation */
+ basedn = ldb_msg_find_attr_as_string(res->msgs[0],
+ SYSDB_ORIG_DN,
+ NULL);
+ if(basedn == NULL) {
+ DEBUG(1,("Could not find originalDN for user [%s]\n",
+ state->username));
+ goto failed;
+ }
+
+ state->basedn = talloc_strdup(state, basedn);
+ if (state->basedn == NULL) {
+ DEBUG(1, ("Could not allocate memory for originalDN\n"));
+ goto failed;
+ }
+ talloc_zfree(res);
+
+ /* Construct the filter */
+ state->filter = talloc_asprintf(
+ state,
+ "(&(%s=%s)(objectclass=%s)(%s))",
+ state->sdap_ctx->opts->user_map[SDAP_AT_USER_NAME].name,
+ state->username,
+ state->sdap_ctx->opts->user_map[SDAP_OC_USER].name,
+ state->access_ctx->filter);
+ if (state->filter == NULL) {
+ DEBUG(0, ("Could not construct access filter\n"));
+ goto failed;
+ }
+
+ DEBUG(6, ("Checking filter against LDAP\n"));
+
+ /* Check whether we have an active LDAP connection */
+ if (state->sdap_ctx->gsh == NULL || ! state->sdap_ctx->gsh->connected) {
+ subreq = sdap_cli_connect_send(state, state->ev,
+ state->sdap_ctx->opts,
+ state->sdap_ctx->be,
+ state->sdap_ctx->service,
+ NULL);
+ if (!subreq) {
+ DEBUG(1, ("sdap_cli_connect_send failed.\n"));
+ goto failed;
+ }
+
+ tevent_req_set_callback(subreq, sdap_access_connect_done, req);
+ return req;
+ }
+
+ /* Make the LDAP request */
+ subreq = sdap_get_generic_send(state,
+ state->ev,
+ state->sdap_ctx->opts,
+ state->sdap_ctx->gsh,
+ state->basedn, LDAP_SCOPE_BASE,
+ state->filter, NULL,
+ NULL, 0);
+ if (subreq == NULL) {
+ DEBUG(1, ("Could not start LDAP communication\n"));
+ goto failed;
+ }
+
+ tevent_req_set_callback(subreq, sdap_access_get_access_done, req);
+
+ return req;
+
+failed:
+ talloc_free(req);
+ return NULL;
+
+finished:
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sdap_access_connect_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_cli_connect_recv(subreq, state->sdap_ctx,
+ &state->sdap_ctx->gsh, NULL);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ /* Could not connect to LDAP. Mark as offline and return
+ * from cache.
+ */
+ be_mark_offline(state->be_ctx);
+ if (state->cached_access) {
+ DEBUG(6, ("Access granted by cached credentials\n"));
+ state->pam_status = PAM_SUCCESS;
+ tevent_req_done(req);
+ return;
+ }
+
+ /* Access denied */
+ DEBUG(6, ("Access denied by cached credentials\n"));
+ state->pam_status = PAM_PERM_DENIED;
+ tevent_req_done(req);
+ }
+
+ /* Connection to LDAP succeeded
+ * Send filter request
+ */
+ subreq = sdap_get_generic_send(state,
+ state->ev,
+ state->sdap_ctx->opts,
+ state->sdap_ctx->gsh,
+ state->basedn,
+ LDAP_SCOPE_BASE,
+ state->filter, NULL,
+ NULL, 0);
+ if (subreq == NULL) {
+ DEBUG(1, ("Could not start LDAP communication\n"));
+ state->pam_status = PAM_SYSTEM_ERR;
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ tevent_req_set_callback(subreq, sdap_access_get_access_done, req);
+}
+
+static void sdap_access_get_access_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ 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);
+ struct sdap_access_req_ctx *state =
+ tevent_req_data(req, struct sdap_access_req_ctx);
+ ret = sdap_get_generic_recv(subreq, state,
+ &num_results, &results);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(1, ("sdap_get_generic_send() returned error [%d][%s]",
+ ret, strerror(ret)));
+ state->pam_status = PAM_SYSTEM_ERR;
+ 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(4, ("User [%s] was not found with the specified filter. "
+ "Denying access.\n", state->username));
+ found = false;
+ }
+ else if (results == NULL) {
+ DEBUG(1, ("num_results > 0, but results is NULL\n"));
+ ret = EIO;
+ state->pam_status = PAM_SYSTEM_ERR;
+ 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(1, ("Received multiple replies\n"));
+ ret = EIO;
+ state->pam_status = PAM_SYSTEM_ERR;
+ goto done;
+ }
+ else { /* Ok, we got a single reply */
+ found = true;
+ }
+
+ if (found) {
+ /* Save "allow" to the cache for future offline
+ * access checks.
+ */
+ DEBUG(6, ("Access granted by online lookup\n"));
+ state->pam_status = PAM_SUCCESS;
+ }
+ else {
+ /* Save "disallow" to the cache for future offline
+ * access checks.
+ */
+ DEBUG(6, ("Access denied by online lookup\n"));
+ state->pam_status = PAM_PERM_DENIED;
+ }
+
+ attrs = sysdb_new_attrs(state);
+ if (attrs == NULL) {
+ ret = ENOMEM;
+ DEBUG(1, ("Could not set up attrs\n"));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_bool(attrs, SYSDB_LDAP_ACCESS,
+ state->pam_status == PAM_SUCCESS ?
+ true :
+ false);
+ if (ret != EOK) {
+ /* Failing to save to the cache is non-fatal.
+ * Just return the result.
+ */
+ ret = EOK;
+ DEBUG(1, ("Could not set up attrs\n"));
+ goto done;
+ }
+
+ ret = sysdb_set_user_attr(attrs,
+ state->be_ctx->sysdb,
+ state->be_ctx->domain,
+ state->username,
+ attrs, SYSDB_MOD_REP);
+ if (ret != EOK) {
+ /* Failing to save to the cache is non-fatal.
+ * Just return the result.
+ */
+ ret = EOK;
+ DEBUG(1, ("Failed to set user access attribute\n"));
+ goto done;
+ }
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ }
+ else {
+ tevent_req_error(req, ret);
+ }
+}
+
+static errno_t sdap_access_recv(struct tevent_req *req, int *pam_status)
+{
+ struct sdap_access_req_ctx *state =
+ tevent_req_data(req, struct sdap_access_req_ctx);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *pam_status = state->pam_status;
+
+ return EOK;
+}
+
+static void sdap_access_done(struct tevent_req *req)
+{
+ errno_t ret;
+ int pam_status;
+ struct be_req *breq =
+ tevent_req_callback_data(req, struct be_req);
+
+ ret = sdap_access_recv(req, &pam_status);
+ talloc_zfree(req);
+ if (ret != EOK) {
+ DEBUG(1, ("Error retrieving access check result.\n"));
+ pam_status = PAM_SYSTEM_ERR;
+ }
+
+ sdap_access_reply(breq, pam_status);
+}
diff --git a/src/providers/ldap/sdap_access.h b/src/providers/ldap/sdap_access.h
new file mode 100644
index 000000000..5dbe86461
--- /dev/null
+++ b/src/providers/ldap/sdap_access.h
@@ -0,0 +1,39 @@
+/*
+ SSSD
+
+ sdap_access.h
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2010 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SDAP_ACCESS_H_
+#define SDAP_ACCESS_H_
+
+#include "providers/dp_backend.h"
+
+#define SYSDB_LDAP_ACCESS "ldap_access_allow"
+
+struct sdap_access_ctx {
+ struct sdap_id_ctx *id_ctx;
+ const char *filter;
+};
+
+void ldap_pam_access_handler(struct be_req *breq);
+
+#endif /* SDAP_ACCESS_H_ */