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);
Event var_compare_op: Comparing "state->direct" to null implies that "state->direct" might be null.
Also see events: [var_deref_op]
At conditional (1): "!state->direct": Taking true branch.
596  	        if (!state->direct) {
597  	            tevent_req_error(req, ENOMEM);
598  	        }
599  	
Event var_deref_op: Dereferencing null variable "state->direct".
Also see events: [var_compare_op]
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 */
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 	}