On 05/13/2010 03:21 PM, Stephen Gallagher wrote:
Attached is a version of the ldap_access_filter patch that applies to
master.

New version of the patch that ports the changes from the codereview of the sssd-1-2 version.

See the other thread for an explanation of the changes.



--
Stephen Gallagher
RHCE 804006346421761

Delivering value year after year.
Red Hat ranks #1 in value among software vendors.
http://www.redhat.com/promo/vendor/
From 0b8708b6e9190c649f240c13f6a0b725936cb1cc Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgall...@redhat.com>
Date: Thu, 6 May 2010 10:09:41 -0400
Subject: [PATCH] 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
---
 src/Makefile.am                          |    2 +
 src/config/SSSDConfig.py                 |    3 +
 src/config/SSSDConfigTest.py             |    2 +-
 src/config/etc/sssd.api.d/sssd-ldap.conf |    3 +
 src/db/sysdb.c                           |   10 +
 src/db/sysdb.h                           |    2 +
 src/man/sssd-ldap.5.xml                  |   39 +++
 src/providers/ipa/ipa_common.c           |    3 +-
 src/providers/ipa/ipa_common.h           |    2 +-
 src/providers/ldap/ldap_common.c         |    3 +-
 src/providers/ldap/ldap_common.h         |    3 +
 src/providers/ldap/ldap_init.c           |   56 ++++
 src/providers/ldap/sdap.h                |    1 +
 src/providers/ldap/sdap_access.c         |  457 ++++++++++++++++++++++++++++++
 src/providers/ldap/sdap_access.h         |   39 +++
 15 files changed, 621 insertions(+), 4 deletions(-)
 create mode 100644 src/providers/ldap/sdap_access.c
 create mode 100644 src/providers/ldap/sdap_access.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 5fb406234abe778413f37044bd0c34aed1cbb176..4b78159c3f0e83b59732b0347cc4a7008ecb6645 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -343,6 +343,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 \
@@ -719,6 +720,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 18df97904de5128d34e4de7c36db1a4743f916d6..33764301a2c18cdada142b57851e0dec0c87b05e 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -147,6 +147,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 7b005f6cac4639526c1ab8d22fcbbdd24e068368..e2cfb6584499dc064bcfc9f597c897004c288853 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -688,7 +688,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 d2b47e13ba94dca93cecd170d274c2ac19288f40..d8b92ab2edd74bed8e0317b2820d828157eda8f1 100644
--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
+++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
@@ -65,5 +65,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 2d4a38d79666e4f166c32a9d39a4c2d26d2d531f..bfad77d8a6c3c1e51ef8aaf09d644b62b4bc8d31 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 5b6f21895df19b08d04f1dd1f783e2a41162a42f..f953a1f34c0c7334e116ee4887529631df46f9c5 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 c119e7f3faebffa0d7c64d661a7f17c7a2399326..3aa9896362010ad4b8a945960074bbf406aabf11 100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -644,6 +644,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 aa84e7a94936778cb38311e785508afa127ddb19..06c0d0d33f20c02bb535b726e4da9e8dc9b6f8d7 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -68,7 +68,8 @@ struct dp_option ipa_def_ldap_opts[] = {
     { "ldap_pwd_policy", DP_OPT_STRING, { "none" } , NULL_STRING },
     { "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_dns_service_name", DP_OPT_STRING, { SSS_LDAP_SRV_NAME }, NULL_STRING },
+    { "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 9daede2db92bed073771cc1e7dad3815e976073e..1643909292bcbe22d7181383a46af07721ee97d5 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 33
+#define IPA_OPTS_BASIC_TEST 34
 
 /* 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 03b2133a870fa85fbf7c7c013b4abb47ea556c44..7b5bd11df3aca63561faa31541707a47fd311df1 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -64,7 +64,8 @@ struct dp_option default_basic_opts[] = {
     { "ldap_pwd_policy", DP_OPT_STRING, { "none" } , NULL_STRING },
     { "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_dns_service_name", DP_OPT_STRING, { SSS_LDAP_SRV_NAME }, NULL_STRING },
+    { "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 3998e30013aaf56562043001b1af4fdde63ccbf9..2fec7e2dad53b00d814202fb9b49e70436903cb0 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 917ece0cbc5f3fbdf3d63603cac2af3a0a16918d..396dbfd01325ca0fdecfc92cd0f008cfd3d308e3 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;
 
@@ -179,6 +195,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 a4da43b351553966eea14a30c06d78b6836aa5e6..8fb623f55237c8da0f3ec6c8a64ca4dbc3dd94ec 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -151,6 +151,7 @@ enum sdap_basic_opt {
     SDAP_REFERRALS,
     SDAP_ACCOUNT_CACHE_EXPIRATION,
     SDAP_DNS_SERVICE_NAME,
+    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 0000000000000000000000000000000000000000..fd3deb783489f244c2bc83ecd26df57cdc504461
--- /dev/null
+++ b/src/providers/ldap/sdap_access.c
@@ -0,0 +1,457 @@
+/*
+    SSSD
+
+    sdap_access.c
+
+    Authors:
+        Stephen Gallagher <sgall...@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 0000000000000000000000000000000000000000..5dbe8646188519748ed1c7e6647409f13c876132
--- /dev/null
+++ b/src/providers/ldap/sdap_access.h
@@ -0,0 +1,39 @@
+/*
+    SSSD
+
+    sdap_access.h
+
+    Authors:
+        Stephen Gallagher <sgall...@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_ */
-- 
1.7.0.1

_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to