summaryrefslogtreecommitdiff
path: root/src/responder/nss
diff options
context:
space:
mode:
authorStephen Gallagher <sgallagh@redhat.com>2010-09-22 16:47:06 -0400
committerStephen Gallagher <sgallagh@redhat.com>2010-10-13 09:49:37 -0400
commitef39c0adcb61b16f9edc7beb4cdc8f3b0d5a8f15 (patch)
treefd913c63d5dfe688a6d0e408af31695b874f608b /src/responder/nss
parent88a9c6a44b474bff0f7e22f9eb28a9e55df2c0b5 (diff)
downloadsssd-ef39c0adcb61b16f9edc7beb4cdc8f3b0d5a8f15.tar.gz
sssd-ef39c0adcb61b16f9edc7beb4cdc8f3b0d5a8f15.tar.bz2
sssd-ef39c0adcb61b16f9edc7beb4cdc8f3b0d5a8f15.zip
Add netgroup support to the NSS responder
Diffstat (limited to 'src/responder/nss')
-rw-r--r--src/responder/nss/nsssrv.c8
-rw-r--r--src/responder/nss/nsssrv.h1
-rw-r--r--src/responder/nss/nsssrv_cmd.c11
-rw-r--r--src/responder/nss/nsssrv_netgroup.c863
-rw-r--r--src/responder/nss/nsssrv_netgroup.h34
5 files changed, 915 insertions, 2 deletions
diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
index f14d698f..4f5aa629 100644
--- a/src/responder/nss/nsssrv.c
+++ b/src/responder/nss/nsssrv.c
@@ -162,6 +162,7 @@ int nss_process_init(TALLOC_CTX *mem_ctx,
struct be_conn *iter;
struct nss_ctx *nctx;
int ret, max_retries;
+ int hret;
nctx = talloc_zero(mem_ctx, struct nss_ctx);
if (!nctx) {
@@ -212,6 +213,13 @@ int nss_process_init(TALLOC_CTX *mem_ctx,
nss_dp_reconnect_init, iter);
}
+ /* Create the lookup table for netgroup results */
+ hret = hash_create(10, &nctx->netgroups, NULL, NULL);
+ if (hret != HASH_SUCCESS) {
+ DEBUG(0,("Unable to initialize netgroup hash table\n"));
+ return EIO;
+ }
+
DEBUG(1, ("NSS Initialization complete\n"));
return EOK;
diff --git a/src/responder/nss/nsssrv.h b/src/responder/nss/nsssrv.h
index d53143dc..0b7265b7 100644
--- a/src/responder/nss/nsssrv.h
+++ b/src/responder/nss/nsssrv.h
@@ -54,6 +54,7 @@ struct nss_ctx {
struct getent_ctx *pctx;
struct getent_ctx *gctx;
+ hash_table_t *netgroups;
bool filter_users_in_groups;
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 6e1dc7bb..d0db2ef8 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -22,6 +22,7 @@
#include "util/util.h"
#include "responder/nss/nsssrv.h"
#include "responder/nss/nsssrv_private.h"
+#include "responder/nss/nsssrv_netgroup.h"
#include "responder/common/negcache.h"
#include "confdb/confdb.h"
#include "db/sysdb.h"
@@ -363,8 +364,11 @@ errno_t check_cache(struct nss_dom_ctx *dctx,
struct cli_ctx *cctx = cmdctx->cctx;
bool off_band_update = false;
- /* when searching for a user, more than one reply is a db error */
- if ((req_type == SSS_DP_USER) && (res->count > 1)) {
+ /* when searching for a user or netgroup, more than one reply is a
+ * db error
+ */
+ if ((req_type == SSS_DP_USER || req_type == SSS_DP_NETGR) &&
+ (res->count > 1)) {
DEBUG(1, ("getpwXXX call returned more than one result!"
" DB Corrupted?\n"));
ret = nss_cmd_send_error(cmdctx, ENOENT);
@@ -3011,6 +3015,9 @@ static struct sss_cmd_table nss_cmds[] = {
{SSS_NSS_GETGRENT, nss_cmd_getgrent},
{SSS_NSS_ENDGRENT, nss_cmd_endgrent},
{SSS_NSS_INITGR, nss_cmd_initgroups},
+ {SSS_NSS_SETNETGRENT, nss_cmd_setnetgrent},
+ {SSS_NSS_GETNETGRENT, nss_cmd_getnetgrent},
+ {SSS_NSS_ENDNETGRENT, nss_cmd_endnetgrent},
{SSS_CLI_NULL, NULL}
};
diff --git a/src/responder/nss/nsssrv_netgroup.c b/src/responder/nss/nsssrv_netgroup.c
new file mode 100644
index 00000000..be99de78
--- /dev/null
+++ b/src/responder/nss/nsssrv_netgroup.c
@@ -0,0 +1,863 @@
+/*
+ SSSD
+
+ nsssrv_netgroup.c
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2010 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ 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 <collection.h>
+#include "util/util.h"
+#include "responder/nss/nsssrv.h"
+#include "responder/nss/nsssrv_private.h"
+#include "responder/nss/nsssrv_netgroup.h"
+#include "responder/common/negcache.h"
+#include "confdb/confdb.h"
+#include "db/sysdb.h"
+
+static errno_t get_netgroup_entry(struct nss_ctx *nctx,
+ char *name,
+ struct getent_ctx **netgr)
+{
+ hash_key_t key;
+ hash_value_t value;
+ int hret;
+
+ key.type = HASH_KEY_STRING;
+ key.str = name;
+
+ hret = hash_lookup(nctx->netgroups, &key, &value);
+ if (hret == HASH_SUCCESS) {
+ *netgr = talloc_get_type(value.ptr, struct getent_ctx);
+ return EOK;
+ } else if (hret == HASH_ERROR_KEY_NOT_FOUND) {
+ return ENOENT;
+ }
+
+ DEBUG(1, ("Unexpected error reading from netgroup hash [%d][%s]\n",
+ hret, hash_error_string(hret)));
+ return EIO;
+}
+
+static struct tevent_req *setnetgrent_send(TALLOC_CTX *mem_ctx,
+ const char *rawname,
+ struct nss_cmd_ctx *cmdctx);
+static void nss_cmd_setnetgrent_done(struct tevent_req *req);
+int nss_cmd_setnetgrent(struct cli_ctx *client)
+{
+ struct nss_cmd_ctx *cmdctx;
+ struct tevent_req *req;
+ const char *rawname;
+ uint8_t *body;
+ size_t blen;
+ errno_t ret = EOK;
+
+ /* Reset the result cursor to zero */
+ client->netgrent_cur = 0;
+
+ cmdctx = talloc_zero(client, struct nss_cmd_ctx);
+ if (!cmdctx) {
+ return ENOMEM;
+ }
+ cmdctx->cctx = client;
+
+ /* get netgroup name to query */
+ sss_packet_get_body(client->creq->in, &body, &blen);
+
+ /* if not terminated fail */
+ if (body[blen -1] != '\0') {
+ ret = EINVAL;
+ goto done;
+ }
+ rawname = (const char *)body;
+
+ req = setnetgrent_send(cmdctx, rawname, cmdctx);
+ if (!req) {
+ DEBUG(0, ("Fatal error calling setnetgrent_send\n"));
+ ret = EIO;
+ goto done;
+ }
+ tevent_req_set_callback(req, nss_cmd_setnetgrent_done, cmdctx);
+
+done:
+ return nss_cmd_done(cmdctx, ret);
+}
+
+static int netgr_hash_remove (TALLOC_CTX *ctx)
+{
+ int hret;
+ hash_key_t key;
+ struct getent_ctx *netgr =
+ talloc_get_type(ctx, struct getent_ctx);
+
+ key.type = HASH_KEY_STRING;
+ key.str = netgr->name;
+
+ /* Remove the netgroup result object from the lookup table */
+ hret = hash_delete(netgr->lookup_table, &key);
+ if (hret != HASH_SUCCESS) {
+ DEBUG(0, ("Could not remove key from table! [%d][%s]\n",
+ hret, hash_error_string(hret)));
+ return -1;
+ }
+ return 0;
+}
+
+struct setnetgrent_ctx {
+ struct nss_ctx *nctx;
+ struct nss_cmd_ctx *cmdctx;
+ struct nss_dom_ctx *dctx;
+ char *netgr_shortname;
+ struct getent_ctx *netgr;
+};
+static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx);
+static struct tevent_req *setnetgrent_send(TALLOC_CTX *mem_ctx,
+ const char *rawname,
+ struct nss_cmd_ctx *cmdctx)
+{
+ char *domname;
+ errno_t ret;
+ struct tevent_req *req;
+ struct setnetgrent_ctx *state;
+ int hret;
+ struct nss_dom_ctx *dctx;
+ struct setent_step_ctx *step_ctx;
+ hash_key_t key;
+ hash_value_t value;
+
+ struct cli_ctx *client = cmdctx->cctx;
+ struct nss_ctx *nctx =
+ talloc_get_type(client->rctx->pvt_ctx, struct nss_ctx);
+
+ req = tevent_req_create(mem_ctx, &state, struct setnetgrent_ctx);
+ if (!req) {
+ DEBUG(0, ("Could not create tevent request for setnetgrent\n"));
+ return NULL;
+ }
+
+ state->nctx = nctx;
+ state->cmdctx = cmdctx;
+
+ state->dctx = talloc_zero(state, struct nss_dom_ctx);
+ if (!state->dctx) {
+ goto error;
+ }
+ dctx = state->dctx;
+ dctx->cmdctx = state->cmdctx;
+
+ ret = sss_parse_name(state, client->rctx->names, rawname,
+ &domname, &state->netgr_shortname);
+ if (ret != EOK) {
+ DEBUG(2, ("Invalid name received [%s]\n", rawname));
+ goto error;
+ }
+
+ DEBUG(4, ("Requesting info for netgroup [%s] from [%s]\n",
+ state->netgr_shortname, domname?domname:"<ALL>"));
+
+ if (domname) {
+ dctx->domain = nss_get_dom(client->rctx->domains, domname);
+ if (!dctx->domain) {
+ goto error;
+ }
+
+ /* Save the netgroup name for getnetgrent */
+ client->netgr_name = talloc_strdup(client, rawname);
+ if (!client->netgr_name) {
+ ret = ENOMEM;
+ goto error;
+ }
+ } else {
+ /* this is a multidomain search */
+ dctx->domain = client->rctx->domains;
+ cmdctx->check_next = true;
+
+ /* Save the netgroup name for getnetgrent */
+ client->netgr_name = talloc_strdup(client, state->netgr_shortname);
+ if (!client->netgr_name) {
+ goto error;
+ }
+ }
+
+ dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
+
+ /* Is the result context already available?
+ * Check for existing lookups for this netgroup
+ */
+ ret = get_netgroup_entry(nctx, client->netgr_name, &state->netgr);
+ if (ret == EOK) {
+ /* Another process already requested this netgroup
+ * Check whether it's ready for processing.
+ */
+ if (state->netgr->ready) {
+ /* Ready to process results */
+ tevent_req_done(req);
+ tevent_req_post(req, nctx->rctx->ev);
+ return req;
+ }
+
+ /* Result object is still being constructed
+ * Register for notification when it's ready
+ */
+ ret = setent_add_ref(cmdctx->cctx, state->netgr, req);
+ if (ret != EOK) {
+ goto error;
+ }
+ /* Will return control below */
+ } else if (ret == ENOENT) {
+ /* This is the first attempt to request this netgroup
+ */
+ state->netgr = talloc_zero(nctx, struct getent_ctx);
+ if (!state->netgr) {
+ goto error;
+ }
+ dctx->netgr = state->netgr;
+
+ /* Save the name used for the lookup table
+ * so we can remove it in the destructor
+ */
+ state->netgr->name = talloc_strdup(state->netgr,
+ client->netgr_name);
+ if (!state->netgr->name) {
+ talloc_free(state->netgr);
+ goto error;
+ }
+
+ state->netgr->lookup_table = nctx->netgroups;
+
+ /* Add a reference for ourselves */
+ ret = setent_add_ref(cmdctx->cctx, state->netgr, req);
+ if (ret != EOK) {
+ talloc_free(state->netgr);
+ goto error;
+ }
+
+ /* Add this entry to the hash table */
+ key.type = HASH_KEY_STRING;
+ key.str = client->netgr_name;
+ value.type = HASH_VALUE_PTR;
+ value.ptr = state->netgr;
+ hret = hash_enter(nctx->netgroups, &key, &value);
+ if (hret != EOK) {
+ DEBUG(0, ("Unable to add hash table entry for [%s]", key.str));
+ DEBUG(4, ("Hash error [%d][%s]", hret, hash_error_string(hret)));
+ talloc_free(state->netgr);
+ goto error;
+ }
+ talloc_set_destructor((TALLOC_CTX *)state->netgr, netgr_hash_remove);
+
+ /* Perform lookup */
+ step_ctx = talloc_zero(state->netgr, struct setent_step_ctx);
+ if (!step_ctx) {
+ ret = ENOMEM;
+ goto error;
+ }
+
+ /* Steal the dom_ctx onto the step_ctx so it doesn't go out of scope if
+ * this request is canceled while other requests are in-progress.
+ */
+ step_ctx->dctx = talloc_steal(step_ctx, state->dctx);
+ step_ctx->nctx = state->nctx;
+ step_ctx->getent_ctx = state->netgr;
+ step_ctx->rctx = client->rctx;
+ step_ctx->check_next = cmdctx->check_next;
+ step_ctx->name =
+ talloc_strdup(step_ctx, state->netgr->name);
+ if (!step_ctx->name) {
+ ret = ENOMEM;
+ goto error;
+ }
+
+ ret = lookup_netgr_step(step_ctx);
+ if (ret != EOK) {
+ if (ret == EAGAIN) {
+ /* We need to reenter the mainloop
+ * We may be refreshing the cache
+ */
+ return req;
+ }
+
+ /* An unexpected error occurred */
+ goto error;
+ }
+ tevent_req_post(req, cmdctx->cctx->ev);
+ /* Will return control below */
+ } else {
+ /* Unexpected error from hash_lookup */
+ goto error;
+ }
+
+ return req;
+
+error:
+ talloc_free(req);
+ return NULL;
+}
+
+static void lookup_netgr_dp_callback(uint16_t err_maj, uint32_t err_min,
+ const char *err_msg, void *ptr);
+
+static void setnetgrent_result_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *pvt);
+static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
+{
+ errno_t ret;
+ struct sss_domain_info *dom = step_ctx->dctx->domain;
+ struct getent_ctx *netgr;
+ struct sysdb_ctx *sysdb;
+ struct timeval tv;
+ struct tevent_timer *te;
+
+ /* Check each domain for this netgroup name */
+ while (dom) {
+ /* if it is a domainless search, skip domains that require fully
+ * qualified names instead */
+ while (dom && step_ctx->check_next && dom->fqnames) {
+ dom = dom->next;
+ }
+
+ /* No domains left to search */
+ if (!dom) break;
+
+ if (dom != step_ctx->dctx->domain) {
+ /* make sure we reset the check_provider flag when we check
+ * a new domain */
+ step_ctx->dctx->check_provider =
+ NEED_CHECK_PROVIDER(dom->provider);
+ }
+
+ /* make sure to update the dctx if we changed domain */
+ step_ctx->dctx->domain = dom;
+
+ /* verify this netgroup has not yet been negatively cached */
+ ret = sss_ncache_check_netgr(step_ctx->nctx->ncache,
+ step_ctx->nctx->neg_timeout,
+ dom->name, step_ctx->name);
+
+ /* if neg cached, return we didn't find it */
+ if (ret == EEXIST) {
+ DEBUG(2, ("Netgroup [%s] does not exist! (negative cache)\n",
+ step_ctx->name));
+ /* if a multidomain search, try with next */
+ if (step_ctx->check_next) {
+ dom = dom->next;
+ continue;
+ }
+ }
+
+ DEBUG(4, ("Requesting info for [%s@%s]\n",
+ step_ctx->name, dom->name));
+ ret = sysdb_get_ctx_from_list(step_ctx->rctx->db_list, dom, &sysdb);
+ if (ret != EOK) {
+ DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
+ return EIO;
+ }
+
+ /* Look up the netgroup in the cache */
+ ret = sysdb_getnetgr(step_ctx->dctx, sysdb, dom,
+ step_ctx->name,
+ &step_ctx->dctx->res);
+ if (ret == ENOENT) {
+ /* This netgroup was not found in this domain */
+ if (!step_ctx->dctx->check_provider) {
+ if (step_ctx->check_next) {
+ dom = dom->next;
+ continue;
+ }
+ else break;
+ }
+ ret = EOK;
+ }
+
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to make request to our cache!\n"));
+ return EIO;
+ }
+
+ ret = get_netgroup_entry(step_ctx->nctx, step_ctx->name,
+ &netgr);
+ if (ret != EOK) {
+ /* Something really bad happened! */
+ DEBUG(0, ("Netgroup entry was lost!\n"));
+ return ret;
+ }
+
+ /* Convert the result to a list of triples */
+ ret = sysdb_netgr_to_triples(netgr, step_ctx->dctx->res,
+ &netgr->triples);
+ if (ret == ENOENT) {
+ /* This netgroup was not found in this domain */
+ if (!step_ctx->dctx->check_provider) {
+ if (step_ctx->check_next) {
+ dom = dom->next;
+ continue;
+ }
+ else break;
+ }
+ ret = EOK;
+ }
+
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to convert results into triples\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 (step_ctx->dctx->check_provider) {
+ ret = check_cache(step_ctx->dctx,
+ step_ctx->nctx,
+ step_ctx->dctx->res,
+ SSS_DP_NETGR,
+ step_ctx->name, 0,
+ lookup_netgr_dp_callback,
+ step_ctx);
+ if (ret != EOK) {
+ /* May return EAGAIN legitimately to indicate that
+ * we need to reenter the mainloop
+ */
+ return ret;
+ }
+ }
+
+ /* Results found */
+ DEBUG(6, ("Returning info for netgroup [%s@%s]\n",
+ step_ctx->name, dom->name));
+ netgr->ready = true;
+
+ /* Set up a lifetime timer for this result object
+ * We don't want this result object to outlive the
+ * entry cache refresh timeout
+ */
+ tv = tevent_timeval_current_ofs(dom->entry_cache_timeout, 0);
+ te = tevent_add_timer(step_ctx->nctx->rctx->ev,
+ step_ctx->nctx->gctx, tv,
+ setnetgrent_result_timeout,
+ netgr);
+ if (!te) {
+ DEBUG(0, ("Could not set up life timer for setnetgrent result object. "
+ "Entries may become stale.\n"));
+ }
+
+ return EOK;
+ }
+
+ /* If we've gotten here, then no domain contained this netgroup */
+ DEBUG(2, ("No matching domain found for [%s], fail!\n",
+ step_ctx->name));
+ netgr->ready = true;
+ netgr->triples = NULL;
+ return ENOENT;
+}
+
+static void lookup_netgr_dp_callback(uint16_t err_maj, uint32_t err_min,
+ const char *err_msg, void *ptr)
+{
+ struct setent_step_ctx *step_ctx =
+ talloc_get_type(ptr, struct setent_step_ctx);
+ struct nss_dom_ctx *dctx = step_ctx->dctx;
+ struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
+ int ret;
+
+ if (err_maj) {
+ DEBUG(2, ("Unable to get information from Data Provider\n"
+ "Error: %u, %u, %s\n"
+ "Will try to return what we have in cache\n",
+ (unsigned int)err_maj, (unsigned int)err_min, err_msg));
+ /* Loop to the 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);
+ }
+ }
+
+ /* ok the backend returned, search to see if we have updated results */
+ ret = lookup_netgr_step(step_ctx);
+ if (ret != EOK) {
+ if (ret == EAGAIN) {
+ return;
+ }
+ }
+
+ /* We have results to return */
+ while(dctx->netgr->reqs) {
+ if (ret == EOK) {
+ tevent_req_done(dctx->netgr->reqs->req);
+ } else {
+ tevent_req_error(dctx->netgr->reqs->req, ret);
+ }
+ /* Freeing each entry in the list removes it from the dlist */
+ talloc_free(dctx->netgr->reqs);
+ }
+}
+
+static void setnetgrent_result_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *pvt)
+{
+ struct getent_ctx *netgr =
+ talloc_get_type(pvt, struct getent_ctx);
+
+ /* Free the netgroup result context
+ * The destructor for the netgroup will remove itself
+ * from the hash table
+ *
+ * If additional getnetgrent() requests come in, they
+ * will invoke an implicit setnetgrent() call and
+ * refresh the result object
+ */
+ talloc_free(netgr);
+}
+
+static errno_t setnetgrent_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+static void nss_cmd_setnetgrent_done(struct tevent_req *req)
+{
+ errno_t reqret;
+ errno_t ret;
+ struct sss_packet *packet;
+ uint8_t *body;
+ size_t blen;
+
+ struct nss_cmd_ctx *cmdctx =
+ tevent_req_callback_data(req, struct nss_cmd_ctx);
+
+ reqret = setnetgrent_recv(req);
+ talloc_zfree(req);
+ if (reqret != EOK && reqret != ENOENT) {
+ DEBUG(1, ("setnetgrent failed\n"));
+ nss_cmd_done(cmdctx, reqret);
+ return;
+ }
+
+ /* Either we succeeded or no domains were eligible */
+ ret = sss_packet_new(cmdctx->cctx->creq, 0,
+ sss_packet_get_cmd(cmdctx->cctx->creq->in),
+ &cmdctx->cctx->creq->out);
+ if (ret == EOK) {
+ if (reqret == ENOENT) {
+ /* Notify the caller that this entry wasn't found */
+ fill_empty(cmdctx->cctx->creq->out);
+ } else {
+ packet = cmdctx->cctx->creq->out;
+ ret = sss_packet_grow(packet, 2*sizeof(uint32_t));
+ if (ret != EOK) {
+ DEBUG(1, ("Couldn't grow the packet\n"));
+ NSS_CMD_FATAL_ERROR(cmdctx);
+ }
+
+ sss_packet_get_body(packet, &body, &blen);
+ ((uint32_t *)body)[0] = 1; /* Got some results */
+ ((uint32_t *)body)[1] = 0; /* reserved */
+ }
+
+ sss_cmd_done(cmdctx->cctx, NULL);
+ return;
+ }
+
+ DEBUG(1, ("Error creating packet\n"));
+}
+
+static void setnetgrent_implicit_done(struct tevent_req *req);
+static errno_t nss_cmd_getnetgrent_process(struct nss_cmd_ctx *cmdctx,
+ struct getent_ctx *netgr);
+int nss_cmd_getnetgrent(struct cli_ctx *client)
+{
+ errno_t ret;
+ struct nss_ctx *nctx;
+ struct nss_cmd_ctx *cmdctx;
+ struct getent_ctx *netgr;
+ struct tevent_req *req;
+
+ DEBUG(4, ("Requesting netgroup data\n"));
+
+ cmdctx = talloc(client, struct nss_cmd_ctx);
+ if (!cmdctx) {
+ return ENOMEM;
+ }
+ cmdctx->cctx = client;
+
+ nctx = talloc_get_type(client->rctx->pvt_ctx, struct nss_ctx);
+
+ if (!client->netgr_name) {
+ /* Tried to run getnetgrent without a preceding
+ * setnetgrent. There is no way to determine which
+ * netgroup is being requested.
+ */
+ return nss_cmd_done(cmdctx, EINVAL);
+ }
+
+ /* Look up the results from the hash */
+ ret = get_netgroup_entry(nctx, client->netgr_name, &netgr);
+ if (ret == ENOENT) {
+ /* We need to invoke an implicit setnetgrent() to
+ * wait for the result object to become available.
+ */
+
+ req = setnetgrent_send(cmdctx, client->netgr_name, cmdctx);
+ if (!req) {
+ return nss_cmd_done(cmdctx, EIO);
+ }
+ tevent_req_set_callback(req, setnetgrent_implicit_done, cmdctx);
+
+ return EOK;
+ } else if (ret != EOK) {
+ DEBUG(1, ("An unexpected error occurred: [%d][%s]\n",
+ ret, strerror(ret)));
+
+ return nss_cmd_done(cmdctx, ret);
+ }
+
+ /* Hash entry was found. Is it ready? */
+ if (!netgr->ready) {
+ /* We need to invoke an implicit setnetgrent() to
+ * wait for the result object to become available.
+ */
+ req = setnetgrent_send(cmdctx, client->netgr_name, cmdctx);
+ if (!req) {
+ return nss_cmd_done(cmdctx, EIO);
+ }
+ tevent_req_set_callback(req, setnetgrent_implicit_done, cmdctx);
+
+ return EOK;
+ }
+
+ DEBUG(6, ("Returning results for [%s]\n", client->netgr_name));
+
+ /* Read the result strings */
+ ret = nss_cmd_getnetgrent_process(cmdctx, netgr);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed: [%d][%s]\n", ret, strerror(ret)));
+ }
+ return ret;
+}
+
+static void setnetgrent_implicit_done(struct tevent_req *req)
+{
+ errno_t ret;
+ struct getent_ctx *netgr;
+ struct nss_cmd_ctx *cmdctx =
+ tevent_req_callback_data(req, struct nss_cmd_ctx);
+ struct nss_ctx *nctx =
+ talloc_get_type(cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx);
+
+ ret = setnetgrent_recv(req);
+ talloc_zfree(req);
+
+ /* ENOENT is acceptable, it just means there were no values
+ * to be returned. This will be handled gracefully in
+ * nss_cmd_retnetgrent later
+ */
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(0, ("Implicit setnetgrent failed with unexpected error "
+ "[%d][%s]\n", ret, strerror(ret)));
+ NSS_CMD_FATAL_ERROR(cmdctx);
+ }
+
+ if (ret == ENOENT) {
+ /* No entries found for this netgroup */
+ nss_cmd_done(cmdctx, ret);
+ return;
+ }
+
+ /* Look up the results from the hash */
+ ret = get_netgroup_entry(nctx, cmdctx->cctx->netgr_name, &netgr);
+ if (ret == ENOENT) {
+ /* Critical error. This should never happen */
+ DEBUG(0, ("Implicit setnetgrent returned success without creating "
+ "result object.\n"));
+ NSS_CMD_FATAL_ERROR(cmdctx);
+ } else if (ret != EOK) {
+ DEBUG(1, ("An unexpected error occurred: [%d][%s]\n",
+ ret, strerror(ret)));
+
+ NSS_CMD_FATAL_ERROR(cmdctx);
+ }
+
+ if (!netgr->ready) {
+ /* Critical error. This should never happen */
+ DEBUG(0, ("Implicit setnetgrent returned success without creating "
+ "result object.\n"));
+ NSS_CMD_FATAL_ERROR(cmdctx);
+ }
+
+ ret = nss_cmd_getnetgrent_process(cmdctx, netgr);
+ if (ret != EOK) {
+ DEBUG(0, ("Immediate retrieval failed with unexpected error "
+ "[%d][%s]\n", ret, strerror(ret)));
+ NSS_CMD_FATAL_ERROR(cmdctx);
+ }
+}
+
+static errno_t nss_cmd_retnetgrent(struct cli_ctx *client,
+ struct sysdb_netgroup_ctx **triples,
+ int num);
+static errno_t nss_cmd_getnetgrent_process(struct nss_cmd_ctx *cmdctx,
+ struct getent_ctx *netgr)
+{
+ struct cli_ctx *client = cmdctx->cctx;
+ uint8_t *body;
+ size_t blen;
+ uint32_t num;
+ errno_t ret;
+
+ /* get max num of entries to return in one call */
+ sss_packet_get_body(client->creq->in, &body, &blen);
+ if (blen != sizeof(uint32_t)) {
+ return EINVAL;
+ }
+ num = *((uint32_t *)body);
+
+ /* create response packet */
+ ret = sss_packet_new(client->creq, 0,
+ sss_packet_get_cmd(client->creq->in),
+ &client->creq->out);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (!netgr->triples || netgr->triples[0] == NULL) {
+ /* No entries */
+ DEBUG(5, ("No triples found\n"));
+ ret = fill_empty(client->creq->out);
+ if (ret != EOK) {
+ return nss_cmd_done(cmdctx, ret);
+ }
+ goto done;
+ }
+
+ ret = nss_cmd_retnetgrent(client, netgr->triples, num);
+
+done:
+ sss_packet_set_error(client->creq->out, ret);
+ sss_cmd_done(client, cmdctx);
+
+ return EOK;
+}
+
+static errno_t nss_cmd_retnetgrent(struct cli_ctx *client,
+ struct sysdb_netgroup_ctx **triples,
+ int count)
+{
+ size_t len;
+ size_t hostlen = 0;
+ size_t userlen = 0;
+ size_t domainlen = 0;
+ uint8_t *body;
+ size_t blen, rp;
+ errno_t ret;
+ struct sss_packet *packet = client->creq->out;
+ int num, start;
+
+ /* first 2 fields (len and reserved), filled up later */
+ rp = 2*sizeof(uint32_t);
+ ret = sss_packet_grow(packet, rp);
+ if (ret != EOK) return ret;
+
+ start = client->netgrent_cur;
+ num = 0;
+ while (triples[client->netgrent_cur] &&
+ (client->netgrent_cur - start) < count) {
+ hostlen = 1;
+ if (triples[client->netgrent_cur]->hostname) {
+ hostlen += strlen(triples[client->netgrent_cur]->hostname);
+ }
+
+ userlen = 1;
+ if (triples[client->netgrent_cur]->username) {
+ userlen += strlen(triples[client->netgrent_cur]->username);
+ }
+
+ domainlen = 1;
+ if (triples[client->netgrent_cur]->domainname) {
+ domainlen += strlen(triples[client->netgrent_cur]->domainname);
+ }
+
+ len = hostlen + userlen + domainlen;
+ ret = sss_packet_grow(packet, len);
+ if (ret != EOK) {
+ return ret;
+ }
+ sss_packet_get_body(packet, &body, &blen);
+
+ if (hostlen == 1) {
+ body[rp] = '\0';
+ } else {
+ memcpy(&body[rp],
+ triples[client->netgrent_cur]->hostname,
+ hostlen);
+ }
+ rp += hostlen;
+
+ if (userlen == 1) {
+ body[rp] = '\0';
+ } else {
+ memcpy(&body[rp],
+ triples[client->netgrent_cur]->username,
+ userlen);
+ }
+ rp += userlen;
+
+ if (domainlen == 1) {
+ body[rp] = '\0';
+ } else {
+ memcpy(&body[rp],
+ triples[client->netgrent_cur]->domainname,
+ domainlen);
+ }
+ rp += domainlen;
+
+ num++;
+ client->netgrent_cur++;
+ }
+
+ sss_packet_get_body(packet, &body, &blen);
+ ((uint32_t *)body)[0] = num; /* num results */
+ ((uint32_t *)body)[1] = 0; /* reserved */
+
+ return EOK;
+}
+
+int nss_cmd_endnetgrent(struct cli_ctx *client)
+{
+ errno_t ret;
+
+ /* create response packet */
+ ret = sss_packet_new(client->creq, 0,
+ sss_packet_get_cmd(client->creq->in),
+ &client->creq->out);
+
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* Reset the indices so that subsequent requests start at zero */
+ client->netgrent_cur = 0;
+ talloc_zfree(client->netgr_name);
+
+ sss_cmd_done(client, NULL);
+ return EOK;
+}
diff --git a/src/responder/nss/nsssrv_netgroup.h b/src/responder/nss/nsssrv_netgroup.h
new file mode 100644
index 00000000..ed345c43
--- /dev/null
+++ b/src/responder/nss/nsssrv_netgroup.h
@@ -0,0 +1,34 @@
+/*
+ SSSD
+
+ nssrv_netgroup.h
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2010 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ 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/>.
+*/
+
+#ifndef NSSRV_NETGROUP_H_
+#define NSSRV_NETGROUP_H_
+
+#define SSS_COL_NETGR 5000
+
+int nss_cmd_setnetgrent(struct cli_ctx *cctx);
+int nss_cmd_getnetgrent(struct cli_ctx *cctx);
+int nss_cmd_endnetgrent(struct cli_ctx *cctx);
+
+#endif /* NSSRV_NETGROUP_H_ */