summaryrefslogtreecommitdiffstats
path: root/sss_client
Commit message (Collapse)AuthorAgeFilesLines
...
* added more flexible handling of client protocolSumit Bose2009-05-152-5/+20
| | | | | - allow different protocol versions for PAM and NSS - support more than one protocol version in the responder
* added syslog support to pam_sssSumit Bose2009-05-081-5/+40
|
* cleanup and fixes for pam_sssSumit Bose2009-05-081-190/+352
| | | | | | | | | | | - if PAM_USER==root return PAM_USER_UNKNOWN - pam_sss now can handle to following options: - use_first_pass: forces the module to use a previous stacked modules password and will never prompt the user - use_authtok: when password changing enforce the module to set the new password to the one provided by a previously stacked password module - forward_pass: store the passwords collected by the module as pam items for modules called later in the stack
* Fix IndentationSimo Sorce2009-04-291-88/+89
|
* reuse authtok which is already in the pam stackSumit Bose2009-04-291-2/+22
|
* Update sss_client configure.ac tooSimo Sorce2009-04-271-1/+1
|
* allow to forward the authtok to other pam modulesSumit Bose2009-04-231-0/+16
| | | | | | | | Other pam modules which are called after pam_sss might want to reuse the given password so that the user is not bothered with multiple password prompt. When pam_sss is configured with the option 'forward_pass' it will use pam_set_item to safe the password for other pam modules.
* sssd 0.3.2sssd-0_3_2Jakub Hrozek2009-04-201-1/+1
|
* Bump up to 0.3.1sssd-0_3_1Simo Sorce2009-04-131-1/+1
|
* Fix compilation error due to implicit castStephen Gallagher2009-03-251-2/+2
|
* added response type PAM_ENV_ITEM and integrated response data into dbus messagesSumit Bose2009-03-202-0/+35
|
* append CFLAGS environment variable to Makefiles CFLAGSSumit Bose2009-03-131-2/+2
|
* Remove unexisting left over headersssd-0_2_1Simo Sorce2009-03-102-2/+1
| | | | Also bump up the version as this error prevented a successful build of 0.2.0
* Bump up to version 0.2.0sssd-0_2_0Simo Sorce2009-03-101-1/+1
| | | | Change version after changes in protocol and MPG behavior.
* added generic PAM return messages and a false login delaySumit Bose2009-03-102-15/+64
|
* Treat uids and gids as 32 bit numbers not 64Simo Sorce2009-03-103-28/+28
| | | | | | In the nss communication protocol we were treating uids and gids as 64 bit values, but uids and gids are really u32 values, change the protocol to reflect the real size.
* Fix bugs in functions dealing with groupsSimo Sorce2009-03-101-1/+1
| | | | | | Fix infinite loop within initgr functions. Fix min length check copy&paste error, was filtering valid groups if the name was short enough and the group had no members.
* NSS libs do not use versioned shared objectsSimo Sorce2009-03-091-1/+1
| | | | | Afaik glibc uses just .so/.so.2, and all other nss libs I can see in the system are the same.
* added sss_client to spec fileSumit Bose2009-03-051-3/+5
|
* added password reset by rootSumit Bose2009-03-051-9/+33
|
* Fix sss_client install targetSimo Sorce2009-03-051-4/+5
| | | | | | | With this fix configure must be passed the right libdir argument depending on the platform you are building on. For example on Linux x86_64: ./configure --libdir=/lib64
* added a privileged pipeSumit Bose2009-03-052-7/+24
|
* first version of LOCAL pam backendSumit Bose2009-03-021-0/+6
|
* Add PAM clientSumit Bose2009-02-2415-0/+5588
Also rename nss_client to sss_client and reuse the same pipe protocol for both the NSS and PAM client libraries. Signed-off-by: Simo Sorce <ssorce@redhat.com>
id='n538' href='#n538'>538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
/*
    SSSD

    Async LDAP Helper routines for sudo

    Authors:
        Pavel Březina <pbrezina@redhat.com>

    Copyright (C) 2012 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 <errno.h>
#include <talloc.h>
#include <tevent.h>

#include "providers/dp_backend.h"
#include "providers/ldap/ldap_common.h"
#include "providers/ldap/sdap.h"
#include "providers/ldap/sdap_async.h"
#include "providers/ldap/sdap_sudo.h"
#include "providers/ldap/sdap_sudo_cache.h"
#include "db/sysdb_sudo.h"

struct sdap_sudo_refresh_state {
    struct be_ctx *be_ctx;
    struct sdap_options *opts;
    struct sdap_id_op *sdap_op;
    struct sdap_id_conn_cache *sdap_conn_cache;
    struct sysdb_ctx *sysdb;
    struct sss_domain_info *domain;

    const char *ldap_filter;    /* search */
    const char *sysdb_filter;   /* delete */

    int dp_error;
    int error;
    char *highest_usn;
    size_t num_rules;
};

