1 /*
2 SSSD
3
4 sss_groupshow
5
6 Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2010
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <talloc.h>
25 #include <popt.h>
26
27 #include "db/sysdb.h"
28 #include "util/util.h"
29 #include "tools/tools_util.h"
30 #include "tools/sss_sync_ops.h"
31
32 #define PADDING_SPACES 4
33 #define GROUP_SHOW_ATTRS { SYSDB_MEMBEROF, SYSDB_GIDNUM, \
34 SYSDB_MEMBER, SYSDB_NAME, \
35 NULL }
36 #define GROUP_SHOW_MPG_ATTRS { SYSDB_MEMBEROF, SYSDB_UIDNUM, \
37 SYSDB_NAME, NULL }
38
39 struct group_info {
40 const char *name;
41 gid_t gid;
42 bool mpg;
43
44 const char **user_members;
45 const char **memberofs;
46
47 struct group_info **group_members;
48 };
49
50 /*==================Helper routines to process results================= */
51 const char *rdn_as_string(TALLOC_CTX *mem_ctx,
52 struct ldb_dn *dn)
53 {
54 const struct ldb_val *val;
55
56 val = ldb_dn_get_rdn_val(dn);
57 if (val == NULL) {
58 return NULL;
59 }
60
61 return ldb_dn_escape_value(mem_ctx, *val);;
62 }
63
64 static int parse_memberofs(struct ldb_context *ldb,
65 struct ldb_message_element *el,
66 struct group_info *gi)
67 {
68 int i;
69 struct ldb_dn *dn = NULL;
70
71 gi->memberofs = talloc_array(gi, const char *, el->num_values+1);
72 if (gi->memberofs == NULL) {
73 return ENOMEM;
74 }
75
76 for (i = 0; i< el->num_values; ++i) {
77 dn = ldb_dn_from_ldb_val(gi, ldb, &(el->values[i]));
78 gi->memberofs[i] = talloc_strdup(gi, rdn_as_string(gi, dn));
79 talloc_zfree(dn);
80 if (gi->memberofs[i] == NULL) {
81 return ENOMEM;
82 }
83 DEBUG(6, ("memberof value: %s\n", gi->memberofs[i]));
84 }
85 gi->memberofs[el->num_values] = NULL;
86
87 return EOK;
88 }
89
90 static int parse_members(TALLOC_CTX *mem_ctx,
91 struct ldb_context *ldb,
92 struct sss_domain_info *domain,
93 struct ldb_message_element *el,
94 const char *parent_name,
95 const char ***user_members,
96 const char ***group_members,
97 int *num_group_members)
98 {
99 struct ldb_dn *user_basedn = NULL, *group_basedn = NULL;
100 struct ldb_dn *parent_dn = NULL;
101 struct ldb_dn *dn = NULL;
102 const char **um = NULL, **gm = NULL;
103 unsigned int um_index = 0, gm_index = 0;
104 TALLOC_CTX *tmp_ctx = NULL;
105 int ret;
106 int i;
107
108 tmp_ctx = talloc_new(mem_ctx);
109 if (!tmp_ctx) {
110 ret = ENOMEM;
111 goto fail;
112 }
113
114 user_basedn = ldb_dn_new_fmt(tmp_ctx, ldb,
115 SYSDB_TMPL_USER_BASE,
116 domain->name);
117 group_basedn = ldb_dn_new_fmt(tmp_ctx, ldb,
118 SYSDB_TMPL_GROUP_BASE,
119 domain->name);
120 if (!user_basedn || !group_basedn) {
121 ret = ENOMEM;
122 goto fail;
123 }
124
125 um = talloc_array(mem_ctx, const char *, el->num_values+1);
126 gm = talloc_array(mem_ctx, const char *, el->num_values+1);
127 if (!um || !gm) {
128 ret = ENOMEM;
129 goto fail;
130 }
131
132 for (i = 0; i< el->num_values; ++i) {
133 dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &(el->values[i]));
134
135 /* user member or group member? */
136 parent_dn = ldb_dn_get_parent(tmp_ctx, dn);
137 if (ldb_dn_compare_base(parent_dn, user_basedn) == 0) {
138 um[um_index] = rdn_as_string(mem_ctx, dn);
139 if (um[um_index] == NULL) {
140 ret = ENOMEM;
141 goto fail;
142 }
143 DEBUG(6, ("User member %s\n", um[um_index]));
144 um_index++;
145 } else if (ldb_dn_compare_base(parent_dn, group_basedn) == 0) {
146 gm[gm_index] = rdn_as_string(mem_ctx, dn);
147 if (gm[gm_index] == NULL) {
148 ret = ENOMEM;
149 goto fail;
150 }
151 if (parent_name && strcmp(gm[gm_index], parent_name) == 0) {
152 DEBUG(6, ("Skipping circular nesting for group %s\n",
153 gm[gm_index]));
154 continue;
155 }
156 DEBUG(6, ("Group member %s\n", gm[gm_index]));
157 gm_index++;
158 } else {
159 DEBUG(2, ("Group member not a user nor group: %s\n",
160 ldb_dn_get_linearized(dn)));
161 ret = EIO;
162 goto fail;
163 }
164
165 talloc_zfree(dn);
166 talloc_zfree(parent_dn);
167 }
168 um[um_index] = NULL;
169 gm[gm_index] = NULL;
170
171 if (um_index > 0) {
172 um = talloc_realloc(mem_ctx, um, const char *, um_index+1);
173 if (!um) {
174 ret = ENOMEM;
175 goto fail;
176 }
177 } else {
178 talloc_zfree(um);
179 }
180
181 if (gm_index > 0) {
182 gm = talloc_realloc(mem_ctx, gm, const char *, gm_index+1);
183 if (!gm) {
184 ret = ENOMEM;
185 goto fail;
186 }
187 } else {
188 talloc_zfree(gm);
189 }
190
191 *user_members = um;
192 *group_members = gm;
193 *num_group_members = gm_index;
194 talloc_zfree(tmp_ctx);
195 return EOK;
196
197 fail:
198 talloc_zfree(um);
199 talloc_zfree(gm);
200 talloc_zfree(tmp_ctx);
201 return ret;
202 }
203
204 static int process_group(TALLOC_CTX *mem_ctx,
205 struct ldb_context *ldb,
206 struct ldb_message *msg,
207 struct sss_domain_info *domain,
208 const char *parent_name,
209 struct group_info **info,
210 const char ***group_members,
211 int *num_group_members)
212 {
213 struct ldb_message_element *el;
214 int ret;
215 struct group_info *gi = NULL;
216
217 DEBUG(6, ("Found entry %s\n", ldb_dn_get_linearized(msg->dn)));
218
219 gi = talloc_zero(mem_ctx, struct group_info);
220 if (!gi) {
221 ret = ENOMEM;
222 goto done;
223 }
224
225 /* mandatory data - name and gid */
226 gi->name = talloc_strdup(gi,
227 ldb_msg_find_attr_as_string(msg,
228 SYSDB_NAME,
229 NULL));
230 gi->gid = ldb_msg_find_attr_as_uint64(msg,
231 SYSDB_GIDNUM, 0);
232 if (gi->gid == 0 || gi->name == NULL) {
233 DEBUG(3, ("No name or no GID?\n"));
234 ret = EIO;
235 goto done;
236 }
237
238 /* list members */
239 el = ldb_msg_find_element(msg, SYSDB_MEMBER);
240 if (el) {
241 ret = parse_members(gi, ldb, domain, el,
242 parent_name,
243 &gi->user_members,
244 group_members, num_group_members);
245 if (ret != EOK) {
246 goto done;
247 }
248 }
249
250 /* list memberofs */
251 el = ldb_msg_find_element(msg, SYSDB_MEMBEROF);
252 if (el) {
253 ret = parse_memberofs(ldb, el, gi);
254 if (ret != EOK) {
255 goto done;
256 }
257 }
258
259 *info = gi;
260 return EOK;
261 done:
262 talloc_zfree(gi);
263 return ret;
264 }
265
266 /*========Find info about a group and recursively about subgroups====== */
267 struct group_show_state {
268 struct tevent_context *ev;
269 struct sysdb_ctx *sysdb;
270 struct sysdb_handle *handle;
271 struct sss_domain_info *domain;
272
273 struct group_info *root;
274 bool recursive;
275 };
276
277 static void group_show_root_done(struct tevent_req *subreq);
278 static void group_show_recurse_done(struct tevent_req *subreq);
279 static void group_show_trim_done(struct tevent_req *subreq);
280
281 struct tevent_req *group_show_recurse_send(TALLOC_CTX *,
282 struct group_show_state *,
283 struct group_info *,
284 const char **,
285 const int );
286 static int group_show_recurse_recv(TALLOC_CTX *, struct tevent_req *,
287 struct group_info ***);
288
289 static struct tevent_req *group_show_trim_memberof_send(TALLOC_CTX *mem_ctx,
290 struct tevent_context *ev,
291 struct sysdb_ctx *sysdb,
292 struct sysdb_handle *handle,
293 struct sss_domain_info *domain,
294 const char *name,
295 const char **memberofs);
296 static int group_show_trim_memberof_recv(TALLOC_CTX *mem_ctx,
297 struct tevent_req *req,
298 const char ***direct);
299
300 struct tevent_req *group_show_send(TALLOC_CTX *mem_ctx,
301 struct tevent_context *ev,
302 struct sysdb_ctx *sysdb,
303 struct sysdb_handle *handle,
304 struct sss_domain_info *domain,
305 bool recursive,
306 const char *name)
307 {
308 struct group_show_state *search_state = NULL;
309 struct tevent_req *subreq = NULL;
310 struct tevent_req *req = NULL;
311 static const char *attrs[] = GROUP_SHOW_ATTRS;
312
313 req = tevent_req_create(mem_ctx, &search_state, struct group_show_state);
314 if (req == NULL) {
315 return NULL;
316 }
317 search_state->ev = ev;
318 search_state->sysdb = sysdb;
319 search_state->handle = handle;
320 search_state->domain = domain;
321 search_state->recursive = recursive;
322
323 /* First, search for the root group */
324 subreq = sysdb_search_group_by_name_send(search_state,
325 search_state->ev,
326 search_state->sysdb,
327 search_state->handle,
328 search_state->domain,
329 name, attrs);
330 if (!subreq) {
331 talloc_zfree(req);
332 return NULL;
333 }
334 tevent_req_set_callback(subreq, group_show_root_done, req);
335
336 return req;
337 }
338
339 static void group_show_root_done(struct tevent_req *subreq)
340 {
341 struct tevent_req *req = tevent_req_callback_data(subreq,
342 struct tevent_req);
343 struct group_show_state *state = tevent_req_data(req,
344 struct group_show_state);
345 int ret;
346 int i;
347 struct ldb_message *msg = NULL;
348 const char **group_members = NULL;
349 int nmembers = 0;
350
351 ret = sysdb_search_group_recv(subreq, state, &msg);
352 talloc_zfree(subreq);
353 if (ret) {
354 DEBUG(2, ("Search failed: %s (%d)\n", strerror(ret), ret));
355 tevent_req_error(req, ret);
356 return;
357 }
358
359 ret = process_group(state,
360 sysdb_ctx_get_ldb(state->sysdb),
361 msg, state->domain, NULL, &state->root,
362 &group_members, &nmembers);
363 if (ret != EOK) {
364 DEBUG(2, ("Group processing failed: %s (%d)\n",
365 strerror(ret), ret));
366 tevent_req_error(req, ret);
367 return;
368 }
369
370 if (state->recursive == false) {
371 if (group_members) {
372 state->root->group_members = talloc_array(state->root,
373 struct group_info *,
374 nmembers+1);
375 for (i=0; group_members[i]; i++) {
376 state->root->group_members[i] = talloc_zero(state->root,
377 struct group_info);
378 if (!state->root->group_members) {
379 tevent_req_error(req, ENOMEM);
380 return;
381 }
382 state->root->group_members[i]->name = talloc_strdup(state->root,
383 group_members[i]);
384 if (!state->root->group_members[i]->name) {
385 tevent_req_error(req, ENOMEM);
386 return;
387 }
388 }
389 state->root->group_members[nmembers] = NULL;
390 }
391
392 if (state->root->memberofs == NULL) {
393 tevent_req_done(req);
394 return;
395 }
396
397 /* if not recursive, only show the direct parent */
398 subreq = group_show_trim_memberof_send(state, state->ev,
399 state->sysdb, state->handle,
400 state->domain, state->root->name,
401 state->root->memberofs);
402 if (!subreq) {
403 tevent_req_error(req, ENOMEM);
404 return;
405 }
406 tevent_req_set_callback(subreq, group_show_trim_done, req);
407 return;
408 }
409
410 if (group_members == NULL) {
411 tevent_req_done(req);
412 return;
413 }
414
415 subreq = group_show_recurse_send(state->root, state,
416 state->root,
417 group_members,
418 nmembers);
419 if (!subreq) {
420 tevent_req_error(req, ret);
421 return;
422 }
423 tevent_req_set_callback(subreq, group_show_recurse_done, req);
424 }
425
426 static void group_show_trim_done(struct tevent_req *subreq)
427 {
428 struct tevent_req *req = tevent_req_callback_data(subreq,
429 struct tevent_req);
430 struct group_show_state *state = tevent_req_data(req,
431 struct group_show_state);
432 int ret;
433
434 ret = group_show_trim_memberof_recv(state->root, subreq,
435 &state->root->memberofs);
436 talloc_zfree(subreq);
437 if (ret) {
438 tevent_req_error(req, ret);
439 return;
440 }
441
442 tevent_req_done(req);
443 return;
444 }
445
446 static void group_show_recurse_done(struct tevent_req *subreq)
447 {
448 struct tevent_req *req = tevent_req_callback_data(subreq,
449 struct tevent_req);
450 struct group_show_state *state = tevent_req_data(req,
451 struct group_show_state);
452 int ret;
453
454 ret = group_show_recurse_recv(state->root,
455 subreq,
456 &state->root->group_members);
457 talloc_zfree(subreq);
458 if (ret) {
459 DEBUG(2, ("Recursive search failed: %s (%d)\n", strerror(ret), ret));
460 tevent_req_error(req, EIO);
461 return;
462 }
463
464 tevent_req_done(req);
465 }
466
467 static int group_show_recv(TALLOC_CTX *mem_ctx,
468 struct tevent_req *req,
469 struct group_info **res)
470 {
471 struct group_show_state *state = tevent_req_data(req,
472 struct group_show_state);
473
474 TEVENT_REQ_RETURN_ON_ERROR(req);
475 *res = talloc_move(mem_ctx, &state->root);
476
477 return EOK;
478 }
479
480 /*=========Nonrecursive search should only show direct parent========== */
481 struct group_show_trim_state {
482 const char *name;
483 struct ldb_dn *dn;
484
485 const char **all;
486 int current;
487
488 const char **direct;
489 int ndirect;
490
491 struct tevent_context *ev;
492 struct sysdb_ctx *sysdb;
493 struct sysdb_handle *handle;
494 struct sss_domain_info *domain;
495 };
496
497 static int group_show_trim_memberof_next(struct tevent_req *req);
498 static void group_show_trim_memberof_done(struct tevent_req *subreq);
499
500 static struct tevent_req *group_show_trim_memberof_send(TALLOC_CTX *mem_ctx,
501 struct tevent_context *ev,
502 struct sysdb_ctx *sysdb,
503 struct sysdb_handle *handle,
504 struct sss_domain_info *domain,
505 const char *name,
506 const char **memberofs)
507 {
508 struct tevent_req *req = NULL;
509 struct group_show_trim_state *state;
510 int ret;
511
512 req = tevent_req_create(mem_ctx, &state, struct group_show_trim_state);
513 if (req == NULL) {
514 return NULL;
515 }
516 state->ev = ev;
517 state->sysdb = sysdb;
518 state->handle = handle;
519 state->domain = domain;
520 state->name = name;
521 state->all = memberofs;
522
523 state->dn = sysdb_group_dn(state->sysdb, state,
524 state->domain->name,
525 state->name);
526 if (!state->dn) {
527 talloc_zfree(req);
528 return NULL;
529 }
530
531 ret = group_show_trim_memberof_next(req);
532 if (ret) {
533 talloc_zfree(req);
534 return NULL;
535 }
536
537 return req;
538 }
539
540 static int group_show_trim_memberof_next(struct tevent_req *req)
541 {
542 const char *filter;
543 struct tevent_req *subreq = NULL;
544 struct group_show_trim_state *state = tevent_req_data(req,
545 struct group_show_trim_state);
546
547 filter = talloc_asprintf(req, "(&(%s=%s)(%s=%s))",
548 SYSDB_NAME, state->all[state->current],
549 SYSDB_MEMBER, ldb_dn_get_linearized(state->dn));
550 if (!filter) {
551 return ENOMEM;
552 }
553
554 subreq = sysdb_search_groups_send(state, state->ev, state->sysdb,
555 state->handle, state->domain,
556 filter, NULL);
557 if (!subreq) {
558 return ENOMEM;
559 }
560 tevent_req_set_callback(subreq, group_show_trim_memberof_done, req);
561
562 return EOK;
563 }
564
565 static void group_show_trim_memberof_done(struct tevent_req *subreq)
566 {
567 struct tevent_req *req = tevent_req_callback_data(subreq,
568 struct tevent_req);
569 struct group_show_trim_state *state = tevent_req_data(req,
570 struct group_show_trim_state);
571 int ret;
572 struct ldb_message **msgs;
573 size_t count = 0;
574 const char *name;
575
576 ret = sysdb_search_groups_recv(subreq, state, &count, &msgs);
577 talloc_zfree(subreq);
578 /* ENOENT is OK, the group is just not a direct parent */
579 if (ret != EOK && ret != ENOENT) {
580 tevent_req_error(req, ret);
581 return;
582 }
583
584 if (count > 0) {
585 name = ldb_msg_find_attr_as_string(msgs[0],
586 SYSDB_NAME, NULL);
587 if (!name) {
588 DEBUG(2, ("Entry %s has no Name Attribute ?!?\n",
589 ldb_dn_get_linearized(msgs[0]->dn)));
590 tevent_req_error(req, EFAULT);
591 return;
592 }
593
594 state->direct = talloc_realloc(state, state->direct,
595 const char *, state->ndirect+2);
596 if (!state->direct) {
597 tevent_req_error(req, ENOMEM);
598 }
599
600 state->direct[state->ndirect] = talloc_strdup(state->direct, name);
601 if (!state->direct[state->ndirect]) {
602 tevent_req_error(req, ENOMEM);
603 }
604
605 state->direct[state->ndirect+1] = NULL;
606 state->ndirect++;
607 }
608
609 state->current++;
610 if (state->all[state->current] != NULL) {
611 ret = group_show_trim_memberof_next(req);
612 if (ret != EOK) {
613 tevent_req_error(req, ret);
614 }
615 return;
616 }
617
618 tevent_req_done(req);
619 }
620
621 static int group_show_trim_memberof_recv(TALLOC_CTX *mem_ctx,
622 struct tevent_req *req,
623 const char ***direct)
624 {
625 struct group_show_trim_state *state = tevent_req_data(req,
626 struct group_show_trim_state);
627
628 TEVENT_REQ_RETURN_ON_ERROR(req);
629 *direct = talloc_move(mem_ctx, &state->direct);
630
631 return EOK;
632 }
633
634 /*==================Recursive search for nested groups================= */
635 struct group_show_recurse {
636 const char **names;
637 int current;
638
639 struct group_info *parent;
640 struct group_show_state *state;
641
642 struct group_info **groups;
643 };
644
645 static int group_show_recurse_search(struct tevent_req *,
646 struct group_show_recurse *);
647 static void group_show_recurse_next(struct tevent_req *);
648 static void group_show_recurse_level_done(struct tevent_req *);
649 static void group_show_recurse_cont(struct tevent_req *);
650
651 struct tevent_req *group_show_recurse_send(TALLOC_CTX *mem_ctx,
652 struct group_show_state *state,
653 struct group_info *parent,
654 const char **group_members,
655 const int nmembers)
656 {
657 struct tevent_req *req = NULL;
658 struct group_show_recurse *recurse_state = NULL;
659
660 req = tevent_req_create(mem_ctx, &recurse_state, struct group_show_recurse);
661 if (req == NULL) {
662 return NULL;
663 }
664 recurse_state->current = 0;
665 recurse_state->parent = parent;
666 recurse_state->names = group_members;
667 recurse_state->state = state;
668 recurse_state->groups = talloc_array(state->root,
669 struct group_info *,
670 nmembers+1); /* trailing NULL */
671
672 if (!recurse_state->names ||
673 !recurse_state->names[recurse_state->current]) {
674 talloc_zfree(req);
675 return NULL;
676 }
677
678 if (group_show_recurse_search(req, recurse_state) != EOK) {
679 talloc_zfree(req);
680 return NULL;
681 }
682
683 return req;
684 }
685
686 static int group_show_recurse_search(struct tevent_req *req,
687 struct group_show_recurse *recurse_state)
688 {
689 static const char *attrs[] = GROUP_SHOW_ATTRS;
690 struct tevent_req *subreq = NULL;
691
692 /* Skip circular groups */
693 if (strcmp(recurse_state->names[recurse_state->current],
694 recurse_state->parent->name) == 0) {
695 group_show_recurse_cont(req);
696 return EOK;
697 }
698
699 subreq = sysdb_search_group_by_name_send(recurse_state->state,
700 recurse_state->state->ev,
701 recurse_state->state->sysdb,
702 recurse_state->state->handle,
703 recurse_state->state->domain,
704 recurse_state->names[recurse_state->current],
705 attrs);
706 if (!subreq) {
707 return ENOMEM;
708 }
709 tevent_req_set_callback(subreq, group_show_recurse_next, req);
710
711 return EOK;
712 }
713
714 static void group_show_recurse_next(struct tevent_req *subreq)
715 {
716 struct tevent_req *req = tevent_req_callback_data(subreq,
717 struct tevent_req);
718 struct group_show_recurse *recurse_state = tevent_req_data(req,
719 struct group_show_recurse);
720 const char **group_members = NULL;
721 int nmembers = 0;
722 struct ldb_message *msg = NULL;
723 int ret;
724 struct tevent_req *recurse_req = NULL;
725
726 ret = sysdb_search_group_recv(subreq, recurse_state, &msg);
727 talloc_zfree(subreq);
728 if (ret) {
729 DEBUG(2, ("Search failed: %s (%d)\n", strerror(ret), ret));
730 tevent_req_error(req, EIO);
731 return;
732 }
733
734 ret = process_group(recurse_state->state->root,
735 sysdb_ctx_get_ldb(recurse_state->state->sysdb),
736 msg,
737 recurse_state->state->domain,
738 recurse_state->parent->name,
739 &recurse_state->groups[recurse_state->current],
740 &group_members,
741 &nmembers);
742 if (ret != EOK) {
743 DEBUG(2, ("Group processing failed: %s (%d)\n",
744 strerror(ret), ret));
745 tevent_req_error(req, ret);
746 return;
747 }
748
749 /* descend to another level */
750 if (nmembers > 0) {
751 recurse_req = group_show_recurse_send(recurse_state,
752 recurse_state->state,
753 recurse_state->groups[recurse_state->current],
754 group_members, nmembers);
755 if (!recurse_req) {
756 tevent_req_error(req, ENOMEM);
757 return;
758 }
759 /* to free group_members in the callback */
Event returned_pointer: Pointer "group_members" returned by "_talloc_move(recurse_req, (void*)&group_members)" is never used.
|
760 group_members = talloc_move(recurse_req, &group_members);
761 tevent_req_set_callback(recurse_req, group_show_recurse_level_done, req);
762 return;
763 }
764
765 /* Move to next group in the same level */
766 group_show_recurse_cont(req);
767 }
768
769 static void group_show_recurse_level_done(struct tevent_req *recurse_req)
770 {
771 int ret;
772 struct tevent_req *req = tevent_req_callback_data(recurse_req,
773 struct tevent_req);
774 struct group_show_recurse *recurse_state = tevent_req_data(recurse_req,
775 struct group_show_recurse);
776
777 ret = group_show_recurse_recv(recurse_state->state->root, recurse_req,
778 &recurse_state->parent->group_members);
779 talloc_zfree(recurse_req);
780 if (ret) {
781 DEBUG(2, ("Recursive search failed: %s (%d)\n", strerror(ret), ret));
782 tevent_req_error(req, EIO);
783 return;
784 }
785
786 /* Move to next group on the upper level */
787 group_show_recurse_cont(req);
788 }
789
790 static void group_show_recurse_cont(struct tevent_req *req)
791 {
792 struct group_show_recurse *recurse_state = tevent_req_data(req,
793 struct group_show_recurse);
794 int ret;
795
796 recurse_state->current++;
797 if (recurse_state->names[recurse_state->current] == NULL) {
798 recurse_state->groups[recurse_state->current] = NULL; /* Sentinel */
799 tevent_req_done(req);
800 return;
801 }
802
803 /* examine next group on the same level */
804 ret = group_show_recurse_search(req, recurse_state);
805 if (ret != EOK) {
806 tevent_req_error(req, ret);
807 return;
808 }
809 }
810
811 static int group_show_recurse_recv(TALLOC_CTX *mem_ctx,
812 struct tevent_req *req,
813 struct group_info ***out)
814 {
815 struct group_show_recurse *recurse_state = tevent_req_data(req,
816 struct group_show_recurse);
817
818 TEVENT_REQ_RETURN_ON_ERROR(req);
819 *out = talloc_move(mem_ctx, &recurse_state->groups);
820
821 return EOK;
822 }
823
824 /*==================Get info about MPG================================= */
825 struct group_show_mpg_state {
826 struct ldb_context *ldb;
827 struct group_info *info;
828 };
829
830 static void group_show_mpg_done(struct tevent_req *);
831
832 struct tevent_req *group_show_mpg_send(TALLOC_CTX *mem_ctx,
833 struct tevent_context *ev,
834 struct sysdb_ctx *sysdb,
835 struct sysdb_handle *handle,
836 struct sss_domain_info *domain,
837 const char *name)
838 {
839 struct tevent_req *req = NULL;
840 struct tevent_req *subreq = NULL;
841 struct group_show_mpg_state *state;
842 static const char *mpg_attrs[] = GROUP_SHOW_MPG_ATTRS;
843
844 req = tevent_req_create(mem_ctx, &state, struct group_show_mpg_state);
845 if (req == NULL) {
846 return NULL;
847 }
848 state->ldb = sysdb_ctx_get_ldb(sysdb);
849
850 subreq = sysdb_search_user_by_name_send(mem_ctx, ev, sysdb, handle,
851 domain, name, mpg_attrs);
852 if (!subreq) {
853 talloc_zfree(req);
854 return NULL;
855 }
856 tevent_req_set_callback(subreq, group_show_mpg_done, req);
857
858 return req;
859 }
860
861 static void group_show_mpg_done(struct tevent_req *subreq)
862 {
863 int ret;
864 struct ldb_message *msg = NULL;
865 struct tevent_req *req = tevent_req_callback_data(subreq,
866 struct tevent_req);
867 struct group_show_mpg_state *state = tevent_req_data(req,
868 struct group_show_mpg_state);
869
870 ret = sysdb_search_user_recv(subreq, req, &msg);
871 talloc_zfree(subreq);
872 if (ret) {
873 DEBUG(2, ("Search failed: %s (%d)\n", strerror(ret), ret));
874 tevent_req_error(req, ret);
875 return;
876 }
877
878 state->info = talloc_zero(state, struct group_info);
879 if (!state->info) {
880 tevent_req_error(req, ENOMEM);
881 return;
882 }
883
884 state->info->name = talloc_strdup(state->info,
885 ldb_msg_find_attr_as_string(msg,
886 SYSDB_NAME,
887 NULL));
888 state->info->gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0);
889 if (state->info->gid == 0 || state->info->name == NULL) {
890 DEBUG(3, ("No name or no GID?\n"));
891 tevent_req_error(req, EIO);
892 return;
893 }
894 state->info->mpg = true;
895
896 tevent_req_done(req);
897 }
898
899 static int group_show_mpg_recv(TALLOC_CTX *mem_ctx,
900 struct tevent_req *req,
901 struct group_info **res)
902 {
903 struct group_show_mpg_state *state = tevent_req_data(req,
904 struct group_show_mpg_state);
905 TEVENT_REQ_RETURN_ON_ERROR(req);
906 *res = talloc_move(mem_ctx, &state->info);
907
908 return EOK;
909 }
910
911 /*==================The main program=================================== */
912 struct sss_groupshow_state {
913 struct group_info *root;
914
915 int ret;
916 bool done;
917 };
918
919 static void sss_group_show_done(struct tevent_req *req)
920 {
921 int ret;
922 struct sss_groupshow_state *sss_state = tevent_req_callback_data(req,
923 struct sss_groupshow_state);
924
925 ret = group_show_recv(sss_state, req, &sss_state->root);
926 talloc_zfree(req);
927
928 sss_state->ret = ret;
929 sss_state->done = true;
930 }
931
932 static void sss_group_show_mpg_done(struct tevent_req *req)
933 {
934 int ret;
935 struct sss_groupshow_state *sss_state = tevent_req_callback_data(req,
936 struct sss_groupshow_state);
937
938 ret = group_show_mpg_recv(sss_state, req, &sss_state->root);
939 talloc_zfree(req);
940
941 sss_state->ret = ret;
942 sss_state->done = true;
943 }
944
945 static void print_group_info(struct group_info *g, int level)
946 {
947 int i;
948 char padding[512];
949 char fmt[8];
950
951 snprintf(fmt, 8, "%%%ds", level*PADDING_SPACES);
952 snprintf(padding, 512, fmt, "");
953
954 printf(_("%s%sGroup: %s\n"), padding,
955 g->mpg ? _("Magic Private ") : "",
956 g->name);
957 printf(_("%sGID number: %d\n"), padding, g->gid);
958
959 printf(_("%sMember users: "), padding);
960 if (g->user_members) {
961 for (i=0; g->user_members[i]; ++i) {
962 printf("%s%s", i>0 ? "," : "",
963 g->user_members[i]);
964 }
965 }
966 printf(_("\n%sIs a member of: "), padding);
967 if (g->memberofs) {
968 for (i=0; g->memberofs[i]; ++i) {
969 printf("%s%s", i>0 ? "," : "",
970 g->memberofs[i]);
971 }
972 }
973 printf(_("\n%sMember groups: "), padding);
974 }
975
976 static void print_recursive(struct group_info **group_members, int level)
977 {
978 int i;
979
980 if (group_members == NULL) {
981 return;
982 }
983
984 level++;
985 for (i=0; group_members[i]; ++i) {
986 printf("\n");
987 print_group_info(group_members[i], level);
988 printf("\n");
989 print_recursive(group_members[i]->group_members, level);
990 }
991 }
992
993 int main(int argc, const char **argv)
994 {
995 int ret = EXIT_SUCCESS;
996 int pc_debug = 0;
997 bool pc_recursive = false;
998 const char *pc_groupname = NULL;
999 struct tools_ctx *tctx = NULL;
1000 struct tevent_req *req = NULL;
1001 struct sss_groupshow_state *state = NULL;
1002 int i;
1003
1004 poptContext pc = NULL;
1005 struct poptOption long_options[] = {
1006 POPT_AUTOHELP
1007 { "debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &pc_debug,
1008 0, _("The debug level to run with"), NULL },
1009 { "recursive", 'R', POPT_ARG_NONE, NULL, 'r',
1010 _("Print indirect group members recursively"), NULL },
1011 POPT_TABLEEND
1012 };
1013
1014 debug_prg_name = argv[0];
1015
1016 ret = set_locale();
1017 if (ret != EOK) {
1018 DEBUG(1, ("set_locale failed (%d): %s\n", ret, strerror(ret)));
1019 ERROR("Error setting the locale\n");
1020 ret = EXIT_FAILURE;
1021 goto fini;
1022 }
1023
1024 /* parse ops_ctx */
1025 pc = poptGetContext(NULL, argc, argv, long_options, 0);
1026 poptSetOtherOptionHelp(pc, "GROUPNAME");
1027 while ((ret = poptGetNextOpt(pc)) > 0) {
1028 switch (ret) {
1029 case 'r':
1030 pc_recursive = true;
1031 break;
1032 }
1033 }
1034
1035 debug_level = pc_debug;
1036
1037 if (ret != -1) {
1038 usage(pc, poptStrerror(ret));
1039 ret = EXIT_FAILURE;
1040 goto fini;
1041 }
1042
1043 pc_groupname = poptGetArg(pc);
1044 if (pc_groupname == NULL) {
1045 usage(pc, _("Specify group to show\n"));
1046 ret = EXIT_FAILURE;
1047 goto fini;
1048 }
1049
1050 CHECK_ROOT(ret, debug_prg_name);
1051
1052 ret = init_sss_tools(&tctx);
1053 if (ret != EOK) {
1054 DEBUG(1, ("init_sss_tools failed (%d): %s\n", ret, strerror(ret)));
1055 if (ret == ENOENT) {
1056 ERROR("Error initializing the tools - no local domain\n");
1057 } else {
1058 ERROR("Error initializing the tools\n");
1059 }
1060 ret = EXIT_FAILURE;
1061 goto fini;
1062 }
1063
1064 /* if the domain was not given as part of FQDN, default to local domain */
1065 ret = parse_name_domain(tctx, pc_groupname);
1066 if (ret != EOK) {
1067 ERROR("Invalid domain specified in FQDN\n");
1068 ret = EXIT_FAILURE;
1069 goto fini;
1070 }
1071
1072 /* The search itself */
1073 state = talloc_zero(tctx, struct sss_groupshow_state);
1074 if (!state) {
1075 goto fini;
1076 }
1077
1078 req = group_show_send(tctx, tctx->ev, tctx->sysdb, tctx->handle,
1079 tctx->local, pc_recursive, tctx->octx->name);
1080 if (!req) {
1081 ERROR("Cannot initiate search\n");
1082 ret = EXIT_FAILURE;
1083 goto fini;
1084 }
1085 tevent_req_set_callback(req, sss_group_show_done, state);
1086 while (!state->done) {
1087 tevent_loop_once(tctx->ev);
1088 }
1089 ret = state->ret;
1090
1091 /* Also show MPGs */
1092 if (ret == ENOENT) {
1093 state->done = false;
1094 state->ret = EOK;
1095
1096 req = group_show_mpg_send(tctx, tctx->ev, tctx->sysdb, tctx->handle,
1097 tctx->local, tctx->octx->name);
1098 if (!req) {
1099 ERROR("Cannot initiate search\n");
1100 ret = EXIT_FAILURE;
1101 goto fini;
1102 }
1103 tevent_req_set_callback(req, sss_group_show_mpg_done, state);
1104 while (!state->done) {
1105 tevent_loop_once(tctx->ev);
1106 }
1107 ret = state->ret;
1108 }
1109
1110 /* Process result */
1111 if (ret) {
1112 DEBUG(1, ("sysdb operation failed (%d)[%s]\n", ret, strerror(ret)));
1113 switch (ret) {
1114 case ENOENT:
1115 ERROR("No such group in local domain. "
1116 "Printing groups only allowed in local domain.\n");
1117 break;
1118
1119 default:
1120 ERROR("Internal error. Could not print group.\n");
1121 break;
1122 }
1123 ret = EXIT_FAILURE;
1124 goto fini;
1125 }
1126
1127 /* print the results */
1128 print_group_info(state->root, 0);
1129 if (pc_recursive) {
1130 printf("\n");
1131 print_recursive(state->root->group_members, 0);
1132 } else {
1133 if (state->root->group_members) {
1134 for (i=0; state->root->group_members[i]; ++i) {
1135 printf("%s%s", i>0 ? "," : "",
1136 state->root->group_members[i]->name);
1137 }
1138 }
1139 printf("\n");
1140 }
1141
1142 fini:
1143 talloc_free(tctx);
1144 poptFreeContext(pc);
1145 exit(ret);
1146 }