nsssrv_cmd.c revision 899d1bdc048cd74518170d7d9535d76d3f46d4af
/*
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 "providers/data_provider.h"
#include "sss_client/idmap/sss_nss_idmap.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,
struct sss_nss_homedir_ctx *homedir_ctx)
{
const char *homedir;
/* Subdomain users store FQDN in their name attribute */
"name-value components.\n", orig_name);
return NULL;
}
/* Check to see which homedir_prefix to use. */
}
/* 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) {
}
}
/* Provider can also return template, try to expand it.*/
}
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++) {
"The shell '%s' is allowed but does not exist. "
"Using fallback\n", user_shell);
}
}
}
"The shell '%s' is not allowed and does not exist.\n",
}
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 fq_len = 0;
bool packet_initialized = false;
int ncret;
struct sss_nss_homedir_ctx homedir_ctx;
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;
}
"sss_replace_space failed, skipping\n");
continue;
}
if (!tmpstr) {
} else {
}
if (!tmpstr) {
} else {
}
if (!tmpstr) {
} else {
}
if (add_domain) {
if (fq_len >= 0) {
fq_len += 1;
} else {
fq_len = 0;
}
}
num = 0;
goto done;
}
if (add_domain) {
"Failed to generate a fully qualified name for user "
continue;
}
} else {
}
num++;
"Failed to store user %s(%s) in mmap cache!\n",
}
}
}
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 */
struct ldb_result *res,
int req_type,
const char *opt_name,
const char *extra,
void *pvt)
{
uint64_t cacheExpire = 0;
/* when searching for a user or netgroup, more than one reply is a
* db error
*/
"getpwXXX call returned more than one result!"
" 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;
static const char *user_attrs[] = SYSDB_PW_ATTRS;
struct ldb_message *msg;
while (dom) {
/* if it is a domainless search, skip domains that require fully
* qualified names instead */
&& !cmdctx->name_is_upn) {
}
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 */
"sss_reverse_replace_space failed\n");
return ENOMEM;
}
/* 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;
}
"Fatal: Sysdb CTX not found for this domain!\n");
return EIO;
}
if (cmdctx->name_is_upn) {
return ret;
}
return ENOMEM;
}
} else {
struct ldb_message *, 1);
return ENOMEM;
}
}
} else {
}
"Failed to make request to our cache!\n");
return EIO;
}
"getpwnam call returned more than one result !?!\n");
return ENOENT;
}
/* set negative cache only if not result of cache check */
}
/* 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;
return ENOMEM;
}
}
case SSS_NSS_GETPWNAM:
}
break;
case SSS_NSS_INITGR:
}
break;
default:
}
return ret;
}
{
int ret;
bool check_subdomains;
if (err_maj) {
"Unable to get information from Data Provider\n"
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
case SSS_NSS_GETPWNAM:
break;
case SSS_NSS_GETGRNAM:
break;
case SSS_NSS_INITGR:
break;
case SSS_NSS_GETPWUID:
break;
case SSS_NSS_GETGRGID:
break;
case SSS_NSS_GETNAMEBYSID:
case SSS_NSS_GETIDBYSID:
case SSS_NSS_GETSIDBYNAME:
case SSS_NSS_GETSIDBYID:
break;
default:
}
goto done;
}
/* Since subdomain users and groups are fully qualified they are
* typically not subject of multi-domain searches. But since POSIX
* ID do not contain a domain name we have to decend to subdomains
* here. */
case SSS_NSS_GETPWUID:
case SSS_NSS_GETGRGID:
case SSS_NSS_GETSIDBYID:
check_subdomains = true;
break;
default:
check_subdomains = false;
}
/* no previous results, just loop to next domain if possible */
if (cmdctx->check_next &&
} else {
/* nothing available */
goto done;
}
}
/* ok the backend returned, search to see if we have updated results */
case SSS_NSS_GETPWNAM:
/* we have results to return */
}
break;
case SSS_NSS_GETGRNAM:
/* we have results to return */
}
break;
case SSS_NSS_INITGR:
/* we have results to return */
}
break;
case SSS_NSS_GETPWUID:
/* we have results to return */
}
break;
case SSS_NSS_GETGRGID:
/* we have results to return */
}
break;
case SSS_NSS_GETNAMEBYSID:
case SSS_NSS_GETIDBYSID:
}
break;
case SSS_NSS_GETSIDBYNAME:
}
break;
case SSS_NSS_GETSIDBYID:
}
break;
default:
}
done:
/* assume Kerberos principal */
}
if (ret) {
}
}
const char *full_name)
{
char *wk_dom_name = NULL;
const char *wk_sid;
int ret;
struct sized_string sid;
&wk_dom_name, &wk_name);
return ret;
}
"Well-Known SID.\n", full_name);
return ret;
}
return ENOMEM;
}
return EOK;
}
{
}
{
struct tevent_req *req;
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
const char *rawname;
char *domname;
int ret;
switch(cmd) {
case SSS_NSS_GETPWNAM:
case SSS_NSS_GETGRNAM:
case SSS_NSS_INITGR:
case SSS_NSS_GETSIDBYNAME:
break;
default:
return EINVAL;
}
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;
}
"Well-Known SID.\n", rawname);
} else {
"nss_check_name_of_well_known_sid failed.\n");
}
goto done;
}
}
/* We need to attach to subdomain request, if the first one is not
* finished yet. We may not be able to lookup object in AD otherwise. */
} else {
}
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 ! */
case SSS_NSS_GETPWNAM:
/* we have results to return */
/* assume Kerberos principal */
}
break;
case SSS_NSS_GETGRNAM:
/* we have results to return */
}
break;
case SSS_NSS_INITGR:
/* we have results to return */
/* assume Kerberos principal */
}
break;
case SSS_NSS_GETSIDBYNAME:
}
break;
default:
}
done:
}
{
goto done;
}
/* assume Kerberos principal */
goto done;
goto done;
}
if (domname) {
goto done;
}
} else {
/* this is a multidomain search */
cmdctx->check_next = true;
}
/* ok, find it ! */
case SSS_NSS_GETPWNAM:
/* we have results to return */
/* assume Kerberos principal */
}
break;
case SSS_NSS_GETGRNAM:
/* we have results to return */
}
break;
case SSS_NSS_INITGR:
/* we have results to return */
/* assume Kerberos principal */
}
break;
case SSS_NSS_GETSIDBYNAME:
}
break;
default:
}
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;
int err;
while (dom) {
/* check that the uid is valid for this domain */
"(id out of range)\n",
if (cmdctx->check_next) {
continue;
}
goto done;
}
/* make sure we reset the check_provider flag when we check
* a new domain */
}
/* make sure to update the dctx if we changed domain */
"Fatal: Sysdb CTX not found for this domain!\n");
goto done;
}
"Failed to make request to our cache!\n");
goto done;
}
"getpwuid call returned more than one result !?!\n");
goto done;
}
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
continue;
}
/* set negative cache only if not result of cache check */
goto done;
}
/* 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
*/
goto done;
}
}
/* One result found */
goto done;
}
/* All domains were tried and none had the entry. */
done:
/* The entry was not found, need to set result in negative cache */
}
}
return ret;
}
{
}
{
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
int ret;
struct tevent_req *req;
switch (cmd) {
case SSS_NSS_GETPWUID:
case SSS_NSS_GETGRGID:
case SSS_NSS_GETSIDBYID:
break;
default:
return EINVAL;
}
if (!cmdctx) {
return ENOMEM;
}
if (!dctx) {
goto done;
}
/* get id to query */
goto done;
}
case SSS_NSS_GETPWUID:
goto done;
}
break;
case SSS_NSS_GETGRGID:
goto done;
}
break;
case SSS_NSS_GETSIDBYID:
}
goto done;
}
break;
default:
goto done;
}
/* id searches are always multidomain */
cmdctx->check_next = true;
} else {
}
goto done;
}
/* ok, find it ! */
case SSS_NSS_GETPWUID:
/* we have results to return */
}
break;
case SSS_NSS_GETGRGID:
/* we have results to return */
}
break;
case SSS_NSS_GETSIDBYID:
}
break;
default:
}
done:
}
{
goto done;
}
/* ok, find it ! */
case SSS_NSS_GETPWUID:
/* we have results to return */
}
break;
case SSS_NSS_GETGRGID:
/* we have results to return */
}
break;
case SSS_NSS_GETNAMEBYSID:
case SSS_NSS_GETIDBYSID:
goto done;
}
}
break;
case SSS_NSS_GETSIDBYID:
}
break;
default:
}
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) {
"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) {
"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 */
"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;
}
}
"Enum from cache failed, skipping domain [%s]\n",
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) {
"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)
{
"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.
*/
}
{
struct setent_step_ctx *step_ctx =
int ret;
if (err_maj) {
"Unable to get information from Data Provider\n"
"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.
*/
"Implicit setpwent failed with unexpected error [%d][%s]\n",
}
/* Restore the saved index and cursor locations */
"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)
{
char *username;
char *domname;
const char *use_member;
struct sss_domain_info *member_dom;
bool add_domain;
"name-value components.\n", member);
return ret;
}
use_member = member;
/* The group is stored in the parent domain, but the member comes from.
* a subdomain. No need to add the domain component, it's already
*/
add_domain = false;
}
/* The group is stored in a subdomain, but the member comes
* from the parent domain. Need to add the domain component
* of the parent domain
*/
add_domain = true;
}
return EOK;
}
struct sss_domain_info *dom,
struct ldb_message_element *el,
int *_memnum)
{
const char *tmpstr;
struct sized_string name;
int nlen = 0;
bool add_domain;
struct sss_domain_info *member_dom;
return ENOMEM;
}
for (i = 0; i < el->num_values; i++) {
"sss_get_cased_name failed, skipping\n");
continue;
}
"sss_replace_space failed\n");
goto done;
}
if (nctx->filter_users_in_groups) {
"Group [%s] member [%s@%s] filtered out!"
" (negative cache)\n",
continue;
}
}
"Could not process member %s, skipping\n", tmpstr);
continue;
}
if (add_domain) {
if (nlen >= 0) {
nlen += 1;
} else {
/* Other failures caught below */
nlen = 0;
}
} else {
}
goto done;
}
if (add_domain) {
" for member [%s@%s] of group [%s]!"
/* reclaim space */
goto done;
}
continue;
}
} 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 fq_len = 0;
int i = 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;
"Incomplete group object for %s[%llu]! Skipping\n",
continue;
}
if (filter_groups) {
"Group [%s@%s] filtered out! (negative cache)\n",
continue;
}
}
"sss_get_cased_name failed, skipping\n");
continue;
}
"sss_replace_space failed, skipping\n");
continue;
}
/* fill in gid and name and set pointer for number of members */
if (add_domain) {
if (fq_len >= 0) {
fq_len += 1;
} else {
/* Other failures caught below */
fq_len = 0;
}
}
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) {
"Failed to generate a fully qualified name for"
/* 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 */
"sss_reverse_replace_space failed\n");
return ENOMEM;
}
/* 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;
}
"Fatal: Sysdb CTX not found for this domain!\n");
return EIO;
}
"Failed to make request to our cache!\n");
return EIO;
}
"getgrnam call returned more than one result !?!\n");
return ENOENT;
}
/* set negative cache only if not result of cache check */
}
/* 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) {
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;
}
{
}
/* 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;
int err;
while (dom) {
/* check that the gid is valid for this domain */
"(id out of range)\n",
if (cmdctx->check_next) {
continue;
}
goto done;
}
/* make sure we reset the check_provider flag when we check
* a new domain */
}
/* make sure to update the dctx if we changed domain */
"Fatal: Sysdb CTX not found for this domain!\n");
goto done;
}
"Failed to make request to our cache!\n");
goto done;
}
"getgrgid call returned more than one result !?!\n");
goto done;
}
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
continue;
}
/* set negative cache only if not result of cache check */
goto done;
}
/* 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
*/
goto done;
}
}
/* One result found */
/* Success. Break from the loop and return EOK */
goto done;
}
/* All domains were tried and none had the entry. */
done:
/* The entry was not found, need to set result in negative cache */
}
}
return 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 nss_cmd_ctx *cmdctx;
struct tevent_req *req;
if (!cmdctx) {
return ENOMEM;
}
if (!req) {
"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) {
"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 */
"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;
}
}
"Enum from cache failed, skipping domain [%s]\n",
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) {
"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)
{
"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.
*/
}
{
struct setent_step_ctx *step_ctx =
int ret;
if (err_maj) {
"Unable to get information from Data Provider\n"
"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.
*/
"Implicit setgrent failed with unexpected error [%d][%s]\n",
}
/* Restore the saved index and cursor locations */
"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;
}
0);
/* If the GID of the original primary group is available but equal to the
* current primary GID it must not be added. */
if (orig_primary_gid != 0) {
if (orig_primary_gid == gid) {
orig_primary_gid = 0;
}
}
/* 0-3: 32bit unsigned number of results
/* skip first entry, it's the user entry */
for (i = 0; i < num; i++) {
if (!gid) {
skipped++;
continue;
} else {
"Incomplete group object for initgroups! Aborting\n");
return EFAULT;
}
}
/* do not add the GID of the original primary group is the user is
* already and explicit member of the group. */
if (orig_primary_gid == gid) {
orig_primary_gid = 0;
}
}
if (orig_primary_gid != 0) {
num++;
}
"Could not set packet size to value:%zu\n", blen);
return ret;
}
return EOK;
}
{
int ret;
return EFAULT;
}
if (ret) {
return ret;
}
return EOK;
}
{
int ret;
static const char *user_attrs[] = SYSDB_PW_ATTRS;
struct ldb_message *msg;
const char *sysdb_name;
while (dom) {
/* if it is a domainless search, skip domains that require fully
* qualified names instead */
&& !cmdctx->name_is_upn) {
}
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 */
"sss_reverse_replace_space failed\n");
return ENOMEM;
}
/* 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;
}
"Fatal: Sysdb CTX not found for this domain!\n");
return EIO;
}
if (cmdctx->name_is_upn) {
return ret;
}
if (sysdb_name == NULL) {
"Sysdb entry does not have a name.\n");
return EINVAL;
}
} else {
}
"Failed to make request to our cache! [%d][%s]\n",
return EIO;
}
/* set negative cache only if not result of cache check */
}
/* 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;
}
/* for now, if we are online, try to always query the backend */
{
}
{
int ret;
int err;
bool user_found = false;
bool group_found = false;
char *sysdb_name = NULL;
char *req_name;
int req_type;
while (dom) {
/* check that the uid is valid for this domain */
"(id out of range)\n",
if (cmdctx->check_next) {
continue;
}
goto done;
}
} else {
/* 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 */
} else {
goto done;
}
"sss_reverse_replace_space failed\n");
goto done;
}
/* For subdomains a fully qualified name is needed for
* sysdb_search_user_by_name and sysdb_search_group_by_name. */
if (IS_SUBDOMAIN(dom)) {
if (sysdb_name == NULL) {
goto done;
}
}
/* 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.
*/
goto done;
}
}
"Fatal: Sysdb CTX not found for this domain!\n");
goto done;
}
&msg);
"Failed to make request to our cache!\n");
goto done;
}
user_found = true;
} else {
&msg);
"Failed to make request to our cache!\n");
goto done;
}
group_found = true;
}
}
} else {
"Failed to make request to our cache!\n");
goto done;
}
user_found = true;
} else {
"Failed to make request to our cache!\n");
goto done;
}
group_found = true;
}
}
}
goto done;
}
if (user_found || group_found) {
goto done;
}
}
}
}
}
/* if a multidomain search, try with next */
if (cmdctx->check_next) {
continue;
}
goto done;
}
/* 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) {
} else {
req_id = 0;
}
if (user_found) {
} else if (group_found) {
} else {
}
dctx);
/* Anything but EOK means we should reenter the mainloop
* because we may be refreshing the cache
*/
goto done;
}
}
/* One result found */
} else {
}
/* Success. Break from the loop and return EOK */
goto done;
}
/* All domains were tried and none had the entry. */
done:
/* The entry was not found, need to set result in negative cache */
}
}
} else {
}
}
return ret;
}
{
int ret;
"domain!\n");
return EIO;
}
return EIO;
}
"result !?!\n");
return ENOENT;
}
/* set negative cache only if not result of cache check */
}
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;
}
bool mpg,
enum sss_id_type *id_type)
{
size_t c;
struct ldb_message_element *el;
return EINVAL;
}
for (c = 0; c < el->num_values; c++) {
break;
}
}
if (c == el->num_values) {
} else {
if (mpg) {
} else {
}
}
return EOK;
}
enum sss_id_type id_type,
struct ldb_message *msg)
{
int ret;
const char *sid_str;
struct sized_string sid;
return EINVAL;
}
return ret;
}
return EOK;
}
struct sss_domain_info *dom,
enum sss_id_type id_type,
struct ldb_message *msg)
{
int ret;
const char *orig_name;
const char *cased_name;
const char *fq_name;
struct sized_string name;
return EINVAL;
}
return ENOMEM;
}
if (cased_name == NULL) {
goto done;
}
if (add_domain) {
goto done;
}
} else {
}
goto done;
}
done:
return ret;
}
enum sss_id_type id_type,
struct ldb_message *msg)
{
int ret;
if (id_type == SSS_ID_TYPE_GID) {
} else {
}
return EINVAL;
}
return ret;
}
return EOK;
}
{
int ret;
enum sss_id_type id_type;
return EINVAL;
return ENOENT;
}
return EFAULT;
}
if (ret != 0) {
return ret;
}
case SSS_NSS_GETNAMEBYSID:
break;
case SSS_NSS_GETIDBYSID:
break;
case SSS_NSS_GETSIDBYNAME:
case SSS_NSS_GETSIDBYID:
break;
default:
return EINVAL;
}
return ret;
}
return EOK;
}
{
const char *wk_name;
const char *wk_dom_name;
int ret;
struct sized_string name;
return ret;
}
"Well-Known SIDs can only be translated to names.\n");
return EINVAL;
}
if (wk_dom_name != NULL) {
return ENOMEM;
}
} else {
}
return ENOMEM;
}
return EOK;
}
{
struct tevent_req *req;
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
const char *sid_str;
int ret;
enum idmap_error_code err;
return EINVAL;
}
if (!cmdctx) {
return ENOMEM;
}
if (!dctx) {
goto done;
}
/* get SID to query */
/* if not terminated fail */
goto done;
}
/* If the body isn't a SID, fail */
&bin_sid, &bin_sid_length);
if (err != IDMAP_SUCCESS) {
body);
goto done;
}
goto done;
}
} else {
}
goto done;
}
} else {
}
goto done;
goto done;
}
/* ok, find it ! */
}
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;
}