Line data Source code
1 : /*
2 : SSSD
3 :
4 : nsssrv_netgroup.c
5 :
6 : Authors:
7 : Stephen Gallagher <sgallagh@redhat.com>
8 :
9 : Copyright (C) 2010 Red Hat
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 :
26 : #include "util/util.h"
27 : #include "responder/nss/nsssrv.h"
28 : #include "responder/nss/nsssrv_private.h"
29 : #include "responder/nss/nsssrv_netgroup.h"
30 : #include "responder/common/negcache.h"
31 : #include "confdb/confdb.h"
32 : #include "db/sysdb.h"
33 :
34 0 : static errno_t get_netgroup_entry(struct nss_ctx *nctx,
35 : char *name,
36 : struct getent_ctx **netgr)
37 : {
38 : hash_key_t key;
39 : hash_value_t value;
40 : int hret;
41 :
42 0 : key.type = HASH_KEY_STRING;
43 0 : key.str = name;
44 :
45 0 : hret = hash_lookup(nctx->netgroups, &key, &value);
46 0 : if (hret == HASH_SUCCESS) {
47 0 : *netgr = talloc_get_type(value.ptr, struct getent_ctx);
48 0 : return EOK;
49 0 : } else if (hret == HASH_ERROR_KEY_NOT_FOUND) {
50 0 : return ENOENT;
51 : }
52 :
53 0 : DEBUG(SSSDBG_CRIT_FAILURE,
54 : "Unexpected error reading from netgroup hash [%d][%s]\n",
55 : hret, hash_error_string(hret));
56 0 : return EIO;
57 : }
58 :
59 : static int netgr_hash_remove(TALLOC_CTX *ctx);
60 0 : static errno_t set_netgroup_entry(struct nss_ctx *nctx,
61 : struct getent_ctx *netgr)
62 : {
63 : hash_key_t key;
64 : hash_value_t value;
65 : int hret;
66 :
67 0 : if (netgr->name == NULL) {
68 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing netgroup name.\n");
69 0 : return EINVAL;
70 : }
71 : /* Add this entry to the hash table */
72 0 : key.type = HASH_KEY_STRING;
73 0 : key.str = netgr->name;
74 0 : value.type = HASH_VALUE_PTR;
75 0 : value.ptr = netgr;
76 0 : hret = hash_enter(nctx->netgroups, &key, &value);
77 0 : if (hret != EOK) {
78 0 : DEBUG(SSSDBG_FATAL_FAILURE,
79 : "Unable to add hash table entry for [%s]\n", key.str);
80 0 : DEBUG(SSSDBG_CONF_SETTINGS,
81 : "Hash error [%d][%s]\n", hret, hash_error_string(hret));
82 0 : return EIO;
83 : }
84 0 : talloc_steal(nctx->netgroups, netgr);
85 0 : talloc_set_destructor((TALLOC_CTX *) netgr, netgr_hash_remove);
86 :
87 0 : return EOK;
88 : }
89 :
90 : static struct tevent_req *setnetgrent_send(TALLOC_CTX *mem_ctx,
91 : const char *rawname,
92 : struct nss_cmd_ctx *cmdctx);
93 : static void nss_cmd_setnetgrent_done(struct tevent_req *req);
94 0 : int nss_cmd_setnetgrent(struct cli_ctx *client)
95 : {
96 : struct nss_cmd_ctx *cmdctx;
97 : struct tevent_req *req;
98 : const char *rawname;
99 : uint8_t *body;
100 : size_t blen;
101 0 : errno_t ret = EOK;
102 :
103 : /* Reset the result cursor to zero */
104 0 : client->netgrent_cur = 0;
105 :
106 0 : cmdctx = talloc_zero(client, struct nss_cmd_ctx);
107 0 : if (!cmdctx) {
108 0 : return ENOMEM;
109 : }
110 0 : cmdctx->cctx = client;
111 :
112 : /* get netgroup name to query */
113 0 : sss_packet_get_body(client->creq->in, &body, &blen);
114 :
115 : /* if not terminated fail */
116 0 : if (body[blen -1] != '\0') {
117 0 : ret = EINVAL;
118 0 : goto done;
119 : }
120 :
121 : /* If the body isn't valid UTF-8, fail */
122 0 : if (!sss_utf8_check(body, blen -1)) {
123 0 : ret = EINVAL;
124 0 : goto done;
125 : }
126 :
127 0 : rawname = (const char *)body;
128 :
129 0 : req = setnetgrent_send(cmdctx, rawname, cmdctx);
130 0 : if (!req) {
131 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Fatal error calling setnetgrent_send\n");
132 0 : ret = EIO;
133 0 : goto done;
134 : }
135 0 : tevent_req_set_callback(req, nss_cmd_setnetgrent_done, cmdctx);
136 :
137 : done:
138 0 : return nss_cmd_done(cmdctx, ret);
139 : }
140 :
141 0 : static int netgr_hash_remove(TALLOC_CTX *ctx)
142 : {
143 : int hret;
144 : hash_key_t key;
145 0 : struct getent_ctx *netgr =
146 : talloc_get_type(ctx, struct getent_ctx);
147 :
148 0 : if (netgr->lookup_table == NULL) {
149 0 : DEBUG(SSSDBG_TRACE_LIBS, "netgroup [%s] was already removed\n",
150 : netgr->name);
151 0 : return EOK;
152 : }
153 :
154 0 : key.type = HASH_KEY_STRING;
155 0 : key.str = netgr->name;
156 :
157 : /* Remove the netgroup result object from the lookup table */
158 0 : hret = hash_delete(netgr->lookup_table, &key);
159 0 : if (hret != HASH_SUCCESS) {
160 0 : DEBUG(SSSDBG_FATAL_FAILURE,
161 : "Could not remove key [%s] from table! [%d][%s]\n",
162 : netgr->name, hret, hash_error_string(hret));
163 0 : return -1;
164 : }
165 0 : return 0;
166 : }
167 :
168 : struct setnetgrent_ctx {
169 : struct nss_ctx *nctx;
170 : struct nss_cmd_ctx *cmdctx;
171 : struct nss_dom_ctx *dctx;
172 : char *netgr_shortname;
173 : struct getent_ctx *netgr;
174 : const char *rawname;
175 : };
176 : static errno_t setnetgrent_retry(struct tevent_req *req);
177 : static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx);
178 0 : static struct tevent_req *setnetgrent_send(TALLOC_CTX *mem_ctx,
179 : const char *rawname,
180 : struct nss_cmd_ctx *cmdctx)
181 : {
182 : char *domname;
183 : errno_t ret;
184 : struct tevent_req *req;
185 : struct setnetgrent_ctx *state;
186 : struct nss_dom_ctx *dctx;
187 :
188 0 : struct cli_ctx *client = cmdctx->cctx;
189 0 : struct nss_ctx *nctx =
190 0 : talloc_get_type(client->rctx->pvt_ctx, struct nss_ctx);
191 :
192 0 : req = tevent_req_create(mem_ctx, &state, struct setnetgrent_ctx);
193 0 : if (!req) {
194 0 : DEBUG(SSSDBG_FATAL_FAILURE,
195 : "Could not create tevent request for setnetgrent\n");
196 0 : return NULL;
197 : }
198 :
199 0 : state->nctx = nctx;
200 0 : state->cmdctx = cmdctx;
201 0 : state->rawname = rawname;
202 :
203 0 : state->dctx = talloc_zero(state, struct nss_dom_ctx);
204 0 : if (!state->dctx) {
205 0 : ret = ENOMEM;
206 0 : goto error;
207 : }
208 0 : dctx = state->dctx;
209 0 : dctx->cmdctx = state->cmdctx;
210 :
211 0 : ret = sss_parse_name_for_domains(state, client->rctx->domains,
212 : NULL, rawname,
213 0 : &domname, &state->netgr_shortname);
214 0 : if (ret != EOK) {
215 0 : DEBUG(SSSDBG_OP_FAILURE, "Invalid name received [%s]\n", rawname);
216 0 : goto error;
217 : }
218 :
219 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for netgroup [%s] from [%s]\n",
220 : state->netgr_shortname, domname?domname:"<ALL>");
221 :
222 0 : if (domname) {
223 0 : dctx->domain = responder_get_domain(client->rctx, domname);
224 0 : if (!dctx->domain) {
225 0 : ret = EINVAL;
226 0 : goto error;
227 : }
228 :
229 : /* Save the netgroup name for getnetgrent */
230 0 : client->netgr_name = talloc_strdup(client, state->netgr_shortname);
231 0 : if (!client->netgr_name) {
232 0 : ret = ENOMEM;
233 0 : goto error;
234 : }
235 : } else {
236 : /* this is a multidomain search */
237 0 : dctx->domain = client->rctx->domains;
238 0 : cmdctx->check_next = true;
239 :
240 : /* Save the netgroup name for getnetgrent */
241 0 : client->netgr_name = talloc_strdup(client, rawname);
242 0 : if (!client->netgr_name) {
243 0 : ret = ENOMEM;
244 0 : goto error;
245 : }
246 : }
247 :
248 0 : ret = setnetgrent_retry(req);
249 0 : if (ret != EOK) {
250 0 : if (ret == EAGAIN) {
251 : /* We need to reenter the mainloop
252 : * We may be refreshing the cache
253 : */
254 0 : return req;
255 : }
256 :
257 0 : goto error;
258 : }
259 :
260 0 : return req;
261 :
262 : error:
263 0 : tevent_req_error(req, ret);
264 0 : tevent_req_post(req, cmdctx->cctx->ev);
265 0 : return req;
266 : }
267 :
268 0 : static errno_t setnetgrent_retry(struct tevent_req *req)
269 : {
270 : errno_t ret;
271 : struct setent_step_ctx *step_ctx;
272 : struct setnetgrent_ctx *state;
273 : struct cli_ctx *client;
274 : struct nss_ctx *nctx;
275 : struct nss_cmd_ctx *cmdctx;
276 : struct nss_dom_ctx *dctx;
277 :
278 0 : state = tevent_req_data(req, struct setnetgrent_ctx);
279 0 : dctx = state->dctx;
280 0 : cmdctx = state->cmdctx;
281 0 : client = cmdctx->cctx;
282 0 : nctx = talloc_get_type(client->rctx->pvt_ctx, struct nss_ctx);
283 :
284 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
285 :
286 : /* Is the result context already available?
287 : * Check for existing lookups for this netgroup
288 : */
289 0 : ret = get_netgroup_entry(nctx, client->netgr_name, &state->netgr);
290 0 : if (ret == EOK) {
291 : /* Another process already requested this netgroup
292 : * Check whether it's ready for processing.
293 : */
294 0 : if (state->netgr->ready) {
295 0 : if (state->netgr->found) {
296 : /* Ready to process results */
297 0 : tevent_req_done(req);
298 : } else {
299 0 : tevent_req_error(req, ENOENT);
300 : }
301 :
302 0 : tevent_req_post(req, nctx->rctx->ev);
303 : /* Return EOK, otherwise this will be treated as
304 : * an error
305 : */
306 0 : return EOK;
307 : }
308 :
309 : /* Result object is still being constructed
310 : * Register for notification when it's ready
311 : */
312 0 : ret = nss_setent_add_ref(state, state->netgr, req);
313 0 : if (ret != EOK) {
314 0 : goto done;
315 : }
316 : /* Will return control below */
317 0 : } else if (ret == ENOENT) {
318 : /* This is the first attempt to request this netgroup
319 : */
320 0 : state->netgr = talloc_zero(nctx, struct getent_ctx);
321 0 : if (!state->netgr) {
322 0 : ret = ENOMEM;
323 0 : goto done;
324 : }
325 0 : dctx->netgr = state->netgr;
326 :
327 : /* Save the name used for the lookup table
328 : * so we can remove it in the destructor
329 : */
330 0 : state->netgr->name = talloc_strdup(state->netgr,
331 0 : client->netgr_name);
332 0 : if (!state->netgr->name) {
333 0 : talloc_free(state->netgr);
334 0 : ret = ENOMEM;
335 0 : goto done;
336 : }
337 :
338 0 : state->netgr->lookup_table = nctx->netgroups;
339 :
340 : /* Add a reference for ourselves */
341 0 : ret = nss_setent_add_ref(state, state->netgr, req);
342 0 : if (ret != EOK) {
343 0 : talloc_free(state->netgr);
344 0 : goto done;
345 : }
346 :
347 0 : ret = set_netgroup_entry(nctx, state->netgr);
348 0 : if (ret != EOK) {
349 0 : DEBUG(SSSDBG_CRIT_FAILURE, "set_netgroup_entry failed.\n");
350 0 : talloc_free(state->netgr);
351 0 : goto done;
352 : }
353 :
354 : /* Perform lookup */
355 0 : step_ctx = talloc_zero(state->netgr, struct setent_step_ctx);
356 0 : if (!step_ctx) {
357 0 : ret = ENOMEM;
358 0 : goto done;
359 : }
360 :
361 : /* Steal the dom_ctx onto the step_ctx so it doesn't go out of scope if
362 : * this request is canceled while other requests are in-progress.
363 : */
364 0 : step_ctx->dctx = talloc_steal(step_ctx, state->dctx);
365 0 : step_ctx->nctx = state->nctx;
366 0 : step_ctx->getent_ctx = state->netgr;
367 0 : step_ctx->rctx = client->rctx;
368 0 : step_ctx->check_next = cmdctx->check_next;
369 0 : step_ctx->name =
370 0 : talloc_strdup(step_ctx, state->netgr->name);
371 0 : if (!step_ctx->name) {
372 0 : ret = ENOMEM;
373 0 : goto done;
374 : }
375 :
376 0 : ret = lookup_netgr_step(step_ctx);
377 0 : switch (ret) {
378 : case EOK:
379 0 : break;
380 : case EMSGSIZE:
381 0 : state->netgr->ready = true;
382 0 : ret = ENOENT;
383 : /* FALLTHROUGH */
384 : default:
385 0 : goto done;
386 : }
387 0 : tevent_req_done(req);
388 0 : tevent_req_post(req, cmdctx->cctx->ev);
389 : /* Will return control below */
390 : } else {
391 : /* Unexpected error from hash_lookup */
392 0 : goto done;
393 : }
394 :
395 0 : ret = EOK;
396 :
397 : done:
398 0 : return ret;
399 : }
400 :
401 : static void lookup_netgr_dp_callback(uint16_t err_maj, uint32_t err_min,
402 : const char *err_msg, void *ptr);
403 :
404 : static void setnetgrent_result_timeout(struct tevent_context *ev,
405 : struct tevent_timer *te,
406 : struct timeval current_time,
407 : void *pvt);
408 :
409 : /* Set up a lifetime timer for this result object
410 : * We don't want this result object to outlive the
411 : * entry cache refresh timeout
412 : */
413 0 : static void set_netgr_lifetime(uint32_t lifetime,
414 : struct setent_step_ctx *step_ctx,
415 : struct getent_ctx *netgr)
416 : {
417 : struct timeval tv;
418 : struct tevent_timer *te;
419 :
420 0 : tv = tevent_timeval_current_ofs(lifetime, 0);
421 0 : te = tevent_add_timer(step_ctx->nctx->rctx->ev,
422 : step_ctx->nctx->gctx, tv,
423 : setnetgrent_result_timeout,
424 : netgr);
425 0 : if (!te) {
426 0 : DEBUG(SSSDBG_FATAL_FAILURE,
427 : "Could not set up life timer for setnetgrent result object. "
428 : "Entries may become stale.\n");
429 : }
430 0 : }
431 :
432 : /* Create dummy netgroup to speed up repeated negative queries */
433 0 : static errno_t create_negcache_netgr(struct setent_step_ctx *step_ctx)
434 : {
435 : errno_t ret;
436 : struct getent_ctx *netgr;
437 :
438 0 : netgr = talloc_zero(step_ctx->nctx, struct getent_ctx);
439 0 : if (netgr == NULL) {
440 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
441 0 : ret = ENOMEM;
442 0 : goto done;
443 : } else {
444 0 : netgr->ready = true;
445 0 : netgr->found = false;
446 0 : netgr->entries = NULL;
447 0 : netgr->lookup_table = step_ctx->nctx->netgroups;
448 0 : netgr->name = talloc_strdup(netgr, step_ctx->name);
449 0 : if (netgr->name == NULL) {
450 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
451 0 : ret = ENOMEM;
452 0 : goto done;
453 : }
454 :
455 0 : ret = set_netgroup_entry(step_ctx->nctx, netgr);
456 0 : if (ret != EOK) {
457 0 : DEBUG(SSSDBG_CRIT_FAILURE, "set_netgroup_entry failed.\n");
458 0 : goto done;
459 : }
460 0 : set_netgr_lifetime(step_ctx->nctx->neg_timeout, step_ctx, netgr);
461 : }
462 :
463 : done:
464 0 : if (ret != EOK) {
465 0 : talloc_free(netgr);
466 : }
467 0 : return ret;
468 : }
469 :
470 0 : static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
471 : {
472 : errno_t ret;
473 0 : struct sss_domain_info *dom = step_ctx->dctx->domain;
474 : struct getent_ctx *netgr;
475 0 : char *name = NULL;
476 : uint32_t lifetime;
477 :
478 : /* Check each domain for this netgroup name */
479 0 : while (dom) {
480 : /* Netgroups are a special case. We have to ignore the
481 : * fully-qualified name requirement because memberNisNetgroup
482 : * entries do not have fully-qualified components and we need
483 : * to be able to always check them. So unlike the other
484 : * maps, here we avoid skipping over fully-qualified domains.
485 : */
486 :
487 0 : if (dom != step_ctx->dctx->domain) {
488 : /* make sure we reset the check_provider flag when we check
489 : * a new domain */
490 0 : step_ctx->dctx->check_provider =
491 0 : NEED_CHECK_PROVIDER(dom->provider);
492 : }
493 :
494 : /* make sure to update the dctx if we changed domain */
495 0 : step_ctx->dctx->domain = dom;
496 :
497 0 : talloc_free(name);
498 0 : name = sss_get_cased_name(step_ctx, step_ctx->name,
499 0 : dom->case_sensitive);
500 0 : if (!name) {
501 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sss_get_cased_name failed\n");
502 0 : ret = ENOMEM;
503 0 : goto done;
504 : }
505 :
506 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for [%s@%s]\n",
507 : name, dom->name);
508 0 : if (dom->sysdb == NULL) {
509 0 : DEBUG(SSSDBG_FATAL_FAILURE,
510 : "Fatal: Sysdb CTX not found for this domain!\n");
511 0 : ret = EIO;
512 0 : goto done;
513 : }
514 :
515 : /* Look up the netgroup in the cache */
516 0 : ret = sysdb_getnetgr(step_ctx->dctx, dom, name, &step_ctx->dctx->res);
517 0 : if (ret == EOK) {
518 0 : if (step_ctx->dctx->res->count > 1) {
519 0 : DEBUG(SSSDBG_FATAL_FAILURE,
520 : "getnetgr call returned more than one result !?!\n");
521 0 : ret = EMSGSIZE;
522 0 : goto done;
523 : }
524 0 : } else if (ret == ENOENT) {
525 : /* This netgroup was not found in this domain */
526 0 : if (!step_ctx->dctx->check_provider) {
527 0 : if (step_ctx->check_next) {
528 0 : dom = get_next_domain(dom, false);
529 0 : continue;
530 : } else {
531 0 : break;
532 : }
533 : }
534 : } else {
535 0 : DEBUG(SSSDBG_CRIT_FAILURE,
536 : "Failed to make request to our cache!\n");
537 0 : ret = EIO;
538 0 : goto done;
539 : }
540 :
541 0 : ret = get_netgroup_entry(step_ctx->nctx, step_ctx->name,
542 : &netgr);
543 0 : if (ret != EOK) {
544 : /* Something really bad happened! */
545 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Netgroup entry was lost!\n");
546 0 : goto done;
547 : }
548 :
549 : /* Convert the result to a list of entries */
550 0 : ret = sysdb_netgr_to_entries(netgr, step_ctx->dctx->res,
551 0 : &netgr->entries);
552 0 : if (ret == ENOENT) {
553 : /* This netgroup was not found in this domain */
554 0 : DEBUG(SSSDBG_OP_FAILURE, "No results for netgroup %s (domain %s)\n",
555 : name, dom->name);
556 :
557 0 : if (!step_ctx->dctx->check_provider) {
558 0 : if (step_ctx->check_next) {
559 0 : dom = get_next_domain(dom, false);
560 0 : continue;
561 : }
562 0 : else break;
563 : }
564 0 : ret = EOK;
565 : }
566 :
567 0 : if (ret != EOK) {
568 0 : DEBUG(SSSDBG_CRIT_FAILURE,
569 : "Failed to convert results into entries\n");
570 0 : netgr->ready = true;
571 0 : netgr->found = false;
572 0 : set_netgr_lifetime(step_ctx->nctx->neg_timeout, step_ctx, netgr);
573 0 : ret = EIO;
574 0 : goto done;
575 : }
576 :
577 : /* if this is a caching provider (or if we haven't checked the cache
578 : * yet) then verify that the cache is uptodate */
579 0 : if (step_ctx->dctx->check_provider) {
580 0 : ret = check_cache(step_ctx->dctx,
581 : step_ctx->nctx,
582 0 : step_ctx->dctx->res,
583 : SSS_DP_NETGR,
584 : name, 0, NULL,
585 : lookup_netgr_dp_callback,
586 : step_ctx);
587 0 : if (ret != EOK) {
588 : /* May return EAGAIN legitimately to indicate that
589 : * we need to reenter the mainloop
590 : */
591 0 : goto done;
592 : }
593 : }
594 :
595 : /* Results found */
596 0 : DEBUG(SSSDBG_TRACE_FUNC, "Returning info for netgroup [%s@%s]\n",
597 : name, dom->name);
598 0 : netgr->ready = true;
599 0 : netgr->found = true;
600 0 : if (step_ctx->nctx->cache_refresh_percent) {
601 0 : lifetime = dom->netgroup_timeout *
602 0 : (step_ctx->nctx->cache_refresh_percent / 100.0);
603 : } else {
604 0 : lifetime = dom->netgroup_timeout;
605 : }
606 0 : if (lifetime < 10) lifetime = 10;
607 0 : set_netgr_lifetime(lifetime, step_ctx, netgr);
608 :
609 0 : ret = EOK;
610 0 : goto done;
611 : }
612 :
613 : /* If we've gotten here, then no domain contained this netgroup */
614 0 : DEBUG(SSSDBG_MINOR_FAILURE,
615 : "No matching domain found for [%s], fail!\n", step_ctx->name);
616 :
617 0 : ret = create_negcache_netgr(step_ctx);
618 0 : if (ret != EOK) {
619 : /* Failure can be ignored, because at worst, there will be a slowdown
620 : * at the next lookup
621 : */
622 0 : DEBUG(SSSDBG_TRACE_ALL,
623 : "create_negcache_netgr failed with: %d:[%s], ignored.\n",
624 : ret, sss_strerror(ret));
625 : }
626 0 : ret = ENOENT;
627 :
628 : done:
629 0 : talloc_free(name);
630 0 : return ret;
631 : }
632 :
633 0 : static void lookup_netgr_dp_callback(uint16_t err_maj, uint32_t err_min,
634 : const char *err_msg, void *ptr)
635 : {
636 0 : struct setent_step_ctx *step_ctx =
637 : talloc_get_type(ptr, struct setent_step_ctx);
638 0 : struct nss_dom_ctx *dctx = step_ctx->dctx;
639 0 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
640 : int ret;
641 :
642 0 : if (err_maj) {
643 0 : DEBUG(SSSDBG_OP_FAILURE,
644 : "Unable to get information from Data Provider\n"
645 : "Error: %u, %u, %s\n"
646 : "Will try to return what we have in cache\n",
647 : (unsigned int)err_maj, (unsigned int)err_min, err_msg);
648 : /* Loop to the next domain if possible */
649 0 : if (cmdctx->check_next && get_next_domain(dctx->domain, false)) {
650 0 : dctx->domain = get_next_domain(dctx->domain, false);
651 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
652 : }
653 : }
654 :
655 : /* ok the backend returned, search to see if we have updated results */
656 0 : ret = lookup_netgr_step(step_ctx);
657 0 : if (ret == EAGAIN) {
658 0 : return;
659 : }
660 :
661 : /* We have results to return */
662 0 : if (ret == EOK) {
663 0 : nss_setent_notify_done(dctx->netgr);
664 : } else {
665 0 : nss_setent_notify_error(dctx->netgr, ret);
666 : }
667 : }
668 :
669 0 : static void setnetgrent_result_timeout(struct tevent_context *ev,
670 : struct tevent_timer *te,
671 : struct timeval current_time,
672 : void *pvt)
673 : {
674 0 : struct getent_ctx *netgr =
675 : talloc_get_type(pvt, struct getent_ctx);
676 :
677 : /* Free the netgroup result context
678 : * The destructor for the netgroup will remove itself
679 : * from the hash table
680 : *
681 : * If additional getnetgrent() requests come in, they
682 : * will invoke an implicit setnetgrent() call and
683 : * refresh the result object
684 : */
685 0 : talloc_free(netgr);
686 0 : }
687 :
688 0 : static errno_t setnetgrent_recv(struct tevent_req *req)
689 : {
690 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
691 0 : return EOK;
692 : }
693 :
694 0 : static void nss_cmd_setnetgrent_done(struct tevent_req *req)
695 : {
696 : errno_t reqret;
697 : errno_t ret;
698 : struct sss_packet *packet;
699 : uint8_t *body;
700 : size_t blen;
701 :
702 0 : struct nss_cmd_ctx *cmdctx =
703 0 : tevent_req_callback_data(req, struct nss_cmd_ctx);
704 :
705 0 : reqret = setnetgrent_recv(req);
706 0 : talloc_zfree(req);
707 0 : if (reqret != EOK && reqret != ENOENT) {
708 0 : DEBUG(SSSDBG_CRIT_FAILURE, "setnetgrent failed\n");
709 0 : nss_cmd_done(cmdctx, reqret);
710 0 : return;
711 : }
712 :
713 : /* Either we succeeded or no domains were eligible */
714 0 : ret = sss_packet_new(cmdctx->cctx->creq, 0,
715 0 : sss_packet_get_cmd(cmdctx->cctx->creq->in),
716 0 : &cmdctx->cctx->creq->out);
717 0 : if (ret == EOK) {
718 0 : if (reqret == ENOENT) {
719 : /* Notify the caller that this entry wasn't found */
720 0 : sss_cmd_empty_packet(cmdctx->cctx->creq->out);
721 : } else {
722 0 : packet = cmdctx->cctx->creq->out;
723 0 : ret = sss_packet_grow(packet, 2*sizeof(uint32_t));
724 0 : if (ret != EOK) {
725 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Couldn't grow the packet\n");
726 0 : NSS_CMD_FATAL_ERROR(cmdctx);
727 : }
728 :
729 0 : sss_packet_get_body(packet, &body, &blen);
730 :
731 : /* Got some results. */
732 0 : SAFEALIGN_SETMEM_UINT32(body, 1, NULL);
733 :
734 : /* reserved */
735 0 : SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL);
736 : }
737 :
738 0 : sss_cmd_done(cmdctx->cctx, cmdctx);
739 0 : return;
740 : }
741 :
742 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error creating packet\n");
743 : }
744 :
745 : static void setnetgrent_implicit_done(struct tevent_req *req);
746 : static errno_t nss_cmd_getnetgrent_process(struct nss_cmd_ctx *cmdctx,
747 : struct getent_ctx *netgr);
748 0 : int nss_cmd_getnetgrent(struct cli_ctx *client)
749 : {
750 : errno_t ret;
751 : struct nss_ctx *nctx;
752 : struct nss_cmd_ctx *cmdctx;
753 : struct getent_ctx *netgr;
754 : struct tevent_req *req;
755 :
756 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Requesting netgroup data\n");
757 :
758 0 : cmdctx = talloc_zero(client, struct nss_cmd_ctx);
759 0 : if (!cmdctx) {
760 0 : return ENOMEM;
761 : }
762 0 : cmdctx->cctx = client;
763 :
764 0 : nctx = talloc_get_type(client->rctx->pvt_ctx, struct nss_ctx);
765 :
766 0 : if (!client->netgr_name) {
767 : /* Tried to run getnetgrent without a preceding
768 : * setnetgrent. There is no way to determine which
769 : * netgroup is being requested.
770 : */
771 0 : return nss_cmd_done(cmdctx, EINVAL);
772 : }
773 :
774 : /* Look up the results from the hash */
775 0 : ret = get_netgroup_entry(nctx, client->netgr_name, &netgr);
776 0 : if (ret == ENOENT) {
777 : /* We need to invoke an implicit setnetgrent() to
778 : * wait for the result object to become available.
779 : */
780 :
781 0 : req = setnetgrent_send(cmdctx, client->netgr_name, cmdctx);
782 0 : if (!req) {
783 0 : return nss_cmd_done(cmdctx, EIO);
784 : }
785 0 : tevent_req_set_callback(req, setnetgrent_implicit_done, cmdctx);
786 :
787 0 : return EOK;
788 0 : } else if (ret != EOK) {
789 0 : DEBUG(SSSDBG_CRIT_FAILURE, "An unexpected error occurred: [%d][%s]\n",
790 : ret, strerror(ret));
791 :
792 0 : return nss_cmd_done(cmdctx, ret);
793 : }
794 :
795 : /* Hash entry was found. Is it ready? */
796 0 : if (!netgr->ready) {
797 : /* We need to invoke an implicit setnetgrent() to
798 : * wait for the result object to become available.
799 : */
800 0 : req = setnetgrent_send(cmdctx, client->netgr_name, cmdctx);
801 0 : if (!req) {
802 0 : return nss_cmd_done(cmdctx, EIO);
803 : }
804 0 : tevent_req_set_callback(req, setnetgrent_implicit_done, cmdctx);
805 :
806 0 : return EOK;
807 0 : } else if (!netgr->found) {
808 0 : DEBUG(SSSDBG_TRACE_FUNC,
809 : "Results for [%s] not found.\n", client->netgr_name);
810 0 : return ENOENT;
811 : }
812 :
813 0 : DEBUG(SSSDBG_TRACE_FUNC,
814 : "Returning results for [%s]\n", client->netgr_name);
815 :
816 : /* Read the result strings */
817 0 : ret = nss_cmd_getnetgrent_process(cmdctx, netgr);
818 0 : if (ret != EOK) {
819 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed: [%d][%s]\n", ret, strerror(ret));
820 : }
821 0 : return ret;
822 : }
823 :
824 0 : static void setnetgrent_implicit_done(struct tevent_req *req)
825 : {
826 : errno_t ret;
827 : struct getent_ctx *netgr;
828 0 : struct nss_cmd_ctx *cmdctx =
829 0 : tevent_req_callback_data(req, struct nss_cmd_ctx);
830 0 : struct nss_ctx *nctx =
831 0 : talloc_get_type(cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx);
832 :
833 0 : ret = setnetgrent_recv(req);
834 0 : talloc_zfree(req);
835 :
836 : /* ENOENT is acceptable, it just means there were no values
837 : * to be returned. This will be handled gracefully in
838 : * nss_cmd_retnetgrent later
839 : */
840 0 : if (ret != EOK && ret != ENOENT) {
841 0 : DEBUG(SSSDBG_FATAL_FAILURE,
842 : "Implicit setnetgrent failed with unexpected error "
843 : "[%d][%s]\n", ret, strerror(ret));
844 0 : NSS_CMD_FATAL_ERROR(cmdctx);
845 : }
846 :
847 0 : if (ret == ENOENT) {
848 : /* No entries found for this netgroup */
849 0 : nss_cmd_done(cmdctx, ret);
850 0 : return;
851 : }
852 :
853 : /* Look up the results from the hash */
854 0 : ret = get_netgroup_entry(nctx, cmdctx->cctx->netgr_name, &netgr);
855 0 : if (ret == ENOENT) {
856 : /* Critical error. This should never happen */
857 0 : DEBUG(SSSDBG_FATAL_FAILURE,
858 : "Implicit setnetgrent returned success without creating "
859 : "result object.\n");
860 0 : NSS_CMD_FATAL_ERROR(cmdctx);
861 0 : } else if (ret != EOK) {
862 0 : DEBUG(SSSDBG_CRIT_FAILURE, "An unexpected error occurred: [%d][%s]\n",
863 : ret, strerror(ret));
864 :
865 0 : NSS_CMD_FATAL_ERROR(cmdctx);
866 : }
867 :
868 0 : if (!netgr->ready) {
869 : /* Critical error. This should never happen */
870 0 : DEBUG(SSSDBG_FATAL_FAILURE,
871 : "Implicit setnetgrent returned success without creating "
872 : "result object.\n");
873 0 : NSS_CMD_FATAL_ERROR(cmdctx);
874 : }
875 :
876 0 : ret = nss_cmd_getnetgrent_process(cmdctx, netgr);
877 0 : if (ret != EOK) {
878 0 : DEBUG(SSSDBG_FATAL_FAILURE,
879 : "Immediate retrieval failed with unexpected error "
880 : "[%d][%s]\n", ret, strerror(ret));
881 0 : NSS_CMD_FATAL_ERROR(cmdctx);
882 : }
883 : }
884 :
885 : static errno_t nss_cmd_retnetgrent(struct cli_ctx *client,
886 : struct sysdb_netgroup_ctx **entries,
887 : int num);
888 0 : static errno_t nss_cmd_getnetgrent_process(struct nss_cmd_ctx *cmdctx,
889 : struct getent_ctx *netgr)
890 : {
891 0 : struct cli_ctx *client = cmdctx->cctx;
892 : uint8_t *body;
893 : size_t blen;
894 : uint32_t num;
895 : errno_t ret;
896 :
897 : /* get max num of entries to return in one call */
898 0 : sss_packet_get_body(client->creq->in, &body, &blen);
899 0 : if (blen != sizeof(uint32_t)) {
900 0 : return EINVAL;
901 : }
902 0 : SAFEALIGN_COPY_UINT32(&num, body, NULL);
903 :
904 : /* create response packet */
905 0 : ret = sss_packet_new(client->creq, 0,
906 0 : sss_packet_get_cmd(client->creq->in),
907 0 : &client->creq->out);
908 0 : if (ret != EOK) {
909 0 : return ret;
910 : }
911 :
912 0 : if (!netgr->entries || netgr->entries[0] == NULL) {
913 : /* No entries */
914 0 : DEBUG(SSSDBG_FUNC_DATA, "No entries found\n");
915 0 : ret = sss_cmd_empty_packet(client->creq->out);
916 0 : if (ret != EOK) {
917 0 : return nss_cmd_done(cmdctx, ret);
918 : }
919 0 : goto done;
920 : }
921 :
922 0 : ret = nss_cmd_retnetgrent(client, netgr->entries, num);
923 :
924 : done:
925 0 : sss_packet_set_error(client->creq->out, ret);
926 0 : sss_cmd_done(client, cmdctx);
927 :
928 0 : return EOK;
929 : }
930 :
931 0 : static errno_t nss_cmd_retnetgrent(struct cli_ctx *client,
932 : struct sysdb_netgroup_ctx **entries,
933 : int count)
934 : {
935 : size_t len;
936 0 : size_t hostlen = 0;
937 0 : size_t userlen = 0;
938 0 : size_t domainlen = 0;
939 0 : size_t grouplen = 0;
940 : uint8_t *body;
941 : size_t blen, rp;
942 : errno_t ret;
943 0 : struct sss_packet *packet = client->creq->out;
944 : int num, start;
945 :
946 : /* first 2 fields (len and reserved), filled up later */
947 0 : rp = 2*sizeof(uint32_t);
948 0 : ret = sss_packet_grow(packet, rp);
949 0 : if (ret != EOK) return ret;
950 :
951 0 : start = client->netgrent_cur;
952 0 : num = 0;
953 0 : while (entries[client->netgrent_cur] &&
954 0 : (client->netgrent_cur - start) < count) {
955 0 : if (entries[client->netgrent_cur]->type == SYSDB_NETGROUP_TRIPLE_VAL) {
956 0 : hostlen = 1;
957 0 : if (entries[client->netgrent_cur]->value.triple.hostname) {
958 0 : hostlen += strlen(entries[client->netgrent_cur]->value.triple.hostname);
959 : }
960 :
961 0 : userlen = 1;
962 0 : if (entries[client->netgrent_cur]->value.triple.username) {
963 0 : userlen += strlen(entries[client->netgrent_cur]->value.triple.username);
964 : }
965 :
966 0 : domainlen = 1;
967 0 : if (entries[client->netgrent_cur]->value.triple.domainname) {
968 0 : domainlen += strlen(entries[client->netgrent_cur]->value.triple.domainname);
969 : }
970 :
971 0 : len = sizeof(uint32_t) + hostlen + userlen + domainlen;
972 0 : ret = sss_packet_grow(packet, len);
973 0 : if (ret != EOK) {
974 0 : return ret;
975 : }
976 0 : sss_packet_get_body(packet, &body, &blen);
977 :
978 0 : SAFEALIGN_SET_UINT32(&body[rp], SSS_NETGR_REP_TRIPLE, &rp);
979 :
980 0 : if (hostlen == 1) {
981 0 : body[rp] = '\0';
982 : } else {
983 0 : memcpy(&body[rp],
984 0 : entries[client->netgrent_cur]->value.triple.hostname,
985 : hostlen);
986 : }
987 0 : rp += hostlen;
988 :
989 0 : if (userlen == 1) {
990 0 : body[rp] = '\0';
991 : } else {
992 0 : memcpy(&body[rp],
993 0 : entries[client->netgrent_cur]->value.triple.username,
994 : userlen);
995 : }
996 0 : rp += userlen;
997 :
998 0 : if (domainlen == 1) {
999 0 : body[rp] = '\0';
1000 : } else {
1001 0 : memcpy(&body[rp],
1002 0 : entries[client->netgrent_cur]->value.triple.domainname,
1003 : domainlen);
1004 : }
1005 0 : rp += domainlen;
1006 0 : } else if (entries[client->netgrent_cur]->type == SYSDB_NETGROUP_GROUP_VAL) {
1007 0 : if (entries[client->netgrent_cur]->value.groupname == NULL ||
1008 0 : entries[client->netgrent_cur]->value.groupname[0] == '\0') {
1009 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1010 : "Empty netgroup member. Please check your cache.\n");
1011 0 : continue;
1012 : }
1013 :
1014 0 : grouplen = 1 + strlen(entries[client->netgrent_cur]->value.groupname);
1015 :
1016 0 : len = sizeof(uint32_t) + grouplen;
1017 :
1018 0 : ret = sss_packet_grow(packet, len);
1019 0 : if (ret != EOK) {
1020 0 : return ret;
1021 : }
1022 :
1023 0 : sss_packet_get_body(packet, &body, &blen);
1024 :
1025 0 : SAFEALIGN_SET_UINT32(&body[rp], SSS_NETGR_REP_GROUP, &rp);
1026 :
1027 0 : memcpy(&body[rp],
1028 0 : entries[client->netgrent_cur]->value.groupname,
1029 : grouplen);
1030 0 : rp += grouplen;
1031 : } else {
1032 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1033 : "Unexpected value type for netgroup entry. "
1034 : "Please check your cache.\n");
1035 0 : continue;
1036 : }
1037 :
1038 0 : num++;
1039 0 : client->netgrent_cur++;
1040 : }
1041 :
1042 0 : sss_packet_get_body(packet, &body, &blen);
1043 :
1044 : /* num results */
1045 0 : SAFEALIGN_COPY_UINT32(body, &num, NULL);
1046 :
1047 : /* reserved */
1048 0 : SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL);
1049 :
1050 0 : return EOK;
1051 : }
1052 :
1053 0 : int nss_cmd_endnetgrent(struct cli_ctx *client)
1054 : {
1055 : errno_t ret;
1056 :
1057 : /* create response packet */
1058 0 : ret = sss_packet_new(client->creq, 0,
1059 0 : sss_packet_get_cmd(client->creq->in),
1060 0 : &client->creq->out);
1061 :
1062 0 : if (ret != EOK) {
1063 0 : return ret;
1064 : }
1065 :
1066 : /* Reset the indices so that subsequent requests start at zero */
1067 0 : client->netgrent_cur = 0;
1068 0 : talloc_zfree(client->netgr_name);
1069 :
1070 0 : sss_cmd_done(client, NULL);
1071 0 : return EOK;
1072 : }
1073 :
1074 : void
1075 0 : netgroup_hash_delete_cb(hash_entry_t *item,
1076 : hash_destroy_enum deltype, void *pvt)
1077 : {
1078 : struct getent_ctx *netgr;
1079 :
1080 0 : if (deltype != HASH_ENTRY_DESTROY) {
1081 0 : return;
1082 : }
1083 :
1084 0 : netgr = talloc_get_type(item->value.ptr, struct getent_ctx);
1085 0 : if (!netgr) {
1086 0 : DEBUG(SSSDBG_OP_FAILURE, "Invalid netgroup\n");
1087 0 : return;
1088 : }
1089 :
1090 : /* So that the destructor wouldn't attempt to remove the netgroup from hash
1091 : * table */
1092 0 : netgr->lookup_table = NULL;
1093 : }
1094 :
1095 0 : errno_t nss_orphan_netgroups(struct nss_ctx *nctx)
1096 : {
1097 : int hret;
1098 : unsigned long mcount;
1099 : unsigned long i;
1100 : hash_key_t *netgroups;
1101 :
1102 0 : if (!nctx || !nctx->netgroups) {
1103 0 : return EINVAL;
1104 : }
1105 :
1106 0 : hret = hash_keys(nctx->netgroups, &mcount, &netgroups);
1107 0 : if (hret != HASH_SUCCESS) {
1108 0 : return EIO;
1109 : }
1110 :
1111 0 : DEBUG(SSSDBG_TRACE_FUNC, "Removing netgroups from memory cache.\n");
1112 :
1113 0 : for (i = 0; i < mcount; i++) {
1114 : /* netgroup entry will be deleted by setnetgrent_result_timeout */
1115 0 : hret = hash_delete(nctx->netgroups, &netgroups[i]);
1116 0 : if (hret != HASH_SUCCESS) {
1117 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Could not delete key from hash\n");
1118 0 : continue;
1119 : }
1120 : }
1121 :
1122 0 : return EOK;
1123 : }
|