struct sdap_sudo_load_sudoers_state {
    struct tevent_context *ev;
    struct sdap_options *opts;
    struct sdap_handle *sh;
    struct sysdb_attrs **ldap_rules; /* search result will be stored here */
    size_t ldap_rules_count;         /* search result will be stored here */

    const char **attrs;
    const char *filter;
    size_t base_iter;
    struct sdap_search_base **search_bases;
    int timeout;
};

static int sdap_sudo_refresh_retry(struct tevent_req *req);

static void sdap_sudo_refresh_connect_done(struct tevent_req *subreq);

static struct tevent_req * sdap_sudo_load_sudoers_send(TALLOC_CTX *mem_ctx,
                                                       struct tevent_context *ev,
                                                       struct sdap_options *opts,
                                                       struct sdap_handle *sh,
                                                       const char *ldap_filter);

static errno_t sdap_sudo_load_sudoers_next_base(struct tevent_req *req);

static void sdap_sudo_load_sudoers_process(struct tevent_req *subreq);

static int sdap_sudo_load_sudoers_recv(struct tevent_req *req,
                                       TALLOC_CTX *mem_ctx,
                                       size_t *rules_count,
                                       struct sysdb_attrs ***rules);

static void sdap_sudo_refresh_load_done(struct tevent_req *subreq);

static int sdap_sudo_purge_sudoers(struct sss_domain_info *dom,
                                   const char *filter,
                                   struct sdap_attr_map *map,
                                   size_t rules_count,
                                   struct sysdb_attrs **rules);

static int sdap_sudo_store_sudoers(TALLOC_CTX *mem_ctx,
                                   struct sss_domain_info *domain,
                                   struct sdap_options *opts,
                                   size_t rules_count,
                                   struct sysdb_attrs **rules,
                                   int cache_timeout,
                                   time_t now,
                                   char **_usn);

struct tevent_req *sdap_sudo_refresh_send(TALLOC_CTX *mem_ctx,
                                          struct be_ctx *be_ctx,
                                          struct sdap_options *opts,
                                          struct sdap_id_conn_cache *conn_cache,
                                          const char *ldap_filter,
                                          const char *sysdb_filter)
{
    struct tevent_req *req;
    struct sdap_sudo_refresh_state *state;
    int ret;

    req = tevent_req_create(mem_ctx, &state, struct sdap_sudo_refresh_state);
    if (!req) {
        return NULL;
    }

    /* if we don't have a search filter, this request is meaningless */
    if (ldap_filter == NULL) {
        ret = EINVAL;
        goto immediately;
    }

    state->be_ctx = be_ctx;
    state->opts = opts;
    state->sdap_op = NULL;
    state->sdap_conn_cache = conn_cache;
    state->sysdb = be_ctx->domain->sysdb;
    state->domain = be_ctx->domain;
    state->ldap_filter = talloc_strdup(state, ldap_filter);
    state->sysdb_filter = talloc_strdup(state, sysdb_filter);
    state->dp_error = DP_ERR_OK;
    state->error = EOK;
    state->highest_usn = NULL;

    if (state->ldap_filter == NULL) {
        ret = ENOMEM;
        goto immediately;
    }

    if (sysdb_filter != NULL && state->sysdb_filter == NULL) {
        ret = ENOMEM;
        goto immediately;
    }

    ret = sdap_sudo_refresh_retry(req);
    if (ret == EAGAIN) {
        /* asynchronous processing */
        return req;
    }

immediately:
    if (ret == EOK) {
        tevent_req_done(req);
    } else {
        tevent_req_error(req, ret);
    }
    tevent_req_post(req, be_ctx->ev);

    return req;
}

