errno_t sysdb_getpwnam_with_views(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain, const char *name, struct ldb_result **res) { int ret; struct ldb_result *orig_obj = NULL; struct ldb_result *override_obj = NULL; TALLOC_CTX *tmp_ctx; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); return ENOMEM; } /* If there are views we first have to search the overrides for matches */ if (DOM_HAS_VIEWS(domain)) { ret = sysdb_search_user_override_by_name(tmp_ctx, domain, name, &override_obj, &orig_obj); if (ret != EOK && ret != ENOENT) { DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_override_by_name failed.\n"); goto done; } } /* If there are no views or nothing was found in the overrides the * original objects are searched. */ if (orig_obj == NULL) { ret = sysdb_getpwnam(tmp_ctx, domain, name, &orig_obj); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "sysdb_getpwnam failed.\n"); goto done; } } /* If there are views we have to check if override values must be added to * the original object. */ if (DOM_HAS_VIEWS(domain) && orig_obj->count == 1) { ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0], override_obj == NULL ? NULL : override_obj->msgs[0], NULL); if (ret != EOK && ret != ENOENT) { DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n"); goto done; } if (ret == ENOENT) { *res = talloc_zero(mem_ctx, struct ldb_result); if (*res == NULL) { DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n"); ret = ENOMEM; } else { ret = EOK; } goto done; }
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; }
/* * getpwnam, getgrnam and friends */ int sysdb_getpwnam_sync(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, const char *name, struct ops_ctx *out) { struct ldb_result *res; const char *str; int ret; ret = sysdb_getpwnam(mem_ctx, sysdb, name, &res); if (ret) { return ret; } switch (res->count) { case 0: DEBUG(1, ("No result for sysdb_getpwnam call\n")); return ENOENT; case 1: /* fill ops_ctx */ out->uid = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_UIDNUM, 0); out->gid = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_GIDNUM, 0); str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL); out->name = talloc_strdup(out, str); if (out->name == NULL) { return ENOMEM; } str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_GECOS, NULL); out->gecos = talloc_strdup(out, str); if (out->gecos == NULL) { return ENOMEM; } str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_HOMEDIR, NULL); out->home = talloc_strdup(out, str); if (out->home == NULL) { return ENOMEM; } str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SHELL, NULL); out->shell = talloc_strdup(out, str); if (out->shell == NULL) { return ENOMEM; } str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_DISABLED, NULL); if (str == NULL) { out->lock = DO_UNLOCK; } else { if (strcasecmp(str, "true") == 0) { out->lock = DO_LOCK; } else if (strcasecmp(str, "false") == 0) { out->lock = DO_UNLOCK; } else { /* Invalid value */ DEBUG(2, ("Invalid value for %s attribute: %s\n", SYSDB_DISABLED, str ? str : "NULL")); return EIO; } } break; default: DEBUG(1, ("More than one result for sysdb_getpwnam call\n")); return EIO; } return EOK; }
static errno_t sudosrv_get_user(struct sudo_dom_ctx *dctx) { TALLOC_CTX *tmp_ctx = NULL; struct sss_domain_info *dom = dctx->domain; struct sudo_cmd_ctx *cmd_ctx = dctx->cmd_ctx; struct cli_ctx *cli_ctx = dctx->cmd_ctx->cli_ctx; struct ldb_result *user; time_t cache_expire = 0; struct tevent_req *dpreq; struct dp_callback_ctx *cb_ctx; const char *original_name = NULL; char *name = NULL; uid_t uid = 0; errno_t ret; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n")); return ENOMEM; } while (dom) { /* if it is a domainless search, skip domains that require fully * qualified names instead */ while (dom && cmd_ctx->check_next && dom->fqnames) { dom = get_next_domain(dom, false); } if (!dom) break; /* make sure to update the dctx if we changed domain */ dctx->domain = dom; talloc_free(name); name = sss_get_cased_name(tmp_ctx, cmd_ctx->username, dom->case_sensitive); if (name == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n")); ret = ENOMEM; goto done; } DEBUG(SSSDBG_FUNC_DATA, ("Requesting info about [%s@%s]\n", name, dom->name)); ret = sysdb_getpwnam(dctx, dctx->domain->sysdb, dctx->domain, name, &user); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("Failed to make request to our cache!\n")); ret = EIO; goto done; } if (user->count > 1) { DEBUG(SSSDBG_CRIT_FAILURE, ("getpwnam call returned more than one result !?!\n")); ret = EIO; goto done; } if (user->count == 0 && !dctx->check_provider) { /* if a multidomain search, try with next */ if (cmd_ctx->check_next) { dctx->check_provider = true; dom = get_next_domain(dom, false); if (dom) continue; } DEBUG(SSSDBG_MINOR_FAILURE, ("No results for getpwnam call\n")); ret = ENOENT; goto done; } /* One result found, check cache expiry */ if (user->count == 1) { cache_expire = ldb_msg_find_attr_as_uint64(user->msgs[0], SYSDB_CACHE_EXPIRE, 0); } /* If cache miss and we haven't checked DP yet OR the entry is * outdated, go to DP */ if ((user->count == 0 || cache_expire < time(NULL)) && dctx->check_provider) { dpreq = sss_dp_get_account_send(cli_ctx, cli_ctx->rctx, dom, false, SSS_DP_INITGROUPS, cmd_ctx->username, 0, NULL); if (!dpreq) { DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory sending data provider request\n")); ret = ENOMEM; goto done; } cb_ctx = talloc_zero(cli_ctx, struct dp_callback_ctx); if(!cb_ctx) { talloc_zfree(dpreq); ret = ENOMEM; goto done; } cb_ctx->callback = sudosrv_check_user_dp_callback; cb_ctx->ptr = dctx; cb_ctx->cctx = cli_ctx; cb_ctx->mem_ctx = cli_ctx; tevent_req_set_callback(dpreq, sudosrv_dp_send_acct_req_done, cb_ctx); /* tell caller we are in an async call */ ret = EAGAIN; goto done; } /* check uid */ uid = ldb_msg_find_attr_as_int(user->msgs[0], SYSDB_UIDNUM, 0); if (uid != cmd_ctx->uid) { /* if a multidomain search, try with next */ if (cmd_ctx->check_next) { dctx->check_provider = true; dom = get_next_domain(dom, false); if (dom) continue; } DEBUG(SSSDBG_MINOR_FAILURE, ("UID does not match\n")); ret = ENOENT; goto done; } /* user is stored in cache, remember cased and original name */ original_name = ldb_msg_find_attr_as_string(user->msgs[0], SYSDB_NAME, NULL); if (original_name == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, ("A user with no name?\n")); ret = EFAULT; goto done; } cmd_ctx->cased_username = talloc_move(cmd_ctx, &name); cmd_ctx->orig_username = talloc_strdup(cmd_ctx, original_name); if (cmd_ctx->orig_username == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n")); ret = ENOMEM; goto done; } /* and set domain */ cmd_ctx->domain = dom; DEBUG(SSSDBG_TRACE_FUNC, ("Returning info for user [%s@%s]\n", cmd_ctx->username, dctx->domain->name)); ret = EOK; goto done; }
static int seed_domain_user_info(const char *name, const char *domain_name, struct sysdb_ctx *sysdb, struct sss_domain_info *domain, bool *is_cached) { TALLOC_CTX *tmp_ctx = NULL; char *fq_name = NULL; struct passwd *passwd = NULL; struct ldb_result *res = NULL; int ret = EOK; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { ret = ENOMEM; goto done; } fq_name = talloc_asprintf(tmp_ctx, "%s@%s", name, domain_name); if (fq_name == NULL) { ret = ENOMEM; goto done; } errno = 0; passwd = getpwnam(fq_name); if (passwd == NULL) { ret = errno; DEBUG(SSSDBG_MINOR_FAILURE, ("getpwnam failed [%d] [%s]\n", ret, strerror(ret))); goto done; } /* look for user in cache */ ret = sysdb_getpwnam(tmp_ctx, sysdb, domain, name, &res); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, ("Couldn't lookup user (%s) in the cache\n", name)); goto done; } if (res->count == 0) { DEBUG(SSSDBG_TRACE_INTERNAL, ("User (%s) wasn't found in the cache\n", name)); *is_cached = false; ret = ENOENT; goto done; } else if (res->count > 1) { DEBUG(SSSDBG_CRIT_FAILURE, ("Multiple user (%s) entries were found in the cache\n", name)); ret = EINVAL; goto done; } else { DEBUG(SSSDBG_TRACE_INTERNAL, ("User found in cache\n")); *is_cached = true; errno = 0; ret = initgroups(fq_name, passwd->pw_gid); if (ret != EOK) { ret = errno; DEBUG(SSSDBG_MINOR_FAILURE, ("initgroups failed [%d] [%s]\n", ret, strerror(ret))); goto done; } } done: if (ret == ENOMEM) { DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to allocate user information\n")); } talloc_zfree(tmp_ctx); return ret; }