nsssrv_cmd.c revision b2d78dfb2cdd6391be62812513ed26d6f4f454c5
/*
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 <time.h>
struct nss_cmd_ctx {
char *name;
bool immediate;
bool check_next;
bool enum_cached;
};
struct dom_ctx {
struct sss_domain_info *domain;
struct ldb_result *res;
int cur;
};
struct getent_ctx {
int num;
int cur;
};
struct nss_dom_ctx {
struct nss_cmd_ctx *cmdctx;
struct sss_domain_info *domain;
bool check_provider;
/* cache results */
struct ldb_result *res;
};
{
int ret;
/* create response packet */
return ret;
}
return EOK;
}
#define NSS_CMD_FATAL_ERROR(cctx) do { \
talloc_free(cctx); \
return; \
} while(0)
talloc_free(cctx); \
return ret; \
} while(0)
const char *domain)
{
struct sss_domain_info *dom;
}
return dom;
}
{
int ret;
return EOK;
}
{
int ret;
/* create response packet */
return ret;
}
return ret;
}
return EOK;
}
{
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;
}
/****************************************************************************
* PASSWD db related functions
***************************************************************************/
struct sss_domain_info *dom,
bool filter_users,
struct ldb_message **msgs,
int *count)
{
struct ldb_message *msg;
const char *name;
const char *gecos;
const char *homedir;
const char *shell;
int delim = 1;
bool packet_initialized = false;
int ncret;
num = 0;
for (i = 0; i < *count; i++) {
continue;
}
if (filter_users) {
continue;
}
}
if (!packet_initialized) {
/* first 2 fields (len and reserved), filled up later */
packet_initialized = true;
}
num = 0;
goto done;
}
if (add_domain) {
/* need more space, got creative with the print format ? */
num = 0;
goto done;
}
delim += t;
s1 += 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,
{
int timeout;
uint64_t cacheExpire = 0;
bool off_band_update = false;
/* when searching for a user, more than one reply is a db error */
" DB Corrupted?\n"));
}
return ENOENT;
}
/* if we have any reply let's check cache validity */
SYSDB_LAST_UPDATE, 0);
if (req_type == SSS_DP_INITGROUPS) {
SYSDB_INITGR_EXPIRE, 1);
}
if (cacheExpire == 0) {
SYSDB_CACHE_EXPIRE, 0);
}
midpoint_refresh = 0;
if(nctx->cache_refresh_percent) {
/* If the percentage results in an expiration
* less than ten seconds after the lastUpdate time,
* that's too often we will simply set it to 10s
*/
}
}
if (cacheExpire > now) {
/* cache still valid */
/* We're past the the cache refresh timeout
* We'll return the value from the cache, but we'll also
* queue the cache entry for update out-of-band.
*/
opt_name));
off_band_update = true;
}
else {
/* Cache is still valid. Just return it. */
return EOK;
}
}
}
if (off_band_update) {
/* No callback required
* This was an out-of-band update. We'll return EOK
* so the calling function can return the cached entry
* immediately.
*/
true, req_type,
} else {
}
} 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 */
}
true, req_type,
}
}
return EAGAIN;
}
return EOK;
}
/* 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 */
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;
}
/* 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) {
SSS_DP_USER, name, 0,
/* 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 vailable */
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;
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;
}
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) {
/* 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 vailable */
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;
if (!cmdctx) {
return ENOMEM;
}
if (!dctx) {
goto done;
}
/* get uid to query */
goto done;
}
goto done;
}
/* uid searches are always multidomain */
cmdctx->check_next = true;
/* 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 ldb_result *res;
struct getent_ctx *pctx;
int timeout;
int ret;
if (!pctx) {
return ENOMEM;
}
}
while (dom) {
}
if (!dom) break;
/* make sure we reset the check_provider flag when we check
* a new domain */
if (cmdctx->enum_cached) {
dctx->check_provider = false;
} else {
}
}
/* 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) {
dctx->check_provider = false;
SSS_DP_USER, NULL, 0);
return ret;
} else {
" Trying to return what we have in cache!\n",
}
}
continue;
}
continue;
}
return ENOMEM;
}
/* do not reply until all domain searches are done */
}
/* set cache mark */
/* this was a getpwent call w/o setpwent,
* return immediately one result */
return nss_cmd_getpwent_immediate(cmdctx);
}
/* create response packet */
}
return ret;
}
{
int ret;
if (err_maj) {
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
}
if (ret) {
}
}
{
struct sss_domain_info *dom;
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
int ret;
if (!cmdctx) {
return ENOMEM;
}
if (!dctx) {
goto done;
}
/* do not query backends if we have a recent enumeration */
if (nctx->enum_cache_timeout) {
cmdctx->enum_cached = true;
}
}
/* check if enumeration is enabled in any domain */
}
} else {
}
}
goto done;
}
/* ok, start the searches */
done:
}
{
return nss_cmd_setpwent_ext(cctx, false);
}
{
struct getent_ctx *pctx;
int n = 0;
}
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;
/* see if we need to trigger an implicit setpwent() */
return nss_cmd_setpwent_ext(cctx, true);
}
if (!cmdctx) {
return ENOMEM;
}
return nss_cmd_getpwent_immediate(cmdctx);
}
{
int ret;
/* create response packet */
/* free results and reset */
done:
return EOK;
}
/****************************************************************************
* GROUP db related functions
***************************************************************************/
#define GID_ROFFSET 0
#define MNUM_ROFFSET sizeof(uint32_t)
struct sss_domain_info *dom,
bool filter_groups,
struct ldb_message **msgs,
int *count)
{
struct ldb_message *msg;
struct ldb_message_element *el;
const char *name;
int i = 0;
int j = 0;
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) {
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;
nsize += t;
/* retry */
}
/* reclaim space */
num = 0;
goto done;
}
rsize = 0;
continue;
}
} else {
}
/* group passwd field */
if (el) {
memnum = 0;
for (j = 0; j < el->num_values; j++) {
if (nctx->filter_users_in_groups) {
" (negative cache)\n",
continue;
}
}
num = 0;
goto done;
}
if (add_domain) {
/* need more space,
* got creative with the print format ? */
num = 0;
goto done;
}
delim += t;
nsize += t;
/* retry */
}
" for member [%s@%s] of group [%s]!"
/* reclaim space */
num = 0;
goto done;
}
continue;
}
} else {
}
memnum++;
}
if (memnum) {
/* set num of members */
}
}
num++;
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 */
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;
}
/* 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) {
SSS_DP_GROUP, name, 0,
/* 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 vailable */
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;
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;
}
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) {
/* 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 vailable */
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;
if (!cmdctx) {
return ENOMEM;
}
if (!dctx) {
goto done;
}
/* get uid to query */
goto done;
}
goto done;
}
/* gid searches are always multidomain */
cmdctx->check_next = true;
/* 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 ldb_result *res;
struct getent_ctx *gctx;
int timeout;
int ret;
if (!gctx) {
return ENOMEM;
}
}
while (dom) {
}
if (!dom) break;
/* make sure we reset the check_provider flag when we check
* a new domain */
if (cmdctx->enum_cached) {
dctx->check_provider = false;
} else {
}
}
/* 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) {
dctx->check_provider = false;
SSS_DP_GROUP, NULL, 0);
return ret;
} else {
" Trying to return what we have in cache!\n",
}
}
continue;
}
continue;
}
return ENOMEM;
}
/* do not reply until all domain searches are done */
}
/* set cache mark */
/* this was a getgrent call w/o setgrent,
* return immediately one result */
return nss_cmd_getgrent_immediate(cmdctx);
}
/* create response packet */
}
return ret;
}
{
int ret;
if (err_maj) {
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
}
if (ret) {
}
}
{
struct sss_domain_info *dom;
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
int ret;
if (!cmdctx) {
return ENOMEM;
}
if (!dctx) {
goto done;
}
/* do not query backends if we have a recent enumeration */
if (nctx->enum_cache_timeout) {
cmdctx->enum_cached = true;
}
}
/* check if enumeration is enabled in any domain */
}
} else {
}
}
goto done;
}
/* ok, start the searches */
done:
}
{
return nss_cmd_setgrent_ext(cctx, false);
}
{
struct getent_ctx *gctx;
int n = 0;
}
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;
/* see if we need to trigger an implicit setpwent() */
return nss_cmd_setgrent_ext(cctx, true);
}
if (!cmdctx) {
return ENOMEM;
}
return nss_cmd_getgrent_immediate(cmdctx);
}
{
int ret;
/* create response packet */
/* free results and reset */
done:
return EOK;
}
{
return ENOENT;
}
/* one less, the first one is the user entry */
return ret;
}
/* skip first entry, it's the user entry */
for (i = 0; i < num; i++) {
if (!gid) {
return EFAULT;
}
}
return EOK;
}
struct ldb_result *res)
{
struct sss_domain_info *dom;
bool neghit = false;
int ncret;
int ret;
if (status != LDB_SUCCESS) {
}
return;
}
if (dctx->check_provider) {
/* Anything but EOK means we should reenter the mainloop
* because we may be refreshing the cache
*/
return;
}
}
case 0:
if (cmdctx->check_next) {
/* skip domains that require FQnames or have negative caches */
neghit = true;
}
/* reset neghit if we still have a domain to check */
if (neghit) {
}
}
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
}
}
}
/* we made another call, end here */
}
/* set negative cache only if not result of cache check */
if (!neghit) {
}
}
}
break;
default:
}
}
}
}
{
int ret;
if (err_maj) {
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
/* return 0 results */
goto done;
}
}
return;
}
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
}
done:
}
}
}
/* for now, if we are online, try to always query the backend */
{
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
struct sss_domain_info *dom;
const char *rawname;
char *domname;
int ret;
int ncret;
bool neghit = false;
if (!cmdctx) {
return ENOMEM;
}
if (!dctx) {
goto done;
}
/* get user name to query */
/* if not terminated fail */
goto done;
}
goto done;
}
if (domname) {
goto done;
}
/* verify this user has not yet been negatively cached,
* or has been permanently filtered */
neghit = true;
}
}
else {
/* skip domains that require FQnames or have negative caches */
/* verify this user has not yet been negatively cached,
* or has been permanently filtered */
neghit = true;
}
/* reset neghit if we still have a domain to check */
}
if (neghit) {
goto done;
}
goto done;
}
if (!domname) {
/* this is a multidomain search */
cmdctx->check_next = true;
}
DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
goto done;
}
}
done:
/* we do not have any entry to return */
}
}
}
}
return ret;
}
return EOK;
}
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;
}
{
enum sss_cli_command cmd;
int i;
}
}
return EINVAL;
}