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; }
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; }
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); }
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); }