Ejemplo n.º 1
0
static errno_t
ssh_user_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx)
{
    struct tevent_req *req;
    struct dp_callback_ctx *cb_ctx;

    /* if it is a domainless search, skip domains that require fully
     * qualified names instead */
    while (cmd_ctx->domain && cmd_ctx->check_next && cmd_ctx->domain->fqnames) {
        cmd_ctx->domain = get_next_domain(cmd_ctx->domain, false);
    }

    if (!cmd_ctx->domain) {
        DEBUG(SSSDBG_OP_FAILURE,
              "No matching domain found for [%s], fail!\n", cmd_ctx->name);
        return ssh_user_handle_not_found(cmd_ctx->name);
    }

    talloc_zfree(cmd_ctx->fqdn);
    cmd_ctx->fqdn = sss_resp_create_fqname(cmd_ctx, cmd_ctx->cctx->rctx,
                                           cmd_ctx->domain, false, cmd_ctx->name);
    if (cmd_ctx->fqdn == NULL) {
        return ENOMEM;
    }

    /* refresh the user's cache entry */
    if (NEED_CHECK_PROVIDER(cmd_ctx->domain->provider)) {
        req = sss_dp_get_account_send(cmd_ctx, cmd_ctx->cctx->rctx,
                                      cmd_ctx->domain, false, SSS_DP_USER,
                                      cmd_ctx->fqdn, 0, NULL);
        if (!req) {
            DEBUG(SSSDBG_CRIT_FAILURE,
                  "Out of memory sending data provider request\n");
            return ENOMEM;
        }

        cb_ctx = talloc_zero(cmd_ctx, struct dp_callback_ctx);
        if (!cb_ctx) {
            talloc_zfree(req);
            return ENOMEM;
        }

        cb_ctx->callback = ssh_user_pubkeys_search_dp_callback;
        cb_ctx->ptr = cmd_ctx;
        cb_ctx->cctx = cmd_ctx->cctx;
        cb_ctx->mem_ctx = cmd_ctx;

        tevent_req_set_callback(req, ssh_dp_send_req_done, cb_ctx);

        /* tell caller we are in an async call */
        return EAGAIN;
    }
Ejemplo n.º 2
0
static int pam_check_user_search(struct pam_auth_req *preq)
{
    struct sss_domain_info *dom = preq->domain;
    char *name = NULL;
    time_t cacheExpire;
    int ret;
    struct tevent_req *dpreq;
    struct dp_callback_ctx *cb_ctx;
    struct pam_ctx *pctx =
            talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);

    while (dom) {
       /* if it is a domainless search, skip domains that require fully
         * qualified names instead */
        while (dom && !preq->pd->domain && dom->fqnames) {
            dom = get_next_domain(dom, false);
        }

        if (!dom) break;

        if (dom != preq->domain) {
            /* make sure we reset the check_provider flag when we check
             * a new domain */
            preq->check_provider = NEED_CHECK_PROVIDER(dom->provider);
        }

        /* make sure to update the preq if we changed domain */
        preq->domain = dom;

        talloc_free(name);
        name = sss_get_cased_name(preq, preq->pd->user,
                                  dom->case_sensitive);
        if (!name) {
            return ENOMEM;
        }

        name = sss_reverse_replace_space(preq, name,
                                         pctx->rctx->override_space);
        if (name == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE,
                  "sss_reverse_replace_space failed\n");
            return ENOMEM;
        }

        /* Refresh the user's cache entry on any PAM query
         * We put a timeout in the client context so that we limit
         * the number of updates within a reasonable timeout
         */
        if (preq->check_provider) {
            ret = pam_initgr_check_timeout(pctx->id_table, name);
            if (ret != EOK
                    && ret != ENOENT) {
                DEBUG(SSSDBG_OP_FAILURE,
                      "Could not look up initgroup timout\n");
                return EIO;
            } else if (ret == ENOENT) {
                /* Call provider first */
                break;
            }
            /* Entry is still valid, get it from the sysdb */
        }

        DEBUG(SSSDBG_CONF_SETTINGS,
              "Requesting info for [%s@%s]\n", name, dom->name);

        if (dom->sysdb == NULL) {
            DEBUG(SSSDBG_FATAL_FAILURE,
                  "Fatal: Sysdb CTX not found for this domain!\n");
            preq->pd->pam_status = PAM_SYSTEM_ERR;
            return EFAULT;
        }

        ret = sysdb_getpwnam(preq, dom, name, &preq->res);
        if (ret != EOK) {
            DEBUG(SSSDBG_CRIT_FAILURE,
                  "Failed to make request to our cache!\n");
            return EIO;
        }

        if (preq->res->count > 1) {
            DEBUG(SSSDBG_FATAL_FAILURE,
                  "getpwnam call returned more than one result !?!\n");
            return ENOENT;
        }

        if (preq->res->count == 0) {
            if (preq->check_provider == false) {
                /* set negative cache only if not result of cache check */
                ret = sss_ncache_set_user(pctx->ncache, false, dom, name);
                if (ret != EOK) {
                    /* Should not be fatal, just slower next time */
                    DEBUG(SSSDBG_MINOR_FAILURE,
                           "Cannot set ncache for [%s@%s]\n", name,
                            dom->name);
                }
            }

            /* if a multidomain search, try with next */
            if (!preq->pd->domain) {
                dom = get_next_domain(dom, false);
                continue;
            }

            DEBUG(SSSDBG_OP_FAILURE, "No results for getpwnam call\n");

            /* TODO: store negative cache ? */

            return ENOENT;
        }

        /* One result found */

        /* if we need to check the remote account go on */
        if (preq->check_provider) {
            cacheExpire = ldb_msg_find_attr_as_uint64(preq->res->msgs[0],
                                                      SYSDB_CACHE_EXPIRE, 0);
            if (cacheExpire < time(NULL)) {
                break;
            }
        }

        DEBUG(SSSDBG_TRACE_FUNC,
              "Returning info for user [%s@%s]\n", name, dom->name);

        /* We might have searched by alias. Pass on the primary name */
        ret = pd_set_primary_name(preq->res->msgs[0], preq->pd);
        if (ret != EOK) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Could not canonicalize username\n");
            return ret;
        }

        return EOK;
    }

    if (!dom) {
        /* Ensure that we don't try to check a provider without a domain,
         * since this will cause a NULL-dereference below.
         */
        preq->check_provider = false;
    }

    if (preq->check_provider) {

        /* dont loop forever :-) */
        preq->check_provider = false;

        dpreq = sss_dp_get_account_send(preq, preq->cctx->rctx,
                                        dom, false, SSS_DP_INITGROUPS,
                                        name, 0, NULL);
        if (!dpreq) {
            DEBUG(SSSDBG_CRIT_FAILURE,
                  "Out of memory sending data provider request\n");
            return ENOMEM;
        }

        cb_ctx = talloc_zero(preq, struct dp_callback_ctx);
        if(!cb_ctx) {
            talloc_zfree(dpreq);
            return ENOMEM;
        }

        cb_ctx->callback = pam_check_user_dp_callback;
        cb_ctx->ptr = preq;
        cb_ctx->cctx = preq->cctx;
        cb_ctx->mem_ctx = preq;

        tevent_req_set_callback(dpreq, pam_dp_send_acct_req_done, cb_ctx);

        /* tell caller we are in an async call */
        return EAGAIN;
    }
