nsssrv_cmd.c revision 6fc4702a3037d9bb5b27bcb58f70edf1802b7b19
/*
SSSD
NSS Responder
Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "responder/nss/nsssrv_private.h"
#include "responder/nss/nsssrv_netgroup.h"
#include "responder/nss/nsssrv_services.h"
#include "responder/nss/nsssrv_mmap_cache.h"
#include "responder/common/negcache.h"
#include <time.h>
{
}
{
}
{
switch (ret) {
case EOK:
/* all fine, just return here */
break;
case ENOENT:
if (ret) {
return EFAULT;
}
break;
case EAGAIN:
/* async processing, just return here */
break;
case EFAULT:
/* very bad error */
return EFAULT;
default:
if (ret) {
return EFAULT;
}
break;
}
return EOK;
}
/***************************
* Enumeration procedures *
***************************/
struct getent_ctx *getent_ctx,
struct tevent_req *req)
{
}
{
}
{
}
struct setent_ctx {
struct nss_dom_ctx *dctx;
struct getent_ctx *getent_ctx;
};
/****************************************************************************
* PASSWD db related functions
***************************************************************************/
{
struct sss_domain_info *dom;
struct ldb_result *res;
struct sized_string key;
const char *id;
int ret;
int i;
continue;
}
SYSDB_CACHE_EXPIRE, 0);
continue;
}
/* names require more manipulation (build up fqname conditionally),
* but uidNumber is unique and always resolvable too, so we use
* that to update the cache, as it points to the same entry */
if (!id) {
("Failed to find uidNumber in %s.\n",
continue;
}
("Internal failure in memory cache code: %d [%s]\n",
}
}
}
}
struct sss_domain_info *dom)
{
return dom->override_gid ?
dom->override_gid :
}
struct ldb_message *msg,
struct sss_domain_info *dom,
const char *name,
{
const char *homedir;
/* Check whether we are unconditionally overriding the server
* for home directory locations.
*/
if (dom->override_homedir) {
} else if (nctx->override_homedir) {
}
/* In the case of a NULL or empty homedir, check to see if
* we have a fallback homedir to use.
*/
if (dom->fallback_homedir) {
} else if (nctx->fallback_homedir) {
}
}
/* Return the value we got from the provider */
}
struct ldb_message *msg,
struct sss_domain_info *dom)
{
const char *user_shell;
int i;
/* Check whether we are unconditionally overriding the server
* for the login shell.
*/
if (dom->override_shell) {
return dom->override_shell;
} else if (nctx->override_shell) {
return nctx->override_shell;
}
if (!user_shell) {
/* Check whether there is a default shell specified */
if (dom->default_shell) {
} else if (nctx->default_shell) {
}
return NULL;
}
if (nctx->vetoed_shells) {
for (i=0; nctx->vetoed_shells[i]; i++) {
"Using fallback\n", user_shell));
}
}
}
if (nctx->etc_shells) {
for (i=0; nctx->etc_shells[i]; i++) {
nctx->etc_shells[i]));
break;
}
}
if (nctx->etc_shells[i]) {
}
}
if (nctx->allowed_shells) {
for (i=0; nctx->allowed_shells[i]; i++) {
"Using fallback\n", user_shell));
}
}
}
user_shell));
}
struct sss_domain_info *dom,
bool filter_users, bool pw_mmap_cache,
struct ldb_message **msgs,
int *count)
{
struct ldb_message *msg;
const char *tmpstr;
const char *orig_name;
struct sized_string name;
struct sized_string gecos;
struct sized_string homedir;
struct sized_string shell;
struct sized_string pwfield;
struct sized_string fullname;
int delim = 1;
const char *namefmt;
bool packet_initialized = false;
int ncret;
num = 0;
for (i = 0; i < *count; i++) {
continue;
}
if (filter_users) {
("User [%s@%s] filtered out! (negative cache)\n",
continue;
}
}
if (!packet_initialized) {
/* first 2 fields (len and reserved), filled up later */
packet_initialized = true;
}
("sss_get_cased_name failed, skipping\n"));
continue;
}
if (!tmpstr) {
} else {
}
if (!tmpstr) {
} else {
}
if (!tmpstr) {
} else {
}
num = 0;
goto done;
}
if (add_domain) {
/* need more space, got creative with the print format ? */
num = 0;
goto done;
}
delim += t;
/* retry */
}
continue;
}
} else {
}
num++;
}
}
}
done:
*count = i;
/* if there are no results just return ENOENT,
* let the caller decide if this is the last packet or not */
if (!packet_initialized) return ENOENT;
return EOK;
}
{
int ret;
int i;
return EFAULT;
}
if (ret) {
return ret;
}
return EOK;
}
/* FIXME: do not check res->count, but get in a msgs and check in parent */
/* FIXME: do not sss_cmd_done, but return error and let parent do it */
struct ldb_result *res,
int req_type,
const char *opt_name,
void *pvt)
{
uint64_t cacheExpire = 0;
/* when searching for a user or netgroup, more than one reply is a
* db error
*/
" DB Corrupted?\n"));
}
return ENOENT;
}
/* if we have any reply let's check cache validity */
if (req_type == SSS_DP_INITGROUPS) {
SYSDB_INITGR_EXPIRE, 1);
}
if (cacheExpire == 0) {
SYSDB_CACHE_EXPIRE, 0);
}
/* if we have any reply let's check cache validity */
return EOK;
goto error;
}
} else {
/* No replies */
}
/* EAGAIN (off band) or ENOENT (cache miss) -> check cache */
/* No callback required
* This was an out-of-band update. We'll return EOK
* so the calling function can return the cached entry
* immediately.
*/
("Performing midpoint cache update on [%s]\n", opt_name));
if (!req) {
("Out of memory sending out-of-band data provider "
"request\n"));
/* This is non-fatal, so we'll continue here */
} else {
}
/* We don't need to listen for a reply, so we will free the
* request here.
*/
} else {
/* This is a cache miss. Or the cache is expired.
* We need to get the updated user information before returning it.
*/
/* dont loop forever :-) */
dctx->check_provider = false;
/* keep around current data in case backend is offline */
}
if (!req) {
("Out of memory sending data provider request\n"));
goto error;
}
if(!cb_ctx) {
goto error;
}
return EAGAIN;
}
return EOK;
}
return EOK;
}
{
struct dp_callback_ctx *cb_ctx =
char *err_msg;
&err_msg);
}
}
struct sss_mc_ctx *mc_ctx)
{
struct sized_string delete_name;
int ret;
return ENOMEM;
}
goto done;
}
} else {
}
("Internal failure in memory cache code: %d [%s]\n",
goto done;
}
done:
return ret;
}
/* search for a user.
* Returns:
* ENOENT, if user is definitely not found
* EAGAIN, if user is beeing fetched from backend via async operations
* EOK, if found
* anything else on a fatal error
*/
{
int ret;
while (dom) {
/* if it is a domainless search, skip domains that require fully
* qualified names instead */
}
if (!dom) break;
/* make sure we reset the check_provider flag when we check
* a new domain */
}
/* make sure to update the dctx if we changed domain */
/* verify this user has not yet been negatively cached,
* or has been permanently filtered */
/* if neg cached, return we didn't find it */
("User [%s] does not exist in [%s]! (negative cache)\n",
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
continue;
}
/* There are no further domains or this was a
* fully-qualified user request.
*/
return ENOENT;
}
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
return EIO;
}
return EIO;
}
DEBUG(0, ("getpwnam call returned more than one result !?!\n"));
return ENOENT;
}
/* set negative cache only if not result of cache check */
return ret;
}
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
if (dom) continue;
}
/* User not found in ldb -> delete user from memory cache. */
nctx->pwd_mc_ctx);
("Deleting user from memcache failed.\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) {
SSS_DP_USER, name, 0,
dctx);
/* Anything but EOK means we should reenter the mainloop
* because we may be refreshing the cache
*/
return ret;
}
}
/* One result found */
return EOK;
}
return ENOENT;
}
{
int ret;
if (err_maj) {
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
goto done;
}
/* no previous results, just loop to next domain if possible */
} else {
/* nothing available */
goto done;
}
}
/* ok the backend returned, search to see if we have updated results */
/* we have results to return */
}
done:
if (ret) {
}
}
{
struct tevent_req *req;
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
const char *rawname;
char *domname;
int ret;
if (!cmdctx) {
return ENOMEM;
}
if (!dctx) {
goto done;
}
/* get user name to query */
/* if not terminated fail */
goto done;
}
/* If the body isn't valid UTF-8, fail */
goto done;
}
} else {
}
goto done;
goto done;
}
if (domname) {
goto done;
}
} else {
/* this is a multidomain search */
cmdctx->check_next = true;
} else {
}
goto done;
}
}
/* ok, find it ! */
/* we have results to return */
}
done:
}
{
goto done;
}
goto done;
}
if (domname) {
goto done;
}
} else {
/* this is a multidomain search */
cmdctx->check_next = true;
}
/* ok, find it ! */
/* we have results to return */
}
done:
}
/* 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
*/
{
int ret;
while (dom) {
/* check that the uid is valid for this domain */
"(id out of range)\n",
if (cmdctx->check_next) {
continue;
}
return ENOENT;
}
/* make sure we reset the check_provider flag when we check
* a new domain */
}
/* make sure to update the dctx if we changed domain */
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
return EIO;
}
return EIO;
}
DEBUG(0, ("getpwuid call returned more than one result !?!\n"));
return ENOENT;
}
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
continue;
}
/* set negative cache only if not result of cache check */
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) {
dctx);
/* Anything but EOK means we should reenter the mainloop
* because we may be refreshing the cache
*/
return ret;
}
}
/* One result found */
return EOK;
}
return ENOENT;
}
{
int ret;
if (err_maj) {
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
goto done;
}
/* no previous results, just loop to next domain if possible */
} else {
/* nothing available */
goto done;
}
}
/* ok the backend returned, search to see if we have updated results */
/* we have results to return */
}
done:
if (ret) {
}
}
{
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
int ret;
struct tevent_req *req;
if (!cmdctx) {
return ENOMEM;
}
if (!dctx) {
goto done;
}
/* get uid to query */
goto done;
}
("Uid [%lu] does not exist! (negative cache)\n",
goto done;
}
/* uid searches are always multidomain */
cmdctx->check_next = true;
} else {
}
goto done;
}
/* ok, find it ! */
/* we have results to return */
}
done:
}
{
goto done;
}
/* ok, find it ! */
/* we have results to return */
}
done:
}
/* 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.
*/
{
struct nss_cmd_ctx *cmdctx;
struct tevent_req *req;
if (!cmdctx) {
return ENOMEM;
}
if (!req) {
DEBUG(0, ("Fatal error calling nss_cmd_setpwent_send\n"));
goto done;
}
done:
}
{
struct tevent_req *req;
struct setent_ctx *state;
struct sss_domain_info *dom;
struct setent_step_ctx *step_ctx;
/* Reset the read pointers */
client->pwent_dom_idx = 0;
if (!req) {
DEBUG(0, ("Could not create tevent request for setpwent\n"));
return NULL;
}
goto error;
}
/* check if enumeration is enabled in any domain */
}
goto error;
}
/* Is the result context already available */
/* All of the necessary data is in place
* We can return now, getpwent requests will work at this point
*/
}
else {
/* Object is still being constructed
* Register for notification when it's
* ready.
*/
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;
*/
goto error;
}
/* Add a callback reference for ourselves */
/* ok, start the searches */
if (!step_ctx) {
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->returned_to_mainloop = false;
}
return req;
return req;
}
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
*/
{
struct ldb_result *res;
struct tevent_timer *te;
struct tevent_req *dpreq;
struct dp_callback_ctx *cb_ctx;
while (dom) {
}
if (!dom) break;
/* make sure we reset the check_provider flag when we check
* a new domain */
}
/* make sure to update the dctx if we changed domain */
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;
if (!dpreq) {
("Enum Cache refresh for domain [%s] failed."
" Trying to return what we have in cache!\n",
} else {
if(!cb_ctx) {
return ENOMEM;
}
return EAGAIN;
}
}
continue;
}
continue;
}
return ENOMEM;
}
/* do not reply until all domain searches are done */
}
/* We've finished all our lookups
* The result object is now safe to read.
*/
/* Set up a lifetime timer for this result object
* We don't want this result object to outlive the
* enum cache refresh timeout
*/
if (!te) {
DEBUG(0, ("Could not set up life timer for setpwent result object. "
"Entries may become stale.\n"));
}
/* Notify the waiting clients */
if (step_ctx->returned_to_mainloop) {
return EAGAIN;
} else {
return EOK;
}
}
struct tevent_timer *te,
struct timeval current_time,
void *pvt)
{
/* Free the passwd enumeration context.
* If additional getpwent requests come in, they will invoke
* an implicit setpwent and refresh the result object.
*/
}
{
struct setent_step_ctx *step_ctx =
int ret;
if (err_maj) {
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
}
/* Notify any waiting processes of failure */
}
}
{
return EOK;
}
{
struct nss_cmd_ctx *cmdctx =
/* Either we succeeded or no domains were eligible */
return;
}
}
/* Something bad happened */
}
{
struct nss_cmd_ctx *cmdctx;
struct tevent_req *req;
if (!cmdctx) {
return ENOMEM;
}
/* 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.
*/
/* Make sure we invoke setpwent if it hasn't been run or is still
* processing from another client
*/
if (!req) {
return EIO;
}
return EOK;
}
return nss_cmd_getpwent_immediate(cmdctx);
}
{
int ret;
/* get max num of entries to return in one call */
return EINVAL;
}
/* create response packet */
return ret;
}
return EOK;
}
{
struct getent_ctx *pctx;
int n = 0;
cctx->pwent_dom_idx++;
}
if (!n) break;
if (n < 0) {
break;
}
true, false, msgs, &n);
}
none:
}
return ret;
}
{
struct nss_cmd_ctx *cmdctx =
/* 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.
*/
DEBUG(0, ("Implicit setpwent failed with unexpected error [%d][%s]\n",
}
/* Restore the saved index and cursor locations */
DEBUG(0, ("Immediate retrieval failed with unexpected error "
}
}
{
int ret;
/* create response packet */
return ret;
}
/* Reset the indices so that subsequent requests start at zero */
cctx->pwent_dom_idx = 0;
done:
return EOK;
}
/****************************************************************************
* GROUP db related functions
***************************************************************************/
{
struct sss_domain_info *dom;
struct ldb_result *res;
struct sized_string key;
const char *id;
int ret;
int i;
continue;
}
SYSDB_CACHE_EXPIRE, 0);
continue;
}
/* names require more manipulation (build up fqname conditionally),
* but uidNumber is unique and always resolvable too, so we use
* that to update the cache, as it points to the same entry */
if (!id) {
("Failed to find gidNumber in %s.\n",
continue;
}
("Internal failure in memory cache code: %d [%s]\n",
}
}
}
}
#define GID_ROFFSET 0
#define MNUM_ROFFSET sizeof(uint32_t)
struct sss_domain_info *dom,
struct ldb_message_element *el,
int *_memnum)
{
char *tmpstr;
struct sized_string name;
if (add_domain) {
delim = 1;
} else {
delim = 0;
dom_len = 0;
}
return ENOMEM;
}
for (i = 0; i < el->num_values; i++) {
("sss_get_cased_name failed, skipping\n"));
continue;
}
if (nctx->filter_users_in_groups) {
("Group [%s] member [%s@%s] filtered out!"
" (negative cache)\n",
continue;
}
}
if (add_domain) {
} else {
}
goto done;
}
if (add_domain) {
/* need more space,
* got creative with the print format ? */
goto done;
}
delim += t;
/* retry */
}
" for member [%s@%s] of group [%s]!"
/* reclaim space */
goto done;
}
continue;
}
} else {
}
if (add_domain) {
} else {
}
memnum++;
}
ret = 0;
done:
return ret;
}
struct sss_domain_info *dom,
bool filter_groups, bool gr_mmap_cache,
struct ldb_message **msgs,
int *count)
{
struct ldb_message *msg;
struct ldb_message_element *el;
const char *tmpstr;
const char *orig_name;
struct sized_string name;
struct sized_string pwfield;
struct sized_string fullname;
int i = 0;
const char *namefmt;
if (add_domain) {
delim = 1;
} else {
delim = 0;
dom_len = 0;
}
num = 0;
/* first 2 fields (len and reserved), filled up later */
goto done;
}
rsize = 0;
for (i = 0; i < *count; i++) {
/* new group */
continue;
}
/* new result starts at end of previous result */
rsize = 0;
continue;
}
if (filter_groups) {
("Group [%s@%s] filtered out! (negative cache)\n",
continue;
}
}
("sss_get_cased_name failed, skipping\n"));
continue;
}
/* fill in gid and name and set pointer for number of members */
num = 0;
goto done;
}
/* 0-3: 32bit number gid */
/* 4-7: 32bit unsigned number of members */
/* 8-X: sequence of strings (name, passwd, mem..) */
if (add_domain) {
/* need more space, got creative with the print format ? */
num = 0;
goto done;
}
rsize += t;
delim += t;
/* retry */
}
/* reclaim space */
num = 0;
goto done;
}
rsize = 0;
continue;
}
} else {
}
/* group passwd field */
memnum = 0;
if (!dom->ignore_group_members) {
if (el) {
&memnum);
num = 0;
goto done;
}
}
if (el) {
&memnum);
num = 0;
goto done;
}
}
}
if (memnum) {
/* set num of members */
}
num++;
/* body was reallocated, so fullname might be pointing to
* where body used to be, not where it is */
rsize - STRS_ROFFSET -
("Failed to store group %s(%s) in mmap cache!",
}
}
continue;
}
done:
*count = i;
if (num == 0) {
/* if num is 0 most probably something went wrong,
* reset packet and return ENOENT */
return ENOENT;
}
return EOK;
}
{
int ret;
int i;
return EFAULT;
}
if (ret) {
return ret;
}
return EOK;
}
/* 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
*/
{
int ret;
while (dom) {
/* if it is a domainless search, skip domains that require fully
* qualified names instead */
}
if (!dom) break;
/* make sure we reset the check_provider flag when we check
* a new domain */
}
/* make sure to update the dctx if we changed domain */
/* verify this group has not yet been negatively cached,
* or has been permanently filtered */
/* if neg cached, return we didn't find it */
("Group [%s] does not exist in [%s]! (negative cache)\n",
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
continue;
}
/* There are no further domains or this was a
* fully-qualified user request.
*/
return ENOENT;
}
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
return EIO;
}
return EIO;
}
DEBUG(0, ("getgrnam call returned more than one result !?!\n"));
return ENOENT;
}
/* set negative cache only if not result of cache check */
return ret;
}
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
if (dom) continue;
}
/* Group not found in ldb -> delete group from memory cache. */
nctx->grp_mc_ctx);
("Deleting group from memcache failed.\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) {
SSS_DP_GROUP, name, 0,
dctx);
/* Anything but EOK means we should reenter the mainloop
* because we may be refreshing the cache
*/
return ret;
}
}
/* One result found */
return EOK;
}
return ENOENT;
}
{
int ret;
if (err_maj) {
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
goto done;
}
/* no previous results, just loop to next domain if possible */
} else {
/* nothing available */
goto done;
}
}
/* ok the backend returned, search to see if we have updated results */
/* we have results to return */
}
done:
if (ret) {
}
}
{
struct tevent_req *req;
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
const char *rawname;
char *domname;
int ret;
if (!cmdctx) {
return ENOMEM;
}
if (!dctx) {
goto done;
}
/* get user name to query */
/* if not terminated fail */
goto done;
}
/* If the body isn't valid UTF-8, fail */
goto done;
}
} else {
}
goto done;
goto done;
}
if (domname) {
goto done;
}
} else {
/* this is a multidomain search */
cmdctx->check_next = true;
} else {
}
goto done;
}
}
/* ok, find it ! */
/* we have results to return */
}
done:
}
{
goto done;
}
goto done;
}
if (domname) {
goto done;
}
} else {
/* this is a multidomain search */
cmdctx->check_next = true;
}
/* ok, find it ! */
/* we have results to return */
}
done:
}
/* 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
*/
{
int ret;
while (dom) {
/* check that the gid is valid for this domain */
"(id out of range)\n",
if (cmdctx->check_next) {
continue;
}
return ENOENT;
}
/* make sure we reset the check_provider flag when we check
* a new domain */
}
/* make sure to update the dctx if we changed domain */
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
return EIO;
}
return EIO;
}
DEBUG(0, ("getgrgid call returned more than one result !?!\n"));
return ENOENT;
}
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
continue;
}
/* set negative cache only if not result of cache check */
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) {
dctx);
/* Anything but EOK means we should reenter the mainloop
* because we may be refreshing the cache
*/
return ret;
}
}
/* One result found */
return EOK;
}
return ENOENT;
}
{
int ret;
if (err_maj) {
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
goto done;
}
/* no previous results, just loop to next domain if possible */
} else {
/* nothing available */
goto done;
}
}
/* ok the backend returned, search to see if we have updated results */
/* we have results to return */
}
done:
if (ret) {
}
}
{
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
int ret;
struct tevent_req *req;
if (!cmdctx) {
return ENOMEM;
}
if (!dctx) {
goto done;
}
/* get uid to query */
goto done;
}
("Gid [%lu] does not exist! (negative cache)\n",
goto done;
}
/* gid searches are always multidomain */
cmdctx->check_next = true;
} else {
}
goto done;
}
/* ok, find it ! */
/* we have results to return */
}
done:
}
{
goto done;
}
/* ok, find it ! */
/* we have results to return */
}
done:
}
/* 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 nss_cmd_ctx *cmdctx;
struct tevent_req *req;
if (!cmdctx) {
return ENOMEM;
}
if (!req) {
DEBUG(0, ("Fatal error calling nss_cmd_setgrent_send\n"));
goto done;
}
done:
}
{
struct tevent_req *req;
struct setent_ctx *state;
struct sss_domain_info *dom;
struct setent_step_ctx *step_ctx;
/* Reset the read pointers */
client->grent_dom_idx = 0;
if (!req) {
DEBUG(0, ("Could not create tevent request for setgrent\n"));
return NULL;
}
goto error;
}
/* check if enumeration is enabled in any domain */
}
goto error;
}
/* Is the result context already available */
/* All of the necessary data is in place
* We can return now, getgrent requests will work at this point
*/
}
else {
/* Object is still being constructed
* Register for notification when it's
* ready.
*/
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;
*/
goto error;
}
/* Add a callback reference for ourselves */
/* ok, start the searches */
if (!step_ctx) {
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->returned_to_mainloop = false;
}
return req;
return req;
}
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
*/
{
struct ldb_result *res;
struct tevent_timer *te;
struct tevent_req *dpreq;
struct dp_callback_ctx *cb_ctx;
while (dom) {
}
if (!dom) break;
/* make sure we reset the check_provider flag when we check
* a new domain */
}
/* make sure to update the dctx if we changed domain */
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;
if (!dpreq) {
("Enum Cache refresh for domain [%s] failed."
" Trying to return what we have in cache!\n",
} else {
if(!cb_ctx) {
return ENOMEM;
}
return EAGAIN;
}
}
continue;
}
continue;
}
return ENOMEM;
}
/* do not reply until all domain searches are done */
}
/* We've finished all our lookups
* The result object is now safe to read.
*/
/* Set up a lifetime timer for this result object
* We don't want this result object to outlive the
* enum cache refresh timeout
*/
if (!te) {
DEBUG(0, ("Could not set up life timer for setgrent result object. "
"Entries may become stale.\n"));
}
/* Notify the waiting clients */
if (step_ctx->returned_to_mainloop) {
return EAGAIN;
} else {
return EOK;
}
}
struct tevent_timer *te,
struct timeval current_time,
void *pvt)
{
/* Free the group enumeration context.
* If additional getgrent requests come in, they will invoke
* an implicit setgrent and refresh the result object.
*/
}
{
struct setent_step_ctx *step_ctx =
int ret;
if (err_maj) {
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
}
/* Notify any waiting processes of failure */
}
}
{
return EOK;
}
{
struct nss_cmd_ctx *cmdctx =
/* Either we succeeded or no domains were eligible */
return;
}
}
/* Something bad happened */
}
{
struct getent_ctx *gctx;
int n = 0;
cctx->grent_dom_idx++;
}
if (!n) break;
}
none:
}
return ret;
}
{
int ret;
/* get max num of entries to return in one call */
return EINVAL;
}
/* create response packet */
return ret;
}
return EOK;
}
{
struct nss_cmd_ctx *cmdctx;
struct tevent_req *req;
if (!cmdctx) {
return ENOMEM;
}
/* 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.
*/
/* Make sure we invoke setgrent if it hasn't been run or is still
* processing from another client
*/
if (!req) {
return EIO;
}
return EOK;
}
return nss_cmd_getgrent_immediate(cmdctx);
}
{
struct nss_cmd_ctx *cmdctx =
/* 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.
*/
DEBUG(0, ("Implicit setgrent failed with unexpected error [%d][%s]\n",
}
/* Restore the saved index and cursor locations */
DEBUG(0, ("Immediate retrieval failed with unexpected error "
}
}
{
int ret;
/* create response packet */
return ret;
}
/* Reset the indices so that subsequent requests start at zero */
cctx->grent_dom_idx = 0;
done:
return EOK;
}
{
struct sss_domain_info *dom;
struct ldb_result *res;
struct sized_string delete_name;
bool changed = false;
int ret;
int i, j;
break;
}
}
("Unknown domain (%s) requested by provider\n", domain));
return;
}
("Failed to make request to our cache! [%d][%s]\n",
goto done;
}
/* copy, we need the original intact in case we need to invalidate
* all the original groups */
/* The user is gone. Invalidate the mc record */
("Internal failure in memory cache code: %d [%s]\n",
}
/* Also invalidate his groups */
changed = true;
} else {
/* we skip the first entry, it's the user itself */
if (id == 0) {
/* probably non-posix group, skip */
continue;
}
for (j = 0; j < gnum; j++) {
gids[j] = 0;
break;
}
}
if (j >= gnum) {
/* we couldn't find a match, this means the groups have
* changed after the refresh */
changed = true;
break;
}
}
if (!changed) {
for (j = 0; j < gnum; j++) {
if (gids[j] != 0) {
/* we found an un-cleared groups, this means the groups
* have changed after the refresh (some got deleted) */
changed = true;
break;
}
}
}
}
if (changed) {
for (i = 0; i < gnum; i++) {
("Internal failure in memory cache code: %d [%s]\n",
}
}
}
done:
}
/* FIXME: what about mpg, should we return the user's GID ? */
/* FIXME: should we filter out GIDs ? */
{
int skipped = 0;
const char *posix;
return ENOENT;
}
/* one less, the first one is the user entry */
return ret;
}
/* skip first entry, it's the user entry */
bindex = 0;
for (i = 0; i < num; i++) {
if (!gid) {
skipped++;
continue;
} else {
return EFAULT;
}
}
bindex++;
}
return EOK;
}
{
int ret;
return EFAULT;
}
if (ret) {
return ret;
}
return EOK;
}
{
int ret;
while (dom) {
/* if it is a domainless search, skip domains that require fully
* qualified names instead */
}
if (!dom) break;
/* make sure we reset the check_provider flag when we check
* a new domain */
}
/* make sure to update the dctx if we changed domain */
/* verify this user has not yet been negatively cached,
* or has been permanently filtered */
/* if neg cached, return we didn't find it */
("User [%s] does not exist in [%s]! (negative cache)\n",
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
continue;
}
/* There are no further domains or this was a
* fully-qualified user request.
*/
return ENOENT;
}
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
return EIO;
}
return EIO;
}
/* set negative cache only if not result of cache check */
return ret;
}
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
if (dom) continue;
}
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) {
SSS_DP_INITGROUPS, name, 0,
dctx);
/* Anything but EOK means we should reenter the mainloop
* because we may be refreshing the cache
*/
return ret;
}
}
return EOK;
}
return ENOENT;
}
{
int ret;
if (err_maj) {
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
goto done;
}
/* no previous results, just loop to next domain if possible */
} else {
/* nothing available */
goto done;
}
}
/* ok the backend returned, search to see if we have updated results */
/* we have results to return */
}
done:
if (ret) {
}
}
/* for now, if we are online, try to always query the backend */
{
struct tevent_req *req;
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
const char *rawname;
char *domname;
int ret;
if (!cmdctx) {
return ENOMEM;
}
if (!dctx) {
goto done;
}
/* get user name to query */
/* if not terminated fail */
goto done;
}
/* If the body isn't valid UTF-8, fail */
goto done;
}
} else {
}
goto done;
goto done;
}
if (domname) {
goto done;
}
} else {
/* this is a multidomain search */
cmdctx->check_next = true;
} else {
}
goto done;
}
}
/* ok, find it ! */
/* we have results to return */
}
done:
}
{
goto done;
}
goto done;
}
if (domname) {
goto done;
}
} else {
/* this is a multidomain search */
cmdctx->check_next = true;
}
/* ok, find it ! */
/* we have results to return */
}
done:
}
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"},
};
return nss_cli_protocol_version;
}
static struct sss_cmd_table nss_cmds[] = {
{SSS_CLI_NULL, NULL}
};
struct sss_cmd_table *get_nss_cmds(void) {
return nss_cmds;
}