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 : uint32_t lifetime;
437 : struct getent_ctx *netgr;
438 :
439 : /* Is there already netgroup with such name? */
440 0 : ret = get_netgroup_entry(step_ctx->nctx, step_ctx->name,
441 : &netgr);
442 0 : if (ret != EOK || netgr == NULL) {
443 :
444 0 : netgr = talloc_zero(step_ctx->nctx, struct getent_ctx);
445 0 : if (netgr == NULL) {
446 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
447 0 : ret = ENOMEM;
448 0 : goto done;
449 : }
450 :
451 0 : netgr->entries = NULL;
452 0 : netgr->lookup_table = step_ctx->nctx->netgroups;
453 0 : netgr->name = talloc_strdup(netgr, step_ctx->name);
454 0 : if (netgr->name == NULL) {
455 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
456 0 : ret = ENOMEM;
457 0 : goto done;
458 : }
459 :
460 0 : ret = set_netgroup_entry(step_ctx->nctx, netgr);
461 0 : if (ret != EOK) {
462 0 : DEBUG(SSSDBG_CRIT_FAILURE, "set_netgroup_entry failed.\n");
463 0 : goto done;
464 : }
465 : }
466 :
467 0 : netgr->ready = true;
468 0 : netgr->found = false;
469 :
470 0 : lifetime = sss_ncache_get_timeout(step_ctx->nctx->rctx->ncache);
471 0 : set_netgr_lifetime(lifetime, step_ctx, netgr);
472 :
473 0 : ret = EOK;
474 :
475 : done:
476 0 : if (ret != EOK) {
477 0 : talloc_free(netgr);
478 : }
479 :
480 0 : return ret;
481 : }
482 :
483 0 : static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
484 : {
485 : errno_t ret;
486 0 : struct sss_domain_info *dom = step_ctx->dctx->domain;
487 : struct getent_ctx *netgr;
488 0 : char *name = NULL;
489 : uint32_t lifetime;
490 : TALLOC_CTX *tmp_ctx;
491 :
492 0 : tmp_ctx = talloc_new(NULL);
493 0 : if (tmp_ctx == NULL) {
494 0 : return ENOMEM;
495 : }
496 :
497 : /* Check each domain for this netgroup name */
498 0 : while (dom) {
499 : /* Netgroups are a special case. We have to ignore the
500 : * fully-qualified name requirement because memberNisNetgroup
501 : * entries do not have fully-qualified components and we need
502 : * to be able to always check them. So unlike the other
503 : * maps, here we avoid skipping over fully-qualified domains.
504 : */
505 :
506 0 : if (dom != step_ctx->dctx->domain) {
507 : /* make sure we reset the check_provider flag when we check
508 : * a new domain */
509 0 : step_ctx->dctx->check_provider =
510 0 : NEED_CHECK_PROVIDER(dom->provider);
511 : }
512 :
513 : /* make sure to update the dctx if we changed domain */
514 0 : step_ctx->dctx->domain = dom;
515 :
516 0 : name = sss_get_cased_name(tmp_ctx, step_ctx->name,
517 0 : dom->case_sensitive);
518 0 : if (!name) {
519 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sss_get_cased_name failed\n");
520 0 : ret = ENOMEM;
521 0 : goto done;
522 : }
523 :
524 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for [%s@%s]\n",
525 : name, dom->name);
526 0 : if (dom->sysdb == NULL) {
527 0 : DEBUG(SSSDBG_FATAL_FAILURE,
528 : "Fatal: Sysdb CTX not found for this domain!\n");
529 0 : ret = EIO;
530 0 : goto done;
531 : }
532 :
533 : /* Look up the netgroup in the cache */
534 0 : ret = sysdb_getnetgr(step_ctx->dctx, dom, name, &step_ctx->dctx->res);
535 0 : if (ret == EOK) {
536 0 : if (step_ctx->dctx->res->count > 1) {
537 0 : DEBUG(SSSDBG_FATAL_FAILURE,
538 : "getnetgr call returned more than one result !?!\n");
539 0 : ret = EMSGSIZE;
540 0 : goto done;
541 : }
542 0 : } else if (ret == ENOENT) {
543 : /* This netgroup was not found in this domain */
544 0 : if (!step_ctx->dctx->check_provider) {
545 0 : if (step_ctx->check_next) {
546 0 : dom = get_next_domain(dom, 0);
547 0 : continue;
548 : } else {
549 0 : break;
550 : }
551 : }
552 : } else {
553 0 : DEBUG(SSSDBG_CRIT_FAILURE,
554 : "Failed to make request to our cache!\n");
555 0 : ret = EIO;
556 0 : goto done;
557 : }
558 :
559 0 : ret = get_netgroup_entry(step_ctx->nctx, step_ctx->name,
560 : &netgr);
561 0 : if (ret != EOK) {
562 : /* Something really bad happened! */
563 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Netgroup entry was lost!\n");
564 0 : goto done;
565 : }
566 :
567 : /* Convert the result to a list of entries */
568 0 : ret = sysdb_netgr_to_entries(netgr, step_ctx->dctx->res,
569 0 : &netgr->entries);
570 0 : if (ret == ENOENT) {
571 : /* This netgroup was not found in this domain */
572 0 : DEBUG(SSSDBG_OP_FAILURE, "No results for netgroup %s (domain %s)\n",
573 : name, dom->name);
574 :
575 0 : if (!step_ctx->dctx->check_provider) {
576 0 : if (step_ctx->check_next) {
577 0 : dom = get_next_domain(dom, 0);
578 0 : continue;
579 : }
580 0 : else break;
581 : }
582 0 : ret = EOK;
583 : }
584 :
585 0 : if (ret != EOK) {
586 0 : DEBUG(SSSDBG_CRIT_FAILURE,
587 : "Failed to convert results into entries\n");
588 0 : netgr->ready = true;
589 0 : netgr->found = false;
590 0 : lifetime = sss_ncache_get_timeout(step_ctx->nctx->rctx->ncache);
591 0 : set_netgr_lifetime(lifetime, step_ctx, netgr);
592 0 : ret = EIO;
593 0 : goto done;
594 : }
595 :
596 : /* if this is a caching provider (or if we haven't checked the cache
597 : * yet) then verify that the cache is uptodate */
598 0 : if (step_ctx->dctx->check_provider) {
599 0 : ret = check_cache(step_ctx->dctx,
600 : step_ctx->nctx,
601 0 : step_ctx->dctx->res,
602 : SSS_DP_NETGR,
603 : name, 0, NULL,
604 : lookup_netgr_dp_callback,
605 : step_ctx);
606 0 : if (ret != EOK) {
607 : /* May return EAGAIN legitimately to indicate that
608 : * we need to reenter the mainloop
609 : */
610 0 : goto done;
611 : }
612 : }
613 :
614 : /* Results found */
615 0 : DEBUG(SSSDBG_TRACE_FUNC, "Returning info for netgroup [%s@%s]\n",
616 : name, dom->name);
617 0 : netgr->ready = true;
618 0 : netgr->found = true;
619 0 : if (step_ctx->nctx->cache_refresh_percent) {
620 0 : lifetime = dom->netgroup_timeout *
621 0 : (step_ctx->nctx->cache_refresh_percent / 100.0);
622 : } else {
623 0 : lifetime = dom->netgroup_timeout;
624 : }
625 0 : if (lifetime < 10) lifetime = 10;
626 0 : set_netgr_lifetime(lifetime, step_ctx, netgr);
627 :
628 0 : ret = EOK;
629 0 : goto done;
630 : }
631 :
632 : /* If we've gotten here, then no domain contained this netgroup */
633 0 : DEBUG(SSSDBG_MINOR_FAILURE,
634 : "No matching domain found for [%s], fail!\n", step_ctx->name);
635 :
636 0 : ret = create_negcache_netgr(step_ctx);
637 0 : if (ret != EOK) {
638 : /* Failure can be ignored, because at worst, there will be a slowdown
639 : * at the next lookup
640 : */
641 0 : DEBUG(SSSDBG_TRACE_ALL,
642 : "create_negcache_netgr failed with: %d:[%s], ignored.\n",
643 : ret, sss_strerror(ret));
644 : }
645 :
646 0 : ret = ENOENT;
647 :
648 : done:
649 0 : talloc_free(tmp_ctx);
650 0 : return ret;
651 : }
652 :
653 0 : static void lookup_netgr_dp_callback(uint16_t err_maj, uint32_t err_min,
654 : const char *err_msg, void *ptr)
655 : {
656 0 : struct setent_step_ctx *step_ctx =
657 : talloc_get_type(ptr, struct setent_step_ctx);
658 0 : struct nss_dom_ctx *dctx = step_ctx->dctx;
659 0 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
660 : int ret;
661 :
662 0 : if (err_maj) {
663 0 : DEBUG(SSSDBG_OP_FAILURE,
664 : "Unable to get information from Data Provider\n"
665 : "Error: %u, %u, %s\n"
666 : "Will try to return what we have in cache\n",
667 : (unsigned int)err_maj, (unsigned int)err_min, err_msg);
668 : /* Loop to the next domain if possible */
669 0 : if (cmdctx->check_next
670 0 : && (dctx->domain = get_next_domain(dctx->domain, 0))) {
671 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
672 : }
673 : }
674 :
675 : /* ok the backend returned, search to see if we have updated results */
676 0 : ret = lookup_netgr_step(step_ctx);
677 0 : if (ret == EAGAIN) {
678 0 : return;
679 : }
680 :
681 : /* We have results to return */
682 0 : if (ret == EOK) {
683 0 : nss_setent_notify_done(dctx->netgr);
684 : } else {
685 0 : nss_setent_notify_error(dctx->netgr, ret);
686 : }
687 : }
688 :
689 0 : static void setnetgrent_result_timeout(struct tevent_context *ev,
690 : struct tevent_timer *te,
691 : struct timeval current_time,
692 : void *pvt)
693 : {
694 0 : struct getent_ctx *netgr =
695 : talloc_get_type(pvt, struct getent_ctx);
696 :
697 : /* Free the netgroup result context
698 : * The destructor for the netgroup will remove itself
699 : * from the hash table
700 : *
701 : * If additional getnetgrent() requests come in, they
702 : * will invoke an implicit setnetgrent() call and
703 : * refresh the result object
704 : */
705 0 : talloc_free(netgr);
706 0 : }
707 :
708 0 : static errno_t setnetgrent_recv(struct tevent_req *req)
709 : {
710 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
711 0 : return EOK;
712 : }
713 :
714 0 : static void nss_cmd_setnetgrent_done(struct tevent_req *req)
715 : {
716 : errno_t reqret;
717 : errno_t ret;
718 : struct sss_packet *packet;
719 : uint8_t *body;
720 : size_t blen;
721 :
722 0 : struct nss_cmd_ctx *cmdctx =
723 0 : tevent_req_callback_data(req, struct nss_cmd_ctx);
724 :
725 0 : reqret = setnetgrent_recv(req);
726 0 : talloc_zfree(req);
727 0 : if (reqret != EOK && reqret != ENOENT) {
728 0 : DEBUG(SSSDBG_CRIT_FAILURE, "setnetgrent failed\n");
729 0 : nss_cmd_done(cmdctx, reqret);
730 0 : return;
731 : }
732 :
733 : /* Either we succeeded or no domains were eligible */
734 0 : ret = sss_packet_new(cmdctx->cctx->creq, 0,
735 0 : sss_packet_get_cmd(cmdctx->cctx->creq->in),
736 0 : &cmdctx->cctx->creq->out);
737 0 : if (ret == EOK) {
738 0 : if (reqret == ENOENT) {
739 : /* Notify the caller that this entry wasn't found */
740 0 : sss_cmd_empty_packet(cmdctx->cctx->creq->out);
741 : } else {
742 0 : packet = cmdctx->cctx->creq->out;
743 0 : ret = sss_packet_grow(packet, 2*sizeof(uint32_t));
744 0 : if (ret != EOK) {
745 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Couldn't grow the packet\n");
746 0 : NSS_CMD_FATAL_ERROR(cmdctx);
747 : }
748 :
749 0 : sss_packet_get_body(packet, &body, &blen);
750 :
751 : /* Got some results. */
752 0 : SAFEALIGN_SETMEM_UINT32(body, 1, NULL);
753 :
754 : /* reserved */
755 0 : SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL);
756 : }
757 :
758 0 : sss_cmd_done(cmdctx->cctx, cmdctx);
759 0 : return;
760 : }
761 :
762 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error creating packet\n");
763 : }
764 :
765 : static void setnetgrent_implicit_done(struct tevent_req *req);
766 : static errno_t nss_cmd_getnetgrent_process(struct nss_cmd_ctx *cmdctx,
767 : struct getent_ctx *netgr);
768 0 : int nss_cmd_getnetgrent(struct cli_ctx *client)
769 : {
770 : errno_t ret;
771 : struct nss_ctx *nctx;
772 : struct nss_cmd_ctx *cmdctx;
773 : struct getent_ctx *netgr;
774 : struct tevent_req *req;
775 :
776 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Requesting netgroup data\n");
777 :
778 0 : cmdctx = talloc_zero(client, struct nss_cmd_ctx);
779 0 : if (!cmdctx) {
780 0 : return ENOMEM;
781 : }
782 0 : cmdctx->cctx = client;
783 :
784 0 : nctx = talloc_get_type(client->rctx->pvt_ctx, struct nss_ctx);
785 :
786 0 : if (!client->netgr_name) {
787 : /* Tried to run getnetgrent without a preceding
788 : * setnetgrent. There is no way to determine which
789 : * netgroup is being requested.
790 : */
791 0 : return nss_cmd_done(cmdctx, EINVAL);
792 : }
793 :
794 : /* Look up the results from the hash */
795 0 : ret = get_netgroup_entry(nctx, client->netgr_name, &netgr);
796 0 : if (ret == ENOENT) {
797 : /* We need to invoke an implicit setnetgrent() to
798 : * wait for the result object to become available.
799 : */
800 :
801 0 : req = setnetgrent_send(cmdctx, client->netgr_name, cmdctx);
802 0 : if (!req) {
803 0 : return nss_cmd_done(cmdctx, EIO);
804 : }
805 0 : tevent_req_set_callback(req, setnetgrent_implicit_done, cmdctx);
806 :
807 0 : return EOK;
808 0 : } else if (ret != EOK) {
809 0 : DEBUG(SSSDBG_CRIT_FAILURE, "An unexpected error occurred: [%d][%s]\n",
810 : ret, strerror(ret));
811 :
812 0 : return nss_cmd_done(cmdctx, ret);
813 : }
814 :
815 : /* Hash entry was found. Is it ready? */
816 0 : if (!netgr->ready) {
817 : /* We need to invoke an implicit setnetgrent() to
818 : * wait for the result object to become available.
819 : */
820 0 : req = setnetgrent_send(cmdctx, client->netgr_name, cmdctx);
821 0 : if (!req) {
822 0 : return nss_cmd_done(cmdctx, EIO);
823 : }
824 0 : tevent_req_set_callback(req, setnetgrent_implicit_done, cmdctx);
825 :
826 0 : return EOK;
827 0 : } else if (!netgr->found) {
828 0 : DEBUG(SSSDBG_TRACE_FUNC,
829 : "Results for [%s] not found.\n", client->netgr_name);
830 0 : return ENOENT;
831 : }
832 :
833 0 : DEBUG(SSSDBG_TRACE_FUNC,
834 : "Returning results for [%s]\n", client->netgr_name);
835 :
836 : /* Read the result strings */
837 0 : ret = nss_cmd_getnetgrent_process(cmdctx, netgr);
838 0 : if (ret != EOK) {
839 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed: [%d][%s]\n", ret, strerror(ret));
840 : }
841 0 : return ret;
842 : }
843 :
844 0 : static void setnetgrent_implicit_done(struct tevent_req *req)
845 : {
846 : errno_t ret;
847 : struct getent_ctx *netgr;
848 0 : struct nss_cmd_ctx *cmdctx =
849 0 : tevent_req_callback_data(req, struct nss_cmd_ctx);
850 0 : struct nss_ctx *nctx =
851 0 : talloc_get_type(cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx);
852 :
853 0 : ret = setnetgrent_recv(req);
854 0 : talloc_zfree(req);
855 :
856 : /* ENOENT is acceptable, it just means there were no values
857 : * to be returned. This will be handled gracefully in
858 : * nss_cmd_retnetgrent later
859 : */
860 0 : if (ret != EOK && ret != ENOENT) {
861 0 : DEBUG(SSSDBG_FATAL_FAILURE,
862 : "Implicit setnetgrent failed with unexpected error "
863 : "[%d][%s]\n", ret, strerror(ret));
864 0 : NSS_CMD_FATAL_ERROR(cmdctx);
865 : }
866 :
867 0 : if (ret == ENOENT) {
868 : /* No entries found for this netgroup */
869 0 : nss_cmd_done(cmdctx, ret);
870 0 : return;
871 : }
872 :
873 : /* Look up the results from the hash */
874 0 : ret = get_netgroup_entry(nctx, cmdctx->cctx->netgr_name, &netgr);
875 0 : if (ret == ENOENT) {
876 : /* Critical error. This should never happen */
877 0 : DEBUG(SSSDBG_FATAL_FAILURE,
878 : "Implicit setnetgrent returned success without creating "
879 : "result object.\n");
880 0 : NSS_CMD_FATAL_ERROR(cmdctx);
881 0 : } else if (ret != EOK) {
882 0 : DEBUG(SSSDBG_CRIT_FAILURE, "An unexpected error occurred: [%d][%s]\n",
883 : ret, strerror(ret));
884 :
885 0 : NSS_CMD_FATAL_ERROR(cmdctx);
886 : }
887 :
888 0 : if (!netgr->ready) {
889 : /* Critical error. This should never happen */
890 0 : DEBUG(SSSDBG_FATAL_FAILURE,
891 : "Implicit setnetgrent returned success without creating "
892 : "result object.\n");
893 0 : NSS_CMD_FATAL_ERROR(cmdctx);
894 : }
895 :
896 0 : ret = nss_cmd_getnetgrent_process(cmdctx, netgr);
897 0 : if (ret != EOK) {
898 0 : DEBUG(SSSDBG_FATAL_FAILURE,
899 : "Immediate retrieval failed with unexpected error "
900 : "[%d][%s]\n", ret, strerror(ret));
901 0 : NSS_CMD_FATAL_ERROR(cmdctx);
902 : }
903 : }
904 :
905 : static errno_t nss_cmd_retnetgrent(struct cli_ctx *client,
906 : struct sysdb_netgroup_ctx **entries,
907 : int num);
908 0 : static errno_t nss_cmd_getnetgrent_process(struct nss_cmd_ctx *cmdctx,
909 : struct getent_ctx *netgr)
910 : {
911 0 : struct cli_ctx *client = cmdctx->cctx;
912 : uint8_t *body;
913 : size_t blen;
914 : uint32_t num;
915 : errno_t ret;
916 :
917 : /* get max num of entries to return in one call */
918 0 : sss_packet_get_body(client->creq->in, &body, &blen);
919 0 : if (blen != sizeof(uint32_t)) {
920 0 : return EINVAL;
921 : }
922 0 : SAFEALIGN_COPY_UINT32(&num, body, NULL);
923 :
924 : /* create response packet */
925 0 : ret = sss_packet_new(client->creq, 0,
926 0 : sss_packet_get_cmd(client->creq->in),
927 0 : &client->creq->out);
928 0 : if (ret != EOK) {
929 0 : return ret;
930 : }
931 :
932 0 : if (!netgr->entries || netgr->entries[0] == NULL) {
933 : /* No entries */
934 0 : DEBUG(SSSDBG_FUNC_DATA, "No entries found\n");
935 0 : ret = sss_cmd_empty_packet(client->creq->out);
936 0 : if (ret != EOK) {
937 0 : return nss_cmd_done(cmdctx, ret);
938 : }
939 0 : goto done;
940 : }
941 :
942 0 : ret = nss_cmd_retnetgrent(client, netgr->entries, num);
943 :
944 : done:
945 0 : sss_packet_set_error(client->creq->out, ret);
946 0 : sss_cmd_done(client, cmdctx);
947 :
948 0 : return EOK;
949 : }
950 :
951 0 : static errno_t nss_cmd_retnetgrent(struct cli_ctx *client,
952 : struct sysdb_netgroup_ctx **entries,
953 : int count)
954 : {
955 : size_t len;
956 0 : size_t hostlen = 0;
957 0 : size_t userlen = 0;
958 0 : size_t domainlen = 0;
959 0 : size_t grouplen = 0;
960 : uint8_t *body;
961 : size_t blen, rp;
962 : errno_t ret;
963 0 : struct sss_packet *packet = client->creq->out;
964 : int num, start;
965 :
966 : /* first 2 fields (len and reserved), filled up later */
967 0 : rp = 2*sizeof(uint32_t);
968 0 : ret = sss_packet_grow(packet, rp);
969 0 : if (ret != EOK) return ret;
970 :
971 0 : start = client->netgrent_cur;
972 0 : num = 0;
973 0 : while (entries[client->netgrent_cur] &&
974 0 : (client->netgrent_cur - start) < count) {
975 0 : if (entries[client->netgrent_cur]->type == SYSDB_NETGROUP_TRIPLE_VAL) {
976 0 : hostlen = 1;
977 0 : if (entries[client->netgrent_cur]->value.triple.hostname) {
978 0 : hostlen += strlen(entries[client->netgrent_cur]->value.triple.hostname);
979 : }
980 :
981 0 : userlen = 1;
982 0 : if (entries[client->netgrent_cur]->value.triple.username) {
983 0 : userlen += strlen(entries[client->netgrent_cur]->value.triple.username);
984 : }
985 :
986 0 : domainlen = 1;
987 0 : if (entries[client->netgrent_cur]->value.triple.domainname) {
988 0 : domainlen += strlen(entries[client->netgrent_cur]->value.triple.domainname);
989 : }
990 :
991 0 : len = sizeof(uint32_t) + hostlen + userlen + domainlen;
992 0 : ret = sss_packet_grow(packet, len);
993 0 : if (ret != EOK) {
994 0 : return ret;
995 : }
996 0 : sss_packet_get_body(packet, &body, &blen);
997 :
998 0 : SAFEALIGN_SET_UINT32(&body[rp], SSS_NETGR_REP_TRIPLE, &rp);
999 :
1000 0 : if (hostlen == 1) {
1001 0 : body[rp] = '\0';
1002 : } else {
1003 0 : memcpy(&body[rp],
1004 0 : entries[client->netgrent_cur]->value.triple.hostname,
1005 : hostlen);
1006 : }
1007 0 : rp += hostlen;
1008 :
1009 0 : if (userlen == 1) {
1010 0 : body[rp] = '\0';
1011 : } else {
1012 0 : memcpy(&body[rp],
1013 0 : entries[client->netgrent_cur]->value.triple.username,
1014 : userlen);
1015 : }
1016 0 : rp += userlen;
1017 :
1018 0 : if (domainlen == 1) {
1019 0 : body[rp] = '\0';
1020 : } else {
1021 0 : memcpy(&body[rp],
1022 0 : entries[client->netgrent_cur]->value.triple.domainname,
1023 : domainlen);
1024 : }
1025 0 : rp += domainlen;
1026 0 : } else if (entries[client->netgrent_cur]->type == SYSDB_NETGROUP_GROUP_VAL) {
1027 0 : if (entries[client->netgrent_cur]->value.groupname == NULL ||
1028 0 : entries[client->netgrent_cur]->value.groupname[0] == '\0') {
1029 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1030 : "Empty netgroup member. Please check your cache.\n");
1031 0 : continue;
1032 : }
1033 :
1034 0 : grouplen = 1 + strlen(entries[client->netgrent_cur]->value.groupname);
1035 :
1036 0 : len = sizeof(uint32_t) + grouplen;
1037 :
1038 0 : ret = sss_packet_grow(packet, len);
1039 0 : if (ret != EOK) {
1040 0 : return ret;
1041 : }
1042 :
1043 0 : sss_packet_get_body(packet, &body, &blen);
1044 :
1045 0 : SAFEALIGN_SET_UINT32(&body[rp], SSS_NETGR_REP_GROUP, &rp);
1046 :
1047 0 : memcpy(&body[rp],
1048 0 : entries[client->netgrent_cur]->value.groupname,
1049 : grouplen);
1050 0 : rp += grouplen;
1051 : } else {
1052 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1053 : "Unexpected value type for netgroup entry. "
1054 : "Please check your cache.\n");
1055 0 : continue;
1056 : }
1057 :
1058 0 : num++;
1059 0 : client->netgrent_cur++;
1060 : }
1061 :
1062 0 : sss_packet_get_body(packet, &body, &blen);
1063 :
1064 : /* num results */
1065 0 : SAFEALIGN_COPY_UINT32(body, &num, NULL);
1066 :
1067 : /* reserved */
1068 0 : SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL);
1069 :
1070 0 : return EOK;
1071 : }
1072 :
1073 0 : int nss_cmd_endnetgrent(struct cli_ctx *client)
1074 : {
1075 : errno_t ret;
1076 :
1077 : /* create response packet */
1078 0 : ret = sss_packet_new(client->creq, 0,
1079 0 : sss_packet_get_cmd(client->creq->in),
1080 0 : &client->creq->out);
1081 :
1082 0 : if (ret != EOK) {
1083 0 : return ret;
1084 : }
1085 :
1086 : /* Reset the indices so that subsequent requests start at zero */
1087 0 : client->netgrent_cur = 0;
1088 0 : talloc_zfree(client->netgr_name);
1089 :
1090 0 : sss_cmd_done(client, NULL);
1091 0 : return EOK;
1092 : }
1093 :
1094 : void
1095 0 : netgroup_hash_delete_cb(hash_entry_t *item,
1096 : hash_destroy_enum deltype, void *pvt)
1097 : {
1098 : struct getent_ctx *netgr;
1099 :
1100 0 : if (deltype != HASH_ENTRY_DESTROY) {
1101 0 : return;
1102 : }
1103 :
1104 0 : netgr = talloc_get_type(item->value.ptr, struct getent_ctx);
1105 0 : if (!netgr) {
1106 0 : DEBUG(SSSDBG_OP_FAILURE, "Invalid netgroup\n");
1107 0 : return;
1108 : }
1109 :
1110 : /* So that the destructor wouldn't attempt to remove the netgroup from hash
1111 : * table */
1112 0 : netgr->lookup_table = NULL;
1113 : }
1114 :
1115 0 : errno_t nss_orphan_netgroups(struct nss_ctx *nctx)
1116 : {
1117 : int hret;
1118 : unsigned long mcount;
1119 : unsigned long i;
1120 : hash_key_t *netgroups;
1121 :
1122 0 : if (!nctx || !nctx->netgroups) {
1123 0 : return EINVAL;
1124 : }
1125 :
1126 0 : hret = hash_keys(nctx->netgroups, &mcount, &netgroups);
1127 0 : if (hret != HASH_SUCCESS) {
1128 0 : return EIO;
1129 : }
1130 :
1131 0 : DEBUG(SSSDBG_TRACE_FUNC, "Removing netgroups from memory cache.\n");
1132 :
1133 0 : for (i = 0; i < mcount; i++) {
1134 : /* netgroup entry will be deleted by setnetgrent_result_timeout */
1135 0 : hret = hash_delete(nctx->netgroups, &netgroups[i]);
1136 0 : if (hret != HASH_SUCCESS) {
1137 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Could not delete key from hash\n");
1138 0 : continue;
1139 : }
1140 : }
1141 :
1142 0 : return EOK;
1143 : }
|