int sdap_sudo_refresh_recv(TALLOC_CTX *mem_ctx,
                           struct tevent_req *req,
                           int *dp_error,
                           int *error,
                           char **usn,
                           size_t *num_rules)
{
    struct sdap_sudo_refresh_state *state;

    state = tevent_req_data(req, struct sdap_sudo_refresh_state);

    TEVENT_REQ_RETURN_ON_ERROR(req);

    *dp_error = state->dp_error;
    *error = state->error;

    if (usn != NULL && state->highest_usn != NULL) {
        *usn = talloc_steal(mem_ctx, state->highest_usn);
    }

    if (num_rules != NULL) {
        *num_rules = state->num_rules;
    }

    return EOK;
}

static int sdap_sudo_refresh_retry(struct tevent_req *req)
{
    struct sdap_sudo_refresh_state *state;
    struct tevent_req *subreq;
    int ret;

    state = tevent_req_data(req, struct sdap_sudo_refresh_state);

    if (be_is_offline(state->be_ctx)) {
        state->dp_error = DP_ERR_OFFLINE;
        state->error = EAGAIN;
        return EOK;
    }

    if (state->sdap_op == NULL) {
        state->sdap_op = sdap_id_op_create(state, state->sdap_conn_cache);
        if (state->sdap_op == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_create() failed\n");
            state->dp_error = DP_ERR_FATAL;
            state->error = EIO;
            return EIO;
        }
    }

    subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
    if (subreq == NULL) {
        DEBUG(SSSDBG_CRIT_FAILURE,
              "sdap_id_op_connect_send() failed: %d(%s)\n", ret, strerror(ret));
        talloc_zfree(state->sdap_op);
        state->dp_error = DP_ERR_FATAL;
        state->error = ret;
        return ret;
    }

    tevent_req_set_callback(subreq, sdap_sudo_refresh_connect_done, req);

    return EAGAIN;
}

static void sdap_sudo_refresh_connect_done(struct tevent_req *subreq)
{
    struct tevent_req *req; /* req from sdap_sudo_refresh_send() */
    struct sdap_sudo_refresh_state *state;
    int dp_error;
    int ret;

    req = tevent_req_callback_data(subreq, struct tevent_req);
    state = tevent_req_data(req, struct sdap_sudo_refresh_state);

    ret = sdap_id_op_connect_recv(subreq, &dp_error);
    talloc_zfree(subreq);

    if (dp_error == DP_ERR_OFFLINE) {
        talloc_zfree(state->sdap_op);
        state->dp_error = DP_ERR_OFFLINE;
        state->error = EAGAIN;
        tevent_req_done(req);
        return;
    } else if (ret != EOK) {
        DEBUG(SSSDBG_CRIT_FAILURE,
              "SUDO LDAP connection failed - %s\n", strerror(ret));
        goto fail;
    }

    DEBUG(SSSDBG_TRACE_FUNC, "SUDO LDAP connection successful\n");

    subreq = sdap_sudo_load_sudoers_send(state, state->be_ctx->ev,
                                         state->opts,
                                         sdap_id_op_handle(state->sdap_op),
                                         state->ldap_filter);
    if (subreq == NULL) {
        ret = EFAULT;
        goto fail;
    }

    tevent_req_set_callback(subreq, sdap_sudo_refresh_load_done, req);

    return;

fail:
    state->dp_error = DP_ERR_FATAL;
    state->error = ret;
    tevent_req_error(req, ret);
}

static struct tevent_req * sdap_sudo_load_sudoers_send(TALLOC_CTX *mem_ctx,
                                                       struct tevent_context *ev,
                                                       struct sdap_options *opts,
                                                       struct sdap_handle *sh,
                                                       const char *ldap_filter)



{
    struct tevent_req *req;
    struct sdap_sudo_load_sudoers_state *state;
    int ret;

    req = tevent_req_create(mem_ctx, &state, struct sdap_sudo_load_sudoers_state);
    if (!req) {
        return NULL;
    }

    state->ev = ev;
    state->opts = opts;
    state->sh = sh;
    state->base_iter = 0;
    state->search_bases = opts->sdom->sudo_search_bases;
    state->filter = ldap_filter;
    state->timeout = dp_opt_get_int(opts->basic, SDAP_SEARCH_TIMEOUT);
    state->ldap_rules = NULL;
    state->ldap_rules_count = 0;

    if (!state->search_bases) {
        DEBUG(SSSDBG_CRIT_FAILURE,
              "SUDOERS lookup request without a search base\n");
        ret = EINVAL;
        goto done;
    }

    /* create attrs from map */
    ret = build_attrs_from_map(state, opts->sudorule_map, SDAP_OPTS_SUDO,
                               NULL, &state->attrs, NULL);
    if (ret != EOK) {
        goto fail;
    }

    /* begin search */
    ret = sdap_sudo_load_sudoers_next_base(req);

done:
    if (ret != EOK) {
        tevent_req_error(req, ret);
        tevent_req_post(req, ev);
    }

    return req;

fail:
    talloc_zfree(req);
    return NULL;
}

