summaryrefslogtreecommitdiffstats
path: root/src/providers/ldap/sdap_access.c
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/providers/ldap/sdap_access.c
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/providers/ldap/sdap_access.c')
-rw-r--r--src/providers/ldap/sdap_access.c457
1 files changed, 457 insertions, 0 deletions
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);
+}