nsssrv_cmd.c revision 065771c9859df9c4137daa5187be3aa5633b3cd5
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce/*
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce SSSD
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce NSS Responder
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce This program is free software; you can redistribute it and/or modify
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce it under the terms of the GNU General Public License as published by
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce the Free Software Foundation; either version 3 of the License, or
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce (at your option) any later version.
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce This program is distributed in the hope that it will be useful,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce but WITHOUT ANY WARRANTY; without even the implied warranty of
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce GNU General Public License for more details.
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce You should have received a copy of the GNU General Public License
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce along with this program. If not, see <http://www.gnu.org/licenses/>.
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce*/
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce#include "util/util.h"
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce#include "util/sss_nss.h"
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce#include "responder/nss/nsssrv.h"
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce#include "responder/nss/nsssrv_private.h"
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce#include "responder/nss/nsssrv_netgroup.h"
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce#include "responder/nss/nsssrv_services.h"
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce#include "responder/nss/nsssrv_mmap_cache.h"
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce#include "responder/common/negcache.h"
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce#include "confdb/confdb.h"
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce#include "db/sysdb.h"
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce#include <time.h>
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorcestatic int nss_cmd_send_error(struct nss_cmd_ctx *cmdctx, int err)
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce{
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce return sss_cmd_send_error(cmdctx->cctx, err);
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce}
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorcestatic int nss_cmd_send_empty(struct nss_cmd_ctx *cmdctx)
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce{
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce struct cli_ctx *cctx = cmdctx->cctx;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce return sss_cmd_send_empty(cctx, cmdctx);
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce}
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorceint nss_cmd_done(struct nss_cmd_ctx *cmdctx, int ret)
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce{
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce switch (ret) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce case EOK:
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce /* all fine, just return here */
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce break;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce case ENOENT:
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce ret = nss_cmd_send_empty(cmdctx);
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce if (ret) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce return EFAULT;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce }
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce break;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce case EAGAIN:
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce /* async processing, just return here */
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce break;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce case EFAULT:
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce /* very bad error */
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce return EFAULT;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce default:
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce ret = nss_cmd_send_error(cmdctx, ret);
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce if (ret) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce return EFAULT;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce }
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce sss_cmd_done(cmdctx->cctx, cmdctx);
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce break;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce }
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce return EOK;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce}
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce/***************************
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce * Enumeration procedures *
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce ***************************/
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorceerrno_t nss_setent_add_ref(TALLOC_CTX *memctx,
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce struct getent_ctx *getent_ctx,
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce struct tevent_req *req)
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce{
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce return setent_add_ref(memctx, getent_ctx, &getent_ctx->reqs, req);
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce}
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorcevoid nss_setent_notify_error(struct getent_ctx *getent_ctx, errno_t ret)
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce{
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce return setent_notify(&getent_ctx->reqs, ret);
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce}
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorcevoid nss_setent_notify_done(struct getent_ctx *getent_ctx)
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce{
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return setent_notify_done(&getent_ctx->reqs);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce}
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorcestruct setent_ctx {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct cli_ctx *client;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct nss_ctx *nctx;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct nss_dom_ctx *dctx;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct getent_ctx *getent_ctx;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce};
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce/****************************************************************************
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * PASSWD db related functions
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ***************************************************************************/
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorcestatic gid_t get_gid_override(struct ldb_message *msg,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct sss_domain_info *dom)
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce{
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return dom->override_gid ?
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce dom->override_gid :
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce}
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorcestatic const char *get_homedir_override(TALLOC_CTX *mem_ctx,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct ldb_message *msg,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct nss_ctx *nctx,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct sss_domain_info *dom,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce const char *name,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce uint32_t uid)
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce{
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce const char *homedir;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* Check whether we are unconditionally overriding the server
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * for home directory locations.
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (dom->override_homedir) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return expand_homedir_template(mem_ctx, dom->override_homedir,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce name, uid, dom->name);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce } else if (nctx->override_homedir) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return expand_homedir_template(mem_ctx, nctx->override_homedir,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce name, uid, dom->name);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce homedir = ldb_msg_find_attr_as_string(msg, SYSDB_HOMEDIR, NULL);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (!homedir || *homedir == '\0') {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* In the case of a NULL or empty homedir, check to see if
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * we have a fallback homedir to use.
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (dom->fallback_homedir) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return expand_homedir_template(mem_ctx, dom->fallback_homedir,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce name, uid, dom->name);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce } else if (nctx->fallback_homedir) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return expand_homedir_template(mem_ctx, nctx->fallback_homedir,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce name, uid, dom->name);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* Return the value we got from the provider */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return talloc_strdup(mem_ctx, homedir);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce}
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorcestatic const char *get_shell_override(TALLOC_CTX *mem_ctx,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct ldb_message *msg,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct nss_ctx *nctx)
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce{
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce const char *user_shell;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce int i;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce user_shell = ldb_msg_find_attr_as_string(msg, SYSDB_SHELL, NULL);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (!user_shell) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* Check whether there is a default shell specified */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (nctx->default_shell) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return talloc_strdup(mem_ctx, nctx->default_shell);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return NULL;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce }
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce if (!nctx->allowed_shells && !nctx->vetoed_shells) return talloc_strdup(mem_ctx, user_shell);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce if (nctx->vetoed_shells) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce for (i=0; nctx->vetoed_shells[i]; i++) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (strcmp(nctx->vetoed_shells[i], user_shell) == 0) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(5, ("The shell '%s' is vetoed. "
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce "Using fallback\n", user_shell));
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce return talloc_strdup(mem_ctx, nctx->shell_fallback);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (nctx->etc_shells) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce for (i=0; nctx->etc_shells[i]; i++) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (strcmp(user_shell, nctx->etc_shells[i]) == 0) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(9, ("Shell %s found in /etc/shells\n",
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce nctx->etc_shells[i]));
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce break;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce if (nctx->etc_shells[i]) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(9, ("Using original shell '%s'\n", user_shell));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return talloc_strdup(mem_ctx, user_shell);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (nctx->allowed_shells) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce for (i=0; nctx->allowed_shells[i]; i++) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (strcmp(nctx->allowed_shells[i], user_shell) == 0) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(5, ("The shell '%s' is allowed but does not exist. "
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce "Using fallback\n", user_shell));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return talloc_strdup(mem_ctx, nctx->shell_fallback);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(5, ("The shell '%s' is not allowed and does not exist.\n",
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce user_shell));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return talloc_strdup(mem_ctx, NOLOGIN_SHELL);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce}
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorcestatic int fill_pwent(struct sss_packet *packet,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct sss_domain_info *dom,
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce struct nss_ctx *nctx,
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce bool filter_users, bool pw_mmap_cache,
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce struct ldb_message **msgs,
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce int *count)
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce{
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce struct ldb_message *msg;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce uint8_t *body;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce const char *tmpstr;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce const char *orig_name;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct sized_string name;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct sized_string gecos;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct sized_string homedir;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct sized_string shell;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct sized_string pwfield;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct sized_string fullname;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce uint32_t uid;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce uint32_t gid;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce size_t rsize, rp, blen;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce size_t dom_len = 0;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce int delim = 1;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce int i, ret, num, t;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce bool add_domain = dom->fqnames;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce const char *domain = dom->name;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce const char *namefmt;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce bool packet_initialized = false;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce int ncret;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce TALLOC_CTX *tmp_ctx = NULL;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce namefmt = dom->names->fq_fmt;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (add_domain) dom_len = strlen(domain);
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce to_sized_string(&pwfield, nctx->pwfield);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce rp = 2*sizeof(uint32_t);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce num = 0;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce for (i = 0; i < *count; i++) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce talloc_zfree(tmp_ctx);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce tmp_ctx = talloc_new(NULL);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce msg = msgs[i];
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce orig_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce uid = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce gid = get_gid_override(msg, dom);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (!orig_name || !uid || !gid) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(SSSDBG_OP_FAILURE, ("Incomplete user object for %s[%llu]! Skipping\n",
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce orig_name?orig_name:"<NULL>", (unsigned long long int)uid));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce continue;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (filter_users) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ncret = sss_ncache_check_user(nctx->ncache,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce nctx->neg_timeout,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce dom, orig_name);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (ncret == EEXIST) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(4, ("User [%s@%s] filtered out! (negative cache)\n",
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce orig_name, domain));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce continue;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (!packet_initialized) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* first 2 fields (len and reserved), filled up later */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ret = sss_packet_grow(packet, 2*sizeof(uint32_t));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (ret != EOK) return ret;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce packet_initialized = true;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce }
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce tmpstr = sss_get_cased_name(tmp_ctx, orig_name, dom->case_sensitive);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce if (tmpstr == NULL) {
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce DEBUG(SSSDBG_CRIT_FAILURE,
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce ("sss_get_cased_name failed, skipping\n"));
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce continue;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce }
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce to_sized_string(&name, tmpstr);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce tmpstr = ldb_msg_find_attr_as_string(msg, SYSDB_GECOS, NULL);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce if (!tmpstr) {
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce to_sized_string(&gecos, "");
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce } else {
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce to_sized_string(&gecos, tmpstr);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce }
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce tmpstr = get_homedir_override(tmp_ctx, msg, nctx, dom, name.str, uid);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce if (!tmpstr) {
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce to_sized_string(&homedir, "/");
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce } else {
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce to_sized_string(&homedir, tmpstr);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce }
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce tmpstr = get_shell_override(tmp_ctx, msg, nctx);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce if (!tmpstr) {
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce to_sized_string(&shell, "");
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce } else {
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce to_sized_string(&shell, tmpstr);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce }
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce rsize = 2 * sizeof(uint32_t) + name.len + gecos.len +
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce homedir.len + shell.len + pwfield.len;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce if (add_domain) rsize += delim + dom_len;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce ret = sss_packet_grow(packet, rsize);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (ret != EOK) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce num = 0;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce goto done;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce sss_packet_get_body(packet, &body, &blen);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce SAFEALIGN_SET_UINT32(&body[rp], uid, &rp);
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce SAFEALIGN_SET_UINT32(&body[rp], gid, &rp);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (add_domain) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ret = snprintf((char *)&body[rp],
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce name.len + delim + dom_len,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce namefmt, name.str, domain);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (ret >= (name.len + delim + dom_len)) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* need more space, got creative with the print format ? */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce t = ret - (name.len + delim + dom_len) + 1;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce ret = sss_packet_grow(packet, t);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce if (ret != EOK) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce num = 0;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce goto done;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce }
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce delim += t;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce sss_packet_get_body(packet, &body, &blen);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* retry */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ret = snprintf((char *)&body[rp],
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce name.len + delim + dom_len,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce namefmt, name.str, domain);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce if (ret != name.len + delim + dom_len - 1) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce DEBUG(1, ("Failed to generate a fully qualified name for user "
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce "[%s] in [%s]! Skipping user.\n", name.str, domain));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce continue;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce } else {
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce memcpy(&body[rp], name.str, name.len);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce }
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce to_sized_string(&fullname, (const char *)&body[rp]);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce rp += fullname.len;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce memcpy(&body[rp], pwfield.str, pwfield.len);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce rp += pwfield.len;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce memcpy(&body[rp], gecos.str, gecos.len);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce rp += gecos.len;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce memcpy(&body[rp], homedir.str, homedir.len);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce rp += homedir.len;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce memcpy(&body[rp], shell.str, shell.len);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce rp += shell.len;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce num++;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (pw_mmap_cache && nctx->pwd_mc_ctx) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce ret = sss_mmap_cache_pw_store(nctx->pwd_mc_ctx,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce &fullname, &pwfield,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce uid, gid,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce &gecos, &homedir, &shell);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (ret != EOK && ret != ENOMEM) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(1, ("Failed to store user %s(%s) in mmap cache!",
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce name.str, domain));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce talloc_zfree(tmp_ctx);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorcedone:
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce *count = i;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* if there are no results just return ENOENT,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * let the caller decide if this is the last packet or not */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (!packet_initialized) return ENOENT;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce sss_packet_get_body(packet, &body, &blen);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce ((uint32_t *)body)[0] = num; /* num results */
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce ((uint32_t *)body)[1] = 0; /* reserved */
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce return EOK;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce}
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorcestatic int nss_cmd_getpw_send_reply(struct nss_dom_ctx *dctx, bool filter)
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce{
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce struct cli_ctx *cctx = cmdctx->cctx;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce struct nss_ctx *nctx;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce int ret;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce int i;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce ret = sss_packet_new(cctx->creq, 0,
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce sss_packet_get_cmd(cctx->creq->in),
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce &cctx->creq->out);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce if (ret != EOK) {
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce return EFAULT;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce }
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce i = dctx->res->count;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce ret = fill_pwent(cctx->creq->out,
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce dctx->domain,
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce nctx, filter, true,
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce dctx->res->msgs, &i);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce if (ret) {
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce return ret;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce }
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce sss_packet_set_error(cctx->creq->out, EOK);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce sss_cmd_done(cctx, cmdctx);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce return EOK;
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce}
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorcestatic void nsssrv_dp_send_acct_req_done(struct tevent_req *req);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce/* FIXME: do not check res->count, but get in a msgs and check in parent */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce/* FIXME: do not sss_cmd_done, but return error and let parent do it */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorceerrno_t check_cache(struct nss_dom_ctx *dctx,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct nss_ctx *nctx,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct ldb_result *res,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce int req_type,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce const char *opt_name,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce uint32_t opt_id,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce sss_dp_callback_t callback,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce void *pvt)
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce{
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce errno_t ret;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct cli_ctx *cctx = cmdctx->cctx;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct tevent_req *req = NULL;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct dp_callback_ctx *cb_ctx = NULL;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce uint64_t cacheExpire = 0;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* when searching for a user or netgroup, more than one reply is a
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * db error
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if ((req_type == SSS_DP_USER || req_type == SSS_DP_NETGR) &&
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce (res->count > 1)) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(1, ("getpwXXX call returned more than one result!"
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce " DB Corrupted?\n"));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ret = nss_cmd_send_error(cmdctx, ENOENT);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (ret != EOK) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce NSS_CMD_FATAL_ERROR_CODE(cctx, ENOENT);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce sss_cmd_done(cctx, cmdctx);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return ENOENT;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* if we have any reply let's check cache validity */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (res->count > 0) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (req_type == SSS_DP_INITGROUPS) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce cacheExpire = ldb_msg_find_attr_as_uint64(res->msgs[0],
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce SYSDB_INITGR_EXPIRE, 1);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (cacheExpire == 0) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce cacheExpire = ldb_msg_find_attr_as_uint64(res->msgs[0],
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce SYSDB_CACHE_EXPIRE, 0);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* if we have any reply let's check cache validity */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ret = sss_cmd_check_cache(res->msgs[0], nctx->cache_refresh_percent,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce cacheExpire);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (ret == EOK) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(SSSDBG_TRACE_FUNC, ("Cached entry is valid, returning..\n"));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return EOK;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce } else if (ret != EAGAIN && ret != ENOENT) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(SSSDBG_CRIT_FAILURE, ("Error checking cache: %d\n", ret));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce goto error;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce } else {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* No replies */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ret = ENOENT;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce /* EAGAIN (off band) or ENOENT (cache miss) -> check cache */
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce if (ret == EAGAIN) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* No callback required
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * This was an out-of-band update. We'll return EOK
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * so the calling function can return the cached entry
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * immediately.
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(SSSDBG_TRACE_FUNC,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ("Performing midpoint cache update on [%s]\n", opt_name));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce req = sss_dp_get_account_send(cctx, cctx->rctx, dctx->domain, true,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce req_type, opt_name, opt_id, NULL);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (!req) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(SSSDBG_CRIT_FAILURE,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ("Out of memory sending out-of-band data provider "
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce "request\n"));
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce /* This is non-fatal, so we'll continue here */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce } else {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(SSSDBG_TRACE_FUNC, ("Updating cache out-of-band\n"));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce /* We don't need to listen for a reply, so we will free the
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce * request here.
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce */
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce talloc_zfree(req);
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce } else {
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce /* This is a cache miss. Or the cache is expired.
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce * We need to get the updated user information before returning it.
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce */
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce /* dont loop forever :-) */
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce dctx->check_provider = false;
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* keep around current data in case backend is offline */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (res->count) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce dctx->res = talloc_steal(dctx, res);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce req = sss_dp_get_account_send(cctx, cctx->rctx, dctx->domain, true,
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce req_type, opt_name, opt_id, NULL);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (!req) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(SSSDBG_CRIT_FAILURE,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ("Out of memory sending data provider request\n"));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ret = ENOMEM;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce goto error;
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce }
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce cb_ctx = talloc_zero(dctx, struct dp_callback_ctx);
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce if(!cb_ctx) {
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce talloc_zfree(req);
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce ret = ENOMEM;
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce goto error;
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce cb_ctx->callback = callback;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce cb_ctx->ptr = pvt;
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce cb_ctx->cctx = dctx->cmdctx->cctx;
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce cb_ctx->mem_ctx = dctx;
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce tevent_req_set_callback(req, nsssrv_dp_send_acct_req_done, cb_ctx);
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return EAGAIN;
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce }
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce return EOK;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorceerror:
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce ret = nss_cmd_send_error(cmdctx, ret);
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce if (ret != EOK) {
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce NSS_CMD_FATAL_ERROR_CODE(cctx, ret);
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce }
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce sss_cmd_done(cctx, cmdctx);
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce return EOK;
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce}
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorcestatic void nsssrv_dp_send_acct_req_done(struct tevent_req *req)
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce{
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce struct dp_callback_ctx *cb_ctx =
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce tevent_req_callback_data(req, struct dp_callback_ctx);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce errno_t ret;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce dbus_uint16_t err_maj;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce dbus_uint32_t err_min;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce char *err_msg;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ret = sss_dp_get_account_recv(cb_ctx->mem_ctx, req,
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce &err_maj, &err_min,
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce &err_msg);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce talloc_zfree(req);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce if (ret != EOK) {
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce NSS_CMD_FATAL_ERROR(cb_ctx->cctx);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce }
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce}
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorcestatic void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min,
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce const char *err_msg, void *ptr);
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce/* search for a user.
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * Returns:
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * ENOENT, if user is definitely not found
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * EAGAIN, if user is beeing fetched from backend via async operations
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * EOK, if found
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * anything else on a fatal error
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
e625eb47a3091d92eda2271b123f8aab06227b63Simo Sorcestatic int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce{
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct sss_domain_info *dom = dctx->domain;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct cli_ctx *cctx = cmdctx->cctx;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce char *name = NULL;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct sysdb_ctx *sysdb;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct nss_ctx *nctx;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce int ret;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce while (dom) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* if it is a domainless search, skip domains that require fully
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce * qualified names instead */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce while (dom && cmdctx->check_next && dom->fqnames) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce dom = dom->next;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce }
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce if (!dom) break;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce if (dom != dctx->domain) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce /* make sure we reset the check_provider flag when we check
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce * a new domain */
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce }
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce /* make sure to update the dctx if we changed domain */
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce dctx->domain = dom;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce talloc_free(name);
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce name = sss_get_cased_name(dctx, cmdctx->name, dom->case_sensitive);
6c82774653f37945bdd0a311eb1ecc289cac683dLukas Slebodnik if (!name) return ENOMEM;
6c82774653f37945bdd0a311eb1ecc289cac683dLukas Slebodnik
6c82774653f37945bdd0a311eb1ecc289cac683dLukas Slebodnik /* verify this user has not yet been negatively cached,
6c82774653f37945bdd0a311eb1ecc289cac683dLukas Slebodnik * or has been permanently filtered */
6c82774653f37945bdd0a311eb1ecc289cac683dLukas Slebodnik ret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
6c82774653f37945bdd0a311eb1ecc289cac683dLukas Slebodnik dom, name);
6c82774653f37945bdd0a311eb1ecc289cac683dLukas Slebodnik
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce /* if neg cached, return we didn't find it */
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce if (ret == EEXIST) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce DEBUG(2, ("User [%s] does not exist in [%s]! (negative cache)\n",
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce name, dom->name));
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce /* if a multidomain search, try with next */
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce if (cmdctx->check_next) {
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce dom = dom->next;
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce continue;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce }
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce /* There are no further domains or this was a
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce * fully-qualified user request.
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce */
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce return ENOENT;
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce }
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(4, ("Requesting info for [%s@%s]\n", name, dom->name));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce sysdb = dom->sysdb;
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce if (sysdb == NULL) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce return EIO;
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce }
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce ret = sysdb_getpwnam(cmdctx, sysdb, name, &dctx->res);
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce if (ret != EOK) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce DEBUG(1, ("Failed to make request to our cache!\n"));
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce return EIO;
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce }
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce if (dctx->res->count > 1) {
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce DEBUG(0, ("getpwnam call returned more than one result !?!\n"));
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce return ENOENT;
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce }
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (dctx->res->count == 0 && !dctx->check_provider) {
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce /* set negative cache only if not result of cache check */
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce ret = sss_ncache_set_user(nctx->ncache, false, dom, name);
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce if (ret != EOK) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce return ret;
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce }
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce /* if a multidomain search, try with next */
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce if (cmdctx->check_next) {
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce dom = dom->next;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce if (dom) continue;
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce }
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce DEBUG(2, ("No results for getpwnam call\n"));
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce return ENOENT;
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce }
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce /* if this is a caching provider (or if we haven't checked the cache
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce * yet) then verify that the cache is uptodate */
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce if (dctx->check_provider) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce ret = check_cache(dctx, nctx, dctx->res,
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce SSS_DP_USER, name, 0,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce nss_cmd_getpwnam_dp_callback,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce dctx);
if (ret != EOK) {
/* Anything but EOK means we should reenter the mainloop
* because we may be refreshing the cache
*/
return ret;
}
}
/* One result found */
DEBUG(6, ("Returning info for user [%s@%s]\n", name, dom->name));
return EOK;
}
DEBUG(SSSDBG_MINOR_FAILURE,
("No matching domain found for [%s], fail!\n", cmdctx->name));
return ENOENT;
}
static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr)
{
struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx);
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct cli_ctx *cctx = cmdctx->cctx;
int ret;
if (err_maj) {
DEBUG(2, ("Unable to get information from Data Provider\n"
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
if (dctx->res && dctx->res->count == 1) {
ret = nss_cmd_getpw_send_reply(dctx, false);
goto done;
}
/* no previous results, just loop to next domain if possible */
if (dctx->domain->next && cmdctx->check_next) {
dctx->domain = dctx->domain->next;
dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
} else {
/* nothing available */
ret = ENOENT;
goto done;
}
}
/* ok the backend returned, search to see if we have updated results */
ret = nss_cmd_getpwnam_search(dctx);
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_getpw_send_reply(dctx, false);
}
done:
ret = nss_cmd_done(cmdctx, ret);
if (ret) {
NSS_CMD_FATAL_ERROR(cctx);
}
}
static void nss_cmd_getpwnam_cb(struct tevent_req *req);
static int nss_cmd_getpwnam(struct cli_ctx *cctx)
{
struct tevent_req *req;
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
const char *rawname;
char *domname;
uint8_t *body;
size_t blen;
int ret;
cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
if (!cmdctx) {
return ENOMEM;
}
cmdctx->cctx = cctx;
dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
if (!dctx) {
ret = ENOMEM;
goto done;
}
dctx->cmdctx = cmdctx;
/* get user name to query */
sss_packet_get_body(cctx->creq->in, &body, &blen);
/* if not terminated fail */
if (body[blen -1] != '\0') {
ret = EINVAL;
goto done;
}
/* If the body isn't valid UTF-8, fail */
if (!sss_utf8_check(body, blen -1)) {
ret = EINVAL;
goto done;
}
rawname = (const char *)body;
domname = NULL;
ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains, rawname,
&domname, &cmdctx->name);
if (ret == EAGAIN) {
req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, domname);
if (req == NULL) {
ret = ENOMEM;
} else {
dctx->rawname = rawname;
tevent_req_set_callback(req, nss_cmd_getpwnam_cb, dctx);
ret = EAGAIN;
}
goto done;
} if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("Invalid name received [%s]\n", rawname));
ret = ENOENT;
goto done;
}
DEBUG(4, ("Requesting info for [%s] from [%s]\n",
cmdctx->name, domname?domname:"<ALL>"));
if (domname) {
dctx->domain = responder_get_domain(dctx, cctx->rctx, domname);
if (!dctx->domain) {
ret = ENOENT;
goto done;
}
} else {
/* this is a multidomain search */
dctx->rawname = rawname;
dctx->domain = cctx->rctx->domains;
cmdctx->check_next = true;
if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, false, NULL);
if (req == NULL) {
ret = ENOMEM;
} else {
tevent_req_set_callback(req, nss_cmd_getpwnam_cb, dctx);
ret = EAGAIN;
}
goto done;
}
}
dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
/* ok, find it ! */
ret = nss_cmd_getpwnam_search(dctx);
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_getpw_send_reply(dctx, false);
}
done:
return nss_cmd_done(cmdctx, ret);
}
static void nss_cmd_getpwnam_cb(struct tevent_req *req)
{
struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx);
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct cli_ctx *cctx = cmdctx->cctx;
char *domname = NULL;
const char *rawname = dctx->rawname;
errno_t ret;
ret = sss_dp_get_domains_recv(req);
talloc_free(req);
if (ret != EOK) {
goto done;
}
ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains, rawname,
&domname, &cmdctx->name);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("Invalid name received [%s]\n", rawname));
ret = ENOENT;
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC, ("Requesting info for [%s] from [%s]\n",
cmdctx->name, domname?domname:"<ALL>"));
if (domname) {
dctx->domain = responder_get_domain(dctx, cctx->rctx, domname);
if (dctx->domain == NULL) {
ret = ENOENT;
goto done;
}
} else {
/* this is a multidomain search */
dctx->domain = cctx->rctx->domains;
cmdctx->check_next = true;
}
dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
/* ok, find it ! */
ret = nss_cmd_getpwnam_search(dctx);
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_getpw_send_reply(dctx, false);
}
done:
nss_cmd_done(cmdctx, ret);
}
static void nss_cmd_getpwuid_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr);
/* search for a uid.
* Returns:
* ENOENT, if uid is definitely not found
* EAGAIN, if uid is beeing fetched from backend via async operations
* EOK, if found
* anything else on a fatal error
*/
struct sss_domain_info *get_next_dom_or_subdom(struct sss_domain_info *dom)
{
/* Note that we don't know if the dom is a domain or a subdomain,
* therefore:
*
* If it is a subdomain and it doesn't have any siblings (subdomains
* of the same primary domain), return next primary domain
*/
if (dom->next == NULL && dom->parent != NULL) {
return dom->parent->next;
}
/* If it's primary domain, the next returned should be its first
* subdomain */
if (dom->subdomains != NULL) {
return dom->subdomains[0];
}
/* Any other scenario */
return dom->next;
}
static int nss_cmd_getpwuid_search(struct nss_dom_ctx *dctx)
{
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct sss_domain_info *dom = dctx->domain;
struct cli_ctx *cctx = cmdctx->cctx;
struct sysdb_ctx *sysdb;
struct nss_ctx *nctx;
int ret;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
while (dom) {
/* check that the uid is valid for this domain */
if ((dom->id_min && (cmdctx->id < dom->id_min)) ||
(dom->id_max && (cmdctx->id > dom->id_max))) {
DEBUG(4, ("Uid [%lu] does not exist in domain [%s]! "
"(id out of range)\n",
(unsigned long)cmdctx->id, dom->name));
if (cmdctx->check_next) {
dom = get_next_dom_or_subdom(dom);
continue;
}
return ENOENT;
}
if (dom != dctx->domain) {
/* make sure we reset the check_provider flag when we check
* a new domain */
dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
}
/* make sure to update the dctx if we changed domain */
dctx->domain = dom;
DEBUG(4, ("Requesting info for [%d@%s]\n", cmdctx->id, dom->name));
sysdb = dom->sysdb;
if (sysdb == NULL) {
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
return EIO;
}
ret = sysdb_getpwuid(cmdctx, sysdb, cmdctx->id, &dctx->res);
if (ret != EOK) {
DEBUG(1, ("Failed to make request to our cache!\n"));
return EIO;
}
if (dctx->res->count > 1) {
DEBUG(0, ("getpwuid call returned more than one result !?!\n"));
return ENOENT;
}
if (dctx->res->count == 0 && !dctx->check_provider) {
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
dom = get_next_dom_or_subdom(dom);
continue;
}
DEBUG(2, ("No results for getpwuid call\n"));
/* set negative cache only if not result of cache check */
ret = sss_ncache_set_uid(nctx->ncache, false, cmdctx->id);
if (ret != EOK) {
return ret;
}
return ENOENT;
}
/* if this is a caching provider (or if we haven't checked the cache
* yet) then verify that the cache is uptodate */
if (dctx->check_provider) {
ret = check_cache(dctx, nctx, dctx->res,
SSS_DP_USER, NULL, cmdctx->id,
nss_cmd_getpwuid_dp_callback,
dctx);
if (ret != EOK) {
/* Anything but EOK means we should reenter the mainloop
* because we may be refreshing the cache
*/
return ret;
}
}
/* One result found */
DEBUG(6, ("Returning info for uid [%d@%s]\n", cmdctx->id, dom->name));
return EOK;
}
DEBUG(2, ("No matching domain found for [%d], fail!\n", cmdctx->id));
return ENOENT;
}
static void nss_cmd_getpwuid_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr)
{
struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx);
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct cli_ctx *cctx = cmdctx->cctx;
int ret;
if (err_maj) {
DEBUG(2, ("Unable to get information from Data Provider\n"
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
if (dctx->res && dctx->res->count == 1) {
ret = nss_cmd_getpw_send_reply(dctx, true);
goto done;
}
/* no previous results, just loop to next domain if possible */
if (dctx->domain->next && cmdctx->check_next) {
dctx->domain = dctx->domain->next;
dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
} else {
/* nothing available */
ret = ENOENT;
goto done;
}
}
/* ok the backend returned, search to see if we have updated results */
ret = nss_cmd_getpwuid_search(dctx);
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_getpw_send_reply(dctx, true);
}
done:
ret = nss_cmd_done(cmdctx, ret);
if (ret) {
NSS_CMD_FATAL_ERROR(cctx);
}
}
static int nss_cmd_getpwuid(struct cli_ctx *cctx)
{
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
struct nss_ctx *nctx;
uint8_t *body;
size_t blen;
int ret;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
if (!cmdctx) {
return ENOMEM;
}
cmdctx->cctx = cctx;
dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
if (!dctx) {
ret = ENOMEM;
goto done;
}
dctx->cmdctx = cmdctx;
/* get uid to query */
sss_packet_get_body(cctx->creq->in, &body, &blen);
if (blen != sizeof(uint32_t)) {
ret = EINVAL;
goto done;
}
cmdctx->id = *((uint32_t *)body);
ret = sss_ncache_check_uid(nctx->ncache, nctx->neg_timeout, cmdctx->id);
if (ret == EEXIST) {
DEBUG(3, ("Uid [%lu] does not exist! (negative cache)\n",
(unsigned long)cmdctx->id));
ret = ENOENT;
goto done;
}
/* uid searches are always multidomain */
dctx->domain = cctx->rctx->domains;
cmdctx->check_next = true;
dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
/* ok, find it ! */
ret = nss_cmd_getpwuid_search(dctx);
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_getpw_send_reply(dctx, true);
}
done:
return nss_cmd_done(cmdctx, ret);
}
/* to keep it simple at this stage we are retrieving the
* full enumeration again for each request for each process
* and we also block on setpwent() for the full time needed
* to retrieve the data. And endpwent() frees all the data.
* Next steps are:
* - use an nsssrv wide cache with data already structured
* so that it can be immediately returned (see nscd way)
* - use mutexes so that setpwent() can return immediately
* even if the data is still being fetched
* - make getpwent() wait on the mutex
*
* Alternatively:
* - use a smarter search mechanism that keeps track of the
* last user searched and return the next X users doing
* an alphabetic sort and starting from the user following
* the last returned user.
*/
static int nss_cmd_getpwent_immediate(struct nss_cmd_ctx *cmdctx);
struct tevent_req * nss_cmd_setpwent_send(TALLOC_CTX *mem_ctx,
struct cli_ctx *client);
static void nss_cmd_setpwent_done(struct tevent_req *req);
static int nss_cmd_setpwent(struct cli_ctx *cctx)
{
struct nss_cmd_ctx *cmdctx;
struct tevent_req *req;
errno_t ret = EOK;
cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
if (!cmdctx) {
return ENOMEM;
}
cmdctx->cctx = cctx;
req = nss_cmd_setpwent_send(cmdctx, cctx);
if (!req) {
DEBUG(0, ("Fatal error calling nss_cmd_setpwent_send\n"));
ret = EIO;
goto done;
}
tevent_req_set_callback(req, nss_cmd_setpwent_done, cmdctx);
done:
return nss_cmd_done(cmdctx, ret);
}
static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx);
struct tevent_req *nss_cmd_setpwent_send(TALLOC_CTX *mem_ctx,
struct cli_ctx *client)
{
errno_t ret;
struct nss_ctx *nctx;
struct tevent_req *req;
struct setent_ctx *state;
struct sss_domain_info *dom;
struct setent_step_ctx *step_ctx;
DEBUG(4, ("Received setpwent request\n"));
nctx = talloc_get_type(client->rctx->pvt_ctx, struct nss_ctx);
/* Reset the read pointers */
client->pwent_dom_idx = 0;
client->pwent_cur = 0;
req = tevent_req_create(mem_ctx, &state, struct setent_ctx);
if (!req) {
DEBUG(0, ("Could not create tevent request for setpwent\n"));
return NULL;
}
state->nctx = nctx;
state->client = client;
state->dctx = talloc_zero(state, struct nss_dom_ctx);
if (!state->dctx) {
ret = ENOMEM;
goto error;
}
/* check if enumeration is enabled in any domain */
for (dom = client->rctx->domains; dom; dom = dom->next) {
if (dom->enumerate != 0) break;
}
state->dctx->domain = dom;
if (state->dctx->domain == NULL) {
DEBUG(2, ("Enumeration disabled on all domains!\n"));
ret = ENOENT;
goto error;
}
state->dctx->check_provider =
NEED_CHECK_PROVIDER(state->dctx->domain->provider);
/* Is the result context already available */
if (state->nctx->pctx) {
if (state->nctx->pctx->ready) {
/* All of the necessary data is in place
* We can return now, getpwent requests will work at this point
*/
tevent_req_done(req);
tevent_req_post(req, state->nctx->rctx->ev);
}
else {
/* Object is still being constructed
* Register for notification when it's
* ready.
*/
ret = nss_setent_add_ref(state, state->nctx->pctx, req);
if (ret != EOK) {
talloc_free(req);
return NULL;
}
}
return req;
}
/* Create a new result context
* We are creating it on the nss_ctx so that it doesn't
* go away if the original request does. We will delete
* it when the refcount goes to zero;
*/
state->nctx->pctx = talloc_zero(nctx, struct getent_ctx);
if (!state->nctx->pctx) {
ret = ENOMEM;
goto error;
}
state->getent_ctx = nctx->pctx;
/* Add a callback reference for ourselves */
ret = nss_setent_add_ref(state, state->nctx->pctx, req);
if (ret) goto error;
/* ok, start the searches */
step_ctx = talloc_zero(state->getent_ctx, struct setent_step_ctx);
if (!step_ctx) {
ret = ENOMEM;
goto error;
}
/* Steal the dom_ctx onto the step_ctx so it doesn't go out of scope if
* this request is canceled while other requests are in-progress.
*/
step_ctx->dctx = talloc_steal(step_ctx, state->dctx);
step_ctx->nctx = state->nctx;
step_ctx->getent_ctx = state->getent_ctx;
step_ctx->rctx = client->rctx;
step_ctx->cctx = client;
step_ctx->returned_to_mainloop = false;
ret = nss_cmd_setpwent_step(step_ctx);
if (ret != EOK && ret != EAGAIN) goto error;
if (ret == EOK) {
tevent_req_post(req, client->rctx->ev);
}
return req;
error:
tevent_req_error(req, ret);
tevent_req_post(req, client->rctx->ev);
return req;
}
static void nss_cmd_setpwent_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr);
static void setpwent_result_timeout(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval current_time,
void *pvt);
/* nss_cmd_setpwent_step returns
* EOK if everything is done and the request needs to be posted explicitly
* EAGAIN if the caller can safely return to the main loop
*/
static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx)
{
errno_t ret;
struct sss_domain_info *dom = step_ctx->dctx->domain;
struct resp_ctx *rctx = step_ctx->rctx;
struct nss_dom_ctx *dctx = step_ctx->dctx;
struct getent_ctx *pctx = step_ctx->getent_ctx;
struct nss_ctx *nctx = step_ctx->nctx;
struct sysdb_ctx *sysdb;
struct ldb_result *res;
struct timeval tv;
struct tevent_timer *te;
struct tevent_req *dpreq;
struct dp_callback_ctx *cb_ctx;
while (dom) {
while (dom && dom->enumerate == 0) {
dom = dom->next;
}
if (!dom) break;
if (dom != dctx->domain) {
/* make sure we reset the check_provider flag when we check
* a new domain */
dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
}
/* make sure to update the dctx if we changed domain */
dctx->domain = dom;
DEBUG(6, ("Requesting info for domain [%s]\n", dom->name));
sysdb = dom->sysdb;
if (sysdb == NULL) {
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
return EIO;
}
/* if this is a caching provider (or if we haven't checked the cache
* yet) then verify that the cache is uptodate */
if (dctx->check_provider) {
step_ctx->returned_to_mainloop = true;
/* Only do this once per provider */
dctx->check_provider = false;
dpreq = sss_dp_get_account_send(step_ctx, rctx, dctx->domain, true,
SSS_DP_USER, NULL, 0, NULL);
if (!dpreq) {
DEBUG(SSSDBG_MINOR_FAILURE,
("Enum Cache refresh for domain [%s] failed."
" Trying to return what we have in cache!\n",
dom->name));
} else {
cb_ctx = talloc_zero(step_ctx, struct dp_callback_ctx);
if(!cb_ctx) {
talloc_zfree(dpreq);
return ENOMEM;
}
cb_ctx->callback = nss_cmd_setpwent_dp_callback;
cb_ctx->ptr = step_ctx;
cb_ctx->cctx = step_ctx->cctx;
cb_ctx->mem_ctx = step_ctx;
tevent_req_set_callback(dpreq, nsssrv_dp_send_acct_req_done, cb_ctx);
return EAGAIN;
}
}
ret = sysdb_enumpwent(dctx, sysdb, &res);
if (ret != EOK) {
DEBUG(1, ("Enum from cache failed, skipping domain [%s]\n",
dom->name));
dom = dom->next;
continue;
}
if (res->count == 0) {
DEBUG(4, ("Domain [%s] has no users, skipping.\n", dom->name));
dom = dom->next;
continue;
}
nctx->pctx->doms = talloc_realloc(pctx, pctx->doms,
struct dom_ctx, pctx->num +1);
if (!pctx->doms) {
talloc_free(pctx);
nctx->pctx = NULL;
return ENOMEM;
}
nctx->pctx->doms[pctx->num].domain = dctx->domain;
nctx->pctx->doms[pctx->num].res = talloc_steal(pctx->doms, res);
nctx->pctx->num++;
/* do not reply until all domain searches are done */
dom = dom->next;
}
/* We've finished all our lookups
* The result object is now safe to read.
*/
nctx->pctx->ready = true;
/* Set up a lifetime timer for this result object
* We don't want this result object to outlive the
* enum cache refresh timeout
*/
tv = tevent_timeval_current_ofs(nctx->enum_cache_timeout, 0);
te = tevent_add_timer(rctx->ev, nctx->pctx, tv,
setpwent_result_timeout, nctx);
if (!te) {
DEBUG(0, ("Could not set up life timer for setpwent result object. "
"Entries may become stale.\n"));
}
/* Notify the waiting clients */
nss_setent_notify_done(nctx->pctx);
if (step_ctx->returned_to_mainloop) {
return EAGAIN;
} else {
return EOK;
}
}
static void setpwent_result_timeout(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval current_time,
void *pvt)
{
struct nss_ctx *nctx = talloc_get_type(pvt, struct nss_ctx);
DEBUG(1, ("setpwent result object has expired. Cleaning up.\n"));
/* Free the passwd enumeration context.
* If additional getpwent requests come in, they will invoke
* an implicit setpwent and refresh the result object.
*/
talloc_zfree(nctx->pctx);
}
static void nss_cmd_setpwent_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr)
{
struct setent_step_ctx *step_ctx =
talloc_get_type(ptr, struct setent_step_ctx);
int ret;
if (err_maj) {
DEBUG(2, ("Unable to get information from Data Provider\n"
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
}
ret = nss_cmd_setpwent_step(step_ctx);
if (ret != EOK && ret != EAGAIN) {
/* Notify any waiting processes of failure */
nss_setent_notify_error(step_ctx->nctx->pctx, ret);
}
}
static errno_t nss_cmd_setpwent_recv(struct tevent_req *req)
{
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
static void nss_cmd_setpwent_done(struct tevent_req *req)
{
errno_t ret;
struct nss_cmd_ctx *cmdctx =
tevent_req_callback_data(req, struct nss_cmd_ctx);
ret = nss_cmd_setpwent_recv(req);
talloc_zfree(req);
if (ret == EOK || ret == ENOENT) {
/* Either we succeeded or no domains were eligible */
ret = sss_packet_new(cmdctx->cctx->creq, 0,
sss_packet_get_cmd(cmdctx->cctx->creq->in),
&cmdctx->cctx->creq->out);
if (ret == EOK) {
sss_cmd_done(cmdctx->cctx, cmdctx);
return;
}
}
/* Something bad happened */
nss_cmd_done(cmdctx, ret);
}
static void nss_cmd_implicit_setpwent_done(struct tevent_req *req);
static int nss_cmd_getpwent(struct cli_ctx *cctx)
{
struct nss_ctx *nctx;
struct nss_cmd_ctx *cmdctx;
struct tevent_req *req;
DEBUG(4, ("Requesting info for all accounts\n"));
cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
if (!cmdctx) {
return ENOMEM;
}
cmdctx->cctx = cctx;
/* Save the current index and cursor locations
* If we end up calling setpwent implicitly, because the response object
* expired and has to be recreated, we want to resume from the same
* location.
*/
cmdctx->saved_dom_idx = cctx->pwent_dom_idx;
cmdctx->saved_cur = cctx->pwent_cur;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
if(!nctx->pctx || !nctx->pctx->ready) {
/* Make sure we invoke setpwent if it hasn't been run or is still
* processing from another client
*/
req = nss_cmd_setpwent_send(cctx, cctx);
if (!req) {
return EIO;
}
tevent_req_set_callback(req, nss_cmd_implicit_setpwent_done, cmdctx);
return EOK;
}
return nss_cmd_getpwent_immediate(cmdctx);
}
static int nss_cmd_retpwent(struct cli_ctx *cctx, int num);
static int nss_cmd_getpwent_immediate(struct nss_cmd_ctx *cmdctx)
{
struct cli_ctx *cctx = cmdctx->cctx;
uint8_t *body;
size_t blen;
uint32_t num;
int ret;
/* get max num of entries to return in one call */
sss_packet_get_body(cctx->creq->in, &body, &blen);
if (blen != sizeof(uint32_t)) {
return EINVAL;
}
num = *((uint32_t *)body);
/* create response packet */
ret = sss_packet_new(cctx->creq, 0,
sss_packet_get_cmd(cctx->creq->in),
&cctx->creq->out);
if (ret != EOK) {
return ret;
}
ret = nss_cmd_retpwent(cctx, num);
sss_packet_set_error(cctx->creq->out, ret);
sss_cmd_done(cctx, cmdctx);
return EOK;
}
static int nss_cmd_retpwent(struct cli_ctx *cctx, int num)
{
struct nss_ctx *nctx;
struct getent_ctx *pctx;
struct ldb_message **msgs = NULL;
struct dom_ctx *pdom = NULL;
int n = 0;
int ret = ENOENT;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
if (!nctx->pctx) goto none;
pctx = nctx->pctx;
while (ret == ENOENT) {
if (cctx->pwent_dom_idx >= pctx->num) break;
pdom = &pctx->doms[cctx->pwent_dom_idx];
n = pdom->res->count - cctx->pwent_cur;
if (n <= 0 && (cctx->pwent_dom_idx+1 < pctx->num)) {
cctx->pwent_dom_idx++;
pdom = &pctx->doms[cctx->pwent_dom_idx];
n = pdom->res->count;
cctx->pwent_cur = 0;
}
if (!n) break;
if (n > num) n = num;
msgs = &(pdom->res->msgs[cctx->pwent_cur]);
ret = fill_pwent(cctx->creq->out, pdom->domain, nctx,
true, false, msgs, &n);
cctx->pwent_cur += n;
}
none:
if (ret == ENOENT) {
ret = sss_cmd_empty_packet(cctx->creq->out);
}
return ret;
}
static void nss_cmd_implicit_setpwent_done(struct tevent_req *req)
{
errno_t ret;
struct nss_cmd_ctx *cmdctx =
tevent_req_callback_data(req, struct nss_cmd_ctx);
ret = nss_cmd_setpwent_recv(req);
talloc_zfree(req);
/* ENOENT is acceptable, as it just means that there were no entries
* to be returned. This will be handled gracefully in nss_cmd_retpwent
* later.
*/
if (ret != EOK && ret != ENOENT) {
DEBUG(0, ("Implicit setpwent failed with unexpected error [%d][%s]\n",
ret, strerror(ret)));
NSS_CMD_FATAL_ERROR(cmdctx);
}
/* Restore the saved index and cursor locations */
cmdctx->cctx->pwent_dom_idx = cmdctx->saved_dom_idx;
cmdctx->cctx->pwent_cur = cmdctx->saved_cur;
ret = nss_cmd_getpwent_immediate(cmdctx);
if (ret != EOK) {
DEBUG(0, ("Immediate retrieval failed with unexpected error "
"[%d][%s]\n", ret, strerror(ret)));
NSS_CMD_FATAL_ERROR(cmdctx);
}
}
static int nss_cmd_endpwent(struct cli_ctx *cctx)
{
struct nss_ctx *nctx;
int ret;
DEBUG(4, ("Terminating request info for all accounts\n"));
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
/* create response packet */
ret = sss_packet_new(cctx->creq, 0,
sss_packet_get_cmd(cctx->creq->in),
&cctx->creq->out);
if (ret != EOK) {
return ret;
}
if (nctx->pctx == NULL) goto done;
/* Reset the indices so that subsequent requests start at zero */
cctx->pwent_dom_idx = 0;
cctx->pwent_cur = 0;
done:
sss_cmd_done(cctx, NULL);
return EOK;
}
/****************************************************************************
* GROUP db related functions
***************************************************************************/
#define GID_ROFFSET 0
#define MNUM_ROFFSET sizeof(uint32_t)
#define STRS_ROFFSET 2*sizeof(uint32_t)
static int fill_members(struct sss_packet *packet,
struct sss_domain_info *dom,
struct nss_ctx *nctx,
struct ldb_message_element *el,
size_t *_rzero,
size_t *_rsize,
int *_memnum)
{
int i, ret = EOK;
int memnum = *_memnum;
size_t rzero= *_rzero;
size_t rsize = *_rsize;
char *tmpstr;
struct sized_string name;
const char *namefmt = dom->names->fq_fmt;
TALLOC_CTX *tmp_ctx = NULL;
size_t delim;
size_t dom_len;
uint8_t *body;
size_t blen;
const char *domain = dom->name;
bool add_domain = dom->fqnames;
if (add_domain) {
delim = 1;
dom_len = strlen(domain);
} else {
delim = 0;
dom_len = 0;
}
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
return ENOMEM;
}
sss_packet_get_body(packet, &body, &blen);
for (i = 0; i < el->num_values; i++) {
tmpstr = sss_get_cased_name(tmp_ctx, (char *)el->values[i].data,
dom->case_sensitive);
if (tmpstr == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE,
("sss_get_cased_name failed, skipping\n"));
continue;
}
if (nctx->filter_users_in_groups) {
ret = sss_ncache_check_user(nctx->ncache,
nctx->neg_timeout,
dom, tmpstr);
if (ret == EEXIST) {
DEBUG(SSSDBG_TRACE_FUNC, ("Group [%s] member [%s@%s] filtered out!"
" (negative cache)\n",
(char *)&body[rzero+STRS_ROFFSET],
tmpstr, domain));
continue;
}
}
to_sized_string(&name, tmpstr);
if (add_domain) {
ret = sss_packet_grow(packet, name.len + delim + dom_len);
} else {
ret = sss_packet_grow(packet, name.len);
}
if (ret != EOK) {
goto done;
}
sss_packet_get_body(packet, &body, &blen);
if (add_domain) {
ret = snprintf((char *)&body[rzero + rsize],
name.len + delim + dom_len,
namefmt, name.str, domain);
if (ret >= (name.len + delim + dom_len)) {
/* need more space,
* got creative with the print format ? */
int t = ret - name.len + delim + dom_len + 1;
ret = sss_packet_grow(packet, t);
if (ret != EOK) {
goto done;
}
sss_packet_get_body(packet, &body, &blen);
delim += t;
/* retry */
ret = snprintf((char *)&body[rzero + rsize],
name.len + delim + dom_len,
namefmt, name.str, domain);
}
if (ret != name.len + delim + dom_len - 1) {
DEBUG(SSSDBG_OP_FAILURE, ("Failed to generate a fully qualified name"
" for member [%s@%s] of group [%s]!"
" Skipping\n", name.str, domain,
(char *)&body[rzero+STRS_ROFFSET]));
/* reclaim space */
ret = sss_packet_shrink(packet, name.len + delim + dom_len);
if (ret != EOK) {
goto done;
}
continue;
}
} else {
memcpy(&body[rzero + rsize], name.str, name.len);
}
if (add_domain) {
rsize += name.len + delim + dom_len;
} else {
rsize += name.len;
}
memnum++;
}
done:
*_memnum = memnum;
*_rzero = rzero;
*_rsize = rsize;
talloc_zfree(tmp_ctx);
return ret;
}
static int fill_grent(struct sss_packet *packet,
struct sss_domain_info *dom,
struct nss_ctx *nctx,
bool filter_groups, bool gr_mmap_cache,
struct ldb_message **msgs,
int *count)
{
struct ldb_message *msg;
struct ldb_message_element *el;
uint8_t *body;
size_t blen;
uint32_t gid;
const char *tmpstr;
const char *orig_name;
struct sized_string name;
struct sized_string pwfield;
struct sized_string fullname;
size_t delim;
size_t dom_len;
int i = 0;
int ret, num, memnum;
size_t rzero, rsize;
bool add_domain = dom->fqnames;
const char *domain = dom->name;
const char *namefmt;
TALLOC_CTX *tmp_ctx = NULL;
namefmt = dom->names->fq_fmt;
if (add_domain) {
delim = 1;
dom_len = strlen(domain);
} else {
delim = 0;
dom_len = 0;
}
to_sized_string(&pwfield, nctx->pwfield);
num = 0;
/* first 2 fields (len and reserved), filled up later */
ret = sss_packet_grow(packet, 2*sizeof(uint32_t));
if (ret != EOK) {
goto done;
}
sss_packet_get_body(packet, &body, &blen);
rzero = 2*sizeof(uint32_t);
rsize = 0;
for (i = 0; i < *count; i++) {
talloc_zfree(tmp_ctx);
tmp_ctx = talloc_new(NULL);
msg = msgs[i];
/* new group */
if (!ldb_msg_check_string_attribute(msg, "objectClass",
SYSDB_GROUP_CLASS)) {
DEBUG(1, ("Wrong object (%s) found on stack!\n",
ldb_dn_get_linearized(msg->dn)));
continue;
}
/* new result starts at end of previous result */
rzero += rsize;
rsize = 0;
/* find group name/gid */
orig_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
if (!orig_name || !gid) {
DEBUG(2, ("Incomplete group object for %s[%llu]! Skipping\n",
orig_name?orig_name:"<NULL>", (unsigned long long int)gid));
continue;
}
if (filter_groups) {
ret = sss_ncache_check_group(nctx->ncache,
nctx->neg_timeout, dom, orig_name);
if (ret == EEXIST) {
DEBUG(4, ("Group [%s@%s] filtered out! (negative cache)\n",
orig_name, domain));
continue;
}
}
tmpstr = sss_get_cased_name(tmp_ctx, orig_name, dom->case_sensitive);
if (tmpstr == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE,
("sss_get_cased_name failed, skipping\n"));
continue;
}
to_sized_string(&name, tmpstr);
/* fill in gid and name and set pointer for number of members */
rsize = STRS_ROFFSET + name.len + pwfield.len; /* name\0x\0 */
if (add_domain) rsize += delim + dom_len;
ret = sss_packet_grow(packet, rsize);
if (ret != EOK) {
num = 0;
goto done;
}
sss_packet_get_body(packet, &body, &blen);
/* 0-3: 32bit number gid */
SAFEALIGN_SET_UINT32(&body[rzero+GID_ROFFSET], gid, NULL);
/* 4-7: 32bit unsigned number of members */
SAFEALIGN_SET_UINT32(&body[rzero+MNUM_ROFFSET], 0, NULL);
/* 8-X: sequence of strings (name, passwd, mem..) */
if (add_domain) {
ret = snprintf((char *)&body[rzero+STRS_ROFFSET],
name.len + delim + dom_len,
namefmt, name.str, domain);
if (ret >= (name.len + delim + dom_len)) {
/* need more space, got creative with the print format ? */
int t = ret - (name.len + delim + dom_len) + 1;
ret = sss_packet_grow(packet, t);
if (ret != EOK) {
num = 0;
goto done;
}
sss_packet_get_body(packet, &body, &blen);
rsize += t;
delim += t;
/* retry */
ret = snprintf((char *)&body[rzero+STRS_ROFFSET],
name.len + delim + dom_len,
namefmt, name.str, domain);
}
if (ret != name.len + delim + dom_len - 1) {
DEBUG(1, ("Failed to generate a fully qualified name for"
" group [%s] in [%s]! Skipping\n", name.str, domain));
/* reclaim space */
ret = sss_packet_shrink(packet, rsize);
if (ret != EOK) {
num = 0;
goto done;
}
rsize = 0;
continue;
}
} else {
memcpy(&body[rzero+STRS_ROFFSET], name.str, name.len);
}
to_sized_string(&fullname, (const char *)&body[rzero+STRS_ROFFSET]);
/* group passwd field */
memcpy(&body[rzero+STRS_ROFFSET + fullname.len],
pwfield.str, pwfield.len);
memnum = 0;
el = ldb_msg_find_element(msg, SYSDB_MEMBERUID);
if (el) {
ret = fill_members(packet, dom, nctx, el, &rzero, &rsize, &memnum);
if (ret != EOK) {
num = 0;
goto done;
}
sss_packet_get_body(packet, &body, &blen);
}
el = ldb_msg_find_element(msg, SYSDB_GHOST);
if (el) {
ret = fill_members(packet, dom, nctx, el, &rzero, &rsize, &memnum);
if (ret != EOK) {
num = 0;
goto done;
}
sss_packet_get_body(packet, &body, &blen);
}
if (memnum) {
/* set num of members */
SAFEALIGN_SET_UINT32(&body[rzero+MNUM_ROFFSET], memnum, NULL);
}
num++;
if (gr_mmap_cache && nctx->grp_mc_ctx) {
/* body was reallocated, so fullname might be pointing to
* where body used to be, not where it is */
to_sized_string(&fullname, (const char *)&body[rzero+STRS_ROFFSET]);
ret = sss_mmap_cache_gr_store(nctx->grp_mc_ctx,
&fullname, &pwfield, gid, memnum,
(char *)&body[rzero] + STRS_ROFFSET +
fullname.len + pwfield.len,
rsize - STRS_ROFFSET -
fullname.len - pwfield.len);
if (ret != EOK && ret != ENOMEM) {
DEBUG(SSSDBG_OP_FAILURE,
("Failed to store group %s(%s) in mmap cache!",
name.str, domain));
}
}
continue;
}
talloc_zfree(tmp_ctx);
done:
*count = i;
if (num == 0) {
/* if num is 0 most probably something went wrong,
* reset packet and return ENOENT */
ret = sss_packet_set_size(packet, 0);
if (ret != EOK) return ret;
return ENOENT;
}
((uint32_t *)body)[0] = num; /* num results */
((uint32_t *)body)[1] = 0; /* reserved */
return EOK;
}
static int nss_cmd_getgr_send_reply(struct nss_dom_ctx *dctx, bool filter)
{
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct cli_ctx *cctx = cmdctx->cctx;
struct nss_ctx *nctx;
int ret;
int i;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
ret = sss_packet_new(cctx->creq, 0,
sss_packet_get_cmd(cctx->creq->in),
&cctx->creq->out);
if (ret != EOK) {
return EFAULT;
}
i = dctx->res->count;
ret = fill_grent(cctx->creq->out,
dctx->domain,
nctx, filter, true,
dctx->res->msgs, &i);
if (ret) {
return ret;
}
sss_packet_set_error(cctx->creq->out, EOK);
sss_cmd_done(cctx, cmdctx);
return EOK;
}
static void nss_cmd_getgrnam_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr);
/* search for a group.
* Returns:
* ENOENT, if group is definitely not found
* EAGAIN, if group is beeing fetched from backend via async operations
* EOK, if found
* anything else on a fatal error
*/
static int nss_cmd_getgrnam_search(struct nss_dom_ctx *dctx)
{
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct sss_domain_info *dom = dctx->domain;
struct cli_ctx *cctx = cmdctx->cctx;
char *name = NULL;
struct sysdb_ctx *sysdb;
struct nss_ctx *nctx;
int ret;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
while (dom) {
/* if it is a domainless search, skip domains that require fully
* qualified names instead */
while (dom && cmdctx->check_next && dom->fqnames) {
dom = dom->next;
}
if (!dom) break;
if (dom != dctx->domain) {
/* make sure we reset the check_provider flag when we check
* a new domain */
dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
}
/* make sure to update the dctx if we changed domain */
dctx->domain = dom;
talloc_free(name);
name = sss_get_cased_name(dctx, cmdctx->name, dom->case_sensitive);
if (!name) return ENOMEM;
/* verify this group has not yet been negatively cached,
* or has been permanently filtered */
ret = sss_ncache_check_group(nctx->ncache, nctx->neg_timeout,
dom, name);
/* if neg cached, return we didn't find it */
if (ret == EEXIST) {
DEBUG(2, ("Group [%s] does not exist in [%s]! (negative cache)\n",
name, dom->name));
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
dom = dom->next;
continue;
}
/* There are no further domains or this was a
* fully-qualified user request.
*/
return ENOENT;
}
DEBUG(4, ("Requesting info for [%s@%s]\n", name, dom->name));
sysdb = dom->sysdb;
if (sysdb == NULL) {
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
return EIO;
}
ret = sysdb_getgrnam(cmdctx, sysdb, name, &dctx->res);
if (ret != EOK) {
DEBUG(1, ("Failed to make request to our cache!\n"));
return EIO;
}
if (dctx->res->count > 1) {
DEBUG(0, ("getgrnam call returned more than one result !?!\n"));
return ENOENT;
}
if (dctx->res->count == 0 && !dctx->check_provider) {
/* set negative cache only if not result of cache check */
ret = sss_ncache_set_group(nctx->ncache, false, dom, name);
if (ret != EOK) {
return ret;
}
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
dom = dom->next;
if (dom) continue;
}
DEBUG(2, ("No results for getgrnam call\n"));
return ENOENT;
}
/* if this is a caching provider (or if we haven't checked the cache
* yet) then verify that the cache is uptodate */
if (dctx->check_provider) {
ret = check_cache(dctx, nctx, dctx->res,
SSS_DP_GROUP, name, 0,
nss_cmd_getgrnam_dp_callback,
dctx);
if (ret != EOK) {
/* Anything but EOK means we should reenter the mainloop
* because we may be refreshing the cache
*/
return ret;
}
}
/* One result found */
DEBUG(6, ("Returning info for group [%s@%s]\n", name, dom->name));
return EOK;
}
DEBUG(SSSDBG_MINOR_FAILURE,
("No matching domain found for [%s], fail!\n", cmdctx->name));
return ENOENT;
}
static void nss_cmd_getgrnam_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr)
{
struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx);
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct cli_ctx *cctx = cmdctx->cctx;
int ret;
if (err_maj) {
DEBUG(2, ("Unable to get information from Data Provider\n"
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
if (dctx->res && dctx->res->count == 1) {
ret = nss_cmd_getgr_send_reply(dctx, false);
goto done;
}
/* no previous results, just loop to next domain if possible */
if (dctx->domain->next && cmdctx->check_next) {
dctx->domain = dctx->domain->next;
dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
} else {
/* nothing available */
ret = ENOENT;
goto done;
}
}
/* ok the backend returned, search to see if we have updated results */
ret = nss_cmd_getgrnam_search(dctx);
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_getgr_send_reply(dctx, false);
}
done:
ret = nss_cmd_done(cmdctx, ret);
if (ret) {
NSS_CMD_FATAL_ERROR(cctx);
}
}
static void nss_cmd_getgrnam_cb(struct tevent_req *req);
static int nss_cmd_getgrnam(struct cli_ctx *cctx)
{
struct tevent_req *req;
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
const char *rawname;
char *domname;
uint8_t *body;
size_t blen;
int ret;
cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
if (!cmdctx) {
return ENOMEM;
}
cmdctx->cctx = cctx;
dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
if (!dctx) {
ret = ENOMEM;
goto done;
}
dctx->cmdctx = cmdctx;
/* get user name to query */
sss_packet_get_body(cctx->creq->in, &body, &blen);
/* if not terminated fail */
if (body[blen -1] != '\0') {
ret = EINVAL;
goto done;
}
/* If the body isn't valid UTF-8, fail */
if (!sss_utf8_check(body, blen -1)) {
ret = EINVAL;
goto done;
}
rawname = (const char *)body;
domname = NULL;
ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains, rawname,
&domname, &cmdctx->name);
if (ret == EAGAIN) {
req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, domname);
if (req == NULL) {
ret = ENOMEM;
} else {
dctx->rawname = rawname;
tevent_req_set_callback(req, nss_cmd_getgrnam_cb, dctx);
ret = EAGAIN;
}
goto done;
} else if (ret != EOK) {
DEBUG(2, ("Invalid name received [%s]\n", rawname));
ret = ENOENT;
goto done;
}
DEBUG(4, ("Requesting info for [%s] from [%s]\n",
cmdctx->name, domname?domname:"<ALL>"));
if (domname) {
dctx->domain = responder_get_domain(dctx, cctx->rctx, domname);
if (!dctx->domain) {
ret = ENOENT;
goto done;
}
} else {
/* this is a multidomain search */
dctx->rawname = rawname;
dctx->domain = cctx->rctx->domains;
cmdctx->check_next = true;
if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, false, NULL);
if (req == NULL) {
ret = ENOMEM;
} else {
tevent_req_set_callback(req, nss_cmd_getgrnam_cb, dctx);
ret = EAGAIN;
}
goto done;
}
}
dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
/* ok, find it ! */
ret = nss_cmd_getgrnam_search(dctx);
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_getgr_send_reply(dctx, false);
}
done:
return nss_cmd_done(cmdctx, ret);
}
static void nss_cmd_getgrnam_cb(struct tevent_req *req)
{
struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx);
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct cli_ctx *cctx = cmdctx->cctx;
char *domname = NULL;
const char *rawname = dctx->rawname;
errno_t ret;
ret = sss_dp_get_domains_recv(req);
talloc_free(req);
if (ret != EOK) {
goto done;
}
ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains, rawname,
&domname, &cmdctx->name);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("Invalid name received [%s]\n", rawname));
ret = ENOENT;
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC, ("Requesting info for [%s] from [%s]\n",
cmdctx->name, domname?domname:"<ALL>"));
if (domname) {
dctx->domain = responder_get_domain(dctx, cctx->rctx, domname);
if (!dctx->domain) {
ret = ENOENT;
goto done;
}
} else {
/* this is a multidomain search */
dctx->domain = cctx->rctx->domains;
cmdctx->check_next = true;
}
dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
/* ok, find it ! */
ret = nss_cmd_getgrnam_search(dctx);
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_getgr_send_reply(dctx, false);
}
done:
nss_cmd_done(cmdctx, ret);
}
static void nss_cmd_getgrgid_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr);
/* search for a gid.
* Returns:
* ENOENT, if gid is definitely not found
* EAGAIN, if gid is beeing fetched from backend via async operations
* EOK, if found
* anything else on a fatal error
*/
static int nss_cmd_getgrgid_search(struct nss_dom_ctx *dctx)
{
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct sss_domain_info *dom = dctx->domain;
struct cli_ctx *cctx = cmdctx->cctx;
struct sysdb_ctx *sysdb;
struct nss_ctx *nctx;
int ret;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
while (dom) {
/* check that the gid is valid for this domain */
if ((dom->id_min && (cmdctx->id < dom->id_min)) ||
(dom->id_max && (cmdctx->id > dom->id_max))) {
DEBUG(4, ("Gid [%lu] does not exist in domain [%s]! "
"(id out of range)\n",
(unsigned long)cmdctx->id, dom->name));
if (cmdctx->check_next) {
dom = get_next_dom_or_subdom(dom);
continue;
}
return ENOENT;
}
if (dom != dctx->domain) {
/* make sure we reset the check_provider flag when we check
* a new domain */
dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
}
/* make sure to update the dctx if we changed domain */
dctx->domain = dom;
DEBUG(4, ("Requesting info for [%d@%s]\n", cmdctx->id, dom->name));
sysdb = dom->sysdb;
if (sysdb == NULL) {
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
return EIO;
}
ret = sysdb_getgrgid(cmdctx, sysdb, cmdctx->id, &dctx->res);
if (ret != EOK) {
DEBUG(1, ("Failed to make request to our cache!\n"));
return EIO;
}
if (dctx->res->count > 1) {
DEBUG(0, ("getgrgid call returned more than one result !?!\n"));
return ENOENT;
}
if (dctx->res->count == 0 && !dctx->check_provider) {
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
dom = get_next_dom_or_subdom(dom);
continue;
}
DEBUG(2, ("No results for getgrgid call\n"));
/* set negative cache only if not result of cache check */
ret = sss_ncache_set_gid(nctx->ncache, false, cmdctx->id);
if (ret != EOK) {
return ret;
}
return ENOENT;
}
/* if this is a caching provider (or if we haven't checked the cache
* yet) then verify that the cache is uptodate */
if (dctx->check_provider) {
ret = check_cache(dctx, nctx, dctx->res,
SSS_DP_GROUP, NULL, cmdctx->id,
nss_cmd_getgrgid_dp_callback,
dctx);
if (ret != EOK) {
/* Anything but EOK means we should reenter the mainloop
* because we may be refreshing the cache
*/
return ret;
}
}
/* One result found */
DEBUG(6, ("Returning info for gid [%d@%s]\n", cmdctx->id, dom->name));
return EOK;
}
DEBUG(2, ("No matching domain found for [%d], fail!\n", cmdctx->id));
return ENOENT;
}
static void nss_cmd_getgrgid_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr)
{
struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx);
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct cli_ctx *cctx = cmdctx->cctx;
int ret;
if (err_maj) {
DEBUG(2, ("Unable to get information from Data Provider\n"
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
if (dctx->res && dctx->res->count == 1) {
ret = nss_cmd_getgr_send_reply(dctx, true);
goto done;
}
/* no previous results, just loop to next domain if possible */
if (dctx->domain->next && cmdctx->check_next) {
dctx->domain = dctx->domain->next;
dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
} else {
/* nothing available */
ret = ENOENT;
goto done;
}
}
/* ok the backend returned, search to see if we have updated results */
ret = nss_cmd_getgrgid_search(dctx);
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_getgr_send_reply(dctx, true);
}
done:
ret = nss_cmd_done(cmdctx, ret);
if (ret) {
NSS_CMD_FATAL_ERROR(cctx);
}
}
static int nss_cmd_getgrgid(struct cli_ctx *cctx)
{
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
struct nss_ctx *nctx;
uint8_t *body;
size_t blen;
int ret;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
if (!cmdctx) {
return ENOMEM;
}
cmdctx->cctx = cctx;
dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
if (!dctx) {
ret = ENOMEM;
goto done;
}
dctx->cmdctx = cmdctx;
/* get uid to query */
sss_packet_get_body(cctx->creq->in, &body, &blen);
if (blen != sizeof(uint32_t)) {
ret = EINVAL;
goto done;
}
cmdctx->id = *((uint32_t *)body);
ret = sss_ncache_check_gid(nctx->ncache, nctx->neg_timeout, cmdctx->id);
if (ret == EEXIST) {
DEBUG(3, ("Gid [%lu] does not exist! (negative cache)\n",
(unsigned long)cmdctx->id));
ret = ENOENT;
goto done;
}
/* gid searches are always multidomain */
dctx->domain = cctx->rctx->domains;
cmdctx->check_next = true;
dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
/* ok, find it ! */
ret = nss_cmd_getgrgid_search(dctx);
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_getgr_send_reply(dctx, true);
}
done:
return nss_cmd_done(cmdctx, ret);
}
/* to keep it simple at this stage we are retrieving the
* full enumeration again for each request for each process
* and we also block on setgrent() for the full time needed
* to retrieve the data. And endgrent() frees all the data.
* Next steps are:
* - use and nsssrv wide cache with data already structured
* so that it can be immediately returned (see nscd way)
* - use mutexes so that setgrent() can return immediately
* even if the data is still being fetched
* - make getgrent() wait on the mutex
*/
struct tevent_req *nss_cmd_setgrent_send(TALLOC_CTX *mem_ctx,
struct cli_ctx *client);
static void nss_cmd_setgrent_done(struct tevent_req *req);
static int nss_cmd_setgrent(struct cli_ctx *cctx)
{
struct nss_cmd_ctx *cmdctx;
struct tevent_req *req;
errno_t ret = EOK;
cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
if (!cmdctx) {
return ENOMEM;
}
cmdctx->cctx = cctx;
req = nss_cmd_setgrent_send(cmdctx, cctx);
if (!req) {
DEBUG(0, ("Fatal error calling nss_cmd_setgrent_send\n"));
ret = EIO;
goto done;
}
tevent_req_set_callback(req, nss_cmd_setgrent_done, cmdctx);
done:
return nss_cmd_done(cmdctx, ret);
}
static errno_t nss_cmd_setgrent_step(struct setent_step_ctx *step_ctx);
struct tevent_req *nss_cmd_setgrent_send(TALLOC_CTX *mem_ctx,
struct cli_ctx *client)
{
errno_t ret;
struct nss_ctx *nctx;
struct tevent_req *req;
struct setent_ctx *state;
struct sss_domain_info *dom;
struct setent_step_ctx *step_ctx;
DEBUG(4, ("Received setgrent request\n"));
nctx = talloc_get_type(client->rctx->pvt_ctx, struct nss_ctx);
/* Reset the read pointers */
client->grent_dom_idx = 0;
client->grent_cur = 0;
req = tevent_req_create(mem_ctx, &state, struct setent_ctx);
if (!req) {
DEBUG(0, ("Could not create tevent request for setgrent\n"));
return NULL;
}
state->nctx = nctx;
state->client = client;
state->dctx = talloc_zero(state, struct nss_dom_ctx);
if (!state->dctx) {
ret = ENOMEM;
goto error;
}
/* check if enumeration is enabled in any domain */
for (dom = client->rctx->domains; dom; dom = dom->next) {
if (dom->enumerate != 0) break;
}
state->dctx->domain = dom;
if (state->dctx->domain == NULL) {
DEBUG(2, ("Enumeration disabled on all domains!\n"));
ret = ENOENT;
goto error;
}
state->dctx->check_provider =
NEED_CHECK_PROVIDER(state->dctx->domain->provider);
/* Is the result context already available */
if (state->nctx->gctx) {
if (state->nctx->gctx->ready) {
/* All of the necessary data is in place
* We can return now, getgrent requests will work at this point
*/
tevent_req_done(req);
tevent_req_post(req, state->nctx->rctx->ev);
}
else {
/* Object is still being constructed
* Register for notification when it's
* ready.
*/
ret = nss_setent_add_ref(state, state->nctx->gctx, req);
if (ret != EOK) {
talloc_free(req);
return NULL;
}
}
return req;
}
/* Create a new result context
* We are creating it on the nss_ctx so that it doesn't
* go away if the original request does. We will delete
* it when the refcount goes to zero;
*/
state->nctx->gctx = talloc_zero(nctx, struct getent_ctx);
if (!state->nctx->gctx) {
ret = ENOMEM;
goto error;
}
state->getent_ctx = nctx->gctx;
/* Add a callback reference for ourselves */
ret = nss_setent_add_ref(state, state->nctx->gctx, req);
if (ret) goto error;
/* ok, start the searches */
step_ctx = talloc_zero(state->getent_ctx, struct setent_step_ctx);
if (!step_ctx) {
ret = ENOMEM;
goto error;
}
/* Steal the dom_ctx onto the step_ctx so it doesn't go out of scope if
* this request is canceled while other requests are in-progress.
*/
step_ctx->dctx = talloc_steal(step_ctx, state->dctx);
step_ctx->nctx = state->nctx;
step_ctx->getent_ctx = state->getent_ctx;
step_ctx->rctx = client->rctx;
step_ctx->cctx = client;
step_ctx->returned_to_mainloop = false;
ret = nss_cmd_setgrent_step(step_ctx);
if (ret != EOK && ret != EAGAIN) goto error;
if (ret == EOK) {
tevent_req_post(req, client->rctx->ev);
}
return req;
error:
tevent_req_error(req, ret);
tevent_req_post(req, client->rctx->ev);
return req;
}
static void nss_cmd_setgrent_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr);
static void setgrent_result_timeout(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval current_time,
void *pvt);
/* nss_cmd_setgrent_step returns
* EOK if everything is done and the request needs to be posted explicitly
* EAGAIN if the caller can safely return to the main loop
*/
static errno_t nss_cmd_setgrent_step(struct setent_step_ctx *step_ctx)
{
errno_t ret;
struct sss_domain_info *dom = step_ctx->dctx->domain;
struct resp_ctx *rctx = step_ctx->rctx;
struct nss_dom_ctx *dctx = step_ctx->dctx;
struct getent_ctx *gctx = step_ctx->getent_ctx;
struct nss_ctx *nctx = step_ctx->nctx;
struct sysdb_ctx *sysdb;
struct ldb_result *res;
struct timeval tv;
struct tevent_timer *te;
struct tevent_req *dpreq;
struct dp_callback_ctx *cb_ctx;
while (dom) {
while (dom && dom->enumerate == 0) {
dom = dom->next;
}
if (!dom) break;
if (dom != dctx->domain) {
/* make sure we reset the check_provider flag when we check
* a new domain */
dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
}
/* make sure to update the dctx if we changed domain */
dctx->domain = dom;
DEBUG(6, ("Requesting info for domain [%s]\n", dom->name));
sysdb = dom->sysdb;
if (sysdb == NULL) {
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
return EIO;
}
/* if this is a caching provider (or if we haven't checked the cache
* yet) then verify that the cache is uptodate */
if (dctx->check_provider) {
step_ctx->returned_to_mainloop = true;
/* Only do this once per provider */
dctx->check_provider = false;
dpreq = sss_dp_get_account_send(step_ctx, rctx, dctx->domain, true,
SSS_DP_GROUP, NULL, 0, NULL);
if (!dpreq) {
DEBUG(SSSDBG_MINOR_FAILURE,
("Enum Cache refresh for domain [%s] failed."
" Trying to return what we have in cache!\n",
dom->name));
} else {
cb_ctx = talloc_zero(step_ctx, struct dp_callback_ctx);
if(!cb_ctx) {
talloc_zfree(dpreq);
return ENOMEM;
}
cb_ctx->callback = nss_cmd_setgrent_dp_callback;
cb_ctx->ptr = step_ctx;
cb_ctx->cctx = step_ctx->cctx;
cb_ctx->mem_ctx = step_ctx;
tevent_req_set_callback(dpreq, nsssrv_dp_send_acct_req_done, cb_ctx);
return EAGAIN;
}
}
ret = sysdb_enumgrent(dctx, sysdb, &res);
if (ret != EOK) {
DEBUG(1, ("Enum from cache failed, skipping domain [%s]\n",
dom->name));
dom = dom->next;
continue;
}
if (res->count == 0) {
DEBUG(4, ("Domain [%s] has no groups, skipping.\n", dom->name));
dom = dom->next;
continue;
}
nctx->gctx->doms = talloc_realloc(gctx, gctx->doms,
struct dom_ctx, gctx->num +1);
if (!gctx->doms) {
talloc_free(gctx);
nctx->gctx = NULL;
return ENOMEM;
}
nctx->gctx->doms[gctx->num].domain = dctx->domain;
nctx->gctx->doms[gctx->num].res = talloc_steal(gctx->doms, res);
nctx->gctx->num++;
/* do not reply until all domain searches are done */
dom = dom->next;
}
/* We've finished all our lookups
* The result object is now safe to read.
*/
nctx->gctx->ready = true;
/* Set up a lifetime timer for this result object
* We don't want this result object to outlive the
* enum cache refresh timeout
*/
tv = tevent_timeval_current_ofs(nctx->enum_cache_timeout, 0);
te = tevent_add_timer(rctx->ev, nctx->gctx, tv,
setgrent_result_timeout, nctx);
if (!te) {
DEBUG(0, ("Could not set up life timer for setgrent result object. "
"Entries may become stale.\n"));
}
/* Notify the waiting clients */
nss_setent_notify_done(nctx->gctx);
if (step_ctx->returned_to_mainloop) {
return EAGAIN;
} else {
return EOK;
}
}
static void setgrent_result_timeout(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval current_time,
void *pvt)
{
struct nss_ctx *nctx = talloc_get_type(pvt, struct nss_ctx);
DEBUG(1, ("setgrent result object has expired. Cleaning up.\n"));
/* Free the group enumeration context.
* If additional getgrent requests come in, they will invoke
* an implicit setgrent and refresh the result object.
*/
talloc_zfree(nctx->gctx);
}
static void nss_cmd_setgrent_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr)
{
struct setent_step_ctx *step_ctx =
talloc_get_type(ptr, struct setent_step_ctx);
int ret;
if (err_maj) {
DEBUG(2, ("Unable to get information from Data Provider\n"
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
}
ret = nss_cmd_setgrent_step(step_ctx);
if (ret != EOK && ret != EAGAIN) {
/* Notify any waiting processes of failure */
nss_setent_notify_error(step_ctx->nctx->gctx, ret);
}
}
static errno_t nss_cmd_setgrent_recv(struct tevent_req *req)
{
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
static void nss_cmd_setgrent_done(struct tevent_req *req)
{
errno_t ret;
struct nss_cmd_ctx *cmdctx =
tevent_req_callback_data(req, struct nss_cmd_ctx);
ret = nss_cmd_setgrent_recv(req);
talloc_zfree(req);
if (ret == EOK || ret == ENOENT) {
/* Either we succeeded or no domains were eligible */
ret = sss_packet_new(cmdctx->cctx->creq, 0,
sss_packet_get_cmd(cmdctx->cctx->creq->in),
&cmdctx->cctx->creq->out);
if (ret == EOK) {
sss_cmd_done(cmdctx->cctx, cmdctx);
return;
}
}
/* Something bad happened */
nss_cmd_done(cmdctx, ret);
}
static int nss_cmd_retgrent(struct cli_ctx *cctx, int num)
{
struct nss_ctx *nctx;
struct getent_ctx *gctx;
struct ldb_message **msgs = NULL;
struct dom_ctx *gdom = NULL;
int n = 0;
int ret = ENOENT;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
if (!nctx->gctx) goto none;
gctx = nctx->gctx;
while (ret == ENOENT) {
if (cctx->grent_dom_idx >= gctx->num) break;
gdom = &gctx->doms[cctx->grent_dom_idx];
n = gdom->res->count - cctx->grent_cur;
if (n <= 0 && (cctx->grent_dom_idx+1 < gctx->num)) {
cctx->grent_dom_idx++;
gdom = &gctx->doms[cctx->grent_dom_idx];
n = gdom->res->count;
cctx->grent_cur = 0;
}
if (!n) break;
if (n > num) n = num;
msgs = &(gdom->res->msgs[cctx->grent_cur]);
ret = fill_grent(cctx->creq->out,
gdom->domain,
nctx, true, false, msgs, &n);
cctx->grent_cur += n;
}
none:
if (ret == ENOENT) {
ret = sss_cmd_empty_packet(cctx->creq->out);
}
return ret;
}
static int nss_cmd_getgrent_immediate(struct nss_cmd_ctx *cmdctx)
{
struct cli_ctx *cctx = cmdctx->cctx;
uint8_t *body;
size_t blen;
uint32_t num;
int ret;
/* get max num of entries to return in one call */
sss_packet_get_body(cctx->creq->in, &body, &blen);
if (blen != sizeof(uint32_t)) {
return EINVAL;
}
num = *((uint32_t *)body);
/* create response packet */
ret = sss_packet_new(cctx->creq, 0,
sss_packet_get_cmd(cctx->creq->in),
&cctx->creq->out);
if (ret != EOK) {
return ret;
}
ret = nss_cmd_retgrent(cctx, num);
sss_packet_set_error(cctx->creq->out, ret);
sss_cmd_done(cctx, cmdctx);
return EOK;
}
static void nss_cmd_implicit_setgrent_done(struct tevent_req *req);
static int nss_cmd_getgrent(struct cli_ctx *cctx)
{
struct nss_ctx *nctx;
struct nss_cmd_ctx *cmdctx;
struct tevent_req *req;
DEBUG(4, ("Requesting info for all groups\n"));
cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
if (!cmdctx) {
return ENOMEM;
}
cmdctx->cctx = cctx;
/* Save the current index and cursor locations
* If we end up calling setgrent implicitly, because the response object
* expired and has to be recreated, we want to resume from the same
* location.
*/
cmdctx->saved_dom_idx = cctx->grent_dom_idx;
cmdctx->saved_cur = cctx->grent_cur;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
if(!nctx->gctx || !nctx->gctx->ready) {
/* Make sure we invoke setgrent if it hasn't been run or is still
* processing from another client
*/
req = nss_cmd_setgrent_send(cctx, cctx);
if (!req) {
return EIO;
}
tevent_req_set_callback(req, nss_cmd_implicit_setgrent_done, cmdctx);
return EOK;
}
return nss_cmd_getgrent_immediate(cmdctx);
}
static errno_t nss_cmd_setgrent_recv(struct tevent_req *req);
static void nss_cmd_implicit_setgrent_done(struct tevent_req *req)
{
errno_t ret;
struct nss_cmd_ctx *cmdctx =
tevent_req_callback_data(req, struct nss_cmd_ctx);
ret = nss_cmd_setgrent_recv(req);
talloc_zfree(req);
/* ENOENT is acceptable, as it just means that there were no entries
* to be returned. This will be handled gracefully in nss_cmd_retpwent
* later.
*/
if (ret != EOK && ret != ENOENT) {
DEBUG(0, ("Implicit setgrent failed with unexpected error [%d][%s]\n",
ret, strerror(ret)));
NSS_CMD_FATAL_ERROR(cmdctx);
}
/* Restore the saved index and cursor locations */
cmdctx->cctx->grent_dom_idx = cmdctx->saved_dom_idx;
cmdctx->cctx->grent_cur = cmdctx->saved_cur;
ret = nss_cmd_getgrent_immediate(cmdctx);
if (ret != EOK) {
DEBUG(0, ("Immediate retrieval failed with unexpected error "
"[%d][%s]\n", ret, strerror(ret)));
NSS_CMD_FATAL_ERROR(cmdctx);
}
}
static int nss_cmd_endgrent(struct cli_ctx *cctx)
{
struct nss_ctx *nctx;
int ret;
DEBUG(4, ("Terminating request info for all groups\n"));
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
/* create response packet */
ret = sss_packet_new(cctx->creq, 0,
sss_packet_get_cmd(cctx->creq->in),
&cctx->creq->out);
if (ret != EOK) {
return ret;
}
if (nctx->gctx == NULL) goto done;
/* Reset the indices so that subsequent requests start at zero */
cctx->grent_dom_idx = 0;
cctx->grent_cur = 0;
done:
sss_cmd_done(cctx, NULL);
return EOK;
}
/* FIXME: what about mpg, should we return the user's GID ? */
/* FIXME: should we filter out GIDs ? */
static int fill_initgr(struct sss_packet *packet, struct ldb_result *res)
{
uint8_t *body;
size_t blen;
gid_t gid;
int ret, i, num, bindex;
int skipped = 0;
const char *posix;
if (res->count == 0) {
return ENOENT;
}
/* one less, the first one is the user entry */
num = res->count -1;
ret = sss_packet_grow(packet, (2 + res->count) * sizeof(uint32_t));
if (ret != EOK) {
return ret;
}
sss_packet_get_body(packet, &body, &blen);
/* skip first entry, it's the user entry */
bindex = 0;
for (i = 0; i < num; i++) {
gid = ldb_msg_find_attr_as_uint64(res->msgs[i + 1], SYSDB_GIDNUM, 0);
posix = ldb_msg_find_attr_as_string(res->msgs[i + 1], SYSDB_POSIX, NULL);
if (!gid) {
if (posix && strcmp(posix, "FALSE") == 0) {
skipped++;
continue;
} else {
DEBUG(1, ("Incomplete group object for initgroups! Aborting\n"));
return EFAULT;
}
}
((uint32_t *)body)[2 + bindex] = gid;
bindex++;
}
((uint32_t *)body)[0] = num-skipped; /* num results */
((uint32_t *)body)[1] = 0; /* reserved */
return EOK;
}
static int nss_cmd_initgr_send_reply(struct nss_dom_ctx *dctx)
{
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct cli_ctx *cctx = cmdctx->cctx;
int ret;
ret = sss_packet_new(cctx->creq, 0,
sss_packet_get_cmd(cctx->creq->in),
&cctx->creq->out);
if (ret != EOK) {
return EFAULT;
}
ret = fill_initgr(cctx->creq->out, dctx->res);
if (ret) {
return ret;
}
sss_packet_set_error(cctx->creq->out, EOK);
sss_cmd_done(cctx, cmdctx);
return EOK;
}
static void nss_cmd_initgroups_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr);
static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx)
{
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct sss_domain_info *dom = dctx->domain;
struct cli_ctx *cctx = cmdctx->cctx;
char *name = NULL;
struct sysdb_ctx *sysdb;
struct nss_ctx *nctx;
int ret;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
while (dom) {
/* if it is a domainless search, skip domains that require fully
* qualified names instead */
while (dom && cmdctx->check_next && dom->fqnames) {
dom = dom->next;
}
if (!dom) break;
if (dom != dctx->domain) {
/* make sure we reset the check_provider flag when we check
* a new domain */
dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
}
/* make sure to update the dctx if we changed domain */
dctx->domain = dom;
talloc_free(name);
name = sss_get_cased_name(dctx, cmdctx->name, dom->case_sensitive);
if (!name) return ENOMEM;
/* verify this user has not yet been negatively cached,
* or has been permanently filtered */
ret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
dom, name);
/* if neg cached, return we didn't find it */
if (ret == EEXIST) {
DEBUG(2, ("User [%s] does not exist in [%s]! (negative cache)\n",
name, dom->name));
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
dom = dom->next;
continue;
}
/* There are no further domains or this was a
* fully-qualified user request.
*/
return ENOENT;
}
DEBUG(4, ("Requesting info for [%s@%s]\n", name, dom->name));
sysdb = dom->sysdb;
if (sysdb == NULL) {
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
return EIO;
}
ret = sysdb_initgroups(cmdctx, sysdb, name, &dctx->res);
if (ret != EOK) {
DEBUG(1, ("Failed to make request to our cache! [%d][%s]\n",
ret, strerror(ret)));
return EIO;
}
if (dctx->res->count == 0 && !dctx->check_provider) {
/* set negative cache only if not result of cache check */
ret = sss_ncache_set_user(nctx->ncache, false, dom, name);
if (ret != EOK) {
return ret;
}
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
dom = dom->next;
if (dom) continue;
}
DEBUG(2, ("No results for initgroups call\n"));
return ENOENT;
}
/* if this is a caching provider (or if we haven't checked the cache
* yet) then verify that the cache is uptodate */
if (dctx->check_provider) {
ret = check_cache(dctx, nctx, dctx->res,
SSS_DP_INITGROUPS, name, 0,
nss_cmd_initgroups_dp_callback,
dctx);
if (ret != EOK) {
/* Anything but EOK means we should reenter the mainloop
* because we may be refreshing the cache
*/
return ret;
}
}
DEBUG(6, ("Initgroups for [%s@%s] completed\n", name, dom->name));
return EOK;
}
DEBUG(SSSDBG_MINOR_FAILURE,
("No matching domain found for [%s], fail!\n", cmdctx->name));
return ENOENT;
}
static void nss_cmd_initgroups_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr)
{
struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx);
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct cli_ctx *cctx = cmdctx->cctx;
int ret;
if (err_maj) {
DEBUG(2, ("Unable to get information from Data Provider\n"
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
if (dctx->res && dctx->res->count != 0) {
ret = nss_cmd_initgr_send_reply(dctx);
goto done;
}
/* no previous results, just loop to next domain if possible */
if (dctx->domain->next && cmdctx->check_next) {
dctx->domain = dctx->domain->next;
dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
} else {
/* nothing available */
ret = ENOENT;
goto done;
}
}
/* ok the backend returned, search to see if we have updated results */
ret = nss_cmd_initgroups_search(dctx);
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_initgr_send_reply(dctx);
}
done:
ret = nss_cmd_done(cmdctx, ret);
if (ret) {
NSS_CMD_FATAL_ERROR(cctx);
}
}
/* for now, if we are online, try to always query the backend */
static void nss_cmd_initgroups_cb(struct tevent_req *req);
static int nss_cmd_initgroups(struct cli_ctx *cctx)
{
struct tevent_req *req;
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
const char *rawname;
char *domname;
uint8_t *body;
size_t blen;
int ret;
cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
if (!cmdctx) {
return ENOMEM;
}
cmdctx->cctx = cctx;
dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
if (!dctx) {
ret = ENOMEM;
goto done;
}
dctx->cmdctx = cmdctx;
/* get user name to query */
sss_packet_get_body(cctx->creq->in, &body, &blen);
/* if not terminated fail */
if (body[blen -1] != '\0') {
ret = EINVAL;
goto done;
}
/* If the body isn't valid UTF-8, fail */
if (!sss_utf8_check(body, blen -1)) {
ret = EINVAL;
goto done;
}
rawname = (const char *)body;
domname = NULL;
ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains, rawname,
&domname, &cmdctx->name);
if (ret == EAGAIN) {
req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, domname);
if (req == NULL) {
ret = ENOMEM;
} else {
dctx->rawname = rawname;
tevent_req_set_callback(req, nss_cmd_initgroups_cb, dctx);
ret = EAGAIN;
}
goto done;
} else if (ret != EOK) {
DEBUG(2, ("Invalid name received [%s]\n", rawname));
ret = ENOENT;
goto done;
}
DEBUG(4, ("Requesting info for [%s] from [%s]\n",
cmdctx->name, domname?domname:"<ALL>"));
if (domname) {
dctx->domain = responder_get_domain(dctx, cctx->rctx, domname);
if (!dctx->domain) {
ret = ENOENT;
goto done;
}
} else {
/* this is a multidomain search */
dctx->rawname = rawname;
dctx->domain = cctx->rctx->domains;
cmdctx->check_next = true;
if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, false, NULL);
if (req == NULL) {
ret = ENOMEM;
} else {
tevent_req_set_callback(req, nss_cmd_initgroups_cb, dctx);
ret = EAGAIN;
}
goto done;
}
}
dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
/* ok, find it ! */
ret = nss_cmd_initgroups_search(dctx);
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_initgr_send_reply(dctx);
}
done:
return nss_cmd_done(cmdctx, ret);
}
static void nss_cmd_initgroups_cb(struct tevent_req *req)
{
struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx);
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct cli_ctx *cctx = cmdctx->cctx;
char *domname = NULL;
const char *rawname = dctx->rawname;
errno_t ret;
ret = sss_dp_get_domains_recv(req);
talloc_free(req);
if (ret != EOK) {
goto done;
}
ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains, rawname,
&domname, &cmdctx->name);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("Invalid name received [%s]\n", rawname));
ret = ENOENT;
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC, ("Requesting info for [%s] from [%s]\n",
cmdctx->name, domname?domname:"<ALL>"));
if (domname) {
dctx->domain = responder_get_domain(dctx, cctx->rctx, domname);
if (!dctx->domain) {
ret = ENOENT;
goto done;
}
} else {
/* this is a multidomain search */
dctx->domain = cctx->rctx->domains;
cmdctx->check_next = true;
}
dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
/* ok, find it ! */
ret = nss_cmd_initgroups_search(dctx);
if (ret == EOK) {
/* we have results to return */
ret = nss_cmd_initgr_send_reply(dctx);
}
done:
nss_cmd_done(cmdctx, ret);
}
struct cli_protocol_version *register_cli_protocol_version(void)
{
static struct cli_protocol_version nss_cli_protocol_version[] = {
{1, "2008-09-05", "initial version, \\0 terminated strings"},
{0, NULL, NULL}
};
return nss_cli_protocol_version;
}
static struct sss_cmd_table nss_cmds[] = {
{SSS_GET_VERSION, sss_cmd_get_version},
{SSS_NSS_GETPWNAM, nss_cmd_getpwnam},
{SSS_NSS_GETPWUID, nss_cmd_getpwuid},
{SSS_NSS_SETPWENT, nss_cmd_setpwent},
{SSS_NSS_GETPWENT, nss_cmd_getpwent},
{SSS_NSS_ENDPWENT, nss_cmd_endpwent},
{SSS_NSS_GETGRNAM, nss_cmd_getgrnam},
{SSS_NSS_GETGRGID, nss_cmd_getgrgid},
{SSS_NSS_SETGRENT, nss_cmd_setgrent},
{SSS_NSS_GETGRENT, nss_cmd_getgrent},
{SSS_NSS_ENDGRENT, nss_cmd_endgrent},
{SSS_NSS_INITGR, nss_cmd_initgroups},
{SSS_NSS_SETNETGRENT, nss_cmd_setnetgrent},
{SSS_NSS_GETNETGRENT, nss_cmd_getnetgrent},
{SSS_NSS_ENDNETGRENT, nss_cmd_endnetgrent},
{SSS_NSS_GETSERVBYNAME, nss_cmd_getservbyname},
{SSS_NSS_GETSERVBYPORT, nss_cmd_getservbyport},
{SSS_NSS_SETSERVENT, nss_cmd_setservent},
{SSS_NSS_GETSERVENT, nss_cmd_getservent},
{SSS_NSS_ENDSERVENT, nss_cmd_endservent},
{SSS_CLI_NULL, NULL}
};
struct sss_cmd_table *get_nss_cmds(void) {
return nss_cmds;
}
int nss_cmd_execute(struct cli_ctx *cctx)
{
enum sss_cli_command cmd;
int i;
cmd = sss_packet_get_cmd(cctx->creq->in);
for (i = 0; nss_cmds[i].cmd != SSS_CLI_NULL; i++) {
if (cmd == nss_cmds[i].cmd) {
return nss_cmds[i].fn(cctx);
}
}
return EINVAL;
}