Ejemplo n.º 3
0
static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
{
    struct sss_domain_info *dom;
    struct pam_auth_req *preq;
    struct pam_data *pd;
    int ret;
    errno_t ncret;
    struct pam_ctx *pctx =
            talloc_get_type(cctx->rctx->pvt_ctx, struct pam_ctx);
    struct tevent_req *req;

    preq = talloc_zero(cctx, struct pam_auth_req);
    if (!preq) {
        return ENOMEM;
    }
    talloc_set_destructor(preq, pam_auth_req_destructor);
    preq->cctx = cctx;

    preq->pd = create_pam_data(preq);
    if (!preq->pd) {
        talloc_free(preq);
        return ENOMEM;
    }
    pd = preq->pd;

    pd->cmd = pam_cmd;
    pd->priv = cctx->priv;

    ret = pam_forwarder_parse_data(cctx, pd);
    if (ret == EAGAIN) {
        req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, pd->domain);
        if (req == NULL) {
            ret = ENOMEM;
        } else {
            tevent_req_set_callback(req, pam_forwarder_cb, preq);
            ret = EAGAIN;
        }
        goto done;
    } else if (ret != EOK) {
        ret = EINVAL;
        goto done;
    }

    /* now check user is valid */
    if (pd->domain) {
        preq->domain = responder_get_domain(cctx->rctx, pd->domain);
        if (!preq->domain) {
            ret = ENOENT;
            goto done;
        }

        ncret = sss_ncache_check_user(pctx->ncache, pctx->neg_timeout,
                                      preq->domain, pd->user);
        if (ncret == EEXIST) {
            /* User found in the negative cache */
            ret = ENOENT;
            goto done;
        }
    } else {
        for (dom = preq->cctx->rctx->domains;
             dom;
             dom = get_next_domain(dom, false)) {
            if (dom->fqnames) continue;

            ncret = sss_ncache_check_user(pctx->ncache, pctx->neg_timeout,
                                          dom, pd->user);
            if (ncret == ENOENT) {
                /* User not found in the negative cache
                 * Proceed with PAM actions
                 */
                break;
            }

            /* Try the next domain */
            DEBUG(SSSDBG_TRACE_FUNC,
                  "User [%s@%s] filtered out (negative cache). "
                   "Trying next domain.\n", pd->user, dom->name);
        }
        if (!dom) {
            ret = ENOENT;
            goto done;
        }
        preq->domain = dom;
    }

    if (preq->domain->provider == NULL) {
        DEBUG(SSSDBG_CRIT_FAILURE,
              "Domain [%s] has no auth provider.\n", preq->domain->name);
        ret = EINVAL;
        goto done;
    }

    preq->check_provider = NEED_CHECK_PROVIDER(preq->domain->provider);

    ret = pam_check_user_search(preq);
    if (ret == EOK) {
        pam_dom_forwarder(preq);
    }