static errno_t sdap_sudo_load_sudoers_next_base(struct tevent_req *req)
{
    struct sdap_sudo_load_sudoers_state *state;
    struct sdap_search_base *search_base;
    struct tevent_req *subreq;
    char *filter;

    state = tevent_req_data(req, struct sdap_sudo_load_sudoers_state);
    search_base = state->search_bases[state->base_iter];
    if (search_base == NULL) {
        /* should not happen */
        DEBUG(SSSDBG_CRIT_FAILURE, "search_base is null\n");
        return EFAULT;
    }

    /* create filter */
    filter = sdap_get_id_specific_filter(state, state->filter,
                                         search_base->filter);
    if (filter == NULL) {
        return ENOMEM;
    }

    /* send request */
    DEBUG(SSSDBG_TRACE_FUNC,
          "Searching for sudo rules with base [%s]\n",
           search_base->basedn);

    subreq = sdap_get_generic_send(state,
                                   state->ev,
                                   state->opts,
                                   state->sh,
                                   search_base->basedn,
                                   search_base->scope,
                                   filter,
                                   state->attrs,
                                   state->opts->sudorule_map,
                                   SDAP_OPTS_SUDO,
                                   state->timeout,
                                   true);
    if (subreq == NULL) {
        return ENOMEM;
    }

    tevent_req_set_callback(subreq, sdap_sudo_load_sudoers_process, req);

    return EOK;
}

static void sdap_sudo_load_sudoers_process(struct tevent_req *subreq)
{
    struct tevent_req *req;
    struct sdap_sudo_load_sudoers_state *state;
    struct sdap_search_base *search_base;
    struct sysdb_attrs **attrs = NULL;
    size_t count;
    int ret;
    int i;

    req = tevent_req_callback_data(subreq, struct tevent_req);
    state = tevent_req_data(req, struct sdap_sudo_load_sudoers_state);
    search_base = state->search_bases[state->base_iter];

    DEBUG(SSSDBG_TRACE_FUNC,
          "Receiving sudo rules with base [%s]\n",
           search_base->basedn);

    ret = sdap_get_generic_recv(subreq, state, &count, &attrs);
    talloc_zfree(subreq);
    if (ret) {
        tevent_req_error(req, ret);
        return;
    }

    /* add rules to result */
    if (count > 0) {
        state->ldap_rules = talloc_realloc(state, state->ldap_rules,
                                           struct sysdb_attrs *,
                                           state->ldap_rules_count + count);
        if (state->ldap_rules == NULL) {
            tevent_req_error(req, ENOMEM);
            return;
        }

        for (i = 0; i < count; i++) {
            state->ldap_rules[state->ldap_rules_count + i] = talloc_steal(
                                                   state->ldap_rules, attrs[i]);
        }

        state->ldap_rules_count += count;
    }

    /* go to next base */
    state->base_iter++;
    if (state->search_bases[state->base_iter]) {
        ret = sdap_sudo_load_sudoers_next_base(req);
        if (ret != EOK) {
            tevent_req_error(req, ret);
        }

        return;
    }

    /* we are done */
    tevent_req_done(req);
}

static int sdap_sudo_load_sudoers_recv(struct tevent_req *req,
                                       TALLOC_CTX *mem_ctx,
                                       size_t *rules_count,
                                       struct sysdb_attrs ***rules)
{
    struct sdap_sudo_load_sudoers_state *state;

    state = tevent_req_data(req, struct sdap_sudo_load_sudoers_state);

    TEVENT_REQ_RETURN_ON_ERROR(req);

    *rules_count = state->ldap_rules_count;
    *rules = talloc_steal(mem_ctx, state->ldap_rules);

    return EOK;
}

