summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSimo Sorce <ssorce@redhat.com>2010-03-14 17:27:05 -0400
committerStephen Gallagher <sgallagh@redhat.com>2010-04-12 09:22:15 -0400
commit7ffaa2afb9e03a6f0b9c602c0f03b2074ea33eac (patch)
treeabc9f4bfcc276834c922463120b35c66ee3bd033 /src
parent0a7a138cd47dcff3f4d53da2db4fa155708b8aeb (diff)
downloadsssd-7ffaa2afb9e03a6f0b9c602c0f03b2074ea33eac.tar.gz
sssd-7ffaa2afb9e03a6f0b9c602c0f03b2074ea33eac.tar.xz
sssd-7ffaa2afb9e03a6f0b9c602c0f03b2074ea33eac.zip
sysdb: convert sysdb_getpwnam
Diffstat (limited to 'src')
-rw-r--r--src/db/sysdb.h2
-rw-r--r--src/db/sysdb_search.c38
-rw-r--r--src/python/pysss.c4
-rw-r--r--src/responder/nss/nsssrv_cmd.c419
-rw-r--r--src/responder/pam/pamsrv.h1
-rw-r--r--src/responder/pam/pamsrv_cmd.c337
-rw-r--r--src/tests/sysdb-tests.c77
-rw-r--r--src/tools/sss_sync_ops.c152
-rw-r--r--src/tools/sss_sync_ops.h2
-rw-r--r--src/tools/sss_useradd.c2
-rw-r--r--src/tools/sss_userdel.c2
-rw-r--r--src/tools/sss_usermod.c2
12 files changed, 415 insertions, 623 deletions
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 7da98fa84..365e6adf6 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -253,7 +253,7 @@ int sysdb_getpwnam(TALLOC_CTX *mem_ctx,
struct sysdb_ctx *ctx,
struct sss_domain_info *domain,
const char *name,
- sysdb_callback_t fn, void *ptr);
+ struct ldb_result **res);
int sysdb_getpwuid(TALLOC_CTX *mem_ctx,
struct sysdb_ctx *ctx,
diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
index 4b9470baa..2403917b7 100644
--- a/src/db/sysdb_search.c
+++ b/src/db/sysdb_search.c
@@ -215,38 +215,42 @@ int sysdb_getpwnam(TALLOC_CTX *mem_ctx,
struct sysdb_ctx *ctx,
struct sss_domain_info *domain,
const char *name,
- sysdb_callback_t fn, void *ptr)
+ struct ldb_result **_res)
{
+ TALLOC_CTX *tmpctx;
static const char *attrs[] = SYSDB_PW_ATTRS;
- struct sysdb_search_ctx *sctx;
- struct tevent_req *req;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res;
+ int ret;
if (!domain) {
return EINVAL;
}
- sctx = init_src_ctx(mem_ctx, domain, ctx, fn, ptr);
- if (!sctx) {
+ tmpctx = talloc_new(mem_ctx);
+ if (!tmpctx) {
return ENOMEM;
}
- sctx->expression = talloc_asprintf(sctx, SYSDB_PWNAM_FILTER, name);
- if (!sctx->expression) {
- talloc_free(sctx);
- return ENOMEM;
+ base_dn = ldb_dn_new_fmt(tmpctx, ctx->ldb,
+ SYSDB_TMPL_USER_BASE, domain->name);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
}
- sctx->attrs = attrs;
-
- req = sysdb_operation_send(mem_ctx, ctx->ev, ctx);
- if (!req) {
- talloc_free(sctx);
- return ENOMEM;
+ ret = ldb_search(ctx->ldb, tmpctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, SYSDB_PWNAM_FILTER, name);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
}
- tevent_req_set_callback(req, user_search, sctx);
+ *_res = talloc_steal(mem_ctx, res);
- return EOK;
+done:
+ talloc_zfree(tmpctx);
+ return ret;
}
int sysdb_getpwuid(TALLOC_CTX *mem_ctx,
diff --git a/src/python/pysss.c b/src/python/pysss.c
index 48c5aea2e..cd5c588f3 100644
--- a/src/python/pysss.c
+++ b/src/python/pysss.c
@@ -268,7 +268,7 @@ static PyObject *py_sss_useradd(PySssLocalObject *self,
tctx->sysdb,
tctx->octx->name,
tctx->local,
- &tctx->octx);
+ tctx->octx);
if (ret != EOK) {
PyErr_SetSssError(ret);
goto fail;
@@ -375,7 +375,7 @@ static PyObject *py_sss_userdel(PySssLocalObject *self,
tctx->sysdb,
tctx->octx->name,
tctx->local,
- &tctx->octx);
+ tctx->octx);
if (ret != EOK) {
PyErr_SetSssError(ret);
goto fail;
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 042517ad8..d25c467ae 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -33,6 +33,7 @@ struct nss_cmd_ctx {
bool immediate;
bool check_next;
bool enum_cached;
+
};
struct dom_ctx {
@@ -115,6 +116,61 @@ static int fill_empty(struct sss_packet *packet)
return EOK;
}
+static int nss_cmd_send_empty(struct nss_cmd_ctx *cmdctx)
+{
+ struct cli_ctx *cctx = cmdctx->cctx;
+ int ret;
+
+ /* create response packet */
+ ret = sss_packet_new(cctx->creq, 0,
+ sss_packet_get_cmd(cctx->creq->in),
+ &cctx->creq->out);
+ if (ret != EOK) {
+ return ret;
+ }
+ ret = fill_empty(cctx->creq->out);
+ if (ret != EOK) {
+ return ret;
+ }
+ sss_packet_set_error(cctx->creq->out, EOK);
+ sss_cmd_done(cctx, cmdctx);
+ return EOK;
+}
+
+static int nss_cmd_done(struct nss_cmd_ctx *cmdctx, int ret)
+{
+ switch (ret) {
+ case EOK:
+ /* all fine, just return here */
+ break;
+
+ case ENOENT:
+ ret = nss_cmd_send_empty(cmdctx);
+ if (ret) {
+ return EFAULT;
+ }
+ break;
+
+ case EAGAIN:
+ /* async processing, just return here */
+ break;
+
+ case EFAULT:
+ /* very bad error */
+ return EFAULT;
+
+ default:
+ ret = nss_cmd_send_error(cmdctx, ret);
+ if (ret) {
+ return EFAULT;
+ }
+ sss_cmd_done(cmdctx->cctx, cmdctx);
+ break;
+ }
+
+ return EOK;
+}
+
/****************************************************************************
* PASSWD db related functions
***************************************************************************/
@@ -260,6 +316,36 @@ done:
return EOK;
}
+static int nss_cmd_getpw_send_reply(struct nss_dom_ctx *dctx, bool filter)
+{
+ struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
+ struct cli_ctx *cctx = cmdctx->cctx;
+ struct nss_ctx *nctx;
+ int ret;
+
+ nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
+
+ ret = sss_packet_new(cctx->creq, 0,
+ sss_packet_get_cmd(cctx->creq->in),
+ &cctx->creq->out);
+ if (ret != EOK) {
+ return EFAULT;
+ }
+ ret = fill_pwent(cctx->creq->out,
+ dctx->domain,
+ nctx, filter,
+ dctx->res->msgs, dctx->res->count);
+ if (ret) {
+ return ret;
+ }
+ sss_packet_set_error(cctx->creq->out, EOK);
+ sss_cmd_done(cctx, cmdctx);
+ 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 */
static errno_t check_cache(struct nss_dom_ctx *dctx,
struct nss_ctx *nctx,
struct ldb_result *res,
@@ -387,7 +473,6 @@ static errno_t check_cache(struct nss_dom_ctx *dctx,
NSS_CMD_FATAL_ERROR_CODE(cctx, EIO);
}
sss_cmd_done(cctx, cmdctx);
- return EIO;
}
return EAGAIN;
@@ -399,155 +484,114 @@ static errno_t check_cache(struct nss_dom_ctx *dctx,
static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr);
-static void nss_cmd_getpwnam_callback(void *ptr, int status,
- struct ldb_result *res)
+/* 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
+ */
+
+static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
{
- struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx);
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
+ struct sss_domain_info *dom = dctx->domain;
struct cli_ctx *cctx = cmdctx->cctx;
+ const char *name = cmdctx->name;
struct sysdb_ctx *sysdb;
- struct sss_domain_info *dom;
struct nss_ctx *nctx;
- uint8_t *body;
- size_t blen;
- bool neghit = false;
- int ncret;
int ret;
nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
- if (status != LDB_SUCCESS) {
- ret = nss_cmd_send_error(cmdctx, status);
- if (ret != EOK) {
- NSS_CMD_FATAL_ERROR(cctx);
- }
- sss_cmd_done(cctx, cmdctx);
- return;
- }
-
- if (dctx->check_provider) {
- ret = check_cache(dctx, nctx, res,
- SSS_DP_USER, cmdctx->name, 0,
- nss_cmd_getpwnam_dp_callback);
- if (ret != EOK) {
- /* Anything but EOK means we should reenter the mainloop
- * because we may be refreshing the cache
- */
- return;
+ while (dom) {
+ /* if it is a domainless search, skip domains that require fully
+ * qualified names instead */
+ while (dom && cmdctx->check_next && dom->fqnames) {
+ dom = dom->next;
}
- }
- switch (res->count) {
- case 0:
- if (cmdctx->check_next) {
+ if (!dom) break;
- ret = EOK;
+ if (dom != dctx->domain) {
+ /* make sure we reset the check_provider flag when we check
+ * a new domain */
+ dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
+ }
- /* skip domains that require FQnames or have negative caches */
- for (dom = dctx->domain->next; dom; dom = dom->next) {
+ /* make sure to update the dctx if we changed domain */
+ dctx->domain = dom;
- if (dom->fqnames) continue;
+ /* verify this user has not yet been negatively cached,
+ * or has been permanently filtered */
+ ret = nss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
+ dom->name, name);
- ncret = nss_ncache_check_user(nctx->ncache,
- nctx->neg_timeout,
- dom->name, cmdctx->name);
- if (ncret == ENOENT) break;
+ /* if neg cached, return we didn't find it */
+ if (ret == EEXIST) {
+ DEBUG(2, ("User [%s] does not exist! (negative cache)\n", name));
+ return ENOENT;
+ }
- neghit = true;
- }
- /* reset neghit if we still have a domain to check */
- if (dom) neghit = false;
+ DEBUG(4, ("Requesting info for [%s@%s]\n", name, dom->name));
- if (neghit) {
- DEBUG(2, ("User [%s] does not exist! (negative cache)\n",
- cmdctx->name));
- ret = ENOENT;
- }
- if (dom == NULL) {
- DEBUG(2, ("No matching domain found for [%s], fail!\n",
- cmdctx->name));
- ret = ENOENT;
- }
+ ret = sysdb_get_ctx_from_list(cctx->rctx->db_list, dom, &sysdb);
+ if (ret != EOK) {
+ DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
+ return EIO;
+ }
- if (ret == EOK) {
- dctx->domain = dom;
- dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
- if (dctx->res) talloc_free(res);
- dctx->res = NULL;
+ ret = sysdb_getpwnam(cmdctx, sysdb, dom, name, &dctx->res);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to make request to our cache!\n"));
+ return EIO;
+ }
- DEBUG(4, ("Requesting info for [%s@%s]\n",
- cmdctx->name, dctx->domain->name));
+ if (dctx->res->count > 1) {
+ DEBUG(0, ("getpwnam call returned more than one result !?!\n"));
+ return ENOENT;
+ }
- ret = sysdb_get_ctx_from_list(cctx->rctx->db_list,
- dctx->domain, &sysdb);
- if (ret != EOK) {
- DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
- NSS_CMD_FATAL_ERROR(cctx);
- }
- ret = sysdb_getpwnam(cmdctx, sysdb,
- dctx->domain, cmdctx->name,
- nss_cmd_getpwnam_callback, dctx);
- if (ret != EOK) {
- DEBUG(1, ("Failed to make request to our cache!\n"));
- }
+ if (dctx->res->count == 0 && !dctx->check_provider) {
+ /* if a multidomain search, try with next */
+ if (cmdctx->check_next) {
+ dom = dom->next;
+ continue;
}
- /* we made another call, end here */
- if (ret == EOK) return;
- }
+ DEBUG(2, ("No results for getpwnam call\n"));
- DEBUG(2, ("No results for getpwnam call\n"));
-
- /* set negative cache only if not result of cache check */
- if (!neghit) {
- ret = nss_ncache_set_user(nctx->ncache, false,
- dctx->domain->name, cmdctx->name);
+ /* set negative cache only if not result of cache check */
+ ret = nss_ncache_set_user(nctx->ncache, false, dom->name, name);
if (ret != EOK) {
- NSS_CMD_FATAL_ERROR(cctx);
+ return ret;
}
- }
- ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t),
- sss_packet_get_cmd(cctx->creq->in),
- &cctx->creq->out);
- if (ret != EOK) {
- NSS_CMD_FATAL_ERROR(cctx);
+ return ENOENT;
}
- sss_packet_get_body(cctx->creq->out, &body, &blen);
- ((uint32_t *)body)[0] = 0; /* 0 results */
- ((uint32_t *)body)[1] = 0; /* reserved */
- break;
- case 1:
- DEBUG(6, ("Returning info for user [%s]\n", cmdctx->name));
-
- /* create response packet */
- ret = sss_packet_new(cctx->creq, 0,
- sss_packet_get_cmd(cctx->creq->in),
- &cctx->creq->out);
- if (ret != EOK) {
- NSS_CMD_FATAL_ERROR(cctx);
- }
- ret = fill_pwent(cctx->creq->out,
- dctx->domain,
- nctx, false,
- res->msgs, res->count);
- if (ret == ENOENT) {
- ret = fill_empty(cctx->creq->out);
+ /* if this is a caching provider (or if we haven't checked the cache
+ * yet) then verify that the cache is uptodate */
+ if (dctx->check_provider) {
+ ret = check_cache(dctx, nctx, dctx->res,
+ SSS_DP_USER, name, 0,
+ nss_cmd_getpwnam_dp_callback);
+ if (ret != EOK) {
+ /* Anything but EOK means we should reenter the mainloop
+ * because we may be refreshing the cache
+ */
+ return ret;
+ }
}
- sss_packet_set_error(cctx->creq->out, ret);
- break;
+ /* One result found */
+ DEBUG(6, ("Returning info for user [%s@%s]\n", name, dom->name));
- default:
- DEBUG(1, ("getpwnam call returned more than one result !?!\n"));
- ret = nss_cmd_send_error(cmdctx, ENOENT);
- if (ret != EOK) {
- NSS_CMD_FATAL_ERROR(cctx);
- }
+ return EOK;
}
- sss_cmd_done(cctx, cmdctx);
+ DEBUG(2, ("No matching domain found for [%s], fail!\n", name));
+ return ENOENT;
}
static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min,
@@ -556,7 +600,6 @@ static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min,
struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx);
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
struct cli_ctx *cctx = cmdctx->cctx;
- struct sysdb_ctx *sysdb;
int ret;
if (err_maj) {
@@ -565,39 +608,33 @@ static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min,
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
- if (!dctx->res) {
- /* return 0 results */
- dctx->res = talloc_zero(dctx, struct ldb_result);
- if (!dctx->res) {
- ret = ENOMEM;
- goto done;
- }
+ if (dctx->res && dctx->res->count == 1) {
+ ret = nss_cmd_getpw_send_reply(dctx, false);
+ goto done;
}
- nss_cmd_getpwnam_callback(dctx, LDB_SUCCESS, dctx->res);
- return;
+ /* no previous results, just loop to next domain if possible */
+ if (dctx->domain->next && cmdctx->check_next) {
+ dctx->domain = dctx->domain->next;
+ dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
+ } else {
+ /* nothing vailable */
+ ret = ENOENT;
+ goto done;
+ }
}
- ret = sysdb_get_ctx_from_list(cctx->rctx->db_list,
- dctx->domain, &sysdb);
- if (ret != EOK) {
- DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
- NSS_CMD_FATAL_ERROR(cctx);
+ /* ok the backend returned, search to see if we have updated results */
+ ret = nss_cmd_getpwnam_search(dctx);
+ if (ret == EOK) {
+ /* we have results to return */
+ ret = nss_cmd_getpw_send_reply(dctx, false);
}
- ret = sysdb_getpwnam(cmdctx, sysdb,
- dctx->domain, cmdctx->name,
- nss_cmd_getpwnam_callback, dctx);
done:
- if (ret != EOK) {
- DEBUG(1, ("Failed to make request to our cache! (%d [%s])\n",
- ret, strerror(ret)));
-
- ret = nss_cmd_send_error(cmdctx, ret);
- if (ret != EOK) {
- NSS_CMD_FATAL_ERROR(cctx);
- }
- sss_cmd_done(cctx, cmdctx);
+ ret = nss_cmd_done(cmdctx, ret);
+ if (ret) {
+ NSS_CMD_FATAL_ERROR(cctx);
}
}
@@ -605,18 +642,11 @@ static int nss_cmd_getpwnam(struct cli_ctx *cctx)
{
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
- struct sss_domain_info *dom;
- struct sysdb_ctx *sysdb;
- struct nss_ctx *nctx;
const char *rawname;
char *domname;
uint8_t *body;
size_t blen;
int ret;
- int ncret;
- bool neghit = false;
-
- nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
if (!cmdctx) {
@@ -659,92 +689,23 @@ static int nss_cmd_getpwnam(struct cli_ctx *cctx)
ret = ENOENT;
goto done;
}
-
- /* verify this user has not yet been negatively cached,
- * or has been permanently filtered */
- ncret = nss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
- dctx->domain->name, cmdctx->name);
- if (ncret == EEXIST) {
- neghit = true;
- }
- }
- else {
- /* skip domains that require FQnames or have negative caches */
- for (dom = cctx->rctx->domains; dom; dom = dom->next) {
-
- if (dom->fqnames) continue;
-
- /* verify this user has not yet been negatively cached,
- * or has been permanently filtered */
- ncret = nss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
- dom->name, cmdctx->name);
- if (ncret == ENOENT) break;
-
- neghit = true;
- }
- /* reset neghit if we still have a domain to check */
- if (dom) neghit = false;
-
- dctx->domain = dom;
- }
- if (neghit) {
- DEBUG(2, ("User [%s] does not exist! (negative cache)\n", rawname));
- ret = ENOENT;
- goto done;
- }
- if (dctx->domain == NULL) {
- DEBUG(2, ("No matching domain found for [%s], fail!\n", rawname));
- ret = ENOENT;
- goto done;
- }
-
- dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
-
- if (!domname) {
+ } else {
/* this is a multidomain search */
+ dctx->domain = cctx->rctx->domains;
cmdctx->check_next = true;
}
- DEBUG(4, ("Requesting info for [%s@%s]\n",
- cmdctx->name, dctx->domain->name));
+ dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
- ret = sysdb_get_ctx_from_list(cctx->rctx->db_list,
- dctx->domain, &sysdb);
- if (ret != EOK) {
- DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
- ret = EFAULT;
- goto done;
- }
- ret = sysdb_getpwnam(cmdctx, sysdb,
- dctx->domain, cmdctx->name,
- nss_cmd_getpwnam_callback, dctx);
- if (ret != EOK) {
- DEBUG(1, ("Failed to make request to our cache!\n"));
+ /* ok, find it ! */
+ ret = nss_cmd_getpwnam_search(dctx);
+ if (ret == EOK) {
+ /* we have results to return */
+ ret = nss_cmd_getpw_send_reply(dctx, false);
}
done:
- if (ret != EOK) {
- if (ret == ENOENT) {
- /* we do not have any entry to return */
- ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t),
- sss_packet_get_cmd(cctx->creq->in),
- &cctx->creq->out);
- if (ret == EOK) {
- sss_packet_get_body(cctx->creq->out, &body, &blen);
- ((uint32_t *)body)[0] = 0; /* 0 results */
- ((uint32_t *)body)[1] = 0; /* reserved */
- }
- }
- if (ret != EOK) {
- ret = nss_cmd_send_error(cmdctx, ret);
- }
- if (ret == EOK) {
- sss_cmd_done(cctx, cmdctx);
- }
- return ret;
- }
-
- return EOK;
+ return nss_cmd_done(cmdctx, ret);
}
static void nss_cmd_getpwuid_dp_callback(uint16_t err_maj, uint32_t err_min,
diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
index 60f9c66ae..1a81fb70f 100644
--- a/src/responder/pam/pamsrv.h
+++ b/src/responder/pam/pamsrv.h
@@ -44,6 +44,7 @@ struct pam_auth_req {
pam_dp_callback_t *callback;
+ struct ldb_result *res;
bool check_provider;
void *data;
};
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index eba78cceb..d5b4eff69 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -564,8 +564,8 @@ static void pam_cache_auth_done(struct pam_auth_req *preq, int ret,
static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr);
-static void pam_check_user_callback(void *ptr, int status,
- struct ldb_result *res);
+static int pam_check_user_search(struct pam_auth_req *preq);
+static int pam_check_user_done(struct pam_auth_req *preq, int ret);
static void pam_dom_forwarder(struct pam_auth_req *preq);
/* TODO: we should probably return some sort of cookie that is set in the
@@ -575,12 +575,10 @@ static void pam_dom_forwarder(struct pam_auth_req *preq);
static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
{
struct sss_domain_info *dom;
- struct sysdb_ctx *sysdb;
struct pam_auth_req *preq;
struct pam_data *pd;
uint8_t *body;
size_t blen;
- int timeout;
int ret;
uint32_t terminator = SSS_END_OF_PAM_REQUEST;
preq = talloc_zero(cctx, struct pam_auth_req);
@@ -663,267 +661,168 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
goto done;
}
- /* When auth is requested always search the provider first,
- * do not rely on cached data unless the provider is completely
- * offline */
- if (NEED_CHECK_PROVIDER(preq->domain->provider) &&
- (pam_cmd == SSS_PAM_AUTHENTICATE || pam_cmd == SSS_PAM_SETCRED)) {
-
- /* no need to re-check later on */
- preq->check_provider = false;
- timeout = SSS_CLI_SOCKET_TIMEOUT/2;
-
- ret = sss_dp_send_acct_req(preq->cctx->rctx, preq,
- pam_check_user_dp_callback, preq,
- timeout, preq->domain->name,
- false, SSS_DP_INITGROUPS,
- preq->pd->user, 0);
- }
- else {
- preq->check_provider = NEED_CHECK_PROVIDER(preq->domain->provider);
-
- ret = sysdb_get_ctx_from_list(cctx->rctx->db_list,
- preq->domain, &sysdb);
- if (ret != EOK) {
- DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
- goto done;
- }
- ret = sysdb_getpwnam(preq, sysdb,
- preq->domain, preq->pd->user,
- pam_check_user_callback, preq);
+ ret = pam_check_user_search(preq);
+ if (ret == EOK) {
+ pam_dom_forwarder(preq);
}
done:
- if (ret != EOK) {
- switch (ret) {
- case ENOENT:
- pd->pam_status = PAM_USER_UNKNOWN;
- default:
- pd->pam_status = PAM_SYSTEM_ERR;
- }
- pam_reply(preq);
- }
- return EOK;
+ return pam_check_user_done(preq, ret);
}
-static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
- const char *err_msg, void *ptr)
+static int pam_check_user_search(struct pam_auth_req *preq)
{
- struct pam_auth_req *preq = talloc_get_type(ptr, struct pam_auth_req);
+ struct sss_domain_info *dom = preq->domain;
+ struct cli_ctx *cctx = preq->cctx;
+ const char *name = preq->pd->user;
struct sysdb_ctx *sysdb;
+ time_t cacheExpire;
int ret;
- if (err_maj) {
- DEBUG(2, ("Unable to get information from Data Provider\n"
- "Error: %u, %u, %s\n",
- (unsigned int)err_maj, (unsigned int)err_min, err_msg));
- }
+ while (dom) {
+ /* if it is a domainless search, skip domains that require fully
+ * qualified names instead */
+ while (dom && !preq->pd->domain && dom->fqnames) {
+ dom = dom->next;
+ }
- /* always try to see if we have the user in cache even if the provider
- * returned an error */
- ret = sysdb_get_ctx_from_list(preq->cctx->rctx->db_list,
- preq->domain, &sysdb);
- if (ret != EOK) {
- DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
- goto done;
- }
- ret = sysdb_getpwnam(preq, sysdb,
- preq->domain, preq->pd->user,
- pam_check_user_callback, preq);
+ if (!dom) break;
-done:
- if (ret != EOK) {
- preq->pd->pam_status = PAM_SYSTEM_ERR;
- pam_reply(preq);
- }
-}
+ if (dom != preq->domain) {
+ /* make sure we reset the check_provider flag when we check
+ * a new domain */
+ preq->check_provider = NEED_CHECK_PROVIDER(dom->provider);
+ }
-static void pam_check_user_callback(void *ptr, int status,
- struct ldb_result *res)
-{
- struct pam_auth_req *preq = talloc_get_type(ptr, struct pam_auth_req);
- struct sss_domain_info *dom;
- struct sysdb_ctx *sysdb;
- uint64_t cacheExpire;
- bool call_provider = false;
- time_t timeout;
- int ret;
+ /* make sure to update the preq if we changed domain */
+ preq->domain = dom;
- if (status != LDB_SUCCESS) {
- preq->pd->pam_status = PAM_SYSTEM_ERR;
- pam_reply(preq);
- return;
- }
+ /* TODO: check negative cache ? */
- timeout = SSS_CLI_SOCKET_TIMEOUT/2;
+ /* Always try to refresh the cache first on authentication */
+ if (preq->check_provider &&
+ (preq->pd->cmd == SSS_PAM_AUTHENTICATE ||
+ preq->pd->cmd == SSS_PAM_SETCRED)) {
- if (preq->check_provider) {
- switch (res->count) {
- case 0:
- call_provider = true;
+ /* call provider first */
break;
+ }
- case 1:
- cacheExpire = ldb_msg_find_attr_as_uint64(res->msgs[0],
+ DEBUG(4, ("Requesting info for [%s@%s]\n", name, dom->name));
+
+ ret = sysdb_get_ctx_from_list(cctx->rctx->db_list, dom, &sysdb);
+ if (ret != EOK) {
+ DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ return EFAULT;
+ }
+ ret = sysdb_getpwnam(preq, sysdb, dom, name, &preq->res);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to make request to our cache!\n"));
+ return EIO;
+ }
+
+ if (preq->res->count > 1) {
+ DEBUG(0, ("getpwnam call returned more than one result !?!\n"));
+ return ENOENT;
+ }
+
+ if (preq->res->count == 0) {
+ /* if a multidomain search, try with next */
+ if (!preq->pd->domain) {
+ dom = dom->next;
+ continue;
+ }
+
+ DEBUG(2, ("No results for getpwnam call\n"));
+
+ /* TODO: store negative cache ? */
+
+ return ENOENT;
+ }
+
+ /* One result found */
+
+ /* if we need to check the remote account go on */
+ if (preq->check_provider) {
+ cacheExpire = ldb_msg_find_attr_as_uint64(preq->res->msgs[0],
SYSDB_CACHE_EXPIRE, 0);
if (cacheExpire < time(NULL)) {
- call_provider = true;
+ break;
}
- break;
-
- default:
- DEBUG(1, ("check user call returned more than one result !?!\n"));
- preq->pd->pam_status = PAM_SYSTEM_ERR;
- pam_reply(preq);
- return;
}
+
+ DEBUG(6, ("Returning info for user [%s@%s]\n", name, dom->name));
+
+ return EOK;
}
- if (call_provider) {
+ if (preq->check_provider) {
/* dont loop forever :-) */
preq->check_provider = false;
- /* keep around current data in case backend is offline */
- if (res->count) {
- preq->data = talloc_steal(preq, res);
- }
-
ret = sss_dp_send_acct_req(preq->cctx->rctx, preq,
pam_check_user_dp_callback, preq,
- timeout, preq->domain->name,
- false, SSS_DP_USER,
- preq->pd->user, 0);
+ SSS_CLI_SOCKET_TIMEOUT/2,
+ dom->name, false, SSS_DP_USER, name, 0);
if (ret != EOK) {
DEBUG(3, ("Failed to dispatch request: %d(%s)\n",
ret, strerror(ret)));
preq->pd->pam_status = PAM_SYSTEM_ERR;
- pam_reply(preq);
+ return EIO;
}
- return;
+ /* tell caller we are in an async call */
+ return EAGAIN;
}
- switch (res->count) {
- case 0:
- if (!preq->pd->domain) {
- /* search next as the domain was unknown */
-
- ret = EOK;
-
- /* skip domains that require FQnames or have negative caches */
- for (dom = preq->domain->next; dom; dom = dom->next) {
-
- if (dom->fqnames) continue;
-
-#if HAVE_NEG_CACHE
- ncret = nss_ncache_check_user(nctx->ncache,
- nctx->neg_timeout,
- dom->name, cmdctx->name);
- if (ncret == ENOENT) break;
-
- neghit = true;
-#endif
- break;
- }
-#if HAVE_NEG_CACHE
- /* reset neghit if we still have a domain to check */
- if (dom) neghit = false;
-
- if (neghit) {
- DEBUG(2, ("User [%s] does not exist! (negative cache)\n",
- cmdctx->name));
- ret = ENOENT;
- }
-#endif
- if (dom == NULL) {
- DEBUG(2, ("No matching domain found for [%s], fail!\n",
- preq->pd->user));
- ret = ENOENT;
- }
+ DEBUG(2, ("No matching domain found for [%s], fail!\n", name));
+ return ENOENT;
+}
- if (ret == EOK) {
- preq->domain = dom;
- preq->data = NULL;
-
- DEBUG(4, ("Requesting info for [%s@%s]\n",
- preq->pd->user, preq->domain->name));
-
- /* When auth is requested always search the provider first,
- * do not rely on cached data unless the provider is
- * completely offline */
- if (NEED_CHECK_PROVIDER(preq->domain->provider) &&
- (preq->pd->cmd == SSS_PAM_AUTHENTICATE ||
- preq->pd->cmd == SSS_PAM_SETCRED)) {
-
- /* no need to re-check later on */
- preq->check_provider = false;
-
- ret = sss_dp_send_acct_req(preq->cctx->rctx, preq,
- pam_check_user_dp_callback,
- preq, timeout,
- preq->domain->name,
- false, SSS_DP_USER,
- preq->pd->user, 0);
- }
- else {
- preq->check_provider = NEED_CHECK_PROVIDER(preq->domain->provider);
+static int pam_check_user_done(struct pam_auth_req *preq, int ret)
+{
+ switch (ret) {
+ case EOK:
+ break;
- ret = sysdb_get_ctx_from_list(preq->cctx->rctx->db_list,
- preq->domain, &sysdb);
- if (ret != EOK) {
- DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
- preq->pd->pam_status = PAM_SYSTEM_ERR;
- pam_reply(preq);
- return;
- }
- ret = sysdb_getpwnam(preq, sysdb,
- preq->domain, preq->pd->user,
- pam_check_user_callback, preq);
- }
- if (ret != EOK) {
- DEBUG(1, ("Failed to make request to our cache!\n"));
- }
- }
+ case EAGAIN:
+ /* performing async request, just return */
+ break;
- /* we made another call, end here */
- if (ret == EOK) return;
- }
- else {
- ret = ENOENT;
- }
+ case ENOENT:
+ preq->pd->pam_status = PAM_USER_UNKNOWN;
+ pam_reply(preq);
+ break;
- DEBUG(2, ("No results for check user call\n"));
+ default:
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ pam_reply(preq);
+ break;
+ }
-#if HAVE_NEG_CACHE
- /* set negative cache only if not result of cache check */
- if (!neghit) {
- ret = nss_ncache_set_user(nctx->ncache, false,
- dctx->domain->name, cmdctx->name);
- if (ret != EOK) {
- NSS_CMD_FATAL_ERROR(cctx);
- }
- }
-#endif
+ return EOK;
+}
- if (ret != EOK) {
- if (ret == ENOENT) {
- preq->pd->pam_status = PAM_USER_UNKNOWN;
- } else {
- preq->pd->pam_status = PAM_SYSTEM_ERR;
- }
- pam_reply(preq);
- return;
- }
- break;
+static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
+ const char *err_msg, void *ptr)
+{
+ struct pam_auth_req *preq = talloc_get_type(ptr, struct pam_auth_req);
+ int ret;
- case 1:
+ if (err_maj) {
+ DEBUG(2, ("Unable to get information from Data Provider\n"
+ "Error: %u, %u, %s\n",
+ (unsigned int)err_maj, (unsigned int)err_min, err_msg));
+ }
- /* BINGO */
+ ret = pam_check_user_search(preq);
+ if (ret == EOK) {
pam_dom_forwarder(preq);
- return;
+ }
- default:
- DEBUG(1, ("check user call returned more than one result !?!\n"));
+ ret = pam_check_user_done(preq, ret);
+ if (ret) {
preq->pd->pam_status = PAM_SYSTEM_ERR;
pam_reply(preq);
}
diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
index 1ce8a3255..acb96b2e8 100644
--- a/src/tests/sysdb-tests.c
+++ b/src/tests/sysdb-tests.c
@@ -306,31 +306,6 @@ static int test_remove_group_by_gid(struct test_data *data)
return ret;
}
-static void test_getpwent(void *pvt, int error, struct ldb_result *res)
-{
- struct test_data *data = talloc_get_type(pvt, struct test_data);
- data->finished = true;
-
- if (error != EOK) {
- data->error = error;
- return;
- }
-
- switch (res->count) {
- case 0:
- data->error = ENOENT;
- break;
-
- case 1:
- data->uid = ldb_msg_find_attr_as_uint(res->msgs[0], SYSDB_UIDNUM, 0);
- break;
-
- default:
- data->error = EFAULT;
- break;
- }
-}
-
static void test_getgrent(void *pvt, int error, struct ldb_result *res)
{
struct test_data *data = talloc_get_type(pvt, struct test_data);
@@ -838,8 +813,9 @@ END_TEST
START_TEST (test_sysdb_getpwnam)
{
struct sysdb_test_ctx *test_ctx;
- struct test_data *data;
- struct test_data *data_uc;
+ struct ldb_result *res;
+ const char *username;
+ uid_t uid;
int ret;
/* Setup */
@@ -849,45 +825,42 @@ START_TEST (test_sysdb_getpwnam)
return;
}
- data = talloc_zero(test_ctx, struct test_data);
- data->ctx = test_ctx;
- data->username = talloc_asprintf(data, "testuser%d", _i);
+ username = talloc_asprintf(test_ctx, "testuser%d", _i);
ret = sysdb_getpwnam(test_ctx,
test_ctx->sysdb,
- data->ctx->domain,
- data->username,
- test_getpwent,
- data);
- if (ret == EOK) {
- ret = test_loop(data);
- }
-
+ test_ctx->domain,
+ username, &res);
if (ret) {
fail("sysdb_getpwnam failed for username %s (%d: %s)",
- data->username, ret, strerror(ret));
+ username, ret, strerror(ret));
+ goto done;
+ }
+
+ if (res->count != 1) {
+ fail("Invalid number of replies. Expected 1, got %d", res->count);
goto done;
}
- fail_unless(data->uid == _i,
- "Did not find the expected UID");
+
+ uid = ldb_msg_find_attr_as_uint(res->msgs[0], SYSDB_UIDNUM, 0);
+ fail_unless(uid == _i, "Did not find the expected UID");
/* Search for the user with the wrong case */
- data_uc = talloc_zero(test_ctx, struct test_data);
- data_uc->ctx = test_ctx;
- data_uc->username = talloc_asprintf(data_uc, "TESTUSER%d", _i);
+ username = talloc_asprintf(test_ctx, "TESTUSER%d", _i);
ret = sysdb_getpwnam(test_ctx,
test_ctx->sysdb,
- data_uc->ctx->domain,
- data_uc->username,
- test_getpwent,
- data_uc);
- if (ret == EOK) {
- ret = test_loop(data_uc);
+ test_ctx->domain,
+ username, &res);
+ if (ret) {
+ fail("sysdb_getpwnam failed for username %s (%d: %s)",
+ username, ret, strerror(ret));
+ goto done;
}
- fail_unless(ret == ENOENT,
- "The upper-case username search should fail. ");
+ if (res->count != 0) {
+ fail("The upper-case username search should fail.");
+ }
done:
talloc_free(test_ctx);
diff --git a/src/tools/sss_sync_ops.c b/src/tools/sss_sync_ops.c
index f9230a389..70203cf2f 100644
--- a/src/tools/sss_sync_ops.c
+++ b/src/tools/sss_sync_ops.c
@@ -591,125 +591,79 @@ int groupdel(TALLOC_CTX *mem_ctx,
/*
* getpwnam, getgrnam and friends
*/
-static void sss_getpwnam_done(void *ptr, int status,
- struct ldb_result *lrs);
-
int sysdb_getpwnam_sync(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sysdb_ctx *sysdb,
const char *name,
struct sss_domain_info *domain,
- struct ops_ctx **out)
-{
- int ret;
- struct sync_op_res *res = NULL;
-
- res = talloc_zero(mem_ctx, struct sync_op_res);
- if (!res) {
- return ENOMEM;
- }
-
- if (out == NULL) {
- DEBUG(1, ("NULL passed for storage pointer\n"));
- return EINVAL;
- }
- res->data = *out;
-
- ret = sysdb_getpwnam(mem_ctx,
- sysdb,
- domain,
- name,
- sss_getpwnam_done,
- res);
-
- SYNC_LOOP(res, ret);
-
- return ret;
-}
-
-static void sss_getpwnam_done(void *ptr, int status,
- struct ldb_result *lrs)
+ struct ops_ctx *out)
{
- struct sync_op_res *res = talloc_get_type(ptr, struct sync_op_res );
+ struct ldb_result *res;
const char *str;
+ int ret;
- res->done = true;
-
- if (status != LDB_SUCCESS) {
- res->error = status;
- return;
+ ret = sysdb_getpwnam(mem_ctx, sysdb, domain, name, &res);
+ if (ret) {
+ return ret;
}
- switch (lrs->count) {
- case 0:
- DEBUG(1, ("No result for sysdb_getpwnam call\n"));
- res->error = ENOENT;
- break;
+ switch (res->count) {
+ case 0:
+ DEBUG(1, ("No result for sysdb_getpwnam call\n"));
+ return ENOENT;
- case 1:
- res->error = EOK;
- /* fill ops_ctx */
- res->data->uid = ldb_msg_find_attr_as_uint64(lrs->msgs[0],
- SYSDB_UIDNUM, 0);
+ case 1:
+ /* fill ops_ctx */
+ out->uid = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_UIDNUM, 0);
- res->data->gid = ldb_msg_find_attr_as_uint64(lrs->msgs[0],
- SYSDB_GIDNUM, 0);
+ out->gid = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_GIDNUM, 0);
- str = ldb_msg_find_attr_as_string(lrs->msgs[0],
- SYSDB_NAME, NULL);
- res->data->name = talloc_strdup(res, str);
- if (res->data->name == NULL) {
- res->error = ENOMEM;
- return;
- }
+ str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL);
+ out->name = talloc_strdup(out, str);
+ if (out->name == NULL) {
+ return ENOMEM;
+ }
- str = ldb_msg_find_attr_as_string(lrs->msgs[0],
- SYSDB_GECOS, NULL);
- res->data->gecos = talloc_strdup(res, str);
- if (res->data->gecos == NULL) {
- res->error = ENOMEM;
- return;
- }
+ str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_GECOS, NULL);
+ out->gecos = talloc_strdup(out, str);
+ if (out->gecos == NULL) {
+ return ENOMEM;
+ }
- str = ldb_msg_find_attr_as_string(lrs->msgs[0],
- SYSDB_HOMEDIR, NULL);
- res->data->home = talloc_strdup(res, str);
- if (res->data->home == NULL) {
- res->error = ENOMEM;
- return;
- }
+ str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_HOMEDIR, NULL);
+ out->home = talloc_strdup(out, str);
+ if (out->home == NULL) {
+ return ENOMEM;
+ }
- str = ldb_msg_find_attr_as_string(lrs->msgs[0],
- SYSDB_SHELL, NULL);
- res->data->shell = talloc_strdup(res, str);
- if (res->data->shell == NULL) {
- res->error = ENOMEM;
- return;
- }
+ str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SHELL, NULL);
+ out->shell = talloc_strdup(out, str);
+ if (out->shell == NULL) {
+ return ENOMEM;
+ }
- str = ldb_msg_find_attr_as_string(lrs->msgs[0],
- SYSDB_DISABLED, NULL);
- if (str == NULL) {
- res->data->lock = DO_UNLOCK;
- } else {
- if (strcasecmp(str, "true") == 0) {
- res->data->lock = DO_LOCK;
- } else if (strcasecmp(str, "false") == 0) {
- res->data->lock = DO_UNLOCK;
- } else { /* Invalid value */
- DEBUG(2, ("Invalid value for %s attribute: %s\n",
- SYSDB_DISABLED, str ? str : "NULL"));
- res->error = EIO;
- return;
- }
+ str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_DISABLED, NULL);
+ if (str == NULL) {
+ out->lock = DO_UNLOCK;
+ } else {
+ if (strcasecmp(str, "true") == 0) {
+ out->lock = DO_LOCK;
+ } else if (strcasecmp(str, "false") == 0) {
+ out->lock = DO_UNLOCK;
+ } else { /* Invalid value */
+ DEBUG(2, ("Invalid value for %s attribute: %s\n",
+ SYSDB_DISABLED, str ? str : "NULL"));
+ return EIO;
}
- break;
+ }
+ break;
- default:
- DEBUG(1, ("More than one result for sysdb_getpwnam call\n"));
- res->error = EIO;
- break;
+ default:
+ DEBUG(1, ("More than one result for sysdb_getpwnam call\n"));
+ return EIO;
}
+
+ return EOK;
}
static void sss_getgrnam_done(void *ptr, int status,
diff --git a/src/tools/sss_sync_ops.h b/src/tools/sss_sync_ops.h
index b2bb51c5c..98fbc47f2 100644
--- a/src/tools/sss_sync_ops.h
+++ b/src/tools/sss_sync_ops.h
@@ -97,7 +97,7 @@ int sysdb_getpwnam_sync(TALLOC_CTX *mem_ctx,
struct sysdb_ctx *sysdb,
const char *name,
struct sss_domain_info *domain,
- struct ops_ctx **out);
+ struct ops_ctx *out);
int sysdb_getgrnam_sync(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
diff --git a/src/tools/sss_useradd.c b/src/tools/sss_useradd.c
index 6198dcae3..0dc1993e5 100644
--- a/src/tools/sss_useradd.c
+++ b/src/tools/sss_useradd.c
@@ -292,7 +292,7 @@ int main(int argc, const char **argv)
tctx->sysdb,
tctx->octx->name,
tctx->local,
- &tctx->octx);
+ tctx->octx);
if (ret != EOK) {
ERROR("Cannot get info about the user\n");
ret = EXIT_FAILURE;
diff --git a/src/tools/sss_userdel.c b/src/tools/sss_userdel.c
index 890b4fec0..55c41c240 100644
--- a/src/tools/sss_userdel.c
+++ b/src/tools/sss_userdel.c
@@ -236,7 +236,7 @@ int main(int argc, const char **argv)
tctx->sysdb,
tctx->octx->name,
tctx->local,
- &tctx->octx);
+ tctx->octx);
if (ret != EOK) {
/* Error message will be printed in the switch */
goto done;
diff --git a/src/tools/sss_usermod.c b/src/tools/sss_usermod.c
index 88e3fda3c..43e685c55 100644
--- a/src/tools/sss_usermod.c
+++ b/src/tools/sss_usermod.c
@@ -143,7 +143,7 @@ int main(int argc, const char **argv)
/* check the username to be able to give sensible error message */
ret = sysdb_getpwnam_sync(tctx, tctx->ev, tctx->sysdb,
tctx->octx->name, tctx->local,
- &tctx->octx);
+ tctx->octx);
if (ret != EOK) {
ERROR("Cannot find user in local domain, "
"modifying users is allowed only in local domain\n");