done:
    return pam_check_user_done(preq, ret);
}
Ejemplo n.º 4
0
static void pam_reply(struct pam_auth_req *preq)
{
    struct cli_ctx *cctx;
    uint8_t *body;
    size_t blen;
    int ret;
    int32_t resp_c;
    int32_t resp_size;
    struct response_data *resp;
    int p;
    struct timeval tv;
    struct tevent_timer *te;
    struct pam_data *pd;
    struct pam_ctx *pctx;
    uint32_t user_info_type;
    time_t exp_date = -1;
    time_t delay_until = -1;

    pd = preq->pd;
    cctx = preq->cctx;
    pctx = talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);


    DEBUG(SSSDBG_FUNC_DATA,
          "pam_reply called with result [%d].\n", pd->pam_status);

    if (pd->pam_status == PAM_AUTHINFO_UNAVAIL) {
        switch(pd->cmd) {
        case SSS_PAM_AUTHENTICATE:
            if ((preq->domain != NULL) &&
                (preq->domain->cache_credentials == true) &&
                (pd->offline_auth == false)) {
                const char *password = NULL;

                /* do auth with offline credentials */
                pd->offline_auth = true;

                if (preq->domain->sysdb == NULL) {
                    DEBUG(SSSDBG_FATAL_FAILURE,
                          "Fatal: Sysdb CTX not found for domain"
                              " [%s]!\n", preq->domain->name);
                    goto done;
                }

                ret = sss_authtok_get_password(pd->authtok, &password, NULL);
                if (ret) {
                    DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get password.\n");
                    goto done;
                }

                ret = sysdb_cache_auth(preq->domain,
                                       pd->user, password,
                                       pctx->rctx->cdb, false,
                                       &exp_date, &delay_until);

                pam_handle_cached_login(preq, ret, exp_date, delay_until);
                return;
            }
            break;
        case SSS_PAM_CHAUTHTOK_PRELIM:
        case SSS_PAM_CHAUTHTOK:
            DEBUG(SSSDBG_FUNC_DATA,
                  "Password change not possible while offline.\n");
            pd->pam_status = PAM_AUTHTOK_ERR;
            user_info_type = SSS_PAM_USER_INFO_OFFLINE_CHPASS;
            ret = pam_add_response(pd, SSS_PAM_USER_INFO, sizeof(uint32_t),
                                   (const uint8_t *) &user_info_type);
            if (ret != EOK) {
                DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
                goto done;
            }
            break;
/* TODO: we need the pam session cookie here to make sure that cached
 * authentication was successful */
        case SSS_PAM_SETCRED:
        case SSS_PAM_ACCT_MGMT:
        case SSS_PAM_OPEN_SESSION:
        case SSS_PAM_CLOSE_SESSION:
            DEBUG(SSSDBG_OP_FAILURE,
                  "Assuming offline authentication setting status for "
                      "pam call %d to PAM_SUCCESS.\n", pd->cmd);
            pd->pam_status = PAM_SUCCESS;
            break;
        default:
            DEBUG(SSSDBG_CRIT_FAILURE, "Unknown PAM call [%d].\n", pd->cmd);
            pd->pam_status = PAM_MODULE_UNKNOWN;
        }
    }

    if (pd->response_delay > 0) {
        ret = gettimeofday(&tv, NULL);
        if (ret != EOK) {
            DEBUG(SSSDBG_CRIT_FAILURE, "gettimeofday failed [%d][%s].\n",
                      errno, strerror(errno));
            goto done;
        }
        tv.tv_sec += pd->response_delay;
        tv.tv_usec = 0;
        pd->response_delay = 0;

        te = tevent_add_timer(cctx->ev, cctx, tv, pam_reply_delay, preq);
        if (te == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE,
                  "Failed to add event pam_reply_delay.\n");
            goto done;
        }

        return;
    }

    /* If this was a successful login, save the lastLogin time */
    if (pd->cmd == SSS_PAM_AUTHENTICATE &&
        pd->pam_status == PAM_SUCCESS &&
        preq->domain->cache_credentials &&
        !pd->offline_auth &&
        !pd->last_auth_saved &&
        NEED_CHECK_PROVIDER(preq->domain->provider)) {
        ret = set_last_login(preq);
        if (ret != EOK) {
            goto done;
        }
        return;
    }

    ret = sss_packet_new(cctx->creq, 0, sss_packet_get_cmd(cctx->creq->in),
                         &cctx->creq->out);
    if (ret != EOK) {
        goto done;
    }

    ret = filter_responses(pctx->rctx->cdb, pd->resp_list);
    if (ret != EOK) {
        DEBUG(SSSDBG_CRIT_FAILURE, "filter_responses failed, not fatal.\n");
    }

    if (pd->domain != NULL) {
        ret = pam_add_response(pd, SSS_PAM_DOMAIN_NAME, strlen(pd->domain)+1,
                               (uint8_t *) pd->domain);
        if (ret != EOK) {
            DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
            goto done;
        }
    }

    resp_c = 0;
    resp_size = 0;
    resp = pd->resp_list;
    while(resp != NULL) {
        if (!resp->do_not_send_to_client) {
            resp_c++;
            resp_size += resp->len;
        }
        resp = resp->next;
    }

    ret = sss_packet_grow(cctx->creq->out, sizeof(int32_t) +
                                           sizeof(int32_t) +
                                           resp_c * 2* sizeof(int32_t) +
                                           resp_size);
    if (ret != EOK) {
        goto done;
    }

    sss_packet_get_body(cctx->creq->out, &body, &blen);
    DEBUG(SSSDBG_FUNC_DATA, "blen: %zu\n", blen);
    p = 0;

    memcpy(&body[p], &pd->pam_status, sizeof(int32_t));
    p += sizeof(int32_t);

    memcpy(&body[p], &resp_c, sizeof(int32_t));
    p += sizeof(int32_t);

    resp = pd->resp_list;
    while(resp != NULL) {
        if (!resp->do_not_send_to_client) {
            memcpy(&body[p], &resp->type, sizeof(int32_t));
            p += sizeof(int32_t);
            memcpy(&body[p], &resp->len, sizeof(int32_t));
            p += sizeof(int32_t);
            memcpy(&body[p], resp->data, resp->len);
            p += resp->len;
        }

        resp = resp->next;
    }

done:
    sss_cmd_done(cctx, preq);
}