static void sdap_sudo_refresh_load_done(struct tevent_req *subreq)
{
    struct tevent_req *req; /* req from sdap_sudo_refresh_send() */
    struct sdap_sudo_refresh_state *state;
    struct sysdb_attrs **rules = NULL;
    size_t rules_count = 0;
    int ret;
    errno_t sret;
    bool in_transaction = false;
    time_t now;

    req = tevent_req_callback_data(subreq, struct tevent_req);
    state = tevent_req_data(req, struct sdap_sudo_refresh_state);

    ret = sdap_sudo_load_sudoers_recv(subreq, state, &rules_count, &rules);
    talloc_zfree(subreq);
    if (ret != EOK) {
        goto done;
    }

    DEBUG(SSSDBG_TRACE_FUNC, "Received %zu rules\n", rules_count);

    /* start transaction */
    ret = sysdb_transaction_start(state->sysdb);
    if (ret != EOK) {
        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
        goto done;
    }
    in_transaction = true;

    /* purge cache */
    ret = sdap_sudo_purge_sudoers(state->domain, state->sysdb_filter,
                                  state->opts->sudorule_map, rules_count, rules);
    if (ret != EOK) {
        goto done;
    }

    /* store rules */
    now = time(NULL);
    ret = sdap_sudo_store_sudoers(state, state->domain,
                                  state->opts, rules_count, rules,
                                  state->domain->sudo_timeout, now,
                                  &state->highest_usn);
    if (ret != EOK) {
        goto done;
    }

    /* commit transaction */
    ret = sysdb_transaction_commit(state->sysdb);
    if (ret != EOK) {
        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
        goto done;
    }
    in_transaction = false;

    DEBUG(SSSDBG_TRACE_FUNC, "Sudoers is successfuly stored in cache\n");

    ret = EOK;
    state->num_rules = rules_count;

done:
    if (in_transaction) {
        sret = sysdb_transaction_cancel(state->sysdb);
        if (sret != EOK) {
            DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n");
        }
    }

    state->error = ret;
    if (ret == EOK) {
        state->dp_error = DP_ERR_OK;
        tevent_req_done(req);
    } else {
        state->dp_error = DP_ERR_FATAL;
        tevent_req_error(req, ret);
    }
}

static int sdap_sudo_purge_sudoers(struct sss_domain_info *dom,
                                   const char *filter,
                                   struct sdap_attr_map *map,
                                   size_t rules_count,
                                   struct sysdb_attrs **rules)
{
    const char *name;
    int i;
    errno_t ret;

    if (filter == NULL) {
        /* removes downloaded rules from the cache */
        if (rules_count == 0 || rules == NULL) {
            return EOK;
        }

        for (i = 0; i < rules_count; i++) {
            ret = sysdb_attrs_get_string(rules[i],
                                         map[SDAP_AT_SUDO_NAME].sys_name,
                                         &name);
            if (ret != EOK) {
                DEBUG(SSSDBG_MINOR_FAILURE,
                      "Failed to retrieve rule name: [%s]\n", strerror(ret));
                continue;
            }

            ret = sysdb_sudo_purge_byname(dom, name);
            if (ret != EOK) {
                DEBUG(SSSDBG_MINOR_FAILURE,
                      "Failed to delete rule %s: [%s]\n",
                       name, strerror(ret));
                continue;
            }
        }

        ret = EOK;
    } else {
        /* purge cache by provided filter */
        ret = sysdb_sudo_purge_byfilter(dom, filter);
        if (ret != EOK) {
            goto done;
        }
    }

done:
    if (ret != EOK) {
        DEBUG(SSSDBG_OP_FAILURE, "failed to purge sudo rules [%d]: %s\n",
                                  ret, strerror(ret));
    }

    return ret;
}

static int sdap_sudo_store_sudoers(TALLOC_CTX *mem_ctx,
                                   struct sss_domain_info *domain,
                                   struct sdap_options *opts,
                                   size_t rules_count,
                                   struct sysdb_attrs **rules,
                                   int cache_timeout,
                                   time_t now,
                                   char **_usn)
{
    errno_t ret;

    /* Empty sudoers? Done. */
    if (rules_count == 0 || rules == NULL) {
        return EOK;
    }

    ret = sdap_save_native_sudorule_list(mem_ctx, domain,
                                         opts->sudorule_map, rules,
                                         rules_count, cache_timeout, now,
                                         _usn);
    if (ret != EOK) {
        DEBUG(SSSDBG_OP_FAILURE, "failed to save sudo rules [%d]: %s\n",
              ret, strerror(ret));
        return ret;
    }

    return EOK;
}