LCOV - code coverage report
Current view: top level - providers/ldap - sdap_async_nested_groups.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 532 1055 50.4 %
Date: 2015-10-19 Functions: 32 42 76.2 %

          Line data    Source code
       1             : /*
       2             :     SSSD
       3             : 
       4             :     Authors:
       5             :         Pavel Březina <pbrezina@redhat.com>
       6             : 
       7             :     Copyright (C) 2013 Red Hat
       8             : 
       9             :     This program is free software; you can redistribute it and/or modify
      10             :     it under the terms of the GNU General Public License as published by
      11             :     the Free Software Foundation; either version 3 of the License, or
      12             :     (at your option) any later version.
      13             : 
      14             :     This program is distributed in the hope that it will be useful,
      15             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :     GNU General Public License for more details.
      18             : 
      19             :     You should have received a copy of the GNU General Public License
      20             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : #include <errno.h>
      24             : #include <string.h>
      25             : #include <tevent.h>
      26             : #include <talloc.h>
      27             : #include <ldb.h>
      28             : #include <dhash.h>
      29             : #include <stdint.h>
      30             : #include <time.h>
      31             : 
      32             : #include "util/util.h"
      33             : #include "db/sysdb.h"
      34             : #include "providers/ldap/ldap_common.h"
      35             : #include "providers/ldap/sdap_async.h"
      36             : #include "providers/ldap/sdap_async_private.h"
      37             : #include "providers/ldap/sdap_idmap.h"
      38             : 
      39             : #define sdap_nested_group_sysdb_search_users(domain, filter) \
      40             :     sdap_nested_group_sysdb_search((domain), (filter), true)
      41             : 
      42             : #define sdap_nested_group_sysdb_search_groups(domain, filter) \
      43             :     sdap_nested_group_sysdb_search((domain), (filter), false)
      44             : 
      45             : enum sdap_nested_group_dn_type {
      46             :     SDAP_NESTED_GROUP_DN_USER,
      47             :     SDAP_NESTED_GROUP_DN_GROUP,
      48             :     SDAP_NESTED_GROUP_DN_UNKNOWN
      49             : };
      50             : 
      51             : struct sdap_nested_group_member {
      52             :     enum sdap_nested_group_dn_type type;
      53             :     const char *dn;
      54             :     const char *user_filter;
      55             :     const char *group_filter;
      56             : };
      57             : 
      58             : struct sdap_nested_group_ctx {
      59             :     struct sss_domain_info *domain;
      60             :     struct sdap_options *opts;
      61             :     struct sdap_search_base **user_search_bases;
      62             :     struct sdap_search_base **group_search_bases;
      63             :     struct sdap_handle *sh;
      64             :     hash_table_t *users;
      65             :     hash_table_t *groups;
      66             :     bool try_deref;
      67             :     int deref_treshold;
      68             :     int max_nesting_level;
      69             : };
      70             : 
      71             : static struct tevent_req *
      72             : sdap_nested_group_process_send(TALLOC_CTX *mem_ctx,
      73             :                              struct tevent_context *ev,
      74             :                              struct sdap_nested_group_ctx *group_ctx,
      75             :                              int nesting_level,
      76             :                              struct sysdb_attrs *group);
      77             : 
      78             : static errno_t sdap_nested_group_process_recv(struct tevent_req *req);
      79             : 
      80             : static struct tevent_req *
      81             : sdap_nested_group_single_send(TALLOC_CTX *mem_ctx,
      82             :                               struct tevent_context *ev,
      83             :                               struct sdap_nested_group_ctx *group_ctx,
      84             :                               struct sdap_nested_group_member *members,
      85             :                               int num_members,
      86             :                               int num_groups_max,
      87             :                               int nesting_level);
      88             : 
      89             : static errno_t sdap_nested_group_single_recv(struct tevent_req *req);
      90             : 
      91             : static struct tevent_req *
      92             : sdap_nested_group_lookup_user_send(TALLOC_CTX *mem_ctx,
      93             :                                    struct tevent_context *ev,
      94             :                                    struct sdap_nested_group_ctx *group_ctx,
      95             :                                    struct sdap_nested_group_member *member);
      96             : 
      97             : static errno_t sdap_nested_group_lookup_user_recv(TALLOC_CTX *mem_ctx,
      98             :                                                   struct tevent_req *req,
      99             :                                                   struct sysdb_attrs **_user);
     100             : 
     101             : static struct tevent_req *
     102             : sdap_nested_group_lookup_group_send(TALLOC_CTX *mem_ctx,
     103             :                                     struct tevent_context *ev,
     104             :                                     struct sdap_nested_group_ctx *group_ctx,
     105             :                                     struct sdap_nested_group_member *member);
     106             : 
     107             : static errno_t sdap_nested_group_lookup_group_recv(TALLOC_CTX *mem_ctx,
     108             :                                                    struct tevent_req *req,
     109             :                                                    struct sysdb_attrs **_group);
     110             : 
     111             : static struct tevent_req *
     112             : sdap_nested_group_lookup_unknown_send(TALLOC_CTX *mem_ctx,
     113             :                                       struct tevent_context *ev,
     114             :                                       struct sdap_nested_group_ctx *group_ctx,
     115             :                                       struct sdap_nested_group_member *member);
     116             : 
     117             : static errno_t
     118             : sdap_nested_group_lookup_unknown_recv(TALLOC_CTX *mem_ctx,
     119             :                                       struct tevent_req *req,
     120             :                                       struct sysdb_attrs **_entry,
     121             :                                       enum sdap_nested_group_dn_type *_type);
     122             : 
     123             : static struct tevent_req *
     124             : sdap_nested_group_deref_send(TALLOC_CTX *mem_ctx,
     125             :                              struct tevent_context *ev,
     126             :                              struct sdap_nested_group_ctx *group_ctx,
     127             :                              struct ldb_message_element *members,
     128             :                              const char *group_dn,
     129             :                              int nesting_level);
     130             : 
     131             : static errno_t sdap_nested_group_deref_recv(struct tevent_req *req);
     132             : 
     133             : static errno_t
     134          12 : sdap_nested_group_extract_hash_table(TALLOC_CTX *mem_ctx,
     135             :                                      hash_table_t *table,
     136             :                                      unsigned long *_num_entries,
     137             :                                      struct sysdb_attrs ***_entries)
     138             : {
     139          12 :     struct sysdb_attrs **entries = NULL;
     140          12 :     struct sysdb_attrs *entry = NULL;
     141             :     hash_value_t *values;
     142             :     unsigned long num_entries;
     143             :     unsigned int i;
     144             :     bool hret;
     145             :     errno_t ret;
     146             : 
     147          12 :     hret = hash_values(table, &num_entries, &values);
     148          12 :     if (hret != HASH_SUCCESS) {
     149           0 :         ret = EIO;
     150           0 :         goto done;
     151             :     }
     152             : 
     153          12 :     if (num_entries > 0) {
     154           9 :         entries = talloc_array(mem_ctx, struct sysdb_attrs *, num_entries);
     155           9 :         if (entries == NULL) {
     156           0 :             ret = ENOMEM;
     157           0 :             goto done;
     158             :         }
     159             : 
     160          26 :         for (i = 0; i < num_entries; i++) {
     161          17 :             entry = talloc_get_type(values[i].ptr, struct sysdb_attrs);
     162          17 :             entries[i] = talloc_steal(entries, entry);
     163             :         }
     164             :     }
     165             : 
     166          12 :     if (_num_entries != NULL) {
     167          12 :         *_num_entries = num_entries;
     168             :     }
     169             : 
     170          12 :     if (_entries != NULL) {
     171          12 :         *_entries = entries;
     172             :     }
     173             : 
     174          12 :     ret = EOK;
     175             : 
     176             : done:
     177          12 :     talloc_free(values);
     178             : 
     179          12 :     if (ret != EOK) {
     180           0 :         talloc_free(entries);
     181             :     }
     182             : 
     183          12 :     return ret;
     184             : }
     185             : 
     186          22 : static errno_t sdap_nested_group_hash_entry(hash_table_t *table,
     187             :                                             struct sysdb_attrs *entry,
     188             :                                             const char *table_name)
     189             : {
     190             :     hash_key_t key;
     191             :     hash_value_t value;
     192          22 :     const char *name = NULL;
     193             :     errno_t ret;
     194             :     int hret;
     195             : 
     196          22 :     ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name);
     197          22 :     if (ret != EOK) {
     198           0 :         return ret;
     199             :     }
     200             : 
     201          22 :     DEBUG(SSSDBG_TRACE_ALL, "Inserting [%s] into hash table [%s]\n",
     202             :                              name, table_name);
     203             : 
     204          22 :     key.type = HASH_KEY_STRING;
     205          22 :     key.str = talloc_strdup(NULL, name);
     206          22 :     if (key.str == NULL) {
     207           0 :         return ENOMEM;
     208             :     }
     209             : 
     210          22 :     if (hash_has_key(table, &key)) {
     211           2 :         talloc_free(key.str);
     212           2 :         return EEXIST;
     213             :     }
     214             : 
     215          20 :     value.type = HASH_VALUE_PTR;
     216          20 :     value.ptr = entry;
     217             : 
     218          20 :     hret = hash_enter(table, &key, &value);
     219          20 :     if (hret != HASH_SUCCESS) {
     220           0 :         talloc_free(key.str);
     221           0 :         return EIO;
     222             :     }
     223             : 
     224          20 :     talloc_steal(table, key.str);
     225          20 :     talloc_steal(table, value.ptr);
     226             : 
     227          20 :     return EOK;
     228             : }
     229             : 
     230             : static errno_t
     231           7 : sdap_nested_group_hash_user(struct sdap_nested_group_ctx *group_ctx,
     232             :                             struct sysdb_attrs *user)
     233             : {
     234           7 :     return sdap_nested_group_hash_entry(group_ctx->users, user, "users");
     235             : }
     236             : 
     237             : static errno_t
     238          15 : sdap_nested_group_hash_group(struct sdap_nested_group_ctx *group_ctx,
     239             :                              struct sysdb_attrs *group)
     240             : {
     241          15 :     struct sdap_attr_map *map = group_ctx->opts->group_map;
     242             :     gid_t gid;
     243             :     errno_t ret;
     244          15 :     bool posix_group = true;
     245             :     bool use_id_mapping;
     246             :     bool can_find_gid;
     247             :     bool need_filter;
     248             : 
     249          15 :     ret = sdap_check_ad_group_type(group_ctx->domain, group_ctx->opts,
     250             :                                    group, "", &need_filter);
     251          15 :     if (ret != EOK) {
     252           0 :         return ret;
     253             :     }
     254             : 
     255          15 :     if (need_filter) {
     256           0 :         posix_group = false;
     257           0 :         gid = 0;
     258             :     }
     259             : 
     260          15 :     use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
     261          15 :                                                           group_ctx->opts->idmap_ctx,
     262          15 :                                                           group_ctx->domain->name,
     263          15 :                                                           group_ctx->domain->domain_id);
     264             : 
     265          15 :     can_find_gid = posix_group && !use_id_mapping;
     266          15 :     if (can_find_gid) {
     267          15 :         ret = sysdb_attrs_get_uint32_t(group, map[SDAP_AT_GROUP_GID].sys_name,
     268             :                                        &gid);
     269             :     }
     270          15 :     if (!can_find_gid || ret == ENOENT || (ret == EOK && gid == 0)) {
     271           0 :         DEBUG(SSSDBG_TRACE_ALL,
     272             :              "The group's gid was %s\n", ret == ENOENT ? "missing" : "zero");
     273           0 :         DEBUG(SSSDBG_TRACE_INTERNAL,
     274             :              "Marking group as non-posix and setting GID=0!\n");
     275             : 
     276           0 :         if (ret == ENOENT || !posix_group) {
     277           0 :             ret = sysdb_attrs_add_uint32(group,
     278           0 :                                          map[SDAP_AT_GROUP_GID].sys_name, 0);
     279           0 :             if (ret != EOK) {
     280           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     281             :                       "Failed to add a GID to non-posix group!\n");
     282           0 :                 return ret;
     283             :             }
     284             :         }
     285             : 
     286           0 :         ret = sysdb_attrs_add_bool(group, SYSDB_POSIX, false);
     287           0 :         if (ret != EOK) {
     288           0 :             DEBUG(SSSDBG_OP_FAILURE,
     289             :                   "Error: Failed to mark group as non-posix!\n");
     290           0 :             return ret;
     291             :         }
     292          15 :     } else if (ret != EOK) {
     293           0 :         return ret;
     294             :     }
     295             : 
     296          15 :     return sdap_nested_group_hash_entry(group_ctx->groups, group, "groups");
     297             : }
     298             : 
     299          32 : static errno_t sdap_nested_group_sysdb_search(struct sss_domain_info *domain,
     300             :                                               const char *filter,
     301             :                                               bool user)
     302             : {
     303             :     static const char *attrs[] = {SYSDB_CACHE_EXPIRE,
     304             :                                   SYSDB_UIDNUM,
     305             :                                   NULL};
     306          32 :     struct ldb_message **msgs = NULL;
     307             :     size_t count;
     308          32 :     time_t now = time(NULL);
     309             :     uint64_t expire;
     310             :     uid_t uid;
     311             :     errno_t ret;
     312             : 
     313          32 :     if (user) {
     314          16 :         ret = sysdb_search_users(NULL, domain, filter, attrs,
     315             :                                  &count, &msgs);
     316             :     } else {
     317          16 :         ret = sysdb_search_groups(NULL, domain, filter, attrs,
     318             :                                   &count, &msgs);
     319             :     }
     320          32 :     if (ret != EOK) {
     321          32 :         goto done;
     322             :     }
     323             : 
     324           0 :     if (count != 1) {
     325           0 :         DEBUG(SSSDBG_OP_FAILURE, "More than one entry found?\n");
     326           0 :         ret = EFAULT;
     327           0 :         goto done;
     328             :     }
     329             : 
     330             :     /* we found an object with this origDN in the sysdb,
     331             :      * check if it is valid */
     332           0 :     if (user) {
     333           0 :         uid = ldb_msg_find_attr_as_uint64(msgs[0], SYSDB_UIDNUM, 0);
     334           0 :         if (uid == 0) {
     335           0 :             DEBUG(SSSDBG_OP_FAILURE, "User with no UID?\n");
     336           0 :             ret = EINVAL;
     337           0 :             goto done;
     338             :         }
     339             :     }
     340             : 
     341           0 :     expire = ldb_msg_find_attr_as_uint64(msgs[0], SYSDB_CACHE_EXPIRE, 0);
     342           0 :     if (expire != 0 && expire <= now) {
     343             :         /* needs refresh */
     344           0 :         ret = EAGAIN;
     345           0 :         goto done;
     346             :     }
     347             : 
     348             :     /* valid object */
     349           0 :     ret = EOK;
     350             : 
     351             : done:
     352          32 :     talloc_zfree(msgs);
     353          32 :     return ret;
     354             : }
     355             : 
     356             : static errno_t
     357          16 : sdap_nested_group_check_cache(struct sdap_options *opts,
     358             :                               struct sss_domain_info *domain,
     359             :                               const char *member_dn,
     360             :                               enum sdap_nested_group_dn_type *_type)
     361             : {
     362          16 :     TALLOC_CTX *tmp_ctx = NULL;
     363          16 :     struct sdap_domain *sdap_domain = NULL;
     364          16 :     struct sss_domain_info *member_domain = NULL;
     365          16 :     char *sanitized_dn = NULL;
     366          16 :     char *filter = NULL;
     367             :     errno_t ret;
     368             : 
     369          16 :     tmp_ctx = talloc_new(NULL);
     370          16 :     if (tmp_ctx == NULL) {
     371           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
     372           0 :         return ENOMEM;
     373             :     }
     374             : 
     375          16 :     ret = sss_filter_sanitize(tmp_ctx, member_dn, &sanitized_dn);
     376          16 :     if (ret != EOK) {
     377           0 :         goto done;
     378             :     }
     379             : 
     380          16 :     filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_ORIG_DN, sanitized_dn);
     381          16 :     if (filter == NULL) {
     382           0 :         ret = ENOMEM;
     383           0 :         goto done;
     384             :     }
     385             : 
     386             :     /* determine correct domain of this member */
     387          16 :     sdap_domain = sdap_domain_get_by_dn(opts, member_dn);
     388          16 :     member_domain = sdap_domain == NULL ? domain : sdap_domain->dom;
     389             : 
     390             :     /* search in users */
     391          16 :     ret = sdap_nested_group_sysdb_search_users(member_domain, filter);
     392          16 :     if (ret == EOK || ret == EAGAIN) {
     393             :         /* user found */
     394           0 :         *_type = SDAP_NESTED_GROUP_DN_USER;
     395           0 :         goto done;
     396          16 :     } else if (ret != ENOENT) {
     397             :         /* error */
     398           0 :         goto done;
     399             :     }
     400             : 
     401             :     /* search in groups */
     402          16 :     ret = sdap_nested_group_sysdb_search_groups(member_domain, filter);
     403          16 :     if (ret == EOK || ret == EAGAIN) {
     404             :         /* group found */
     405           0 :         *_type = SDAP_NESTED_GROUP_DN_GROUP;
     406           0 :         goto done;
     407          16 :     } else if (ret != ENOENT) {
     408             :         /* error */
     409           0 :         goto done;
     410             :     }
     411             : 
     412             :     /* not found in the sysdb */
     413          16 :     ret = ENOENT;
     414             : 
     415             : done:
     416          16 :     talloc_free(tmp_ctx);
     417          16 :     return ret;
     418             : }
     419             : 
     420             : static bool
     421          32 : sdap_nested_member_is_ent(struct sdap_nested_group_ctx *group_ctx,
     422             :                           const char *dn, char **filter, bool is_user)
     423             : {
     424          32 :     struct sdap_domain *sditer = NULL;
     425          32 :     bool ret = false;
     426             :     struct sdap_search_base **search_bases;
     427             : 
     428          48 :     DLIST_FOR_EACH(sditer, group_ctx->opts->sdom) {
     429          32 :         search_bases = is_user ? sditer->user_search_bases : \
     430             :                                  sditer->group_search_bases;
     431             : 
     432          32 :         ret = sss_ldap_dn_in_search_bases(group_ctx, dn, search_bases,
     433             :                                           filter);
     434          32 :         if (ret == true) {
     435          16 :             break;
     436             :         }
     437             :     }
     438             : 
     439          32 :     return ret;
     440             : }
     441             : 
     442             : static inline bool
     443          16 : sdap_nested_member_is_user(struct sdap_nested_group_ctx *group_ctx,
     444             :                            const char *dn, char **filter)
     445             : {
     446          16 :     return sdap_nested_member_is_ent(group_ctx, dn, filter, true);
     447             : }
     448             : 
     449             : static inline bool
     450          16 : sdap_nested_member_is_group(struct sdap_nested_group_ctx *group_ctx,
     451             :                             const char *dn, char **filter)
     452             : {
     453          16 :     return sdap_nested_member_is_ent(group_ctx, dn, filter, false);
     454             : }
     455             : 
     456             : static errno_t
     457          10 : sdap_nested_group_split_members(TALLOC_CTX *mem_ctx,
     458             :                                 struct sdap_nested_group_ctx *group_ctx,
     459             :                                 int nesting_level,
     460             :                                 struct ldb_message_element *members,
     461             :                                 struct sdap_nested_group_member **_missing,
     462             :                                 int *_num_missing,
     463             :                                 int *_num_groups)
     464             : {
     465          10 :     TALLOC_CTX *tmp_ctx = NULL;
     466          10 :     struct sdap_nested_group_member *missing = NULL;
     467             :     enum sdap_nested_group_dn_type type;
     468          10 :     char *dn = NULL;
     469          10 :     char *user_filter = NULL;
     470          10 :     char *group_filter = NULL;
     471          10 :     int num_missing = 0;
     472          10 :     int num_groups = 0;
     473             :     hash_key_t key;
     474             :     bool bret;
     475             :     bool is_user;
     476             :     bool is_group;
     477             :     errno_t ret;
     478             :     int i;
     479             : 
     480          10 :     tmp_ctx = talloc_new(NULL);
     481          10 :     if (tmp_ctx == NULL) {
     482           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
     483           0 :         return ENOMEM;
     484             :     }
     485             : 
     486          10 :     missing = talloc_zero_array(tmp_ctx, struct sdap_nested_group_member,
     487             :                                 members->num_values);
     488          10 :     if (missing == NULL) {
     489           0 :         ret = ENOMEM;
     490           0 :         goto done;
     491             :     }
     492             : 
     493             :     /* create list of missing members
     494             :      * skip dn if:
     495             :      * - is present in user or group hash table
     496             :      * - is present in sysdb and not expired
     497             :      * - it is a group and we have reached the maximal nesting level
     498             :      * - it is not under user nor group search bases
     499             :      *
     500             :      * if dn is in sysdb but expired
     501             :      * - we know what object type it is
     502             :      *
     503             :      * if dn is not in hash table or sysdb
     504             :      * - try to determine type of object by search base that match dn
     505             :      */
     506          26 :     for (i = 0; i < members->num_values; i++) {
     507          16 :         dn = (char*)members->values[i].data;
     508          16 :         type = SDAP_NESTED_GROUP_DN_UNKNOWN;
     509             : 
     510             :         /* check hash tables */
     511          16 :         key.type = HASH_KEY_STRING;
     512          16 :         key.str = dn;
     513             : 
     514          16 :         bret = hash_has_key(group_ctx->users, &key);
     515          16 :         if (bret) {
     516           0 :             continue;
     517             :         }
     518             : 
     519          16 :         bret = hash_has_key(group_ctx->groups, &key);
     520          16 :         if (bret) {
     521           0 :             continue;
     522             :         }
     523             : 
     524             :         /* check sysdb */
     525          16 :         ret = sdap_nested_group_check_cache(group_ctx->opts, group_ctx->domain,
     526             :                                             dn, &type);
     527          16 :         if (ret == EOK) {
     528             :             /* found and valid */
     529           0 :             DEBUG(SSSDBG_TRACE_ALL, "[%s] found in cache, skipping\n", dn);
     530           0 :             continue;
     531          16 :         } else if (ret != EAGAIN && ret != ENOENT) {
     532             :             /* error */
     533           0 :             goto done;
     534             :         }
     535             : 
     536             :         /* try to determine type by dn */
     537          16 :         if (type == SDAP_NESTED_GROUP_DN_UNKNOWN) {
     538             :             /* user */
     539          16 :             is_user = sdap_nested_member_is_user(group_ctx, dn,
     540             :                                                  &user_filter);
     541             : 
     542          16 :             is_group = sdap_nested_member_is_group(group_ctx, dn,
     543             :                                                    &group_filter);
     544             : 
     545          16 :             if (is_user && is_group) {
     546             :                 /* search bases overlap */
     547           0 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is unknown object\n", dn);
     548           0 :                 type = SDAP_NESTED_GROUP_DN_UNKNOWN;
     549          16 :             } else if (is_user) {
     550           8 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is a user\n", dn);
     551           8 :                 type = SDAP_NESTED_GROUP_DN_USER;
     552           8 :             } else if (is_group) {
     553           8 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is a group\n", dn);
     554           8 :                 type = SDAP_NESTED_GROUP_DN_GROUP;
     555             :             } else {
     556             :                 /* dn is outside search bases */
     557           0 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is out of scope of configured "
     558             :                       "search bases, skipping\n", dn);
     559           0 :                 continue;
     560             :             }
     561             :         }
     562             : 
     563             :         /* check nesting level */
     564          16 :         if (type == SDAP_NESTED_GROUP_DN_GROUP) {
     565           8 :             if (nesting_level >= group_ctx->max_nesting_level) {
     566           0 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is outside nesting limit "
     567             :                       "(level %d), skipping\n", dn, nesting_level);
     568           0 :                 talloc_zfree(user_filter);
     569           0 :                 talloc_zfree(group_filter);
     570           0 :                 continue;
     571             :             }
     572             :         }
     573             : 
     574          16 :         missing[num_missing].dn = talloc_strdup(missing, dn);
     575          16 :         if (missing[num_missing].dn == NULL) {
     576           0 :             ret = ENOMEM;
     577           0 :             goto done;
     578             :         }
     579             : 
     580          16 :         missing[num_missing].type = type;
     581          16 :         missing[num_missing].user_filter = talloc_steal(missing, user_filter);
     582          16 :         missing[num_missing].group_filter = talloc_steal(missing, group_filter);
     583             : 
     584          16 :         num_missing++;
     585             : 
     586          16 :         if (type != SDAP_NESTED_GROUP_DN_USER) {
     587           8 :             num_groups++;
     588             :         }
     589             :     }
     590             : 
     591          10 :     missing = talloc_realloc(mem_ctx, missing,
     592             :                              struct sdap_nested_group_member, num_missing);
     593             :     /* talloc_realloc behaves as talloc_free if 3rd parameter (count) is 0,
     594             :      * so it's OK to return NULL then
     595             :      */
     596          10 :     if (missing == NULL && num_missing > 0) {
     597           0 :         ret = ENOMEM;
     598           0 :         goto done;
     599             :     }
     600             : 
     601          10 :     if (_missing) {
     602          10 :         *_missing = talloc_steal(mem_ctx, missing);
     603             :     }
     604             : 
     605          10 :     if (_num_missing) {
     606          10 :         *_num_missing = num_missing;
     607             :     }
     608             : 
     609          10 :     if (_num_groups) {
     610          10 :         *_num_groups = num_groups;
     611             :     }
     612             : 
     613          10 :     ret = EOK;
     614             : 
     615             : done:
     616          10 :     talloc_free(tmp_ctx);
     617             : 
     618          10 :     return ret;
     619             : }
     620             : 
     621             : 
     622             : struct sdap_nested_group_state {
     623             :     struct sdap_nested_group_ctx *group_ctx;
     624             : };
     625             : 
     626             : static void sdap_nested_group_done(struct tevent_req *subreq);
     627             : 
     628             : struct tevent_req *
     629           7 : sdap_nested_group_send(TALLOC_CTX *mem_ctx,
     630             :                        struct tevent_context *ev,
     631             :                        struct sdap_domain *sdom,
     632             :                        struct sdap_options *opts,
     633             :                        struct sdap_handle *sh,
     634             :                        struct sysdb_attrs *group)
     635             : {
     636           7 :     struct sdap_nested_group_state *state = NULL;
     637           7 :     struct tevent_req *req = NULL;
     638           7 :     struct tevent_req *subreq = NULL;
     639             :     errno_t ret;
     640             :     int i;
     641             : 
     642           7 :     req = tevent_req_create(mem_ctx, &state, struct sdap_nested_group_state);
     643           7 :     if (req == NULL) {
     644           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
     645           0 :         return NULL;
     646             :     }
     647             : 
     648             :     /* create main nested group context */
     649           7 :     state->group_ctx = talloc_zero(state, struct sdap_nested_group_ctx);
     650           7 :     if (state->group_ctx == NULL) {
     651           0 :         ret = ENOMEM;
     652           0 :         goto immediately;
     653             :     }
     654             : 
     655           7 :     ret = sss_hash_create(state->group_ctx, 32, &state->group_ctx->users);
     656           7 :     if (ret != EOK) {
     657           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n",
     658             :                                     ret, strerror(ret));
     659           0 :         goto immediately;
     660             :     }
     661             : 
     662           7 :     ret = sss_hash_create(state->group_ctx, 32, &state->group_ctx->groups);
     663           7 :     if (ret != EOK) {
     664           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n",
     665             :                                     ret, strerror(ret));
     666           0 :         goto immediately;
     667             :     }
     668             : 
     669           7 :     state->group_ctx->try_deref = true;
     670           7 :     state->group_ctx->deref_treshold = dp_opt_get_int(opts->basic,
     671             :                                                       SDAP_DEREF_THRESHOLD);
     672           7 :     state->group_ctx->max_nesting_level = dp_opt_get_int(opts->basic,
     673             :                                                          SDAP_NESTING_LEVEL);
     674           7 :     state->group_ctx->domain = sdom->dom;
     675           7 :     state->group_ctx->opts = opts;
     676           7 :     state->group_ctx->user_search_bases = sdom->user_search_bases;
     677           7 :     state->group_ctx->group_search_bases = sdom->group_search_bases;
     678           7 :     state->group_ctx->sh = sh;
     679           7 :     state->group_ctx->try_deref = sdap_has_deref_support(sh, opts);
     680             : 
     681             :     /* disable deref if threshold <= 0 */
     682           7 :     if (state->group_ctx->deref_treshold <= 0) {
     683           0 :         state->group_ctx->try_deref = false;
     684             :     }
     685             : 
     686             :     /* if any search base contains filter, disable dereference. */
     687           7 :     if (state->group_ctx->try_deref) {
     688           0 :         for (i = 0; opts->sdom->user_search_bases[i] != NULL; i++) {
     689           0 :             if (opts->sdom->user_search_bases[i]->filter != NULL) {
     690           0 :                 DEBUG(SSSDBG_TRACE_FUNC, "User search base contains filter, "
     691             :                                           "dereference will be disabled\n");
     692           0 :                 state->group_ctx->try_deref = false;
     693           0 :                 break;
     694             :             }
     695             :         }
     696             :     }
     697             : 
     698           7 :     if (state->group_ctx->try_deref) {
     699           0 :         for (i = 0; opts->sdom->group_search_bases[i] != NULL; i++) {
     700           0 :             if (opts->sdom->group_search_bases[i]->filter != NULL) {
     701           0 :                 DEBUG(SSSDBG_TRACE_FUNC, "Group search base contains filter, "
     702             :                                           "dereference will be disabled\n");
     703           0 :                 state->group_ctx->try_deref = false;
     704           0 :                 break;
     705             :             }
     706             :         }
     707             :     }
     708             : 
     709             :     /* insert initial group into hash table */
     710           7 :     ret = sdap_nested_group_hash_group(state->group_ctx, group);
     711           7 :     if (ret != EOK) {
     712           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to insert group into hash table "
     713             :                                     "[%d]: %s\n", ret, strerror(ret));
     714           0 :         goto immediately;
     715             :     }
     716             : 
     717             :     /* resolve group */
     718           7 :     subreq = sdap_nested_group_process_send(state, ev, state->group_ctx,
     719             :                                             0, group);
     720           7 :     if (subreq == NULL) {
     721           0 :         ret = ENOMEM;
     722           0 :         goto immediately;
     723             :     }
     724             : 
     725           7 :     tevent_req_set_callback(subreq, sdap_nested_group_done, req);
     726             : 
     727           7 :     return req;
     728             : 
     729             : immediately:
     730           0 :     if (ret == EOK) {
     731           0 :         tevent_req_done(req);
     732             :     } else {
     733           0 :         tevent_req_error(req, ret);
     734             :     }
     735           0 :     tevent_req_post(req, ev);
     736             : 
     737           0 :     return req;
     738             : }
     739             : 
     740           7 : static void sdap_nested_group_done(struct tevent_req *subreq)
     741             : {
     742           7 :     struct tevent_req *req = NULL;
     743             :     errno_t ret;
     744             : 
     745           7 :     req = tevent_req_callback_data(subreq, struct tevent_req);
     746             : 
     747           7 :     ret = sdap_nested_group_process_recv(subreq);
     748           7 :     talloc_zfree(subreq);
     749           7 :     if (ret != EOK) {
     750           1 :         tevent_req_error(req, ret);
     751           1 :         return;
     752             :     }
     753             : 
     754           6 :     tevent_req_done(req);
     755             : }
     756             : 
     757           7 : errno_t sdap_nested_group_recv(TALLOC_CTX *mem_ctx,
     758             :                                struct tevent_req *req,
     759             :                                unsigned long *_num_users,
     760             :                                struct sysdb_attrs ***_users,
     761             :                                unsigned long *_num_groups,
     762             :                                struct sysdb_attrs ***_groups)
     763             : {
     764           7 :     struct sdap_nested_group_state *state = NULL;
     765           7 :     struct sysdb_attrs **users = NULL;
     766           7 :     struct sysdb_attrs **groups = NULL;
     767             :     unsigned long num_users;
     768             :     unsigned long num_groups;
     769             :     errno_t ret;
     770             : 
     771           7 :     state = tevent_req_data(req, struct sdap_nested_group_state);
     772             : 
     773           8 :     TEVENT_REQ_RETURN_ON_ERROR(req);
     774             : 
     775           6 :     ret = sdap_nested_group_extract_hash_table(state, state->group_ctx->users,
     776             :                                                &num_users, &users);
     777           6 :     if (ret != EOK) {
     778           0 :         return ret;
     779             :     }
     780             : 
     781           6 :     DEBUG(SSSDBG_TRACE_FUNC, "%lu users found in the hash table\n",
     782             :                               num_users);
     783             : 
     784           6 :     ret = sdap_nested_group_extract_hash_table(state, state->group_ctx->groups,
     785             :                                                &num_groups, &groups);
     786           6 :     if (ret != EOK) {
     787           0 :         return ret;
     788             :     }
     789             : 
     790           6 :     DEBUG(SSSDBG_TRACE_FUNC, "%lu groups found in the hash table\n",
     791             :                               num_groups);
     792             : 
     793           6 :     if (_num_users != NULL) {
     794           6 :         *_num_users = num_users;
     795             :     }
     796             : 
     797           6 :     if (_users != NULL) {
     798           6 :         *_users = talloc_steal(mem_ctx, users);
     799             :     }
     800             : 
     801           6 :     if (_num_groups!= NULL) {
     802           6 :         *_num_groups = num_groups;
     803             :     }
     804             : 
     805           6 :     if (_groups != NULL) {
     806           6 :         *_groups = talloc_steal(mem_ctx, groups);
     807             :     }
     808             : 
     809           6 :     return EOK;
     810             : }
     811             : 
     812             : struct sdap_nested_group_process_state {
     813             :     struct tevent_context *ev;
     814             :     struct sdap_nested_group_ctx *group_ctx;
     815             :     struct sdap_nested_group_member *missing;
     816             :     int num_missing_total;
     817             :     int num_missing_groups;
     818             :     int nesting_level;
     819             :     char *group_dn;
     820             :     bool deref;
     821             : };
     822             : 
     823             : static void sdap_nested_group_process_done(struct tevent_req *subreq);
     824             : 
     825             : static struct tevent_req *
     826          14 : sdap_nested_group_process_send(TALLOC_CTX *mem_ctx,
     827             :                                struct tevent_context *ev,
     828             :                                struct sdap_nested_group_ctx *group_ctx,
     829             :                                int nesting_level,
     830             :                                struct sysdb_attrs *group)
     831             : {
     832          14 :     struct sdap_nested_group_process_state *state = NULL;
     833          14 :     struct sdap_attr_map *group_map = NULL;
     834          14 :     struct tevent_req *req = NULL;
     835          14 :     struct tevent_req *subreq = NULL;
     836          14 :     struct ldb_message_element *members = NULL;
     837          14 :     const char *orig_dn = NULL;
     838             :     errno_t ret;
     839             : 
     840          14 :     req = tevent_req_create(mem_ctx, &state,
     841             :                             struct sdap_nested_group_process_state);
     842          14 :     if (req == NULL) {
     843           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
     844           0 :         return NULL;
     845             :     }
     846             : 
     847          14 :     state->ev = ev;
     848          14 :     state->group_ctx = group_ctx;
     849          14 :     state->nesting_level = nesting_level;
     850          14 :     group_map = state->group_ctx->opts->group_map;
     851             : 
     852             :     /* get original dn */
     853          14 :     ret = sysdb_attrs_get_string(group, SYSDB_ORIG_DN, &orig_dn);
     854          14 :     if (ret != EOK) {
     855           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve original dn "
     856             :                                     "[%d]: %s\n", ret, strerror(ret));
     857           0 :         goto immediately;
     858             :     }
     859             : 
     860          14 :     state->group_dn = talloc_strdup(state, orig_dn);
     861          14 :     if (state->group_dn == NULL) {
     862           0 :         ret = ENOMEM;
     863           0 :         goto immediately;
     864             :     }
     865             : 
     866          14 :     DEBUG(SSSDBG_TRACE_INTERNAL, "About to process group [%s]\n", orig_dn);
     867             : 
     868             :     /* get member list */
     869          14 :     ret = sysdb_attrs_get_el_ext(group, group_map[SDAP_AT_GROUP_MEMBER].sys_name,
     870             :                                  false, &members);
     871          14 :     if (ret == ENOENT) {
     872           4 :         ret = EOK; /* no members */
     873           4 :         goto immediately;
     874          10 :     } else if (ret != EOK) {
     875           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve member list "
     876             :                                     "[%d]: %s\n", ret, strerror(ret));
     877           0 :         goto immediately;
     878             :     }
     879             : 
     880             :     /* get members that need to be refreshed */
     881          40 :     ret = sdap_nested_group_split_members(state, state->group_ctx,
     882          10 :                                           state->nesting_level, members,
     883          10 :                                           &state->missing,
     884          10 :                                           &state->num_missing_total,
     885          10 :                                           &state->num_missing_groups);
     886          10 :     if (ret != EOK) {
     887           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to split member list "
     888             :                                     "[%d]: %s\n", ret, sss_strerror(ret));
     889           0 :         goto immediately;
     890             :     }
     891             : 
     892          10 :     DEBUG(SSSDBG_TRACE_INTERNAL, "Looking up %d/%d members of group [%s]\n",
     893             :           state->num_missing_total, members->num_values, orig_dn);
     894             : 
     895          10 :     if (state->num_missing_total == 0) {
     896           0 :         ret = EOK; /* we're done */
     897           0 :         goto immediately;
     898             :     }
     899             : 
     900             :     /* process members */
     901          10 :     if (group_ctx->try_deref
     902           0 :             && state->num_missing_total > group_ctx->deref_treshold) {
     903           0 :         DEBUG(SSSDBG_TRACE_INTERNAL, "Dereferencing members of group [%s]\n",
     904             :                                       orig_dn);
     905           0 :         state->deref = true;
     906           0 :         subreq = sdap_nested_group_deref_send(state, ev, group_ctx, members,
     907             :                                               orig_dn,
     908           0 :                                               state->nesting_level);
     909             :     } else {
     910          10 :         DEBUG(SSSDBG_TRACE_INTERNAL, "Members of group [%s] will be "
     911             :                                       "processed individually\n", orig_dn);
     912          10 :         state->deref = false;
     913          40 :         subreq = sdap_nested_group_single_send(state, ev, group_ctx,
     914          10 :                                                state->missing,
     915          10 :                                                state->num_missing_total,
     916          10 :                                                state->num_missing_groups,
     917          10 :                                                state->nesting_level);
     918             :     }
     919          10 :     if (subreq == NULL) {
     920           0 :         ret = ENOMEM;
     921           0 :         goto immediately;
     922             :     }
     923             : 
     924          10 :     tevent_req_set_callback(subreq, sdap_nested_group_process_done, req);
     925             : 
     926          10 :     return req;
     927             : 
     928             : immediately:
     929           4 :     if (ret == EOK) {
     930           4 :         tevent_req_done(req);
     931             :     } else {
     932           0 :         tevent_req_error(req, ret);
     933             :     }
     934           4 :     tevent_req_post(req, ev);
     935             : 
     936           4 :     return req;
     937             : }
     938             : 
     939          10 : static void sdap_nested_group_process_done(struct tevent_req *subreq)
     940             : {
     941          10 :     struct sdap_nested_group_process_state *state = NULL;
     942          10 :     struct tevent_req *req = NULL;
     943             :     errno_t ret;
     944             : 
     945          10 :     req = tevent_req_callback_data(subreq, struct tevent_req);
     946          10 :     state = tevent_req_data(req, struct sdap_nested_group_process_state);
     947             : 
     948          10 :     if (state->deref) {
     949           0 :         ret = sdap_nested_group_deref_recv(subreq);
     950           0 :         talloc_zfree(subreq);
     951           0 :         if (ret == ENOTSUP) {
     952             :             /* dereference is not supported, try again without dereference */
     953           0 :             state->group_ctx->try_deref = false;
     954           0 :             state->deref = false;
     955             : 
     956           0 :             DEBUG(SSSDBG_TRACE_INTERNAL, "Members of group [%s] will be "
     957             :                   "processed individually\n", state->group_dn);
     958             : 
     959           0 :             subreq = sdap_nested_group_single_send(state,
     960             :                                                    state->ev,
     961             :                                                    state->group_ctx,
     962             :                                                    state->missing,
     963             :                                                    state->num_missing_total,
     964             :                                                    state->num_missing_groups,
     965             :                                                    state->nesting_level);
     966           0 :             if (subreq == NULL) {
     967           0 :                 ret = ENOMEM;
     968           0 :                 goto done;
     969             :             }
     970             : 
     971           0 :             tevent_req_set_callback(subreq, sdap_nested_group_process_done,
     972             :                                     req);
     973             : 
     974           0 :             ret = EAGAIN;
     975             :         }
     976             :     } else {
     977          10 :         ret = sdap_nested_group_single_recv(subreq);
     978          10 :         talloc_zfree(subreq);
     979             :     }
     980             : 
     981             : done:
     982          10 :     if (ret == EOK) {
     983           7 :         tevent_req_done(req);
     984           3 :     } else if (ret != EAGAIN) {
     985           3 :         tevent_req_error(req, ret);
     986             :     }
     987          10 : }
     988             : 
     989          14 : static errno_t sdap_nested_group_process_recv(struct tevent_req *req)
     990             : {
     991          17 :     TEVENT_REQ_RETURN_ON_ERROR(req);
     992             : 
     993          11 :     return EOK;
     994             : }
     995             : 
     996             : struct sdap_nested_group_recurse_state {
     997             :     struct tevent_context *ev;
     998             :     struct sdap_nested_group_ctx *group_ctx;
     999             :     struct sysdb_attrs **groups;
    1000             :     int num_groups;
    1001             :     int index;
    1002             :     int nesting_level;
    1003             : };
    1004             : 
    1005             : static errno_t sdap_nested_group_recurse_step(struct tevent_req *req);
    1006             : static void sdap_nested_group_recurse_done(struct tevent_req *subreq);
    1007             : 
    1008             : static struct tevent_req *
    1009           9 : sdap_nested_group_recurse_send(TALLOC_CTX *mem_ctx,
    1010             :                                struct tevent_context *ev,
    1011             :                                struct sdap_nested_group_ctx *group_ctx,
    1012             :                                struct sysdb_attrs **nested_groups,
    1013             :                                int num_groups,
    1014             :                                int nesting_level)
    1015             : {
    1016           9 :     struct sdap_nested_group_recurse_state *state = NULL;
    1017           9 :     struct tevent_req *req = NULL;
    1018             :     errno_t ret;
    1019             : 
    1020           9 :     req = tevent_req_create(mem_ctx, &state,
    1021             :                             struct sdap_nested_group_recurse_state);
    1022           9 :     if (req == NULL) {
    1023           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    1024           0 :         return NULL;
    1025             :     }
    1026             : 
    1027           9 :     state->ev = ev;
    1028           9 :     state->group_ctx = group_ctx;
    1029           9 :     state->groups = nested_groups;
    1030           9 :     state->num_groups = num_groups;
    1031           9 :     state->index = 0;
    1032           9 :     state->nesting_level = nesting_level;
    1033             : 
    1034             :     /* process each group individually */
    1035           9 :     ret = sdap_nested_group_recurse_step(req);
    1036           9 :     if (ret != EAGAIN) {
    1037           3 :         goto immediately;
    1038             :     }
    1039             : 
    1040           6 :     return req;
    1041             : 
    1042             : immediately:
    1043           3 :     if (ret == EOK) {
    1044           3 :         tevent_req_done(req);
    1045             :     } else {
    1046           0 :         tevent_req_error(req, ret);
    1047             :     }
    1048           3 :     tevent_req_post(req, ev);
    1049             : 
    1050           3 :     return req;
    1051             : }
    1052             : 
    1053          14 : static errno_t sdap_nested_group_recurse_step(struct tevent_req *req)
    1054             : {
    1055          14 :     struct sdap_nested_group_recurse_state *state = NULL;
    1056          14 :     struct tevent_req *subreq = NULL;
    1057             : 
    1058          14 :     state = tevent_req_data(req, struct sdap_nested_group_recurse_state);
    1059             : 
    1060          14 :     if (state->index >= state->num_groups) {
    1061             :         /* we're done */
    1062           7 :         return EOK;
    1063             :     }
    1064             : 
    1065           7 :     subreq = sdap_nested_group_process_send(state, state->ev, state->group_ctx,
    1066             :                                             state->nesting_level,
    1067           7 :                                             state->groups[state->index]);
    1068           7 :     if (subreq == NULL) {
    1069           0 :         return ENOMEM;
    1070             :     }
    1071             : 
    1072           7 :     tevent_req_set_callback(subreq, sdap_nested_group_recurse_done, req);
    1073             : 
    1074           7 :     state->index++;
    1075             : 
    1076           7 :     return EAGAIN;
    1077             : }
    1078             : 
    1079           7 : static void sdap_nested_group_recurse_done(struct tevent_req *subreq)
    1080             : {
    1081           7 :     struct tevent_req *req = NULL;
    1082             :     errno_t ret;
    1083             : 
    1084           7 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1085             : 
    1086           7 :     ret = sdap_nested_group_process_recv(subreq);
    1087           7 :     talloc_zfree(subreq);
    1088           7 :     if (ret != EOK) {
    1089           2 :         goto done;
    1090             :     }
    1091             : 
    1092           5 :     ret = sdap_nested_group_recurse_step(req);
    1093             : 
    1094             : done:
    1095           7 :     if (ret == EOK) {
    1096           4 :         tevent_req_done(req);
    1097           3 :     } else if (ret != EAGAIN) {
    1098           2 :         tevent_req_error(req, ret);
    1099             :     }
    1100             : 
    1101           7 :     return;
    1102             : }
    1103             : 
    1104           9 : static errno_t sdap_nested_group_recurse_recv(struct tevent_req *req)
    1105             : {
    1106          11 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1107             : 
    1108           7 :     return EOK;
    1109             : }
    1110             : 
    1111             : struct sdap_nested_group_single_state {
    1112             :     struct tevent_context *ev;
    1113             :     struct sdap_nested_group_ctx *group_ctx;
    1114             :     struct sdap_nested_group_member *members;
    1115             :     int nesting_level;
    1116             : 
    1117             :     struct sdap_nested_group_member *current_member;
    1118             :     int num_members;
    1119             :     int member_index;
    1120             : 
    1121             :     struct sysdb_attrs **nested_groups;
    1122             :     int num_groups;
    1123             : };
    1124             : 
    1125             : static errno_t sdap_nested_group_single_step(struct tevent_req *req);
    1126             : static void sdap_nested_group_single_step_done(struct tevent_req *subreq);
    1127             : static void sdap_nested_group_single_done(struct tevent_req *subreq);
    1128             : 
    1129             : static struct tevent_req *
    1130          10 : sdap_nested_group_single_send(TALLOC_CTX *mem_ctx,
    1131             :                               struct tevent_context *ev,
    1132             :                               struct sdap_nested_group_ctx *group_ctx,
    1133             :                               struct sdap_nested_group_member *members,
    1134             :                               int num_members,
    1135             :                               int num_groups_max,
    1136             :                               int nesting_level)
    1137             : {
    1138          10 :     struct sdap_nested_group_single_state *state = NULL;
    1139          10 :     struct tevent_req *req = NULL;
    1140             :     errno_t ret;
    1141             : 
    1142          10 :     req = tevent_req_create(mem_ctx, &state,
    1143             :                             struct sdap_nested_group_single_state);
    1144          10 :     if (req == NULL) {
    1145           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    1146           0 :         return NULL;
    1147             :     }
    1148             : 
    1149          10 :     state->ev = ev;
    1150          10 :     state->group_ctx = group_ctx;
    1151          10 :     state->members = members;
    1152          10 :     state->nesting_level = nesting_level;
    1153          10 :     state->current_member = NULL;
    1154          10 :     state->num_members = num_members;
    1155          10 :     state->member_index = 0;
    1156          10 :     state->nested_groups = talloc_zero_array(state, struct sysdb_attrs *,
    1157             :                                              num_groups_max);
    1158          10 :     if (state->nested_groups == NULL) {
    1159           0 :         ret = ENOMEM;
    1160           0 :         goto immediately;
    1161             :     }
    1162          10 :     state->num_groups = 0; /* we will count exact number of the groups */
    1163             : 
    1164             :     /* process each member individually */
    1165          10 :     ret = sdap_nested_group_single_step(req);
    1166          10 :     if (ret != EAGAIN) {
    1167           0 :         goto immediately;
    1168             :     }
    1169             : 
    1170          10 :     return req;
    1171             : 
    1172             : immediately:
    1173           0 :     if (ret == EOK) {
    1174           0 :         tevent_req_done(req);
    1175             :     } else {
    1176           0 :         tevent_req_error(req, ret);
    1177             :     }
    1178           0 :     tevent_req_post(req, ev);
    1179             : 
    1180           0 :     return req;
    1181             : }
    1182             : 
    1183          25 : static errno_t sdap_nested_group_single_step(struct tevent_req *req)
    1184             : {
    1185          25 :     struct sdap_nested_group_single_state *state = NULL;
    1186          25 :     struct tevent_req *subreq = NULL;
    1187             : 
    1188          25 :     state = tevent_req_data(req, struct sdap_nested_group_single_state);
    1189             : 
    1190          25 :     if (state->member_index >= state->num_members) {
    1191             :         /* we're done */
    1192           9 :         return EOK;
    1193             :     }
    1194             : 
    1195          16 :     state->current_member = &state->members[state->member_index];
    1196          16 :     state->member_index++;
    1197             : 
    1198          16 :     switch (state->current_member->type) {
    1199             :     case SDAP_NESTED_GROUP_DN_USER:
    1200           8 :         subreq = sdap_nested_group_lookup_user_send(state, state->ev,
    1201             :                                                     state->group_ctx,
    1202             :                                                     state->current_member);
    1203           8 :         break;
    1204             :     case SDAP_NESTED_GROUP_DN_GROUP:
    1205           8 :         subreq = sdap_nested_group_lookup_group_send(state, state->ev,
    1206             :                                                      state->group_ctx,
    1207             :                                                      state->current_member);
    1208           8 :         break;
    1209             :     case SDAP_NESTED_GROUP_DN_UNKNOWN:
    1210           0 :         subreq = sdap_nested_group_lookup_unknown_send(state, state->ev,
    1211             :                                                    state->group_ctx,
    1212             :                                                    state->current_member);
    1213           0 :         break;
    1214             :     }
    1215             : 
    1216          16 :     if (subreq == NULL) {
    1217           0 :         return ENOMEM;
    1218             :     }
    1219             : 
    1220          16 :     tevent_req_set_callback(subreq, sdap_nested_group_single_step_done, req);
    1221             : 
    1222          16 :     return EAGAIN;
    1223             : }
    1224             : 
    1225             : static errno_t
    1226          16 : sdap_nested_group_single_step_process(struct tevent_req *subreq)
    1227             : {
    1228          16 :     struct sdap_nested_group_single_state *state = NULL;
    1229          16 :     struct tevent_req *req = NULL;
    1230          16 :     struct sysdb_attrs *entry = NULL;
    1231          16 :     enum sdap_nested_group_dn_type type = SDAP_NESTED_GROUP_DN_UNKNOWN;
    1232          16 :     const char *orig_dn = NULL;
    1233             :     errno_t ret;
    1234             : 
    1235          16 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1236          16 :     state = tevent_req_data(req, struct sdap_nested_group_single_state);
    1237             : 
    1238             :     /* set correct type if possible */
    1239          16 :     if (state->current_member->type == SDAP_NESTED_GROUP_DN_UNKNOWN) {
    1240           0 :         ret = sdap_nested_group_lookup_unknown_recv(state, subreq,
    1241             :                                                     &entry, &type);
    1242           0 :         if (ret != EOK) {
    1243           0 :             goto done;
    1244             :         }
    1245             : 
    1246           0 :         if (entry != NULL) {
    1247           0 :             state->current_member->type = type;
    1248             :         }
    1249             :     }
    1250             : 
    1251          16 :     switch (state->current_member->type) {
    1252             :     case SDAP_NESTED_GROUP_DN_USER:
    1253           8 :         if (entry == NULL) {
    1254             :             /* type was not unknown, receive data */
    1255           8 :             ret = sdap_nested_group_lookup_user_recv(state, subreq, &entry);
    1256           8 :             if (ret != EOK) {
    1257           1 :                 goto done;
    1258             :             }
    1259             : 
    1260           7 :             if (entry == NULL) {
    1261             :                 /* user not found, continue */
    1262           0 :                 break;
    1263             :             }
    1264             :         }
    1265             : 
    1266             :         /* save user in hash table */
    1267           7 :         ret = sdap_nested_group_hash_user(state->group_ctx, entry);
    1268           7 :         if (ret == EEXIST) {
    1269             :             /* the user is already present, skip it */
    1270           1 :             talloc_zfree(entry);
    1271           1 :             ret = EOK;
    1272           1 :             goto done;
    1273           6 :         } else if (ret != EOK) {
    1274           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Unable to save user in hash table "
    1275             :                                         "[%d]: %s\n", ret, strerror(ret));
    1276           0 :             goto done;
    1277             :         }
    1278           6 :         break;
    1279             :     case SDAP_NESTED_GROUP_DN_GROUP:
    1280           8 :         if (entry == NULL) {
    1281             :             /* type was not unknown, receive data */
    1282           8 :             ret = sdap_nested_group_lookup_group_recv(state, subreq, &entry);
    1283           8 :             if (ret != EOK) {
    1284           0 :                 goto done;
    1285             :             }
    1286             : 
    1287           8 :             if (entry == NULL) {
    1288             :                 /* group not found, continue */
    1289           0 :                 break;
    1290             :             }
    1291             :         } else {
    1292             :             /* the type was unknown so we had to pull the group,
    1293             :              * but we don't want to process it if we have reached
    1294             :              * the nesting level */
    1295           0 :             if (state->nesting_level >= state->group_ctx->max_nesting_level) {
    1296           0 :                 ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &orig_dn);
    1297           0 :                 if (ret != EOK) {
    1298           0 :                     DEBUG(SSSDBG_MINOR_FAILURE,
    1299             :                           "The entry has no originalDN\n");
    1300           0 :                     orig_dn = "invalid";
    1301             :                 }
    1302             : 
    1303           0 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is outside nesting limit "
    1304             :                       "(level %d), skipping\n", orig_dn, state->nesting_level);
    1305           0 :                 break;
    1306             :             }
    1307             :         }
    1308             : 
    1309             :         /* save group in hash table */
    1310           8 :         ret = sdap_nested_group_hash_group(state->group_ctx, entry);
    1311           8 :         if (ret == EEXIST) {
    1312             :             /* the group is already present, skip it */
    1313           1 :             talloc_zfree(entry);
    1314           1 :             ret = EOK;
    1315           1 :             goto done;
    1316           7 :         } else if (ret != EOK) {
    1317           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Unable to save group in hash table "
    1318             :                                         "[%d]: %s\n", ret, strerror(ret));
    1319           0 :             goto done;
    1320             :         }
    1321             : 
    1322             :         /* remember the group for later processing */
    1323           7 :         state->nested_groups[state->num_groups] = entry;
    1324           7 :         state->num_groups++;
    1325             : 
    1326           7 :         break;
    1327             :     case SDAP_NESTED_GROUP_DN_UNKNOWN:
    1328             :         /* not found in users nor nested_groups, continue */
    1329           0 :         break;
    1330             :     }
    1331             : 
    1332          13 :     ret = EOK;
    1333             : 
    1334             : done:
    1335          16 :     return ret;
    1336             : }
    1337             : 
    1338          16 : static void sdap_nested_group_single_step_done(struct tevent_req *subreq)
    1339             : {
    1340          16 :     struct sdap_nested_group_single_state *state = NULL;
    1341          16 :     struct tevent_req *req = NULL;
    1342             :     errno_t ret;
    1343             : 
    1344          16 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1345          16 :     state = tevent_req_data(req, struct sdap_nested_group_single_state);
    1346             : 
    1347             :     /* process direct members */
    1348          16 :     ret = sdap_nested_group_single_step_process(subreq);
    1349          16 :     talloc_zfree(subreq);
    1350          16 :     if (ret != EOK) {
    1351           1 :         DEBUG(SSSDBG_CRIT_FAILURE, "Error processing direct membership "
    1352             :                                     "[%d]: %s\n", ret, strerror(ret));
    1353           1 :         goto done;
    1354             :     }
    1355             : 
    1356          15 :     ret = sdap_nested_group_single_step(req);
    1357          15 :     if (ret == EOK) {
    1358             :         /* we have processed all direct members,
    1359             :          * now recurse and process nested groups */
    1360           9 :         subreq = sdap_nested_group_recurse_send(state, state->ev,
    1361             :                                                 state->group_ctx,
    1362             :                                                 state->nested_groups,
    1363             :                                                 state->num_groups,
    1364           9 :                                                 state->nesting_level + 1);
    1365           9 :         if (subreq == NULL) {
    1366           0 :             ret = ENOMEM;
    1367           0 :             goto done;
    1368             :         }
    1369             : 
    1370           9 :         tevent_req_set_callback(subreq, sdap_nested_group_single_done, req);
    1371           6 :     } else if (ret != EAGAIN) {
    1372             :         /* error */
    1373           0 :         goto done;
    1374             :     }
    1375             : 
    1376             :     /* we're not done yet */
    1377          15 :     ret = EAGAIN;
    1378             : 
    1379             : done:
    1380          16 :     if (ret == EOK) {
    1381             :         /* tevent_req_error() cannot cope with EOK */
    1382           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "We should not get here with EOK\n");
    1383           0 :         tevent_req_error(req, EINVAL);
    1384          16 :     } else if (ret != EAGAIN) {
    1385           1 :         tevent_req_error(req, ret);
    1386             :     }
    1387             : 
    1388          16 :     return;
    1389             : }
    1390             : 
    1391           9 : static void sdap_nested_group_single_done(struct tevent_req *subreq)
    1392             : {
    1393           9 :     struct tevent_req *req = NULL;
    1394             :     errno_t ret;
    1395             : 
    1396           9 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1397             : 
    1398             :     /* all nested groups are completed */
    1399           9 :     ret = sdap_nested_group_recurse_recv(subreq);
    1400           9 :     talloc_zfree(subreq);
    1401           9 :     if (ret != EOK) {
    1402           2 :         DEBUG(SSSDBG_CRIT_FAILURE, "Error processing nested groups "
    1403             :                                     "[%d]: %s.\n", ret, strerror(ret));
    1404           2 :         tevent_req_error(req, ret);
    1405           2 :         return;
    1406             :     }
    1407             : 
    1408           7 :     tevent_req_done(req);
    1409             : 
    1410           7 :     return;
    1411             : }
    1412             : 
    1413          10 : static errno_t sdap_nested_group_single_recv(struct tevent_req *req)
    1414             : {
    1415          13 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1416             : 
    1417           7 :     return EOK;
    1418             : }
    1419             : 
    1420             : /* This should be a function pointer set from the IPA provider */
    1421           0 : static errno_t sdap_nested_group_get_ipa_user(TALLOC_CTX *mem_ctx,
    1422             :                                               const char *user_dn,
    1423             :                                               struct sysdb_ctx *sysdb,
    1424             :                                               struct sysdb_attrs **_user)
    1425             : {
    1426             :     errno_t ret;
    1427           0 :     struct sysdb_attrs *user = NULL;
    1428             :     char *name;
    1429           0 :     struct ldb_dn *dn = NULL;
    1430             :     const char *rdn_name;
    1431             :     const char *users_comp_name;
    1432             :     const char *acct_comp_name;
    1433             :     const struct ldb_val *rdn_val;
    1434             :     const struct ldb_val *users_comp_val;
    1435             :     const struct ldb_val *acct_comp_val;
    1436             :     TALLOC_CTX *tmp_ctx;
    1437             : 
    1438           0 :     tmp_ctx = talloc_new(NULL);
    1439           0 :     if (!tmp_ctx) return ENOMEM;
    1440             : 
    1441             :     /* return username if dn is in form:
    1442             :      * uid=username,cn=users,cn=accounts,dc=example,dc=com */
    1443             : 
    1444           0 :     dn = ldb_dn_new(tmp_ctx, sysdb_ctx_get_ldb(sysdb), user_dn);
    1445           0 :     if (dn == NULL) {
    1446           0 :         ret = ENOMEM;
    1447           0 :         goto done;
    1448             :     }
    1449             : 
    1450             :     /* rdn, users, accounts and least one domain component */
    1451           0 :     if (ldb_dn_get_comp_num(dn) < 4) {
    1452           0 :         ret = ENOENT;
    1453           0 :         goto done;
    1454             :     }
    1455             : 
    1456           0 :     rdn_name = ldb_dn_get_rdn_name(dn);
    1457           0 :     if (rdn_name == NULL) {
    1458           0 :         ret = EINVAL;
    1459           0 :         goto done;
    1460             :     }
    1461             : 
    1462             :     /* rdn must be 'uid' */
    1463           0 :     if (strcasecmp("uid", rdn_name) != 0) {
    1464           0 :         ret = ENOENT;
    1465           0 :         goto done;
    1466             :     }
    1467             : 
    1468             :     /* second component must be 'cn=users' */
    1469           0 :     users_comp_name = ldb_dn_get_component_name(dn, 1);
    1470           0 :     if (strcasecmp("cn", users_comp_name) != 0) {
    1471           0 :         ret = ENOENT;
    1472           0 :         goto done;
    1473             :     }
    1474             : 
    1475           0 :     users_comp_val = ldb_dn_get_component_val(dn, 1);
    1476           0 :     if (strncasecmp("users", (const char *) users_comp_val->data,
    1477             :                     users_comp_val->length) != 0) {
    1478           0 :         ret = ENOENT;
    1479           0 :         goto done;
    1480             :     }
    1481             : 
    1482             :     /* third component must be 'cn=accounts' */
    1483           0 :     acct_comp_name = ldb_dn_get_component_name(dn, 2);
    1484           0 :     if (strcasecmp("cn", acct_comp_name) != 0) {
    1485           0 :         ret = ENOENT;
    1486           0 :         goto done;
    1487             :     }
    1488             : 
    1489           0 :     acct_comp_val = ldb_dn_get_component_val(dn, 2);
    1490           0 :     if (strncasecmp("accounts", (const char *) acct_comp_val->data,
    1491             :                     acct_comp_val->length) != 0) {
    1492           0 :         ret = ENOENT;
    1493           0 :         goto done;
    1494             :     }
    1495             : 
    1496             :     /* value of rdn is username */
    1497           0 :     user = sysdb_new_attrs(tmp_ctx);
    1498           0 :     if (user == NULL) {
    1499           0 :         ret = ENOMEM;
    1500           0 :         goto done;
    1501             :     }
    1502             : 
    1503           0 :     rdn_val = ldb_dn_get_rdn_val(dn);
    1504           0 :     name = talloc_strndup(user, (const char *)rdn_val->data, rdn_val->length);
    1505           0 :     if (name == NULL) {
    1506           0 :         ret = ENOMEM;
    1507           0 :         goto done;
    1508             :     }
    1509             : 
    1510           0 :     ret = sysdb_attrs_add_string(user, SYSDB_NAME, name);
    1511           0 :     if (ret != EOK) {
    1512           0 :         goto done;
    1513             :     }
    1514             : 
    1515           0 :     ret = sysdb_attrs_add_string(user, SYSDB_ORIG_DN, user_dn);
    1516           0 :     if (ret != EOK) {
    1517           0 :         goto done;
    1518             :     }
    1519             : 
    1520           0 :     ret = sysdb_attrs_add_string(user, SYSDB_OBJECTCLASS, SYSDB_USER_CLASS);
    1521           0 :     if (ret != EOK) {
    1522           0 :         goto done;
    1523             :     }
    1524             : 
    1525           0 :     *_user = talloc_steal(mem_ctx, user);
    1526             : 
    1527             : done:
    1528           0 :     talloc_free(tmp_ctx);
    1529           0 :     return ret;
    1530             : }
    1531             : 
    1532             : struct sdap_nested_group_lookup_user_state {
    1533             :     struct sysdb_attrs *user;
    1534             : };
    1535             : 
    1536             : static void sdap_nested_group_lookup_user_done(struct tevent_req *subreq);
    1537             : 
    1538             : static struct tevent_req *
    1539           8 : sdap_nested_group_lookup_user_send(TALLOC_CTX *mem_ctx,
    1540             :                                    struct tevent_context *ev,
    1541             :                                    struct sdap_nested_group_ctx *group_ctx,
    1542             :                                    struct sdap_nested_group_member *member)
    1543             : {
    1544           8 :     struct sdap_nested_group_lookup_user_state *state = NULL;
    1545           8 :     struct tevent_req *req = NULL;
    1546           8 :     struct tevent_req *subreq = NULL;
    1547           8 :     const char **attrs = NULL;
    1548           8 :     const char *base_filter = NULL;
    1549           8 :     const char *filter = NULL;
    1550             :     errno_t ret;
    1551             : 
    1552           8 :     req = tevent_req_create(mem_ctx, &state,
    1553             :                             struct sdap_nested_group_lookup_user_state);
    1554           8 :     if (req == NULL) {
    1555           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    1556           0 :         return NULL;
    1557             :     }
    1558             : 
    1559           8 :     if (group_ctx->opts->schema_type == SDAP_SCHEMA_IPA_V1) {
    1560             :         /* if the schema is IPA, then just shortcut and guess the name */
    1561           0 :         ret = sdap_nested_group_get_ipa_user(state, member->dn,
    1562           0 :                                              group_ctx->domain->sysdb,
    1563           0 :                                              &state->user);
    1564           0 :         if (ret == EOK) {
    1565           0 :             goto immediately;
    1566             :         }
    1567             : 
    1568           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "Couldn't parse out user information "
    1569             :               "based on DN %s, falling back to an LDAP lookup\n", member->dn);
    1570             :     }
    1571             : 
    1572             :     /* only pull down username and originalDN */
    1573           8 :     attrs = talloc_array(state, const char *, 3);
    1574           8 :     if (attrs == NULL) {
    1575           0 :         ret = ENOMEM;
    1576           0 :         goto immediately;
    1577             :     }
    1578             : 
    1579           8 :     attrs[0] = "objectClass";
    1580           8 :     attrs[1] = group_ctx->opts->user_map[SDAP_AT_USER_NAME].name;
    1581           8 :     attrs[2] = NULL;
    1582             : 
    1583             :     /* create filter */
    1584           8 :     base_filter = talloc_asprintf(state, "(objectclass=%s)",
    1585           8 :                                   group_ctx->opts->user_map[SDAP_OC_USER].name);
    1586           8 :     if (base_filter == NULL) {
    1587           0 :         ret = ENOMEM;
    1588           0 :         goto immediately;
    1589             :     }
    1590             : 
    1591             :     /* use search base filter if needed */
    1592           8 :     filter = sdap_get_id_specific_filter(state, base_filter,
    1593             :                                          member->user_filter);
    1594           8 :     if (filter == NULL) {
    1595           0 :         ret = ENOMEM;
    1596           0 :         goto immediately;
    1597             :     }
    1598             : 
    1599             :     /* search */
    1600          24 :     subreq = sdap_get_generic_send(state, ev, group_ctx->opts, group_ctx->sh,
    1601             :                                    member->dn, LDAP_SCOPE_BASE, filter, attrs,
    1602           8 :                                    group_ctx->opts->user_map,
    1603           8 :                                    group_ctx->opts->user_map_cnt,
    1604           8 :                                    dp_opt_get_int(group_ctx->opts->basic,
    1605             :                                                   SDAP_SEARCH_TIMEOUT),
    1606             :                                    false);
    1607           8 :     if (subreq == NULL) {
    1608           0 :         ret = ENOMEM;
    1609           0 :         goto immediately;
    1610             :     }
    1611             : 
    1612           8 :     tevent_req_set_callback(subreq, sdap_nested_group_lookup_user_done, req);
    1613             : 
    1614           8 :     return req;
    1615             : 
    1616             : immediately:
    1617           0 :     if (ret == EOK) {
    1618           0 :         tevent_req_done(req);
    1619             :     } else {
    1620           0 :         tevent_req_error(req, ret);
    1621             :     }
    1622           0 :     tevent_req_post(req, ev);
    1623             : 
    1624           0 :     return req;
    1625             : }
    1626             : 
    1627           8 : static void sdap_nested_group_lookup_user_done(struct tevent_req *subreq)
    1628             : {
    1629           8 :     struct sdap_nested_group_lookup_user_state *state = NULL;
    1630           8 :     struct tevent_req *req = NULL;
    1631           8 :     struct sysdb_attrs **user = NULL;
    1632           8 :     size_t count = 0;
    1633             :     errno_t ret;
    1634             : 
    1635           8 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1636           8 :     state = tevent_req_data(req, struct sdap_nested_group_lookup_user_state);
    1637             : 
    1638           8 :     ret = sdap_get_generic_recv(subreq, state, &count, &user);
    1639           8 :     talloc_zfree(subreq);
    1640           8 :     if (ret == ENOENT) {
    1641           0 :         count = 0;
    1642           8 :     } else if (ret != EOK) {
    1643           1 :         goto done;
    1644             :     }
    1645             : 
    1646           7 :     if (count == 1) {
    1647           7 :         state->user = user[0];
    1648           0 :     } else if (count == 0) {
    1649             :         /* group not found */
    1650           0 :         state->user = NULL;
    1651             :     } else {
    1652           0 :         DEBUG(SSSDBG_OP_FAILURE,
    1653             :               "BASE search returned more than one records\n");
    1654           0 :         ret = EIO;
    1655           0 :         goto done;
    1656             :     }
    1657             : 
    1658           7 :     ret = EOK;
    1659             : 
    1660             : done:
    1661           8 :     if (ret != EOK) {
    1662           1 :         tevent_req_error(req, ret);
    1663           1 :         return;
    1664             :     }
    1665             : 
    1666           7 :     tevent_req_done(req);
    1667             : }
    1668             : 
    1669           8 : static errno_t sdap_nested_group_lookup_user_recv(TALLOC_CTX *mem_ctx,
    1670             :                                                   struct tevent_req *req,
    1671             :                                                   struct sysdb_attrs **_user)
    1672             : {
    1673           8 :     struct sdap_nested_group_lookup_user_state *state = NULL;
    1674           8 :     state = tevent_req_data(req, struct sdap_nested_group_lookup_user_state);
    1675             : 
    1676           9 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1677             : 
    1678           7 :     if (_user != NULL) {
    1679           7 :         *_user = talloc_steal(mem_ctx, state->user);
    1680             :     }
    1681             : 
    1682           7 :     return EOK;
    1683             : }
    1684             : 
    1685             : struct sdap_nested_group_lookup_group_state {
    1686             :     struct sysdb_attrs *group;
    1687             : };
    1688             : 
    1689             : static void sdap_nested_group_lookup_group_done(struct tevent_req *subreq);
    1690             : 
    1691             : static struct tevent_req *
    1692           8 : sdap_nested_group_lookup_group_send(TALLOC_CTX *mem_ctx,
    1693             :                                     struct tevent_context *ev,
    1694             :                                     struct sdap_nested_group_ctx *group_ctx,
    1695             :                                     struct sdap_nested_group_member *member)
    1696             : {
    1697           8 :      struct sdap_nested_group_lookup_group_state *state = NULL;
    1698           8 :      struct tevent_req *req = NULL;
    1699           8 :      struct tevent_req *subreq = NULL;
    1700           8 :      struct sdap_attr_map *map = group_ctx->opts->group_map;
    1701           8 :      const char **attrs = NULL;
    1702           8 :      const char *base_filter = NULL;
    1703           8 :      const char *filter = NULL;
    1704             :      char *oc_list;
    1705             :      errno_t ret;
    1706             : 
    1707           8 :      req = tevent_req_create(mem_ctx, &state,
    1708             :                              struct sdap_nested_group_lookup_group_state);
    1709           8 :      if (req == NULL) {
    1710           0 :          DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    1711           0 :          return NULL;
    1712             :      }
    1713             : 
    1714           8 :      ret = build_attrs_from_map(state, group_ctx->opts->group_map,
    1715             :                                 SDAP_OPTS_GROUP, NULL, &attrs, NULL);
    1716           8 :      if (ret != EOK) {
    1717           0 :          goto immediately;
    1718             :      }
    1719             : 
    1720             :      /* create filter */
    1721           8 :      oc_list = sdap_make_oc_list(state, map);
    1722           8 :      if (oc_list == NULL) {
    1723           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create objectClass list.\n");
    1724           0 :         ret = ENOMEM;
    1725           0 :         goto immediately;
    1726             :      }
    1727             : 
    1728           8 :      base_filter = talloc_asprintf(attrs, "(&(%s)(%s=*))", oc_list,
    1729           8 :                                    map[SDAP_AT_GROUP_NAME].name);
    1730           8 :      if (base_filter == NULL) {
    1731           0 :          ret = ENOMEM;
    1732           0 :          goto immediately;
    1733             :      }
    1734             : 
    1735             :      /* use search base filter if needed */
    1736           8 :      filter = sdap_get_id_specific_filter(state, base_filter,
    1737             :                                           member->group_filter);
    1738           8 :      if (filter == NULL) {
    1739           0 :          ret = ENOMEM;
    1740           0 :          goto immediately;
    1741             :      }
    1742             : 
    1743             :      /* search */
    1744           8 :      subreq = sdap_get_generic_send(state, ev, group_ctx->opts, group_ctx->sh,
    1745             :                                     member->dn, LDAP_SCOPE_BASE, filter, attrs,
    1746             :                                     map, SDAP_OPTS_GROUP,
    1747           8 :                                     dp_opt_get_int(group_ctx->opts->basic,
    1748             :                                                    SDAP_SEARCH_TIMEOUT),
    1749             :                                     false);
    1750           8 :      if (subreq == NULL) {
    1751           0 :          ret = ENOMEM;
    1752           0 :          goto immediately;
    1753             :      }
    1754             : 
    1755           8 :      tevent_req_set_callback(subreq, sdap_nested_group_lookup_group_done, req);
    1756             : 
    1757           8 :      return req;
    1758             : 
    1759             : immediately:
    1760           0 :     if (ret == EOK) {
    1761           0 :         tevent_req_done(req);
    1762             :     } else {
    1763           0 :         tevent_req_error(req, ret);
    1764             :     }
    1765           0 :     tevent_req_post(req, ev);
    1766             : 
    1767           0 :     return req;
    1768             : }
    1769             : 
    1770           8 : static void sdap_nested_group_lookup_group_done(struct tevent_req *subreq)
    1771             : {
    1772           8 :     struct sdap_nested_group_lookup_group_state *state = NULL;
    1773           8 :     struct tevent_req *req = NULL;
    1774           8 :     struct sysdb_attrs **group = NULL;
    1775           8 :     size_t count = 0;
    1776             :     errno_t ret;
    1777             : 
    1778           8 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1779           8 :     state = tevent_req_data(req, struct sdap_nested_group_lookup_group_state);
    1780             : 
    1781           8 :     ret = sdap_get_generic_recv(subreq, state, &count, &group);
    1782           8 :     talloc_zfree(subreq);
    1783           8 :     if (ret == ENOENT) {
    1784           0 :         count = 0;
    1785           8 :     } else if (ret != EOK) {
    1786           0 :         goto done;
    1787             :     }
    1788             : 
    1789           8 :     if (count == 1) {
    1790           8 :         state->group = group[0];
    1791           0 :     } else if (count == 0) {
    1792             :         /* group not found */
    1793           0 :         state->group = NULL;
    1794             :     } else {
    1795           0 :         DEBUG(SSSDBG_OP_FAILURE,
    1796             :               "BASE search returned more than one records\n");
    1797           0 :         ret = EIO;
    1798           0 :         goto done;
    1799             :     }
    1800             : 
    1801           8 :     ret = EOK;
    1802             : 
    1803             : done:
    1804           8 :     if (ret != EOK) {
    1805           0 :         tevent_req_error(req, ret);
    1806           0 :         return;
    1807             :     }
    1808             : 
    1809           8 :     tevent_req_done(req);
    1810             : }
    1811             : 
    1812           8 : static errno_t sdap_nested_group_lookup_group_recv(TALLOC_CTX *mem_ctx,
    1813             :                                                    struct tevent_req *req,
    1814             :                                                    struct sysdb_attrs **_group)
    1815             : {
    1816           8 :      struct sdap_nested_group_lookup_group_state *state = NULL;
    1817           8 :      state = tevent_req_data(req, struct sdap_nested_group_lookup_group_state);
    1818             : 
    1819           8 :      TEVENT_REQ_RETURN_ON_ERROR(req);
    1820             : 
    1821           8 :      if (_group != NULL) {
    1822           8 :          *_group = talloc_steal(mem_ctx, state->group);
    1823             :      }
    1824             : 
    1825           8 :      return EOK;
    1826             : }
    1827             : 
    1828             : struct sdap_nested_group_lookup_unknown_state {
    1829             :     struct tevent_context *ev;
    1830             :     struct sdap_nested_group_ctx *group_ctx;
    1831             :     struct sdap_nested_group_member *member;
    1832             :     enum sdap_nested_group_dn_type type;
    1833             :     struct sysdb_attrs *entry;
    1834             : };
    1835             : 
    1836             : static void
    1837             : sdap_nested_group_lookup_unknown_user_done(struct tevent_req *subreq);
    1838             : 
    1839             : static void
    1840             : sdap_nested_group_lookup_unknown_group_done(struct tevent_req *subreq);
    1841             : 
    1842             : static struct tevent_req *
    1843           0 : sdap_nested_group_lookup_unknown_send(TALLOC_CTX *mem_ctx,
    1844             :                                       struct tevent_context *ev,
    1845             :                                       struct sdap_nested_group_ctx *group_ctx,
    1846             :                                       struct sdap_nested_group_member *member)
    1847             : {
    1848           0 :     struct sdap_nested_group_lookup_unknown_state *state = NULL;
    1849           0 :     struct tevent_req *req = NULL;
    1850           0 :     struct tevent_req *subreq = NULL;
    1851             :     errno_t ret;
    1852             : 
    1853           0 :     req = tevent_req_create(mem_ctx, &state,
    1854             :                             struct sdap_nested_group_lookup_unknown_state);
    1855           0 :     if (req == NULL) {
    1856           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    1857           0 :         return NULL;
    1858             :     }
    1859             : 
    1860           0 :     state->ev = ev;
    1861           0 :     state->group_ctx = group_ctx;
    1862           0 :     state->member = member;
    1863             : 
    1864             :     /* try users first */
    1865           0 :     subreq = sdap_nested_group_lookup_user_send(state,
    1866           0 :                                                 state->ev,
    1867           0 :                                                 state->group_ctx,
    1868           0 :                                                 state->member);
    1869           0 :     if (subreq == NULL) {
    1870           0 :         ret = ENOMEM;
    1871           0 :         goto immediately;
    1872             :     }
    1873             : 
    1874           0 :     tevent_req_set_callback(subreq, sdap_nested_group_lookup_unknown_user_done,
    1875             :                             req);
    1876             : 
    1877           0 :     return req;
    1878             : 
    1879             : immediately:
    1880           0 :     if (ret == EOK) {
    1881           0 :         tevent_req_done(req);
    1882             :     } else {
    1883           0 :         tevent_req_error(req, ret);
    1884             :     }
    1885           0 :     tevent_req_post(req, ev);
    1886             : 
    1887           0 :     return req;
    1888             : }
    1889             : 
    1890             : static void
    1891           0 : sdap_nested_group_lookup_unknown_user_done(struct tevent_req *subreq)
    1892             : {
    1893           0 :     struct sdap_nested_group_lookup_unknown_state *state = NULL;
    1894           0 :     struct tevent_req *req = NULL;
    1895           0 :     struct sysdb_attrs *entry = NULL;
    1896             :     errno_t ret;
    1897             : 
    1898           0 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1899           0 :     state = tevent_req_data(req, struct sdap_nested_group_lookup_unknown_state);
    1900             : 
    1901           0 :     ret = sdap_nested_group_lookup_user_recv(state, subreq, &entry);
    1902           0 :     talloc_zfree(subreq);
    1903           0 :     if (ret != EOK) {
    1904           0 :         goto done;
    1905             :     }
    1906             : 
    1907           0 :     if (entry != NULL) {
    1908             :         /* found in users */
    1909           0 :         state->entry = entry;
    1910           0 :         state->type = SDAP_NESTED_GROUP_DN_USER;
    1911           0 :         ret = EOK;
    1912           0 :         goto done;
    1913             :     }
    1914             : 
    1915             :     /* not found in users, try group */
    1916           0 :     subreq = sdap_nested_group_lookup_group_send(state,
    1917             :                                                  state->ev,
    1918             :                                                  state->group_ctx,
    1919             :                                                  state->member);
    1920           0 :     if (subreq == NULL) {
    1921           0 :         ret = ENOMEM;
    1922           0 :         goto done;
    1923             :     }
    1924             : 
    1925           0 :     tevent_req_set_callback(subreq, sdap_nested_group_lookup_unknown_group_done,
    1926             :                             req);
    1927             : 
    1928           0 :     ret = EAGAIN;
    1929             : 
    1930             : done:
    1931           0 :     if (ret == EOK) {
    1932           0 :         tevent_req_done(req);
    1933           0 :     } else if (ret != EAGAIN) {
    1934           0 :         tevent_req_error(req, ret);
    1935             :     }
    1936             : 
    1937           0 :     return;
    1938             : }
    1939             : 
    1940             : static void
    1941           0 : sdap_nested_group_lookup_unknown_group_done(struct tevent_req *subreq)
    1942             : {
    1943           0 :     struct sdap_nested_group_lookup_unknown_state *state = NULL;
    1944           0 :     struct tevent_req *req = NULL;
    1945           0 :     struct sysdb_attrs *entry = NULL;
    1946             :     errno_t ret;
    1947             : 
    1948           0 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1949           0 :     state = tevent_req_data(req, struct sdap_nested_group_lookup_unknown_state);
    1950             : 
    1951           0 :     ret = sdap_nested_group_lookup_group_recv(state, subreq, &entry);
    1952           0 :     talloc_zfree(subreq);
    1953           0 :     if (ret != EOK) {
    1954           0 :         goto done;
    1955             :     }
    1956             : 
    1957           0 :     if (entry == NULL) {
    1958             :         /* not found, end request */
    1959           0 :         state->entry = NULL;
    1960           0 :         state->type = SDAP_NESTED_GROUP_DN_UNKNOWN;
    1961             :     } else {
    1962             :         /* found in groups */
    1963           0 :         state->entry = entry;
    1964           0 :         state->type = SDAP_NESTED_GROUP_DN_GROUP;
    1965             :     }
    1966             : 
    1967           0 :     ret = EOK;
    1968             : 
    1969             : done:
    1970           0 :     if (ret != EOK) {
    1971           0 :         tevent_req_error(req, ret);
    1972           0 :         return;
    1973             :     }
    1974             : 
    1975           0 :     tevent_req_done(req);
    1976             : }
    1977             : 
    1978             : static errno_t
    1979           0 : sdap_nested_group_lookup_unknown_recv(TALLOC_CTX *mem_ctx,
    1980             :                                       struct tevent_req *req,
    1981             :                                       struct sysdb_attrs **_entry,
    1982             :                                       enum sdap_nested_group_dn_type *_type)
    1983             : {
    1984           0 :     struct sdap_nested_group_lookup_unknown_state *state = NULL;
    1985           0 :     state = tevent_req_data(req, struct sdap_nested_group_lookup_unknown_state);
    1986             : 
    1987           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1988             : 
    1989           0 :     if (_entry != NULL) {
    1990           0 :         *_entry = talloc_steal(mem_ctx, state->entry);
    1991             :     }
    1992             : 
    1993           0 :     if (_type != NULL) {
    1994           0 :         *_type = state->type;
    1995             :     }
    1996             : 
    1997             : 
    1998           0 :     return EOK;
    1999             : }
    2000             : 
    2001             : struct sdap_nested_group_deref_state {
    2002             :     struct tevent_context *ev;
    2003             :     struct sdap_nested_group_ctx *group_ctx;
    2004             :     struct ldb_message_element *members;
    2005             :     int nesting_level;
    2006             : 
    2007             :     struct sysdb_attrs **nested_groups;
    2008             :     int num_groups;
    2009             : };
    2010             : 
    2011             : static void sdap_nested_group_deref_direct_done(struct tevent_req *subreq);
    2012             : static void sdap_nested_group_deref_done(struct tevent_req *subreq);
    2013             : 
    2014             : static struct tevent_req *
    2015           0 : sdap_nested_group_deref_send(TALLOC_CTX *mem_ctx,
    2016             :                              struct tevent_context *ev,
    2017             :                              struct sdap_nested_group_ctx *group_ctx,
    2018             :                              struct ldb_message_element *members,
    2019             :                              const char *group_dn,
    2020             :                              int nesting_level)
    2021             : {
    2022           0 :     struct sdap_nested_group_deref_state *state = NULL;
    2023           0 :     struct tevent_req *req = NULL;
    2024           0 :     struct tevent_req *subreq = NULL;
    2025           0 :     struct sdap_attr_map_info *maps = NULL;
    2026             :     static const int num_maps = 2;
    2027           0 :     struct sdap_options *opts = group_ctx->opts;
    2028           0 :     const char **attrs = NULL;
    2029           0 :     size_t num_attrs = 0;
    2030             :     errno_t ret;
    2031             : 
    2032           0 :     req = tevent_req_create(mem_ctx, &state,
    2033             :                             struct sdap_nested_group_deref_state);
    2034           0 :     if (req == NULL) {
    2035           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    2036           0 :         return NULL;
    2037             :     }
    2038             : 
    2039           0 :     state->ev = ev;
    2040           0 :     state->group_ctx = group_ctx;
    2041           0 :     state->members = members;
    2042           0 :     state->nesting_level = nesting_level;
    2043           0 :     state->num_groups = 0; /* we will count exact number of the groups */
    2044             : 
    2045           0 :     maps = talloc_array(state, struct sdap_attr_map_info, num_maps);
    2046           0 :     if (maps == NULL) {
    2047           0 :         ret = ENOMEM;
    2048           0 :         goto immediately;
    2049             :     }
    2050             : 
    2051           0 :     maps[0].map = opts->user_map;
    2052           0 :     maps[0].num_attrs = opts->user_map_cnt;
    2053           0 :     maps[1].map = opts->group_map;
    2054           0 :     maps[1].num_attrs = SDAP_OPTS_GROUP;
    2055             : 
    2056             :     /* pull down the whole group map,
    2057             :      * but only pull down username and originalDN for users */
    2058           0 :     ret = build_attrs_from_map(state, opts->group_map, SDAP_OPTS_GROUP,
    2059             :                                NULL, &attrs, &num_attrs);
    2060           0 :     if (ret != EOK) {
    2061           0 :         goto immediately;
    2062             :     }
    2063             : 
    2064           0 :     attrs = talloc_realloc(state, attrs, const char *, num_attrs + 2);
    2065           0 :     if (attrs == NULL) {
    2066           0 :         ret = ENOMEM;
    2067           0 :         goto immediately;
    2068             :     }
    2069             : 
    2070           0 :     attrs[num_attrs] = group_ctx->opts->user_map[SDAP_AT_USER_NAME].name;
    2071           0 :     attrs[num_attrs + 1] = NULL;
    2072             : 
    2073             :     /* send request */
    2074           0 :     subreq = sdap_deref_search_send(state, ev, opts, group_ctx->sh, group_dn,
    2075           0 :                                     opts->group_map[SDAP_AT_GROUP_MEMBER].name,
    2076             :                                     attrs, num_maps, maps,
    2077             :                                     dp_opt_get_int(opts->basic,
    2078             :                                                    SDAP_SEARCH_TIMEOUT));
    2079           0 :     if (subreq == NULL) {
    2080           0 :         ret = ENOMEM;
    2081           0 :         goto immediately;
    2082             :     }
    2083             : 
    2084           0 :     tevent_req_set_callback(subreq, sdap_nested_group_deref_direct_done, req);
    2085             : 
    2086           0 :     return req;
    2087             : 
    2088             : immediately:
    2089           0 :     if (ret == EOK) {
    2090           0 :         tevent_req_done(req);
    2091             :     } else {
    2092           0 :         tevent_req_error(req, ret);
    2093             :     }
    2094           0 :     tevent_req_post(req, ev);
    2095             : 
    2096           0 :     return req;
    2097             : }
    2098             : 
    2099             : static errno_t
    2100           0 : sdap_nested_group_deref_direct_process(struct tevent_req *subreq)
    2101             : {
    2102           0 :     struct sdap_nested_group_deref_state *state = NULL;
    2103           0 :     struct tevent_req *req = NULL;
    2104           0 :     struct sdap_options *opts = NULL;
    2105           0 :     struct sdap_deref_attrs **entries = NULL;
    2106           0 :     struct ldb_message_element *members = NULL;
    2107           0 :     const char *orig_dn = NULL;
    2108           0 :     const char *member_dn = NULL;
    2109           0 :     size_t num_entries = 0;
    2110             :     size_t i, j;
    2111             :     bool member_found;
    2112             :     errno_t ret;
    2113             : 
    2114           0 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    2115           0 :     state = tevent_req_data(req, struct sdap_nested_group_deref_state);
    2116             : 
    2117           0 :     opts = state->group_ctx->opts;
    2118           0 :     members = state->members;
    2119             : 
    2120           0 :     ret = sdap_deref_search_recv(subreq, state, &num_entries, &entries);
    2121           0 :     if (ret != EOK) {
    2122           0 :         goto done;
    2123             :     }
    2124             : 
    2125           0 :     DEBUG(SSSDBG_TRACE_INTERNAL, "Received %zu dereference results, "
    2126             :           "about to process them\n", num_entries);
    2127             : 
    2128             :     /*
    2129             :      * We don't have any knowledge about possible number of groups when
    2130             :      * dereferencing. We expect that every member is a group and we will
    2131             :      * allocate enough space to hold it. We will shrink the memory later.
    2132             :      */
    2133           0 :     state->nested_groups = talloc_zero_array(state, struct sysdb_attrs *,
    2134             :                                              num_entries);
    2135           0 :     if (state->nested_groups == NULL) {
    2136           0 :         ret = ENOMEM;
    2137           0 :         goto done;
    2138             :     }
    2139             : 
    2140           0 :     for (i = 0; i < num_entries; i++) {
    2141           0 :         ret = sysdb_attrs_get_string(entries[i]->attrs,
    2142             :                                      SYSDB_ORIG_DN, &orig_dn);
    2143           0 :         if (ret != EOK) {
    2144           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "The entry has no originalDN\n");
    2145           0 :             goto done;
    2146             :         }
    2147             : 
    2148             :         /* Ensure that all members returned from the deref request are included
    2149             :          * in the member processing. Sometimes we will get more results back
    2150             :          * from deref/asq than we got from the initial lookup, as is the case
    2151             :          * with Active Directory and its range retrieval mechanism.
    2152             :          */
    2153           0 :         member_found = false;
    2154           0 :         for (j = 0; j < members->num_values; j++) {
    2155             :             /* FIXME: This is inefficient for very large sets of groups */
    2156           0 :             member_dn = (const char *)members->values[j].data;
    2157           0 :             if (strcasecmp(orig_dn, member_dn) == 0) {
    2158           0 :                 member_found = true;
    2159           0 :                 break;
    2160             :             }
    2161             :         }
    2162             : 
    2163           0 :         if (!member_found) {
    2164             :             /* Append newly found member to member list.
    2165             :              * Changes in state->members will propagate into sysdb_attrs of
    2166             :              * the group. */
    2167           0 :             state->members->values = talloc_realloc(members, members->values,
    2168             :                                                     struct ldb_val,
    2169             :                                                     members->num_values + 1);
    2170           0 :             if (members->values == NULL) {
    2171           0 :                 ret = ENOMEM;
    2172           0 :                 goto done;
    2173             :             }
    2174             : 
    2175           0 :             members->values[members->num_values].data =
    2176           0 :                     (uint8_t *)talloc_strdup(members->values, orig_dn);
    2177           0 :             if (members->values[members->num_values].data == NULL) {
    2178           0 :                 ret = ENOMEM;
    2179           0 :                 goto done;
    2180             :             }
    2181             : 
    2182           0 :             members->values[members->num_values].length = strlen(orig_dn);
    2183           0 :             members->num_values++;
    2184             :         }
    2185             : 
    2186           0 :         if (entries[i]->map == opts->user_map) {
    2187             :             /* we found a user */
    2188             : 
    2189             :             /* skip the user if it is not amongst configured search bases */
    2190           0 :             if (!sdap_nested_member_is_user(state->group_ctx, orig_dn, NULL)) {
    2191           0 :                 continue;
    2192             :             }
    2193             : 
    2194             :             /* save user in hash table */
    2195           0 :             ret = sdap_nested_group_hash_user(state->group_ctx,
    2196           0 :                                               entries[i]->attrs);
    2197           0 :             if (ret != EOK && ret != EEXIST) {
    2198           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    2199             :                       "Unable to save user in hash table "
    2200             :                        "[%d]: %s\n", ret, strerror(ret));
    2201           0 :                 goto done;
    2202             :             }
    2203             : 
    2204           0 :         } else if (entries[i]->map == opts->group_map) {
    2205             :             /* we found a group */
    2206             : 
    2207             :             /* skip the group if we have reached the nesting limit */
    2208           0 :             if (state->nesting_level >= state->group_ctx->max_nesting_level) {
    2209           0 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is outside nesting limit "
    2210             :                       "(level %d), skipping\n", orig_dn, state->nesting_level);
    2211           0 :                 continue;
    2212             :             }
    2213             : 
    2214             :             /* skip the group if it is not amongst configured search bases */
    2215           0 :             if (!sdap_nested_member_is_group(state->group_ctx, orig_dn, NULL)) {
    2216           0 :                 continue;
    2217             :             }
    2218             : 
    2219             :             /* save group in hash table */
    2220           0 :             ret = sdap_nested_group_hash_group(state->group_ctx,
    2221           0 :                                                entries[i]->attrs);
    2222           0 :             if (ret == EEXIST) {
    2223           0 :                 continue;
    2224           0 :             } else if (ret != EOK) {
    2225           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    2226             :                       "Unable to save group in hash table "
    2227             :                        "[%d]: %s\n", ret, strerror(ret));
    2228           0 :                 goto done;
    2229             :             }
    2230             : 
    2231             :             /* remember the group for later processing */
    2232           0 :             state->nested_groups[state->num_groups] = entries[i]->attrs;
    2233           0 :             state->num_groups++;
    2234             : 
    2235             :         } else {
    2236             :             /* this should never happen, but if it does, do not loop forever */
    2237           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
    2238             :                   "Entry does not match any known map, skipping\n");
    2239           0 :             continue;
    2240             :         }
    2241             :     }
    2242             : 
    2243             :     /* adjust size of nested groups array */
    2244           0 :     if (state->num_groups > 0) {
    2245           0 :         state->nested_groups = talloc_realloc(state, state->nested_groups,
    2246             :                                               struct sysdb_attrs *,
    2247             :                                               state->num_groups);
    2248           0 :         if (state->nested_groups == NULL) {
    2249           0 :             ret = ENOMEM;
    2250           0 :             goto done;
    2251             :         }
    2252             :     } else {
    2253           0 :         talloc_zfree(state->nested_groups);
    2254             :     }
    2255             : 
    2256           0 :     ret = EOK;
    2257             : 
    2258             : done:
    2259           0 :     return ret;
    2260             : }
    2261             : 
    2262           0 : static void sdap_nested_group_deref_direct_done(struct tevent_req *subreq)
    2263             : {
    2264           0 :     struct sdap_nested_group_deref_state *state = NULL;
    2265           0 :     struct tevent_req *req = NULL;
    2266             :     errno_t ret;
    2267             : 
    2268           0 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    2269           0 :     state = tevent_req_data(req, struct sdap_nested_group_deref_state);
    2270             : 
    2271             :     /* process direct members */
    2272           0 :     ret = sdap_nested_group_deref_direct_process(subreq);
    2273           0 :     talloc_zfree(subreq);
    2274           0 :     if (ret != EOK) {
    2275           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Error processing direct membership "
    2276             :                                     "[%d]: %s\n", ret, strerror(ret));
    2277           0 :         goto done;
    2278             :     }
    2279             : 
    2280             :     /* we have processed all direct members,
    2281             :      * now recurse and process nested groups */
    2282           0 :     subreq = sdap_nested_group_recurse_send(state, state->ev,
    2283             :                                             state->group_ctx,
    2284             :                                             state->nested_groups,
    2285             :                                             state->num_groups,
    2286           0 :                                             state->nesting_level + 1);
    2287           0 :     if (subreq == NULL) {
    2288           0 :         ret = ENOMEM;
    2289           0 :         goto done;
    2290             :     }
    2291             : 
    2292           0 :     tevent_req_set_callback(subreq, sdap_nested_group_deref_done, req);
    2293             : 
    2294           0 :     ret = EAGAIN;
    2295             : 
    2296             : done:
    2297           0 :     if (ret == EOK) {
    2298             :         /* tevent_req_error() cannot cope with EOK */
    2299           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "We should not get here with EOK\n");
    2300           0 :         tevent_req_error(req, EINVAL);
    2301           0 :     } else if (ret != EAGAIN) {
    2302           0 :         tevent_req_error(req, ret);
    2303             :     }
    2304             : 
    2305           0 :     return;
    2306             : 
    2307             : }
    2308             : 
    2309           0 : static void sdap_nested_group_deref_done(struct tevent_req *subreq)
    2310             : {
    2311           0 :     struct tevent_req *req = NULL;
    2312             :     errno_t ret;
    2313             : 
    2314           0 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    2315             : 
    2316             :     /* process nested groups */
    2317           0 :     ret = sdap_nested_group_recurse_recv(subreq);
    2318           0 :     talloc_zfree(subreq);
    2319             : 
    2320           0 :     if (ret == EOK) {
    2321           0 :         tevent_req_done(req);
    2322             :     } else {
    2323           0 :         tevent_req_error(req, ret);
    2324             :     }
    2325             : 
    2326           0 :     return;
    2327             : }
    2328             : 
    2329           0 : static errno_t sdap_nested_group_deref_recv(struct tevent_req *req)
    2330             : {
    2331           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    2332             : 
    2333           0 :     return EOK;
    2334             : }

Generated by: LCOV version 1.10