LCOV - code coverage report
Current view: top level - providers/ldap - sdap_async_nested_groups.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 719 1286 55.9 %
Date: 2016-06-29 Functions: 43 53 81.1 %

          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 "util/probes.h"
      34             : #include "db/sysdb.h"
      35             : #include "providers/ldap/ldap_common.h"
      36             : #include "providers/ldap/sdap_async.h"
      37             : #include "providers/ldap/sdap_async_private.h"
      38             : #include "providers/ldap/sdap_idmap.h"
      39             : #include "providers/ipa/ipa_dn.h"
      40             : 
      41             : #define sdap_nested_group_sysdb_search_users(domain, filter) \
      42             :     sdap_nested_group_sysdb_search((domain), (filter), true)
      43             : 
      44             : #define sdap_nested_group_sysdb_search_groups(domain, filter) \
      45             :     sdap_nested_group_sysdb_search((domain), (filter), false)
      46             : 
      47             : enum sdap_nested_group_dn_type {
      48             :     SDAP_NESTED_GROUP_DN_USER,
      49             :     SDAP_NESTED_GROUP_DN_GROUP,
      50             :     SDAP_NESTED_GROUP_DN_UNKNOWN
      51             : };
      52             : 
      53             : struct sdap_nested_group_member {
      54             :     enum sdap_nested_group_dn_type type;
      55             :     const char *dn;
      56             :     const char *user_filter;
      57             :     const char *group_filter;
      58             : };
      59             : 
      60             : #ifndef EXTERNAL_MEMBERS_CHUNK
      61             : #define EXTERNAL_MEMBERS_CHUNK  16
      62             : #endif /* EXTERNAL_MEMBERS_CHUNK */
      63             : 
      64             : struct sdap_external_missing_member {
      65             :     const char **parent_group_dns;
      66             :     size_t parent_dn_idx;
      67             : };
      68             : 
      69             : struct sdap_nested_group_ctx {
      70             :     struct sss_domain_info *domain;
      71             :     struct sdap_options *opts;
      72             :     struct sdap_search_base **user_search_bases;
      73             :     struct sdap_search_base **group_search_bases;
      74             :     struct sdap_handle *sh;
      75             :     hash_table_t *users;
      76             :     hash_table_t *groups;
      77             :     hash_table_t *missing_external;
      78             :     bool try_deref;
      79             :     int deref_treshold;
      80             :     int max_nesting_level;
      81             : };
      82             : 
      83             : static struct tevent_req *
      84             : sdap_nested_group_process_send(TALLOC_CTX *mem_ctx,
      85             :                              struct tevent_context *ev,
      86             :                              struct sdap_nested_group_ctx *group_ctx,
      87             :                              int nesting_level,
      88             :                              struct sysdb_attrs *group);
      89             : 
      90             : static errno_t sdap_nested_group_process_recv(struct tevent_req *req);
      91             : 
      92             : static struct tevent_req *
      93             : sdap_nested_group_single_send(TALLOC_CTX *mem_ctx,
      94             :                               struct tevent_context *ev,
      95             :                               struct sdap_nested_group_ctx *group_ctx,
      96             :                               struct sdap_nested_group_member *members,
      97             :                               int num_members,
      98             :                               int num_groups_max,
      99             :                               int nesting_level);
     100             : 
     101             : static errno_t sdap_nested_group_single_recv(struct tevent_req *req);
     102             : 
     103             : static struct tevent_req *
     104             : sdap_nested_group_lookup_user_send(TALLOC_CTX *mem_ctx,
     105             :                                    struct tevent_context *ev,
     106             :                                    struct sdap_nested_group_ctx *group_ctx,
     107             :                                    struct sdap_nested_group_member *member);
     108             : 
     109             : static errno_t sdap_nested_group_lookup_user_recv(TALLOC_CTX *mem_ctx,
     110             :                                                   struct tevent_req *req,
     111             :                                                   struct sysdb_attrs **_user);
     112             : 
     113             : static struct tevent_req *
     114             : sdap_nested_group_lookup_group_send(TALLOC_CTX *mem_ctx,
     115             :                                     struct tevent_context *ev,
     116             :                                     struct sdap_nested_group_ctx *group_ctx,
     117             :                                     struct sdap_nested_group_member *member);
     118             : 
     119             : static errno_t sdap_nested_group_lookup_group_recv(TALLOC_CTX *mem_ctx,
     120             :                                                    struct tevent_req *req,
     121             :                                                    struct sysdb_attrs **_group);
     122             : 
     123             : static struct tevent_req *
     124             : sdap_nested_group_lookup_unknown_send(TALLOC_CTX *mem_ctx,
     125             :                                       struct tevent_context *ev,
     126             :                                       struct sdap_nested_group_ctx *group_ctx,
     127             :                                       struct sdap_nested_group_member *member);
     128             : 
     129             : static errno_t
     130             : sdap_nested_group_lookup_unknown_recv(TALLOC_CTX *mem_ctx,
     131             :                                       struct tevent_req *req,
     132             :                                       struct sysdb_attrs **_entry,
     133             :                                       enum sdap_nested_group_dn_type *_type);
     134             : 
     135             : static struct tevent_req *
     136             : sdap_nested_group_deref_send(TALLOC_CTX *mem_ctx,
     137             :                              struct tevent_context *ev,
     138             :                              struct sdap_nested_group_ctx *group_ctx,
     139             :                              struct ldb_message_element *members,
     140             :                              const char *group_dn,
     141             :                              int nesting_level);
     142             : 
     143             : static errno_t sdap_nested_group_deref_recv(struct tevent_req *req);
     144             : 
     145             : static errno_t
     146          14 : sdap_nested_group_extract_hash_table(TALLOC_CTX *mem_ctx,
     147             :                                      hash_table_t *table,
     148             :                                      unsigned long *_num_entries,
     149             :                                      struct sysdb_attrs ***_entries)
     150             : {
     151          14 :     struct sysdb_attrs **entries = NULL;
     152          14 :     struct sysdb_attrs *entry = NULL;
     153             :     hash_value_t *values;
     154             :     unsigned long num_entries;
     155             :     unsigned int i;
     156             :     bool hret;
     157             :     errno_t ret;
     158             : 
     159          14 :     hret = hash_values(table, &num_entries, &values);
     160          14 :     if (hret != HASH_SUCCESS) {
     161           0 :         ret = EIO;
     162           0 :         goto done;
     163             :     }
     164             : 
     165          14 :     if (num_entries > 0) {
     166          10 :         entries = talloc_array(mem_ctx, struct sysdb_attrs *, num_entries);
     167          10 :         if (entries == NULL) {
     168           0 :             ret = ENOMEM;
     169           0 :             goto done;
     170             :         }
     171             : 
     172          31 :         for (i = 0; i < num_entries; i++) {
     173          21 :             entry = talloc_get_type(values[i].ptr, struct sysdb_attrs);
     174          21 :             entries[i] = talloc_steal(entries, entry);
     175             :         }
     176             :     }
     177             : 
     178          14 :     if (_num_entries != NULL) {
     179          14 :         *_num_entries = num_entries;
     180             :     }
     181             : 
     182          14 :     if (_entries != NULL) {
     183          14 :         *_entries = entries;
     184             :     }
     185             : 
     186          14 :     ret = EOK;
     187             : 
     188             : done:
     189          14 :     talloc_free(values);
     190             : 
     191          14 :     if (ret != EOK) {
     192           0 :         talloc_free(entries);
     193             :     }
     194             : 
     195          14 :     return ret;
     196             : }
     197             : 
     198          28 : static errno_t sdap_nested_group_hash_insert(hash_table_t *table,
     199             :                                              const char *entry_key,
     200             :                                              void *entry_value,
     201             :                                              bool overwrite,
     202             :                                              const char *table_name)
     203             : {
     204             :     hash_key_t key;
     205             :     hash_value_t value;
     206             :     int hret;
     207             : 
     208          28 :     DEBUG(SSSDBG_TRACE_ALL, "Inserting [%s] into hash table [%s]\n",
     209             :                              entry_key, table_name);
     210             : 
     211          28 :     key.type = HASH_KEY_STRING;
     212          28 :     key.str = talloc_strdup(NULL, entry_key);
     213          28 :     if (key.str == NULL) {
     214           0 :         return ENOMEM;
     215             :     }
     216             : 
     217          28 :     if (overwrite == false && hash_has_key(table, &key)) {
     218           2 :         talloc_free(key.str);
     219           2 :         return EEXIST;
     220             :     }
     221             : 
     222          26 :     value.type = HASH_VALUE_PTR;
     223          26 :     value.ptr = entry_value;
     224             : 
     225          26 :     hret = hash_enter(table, &key, &value);
     226          26 :     if (hret != HASH_SUCCESS) {
     227           0 :         talloc_free(key.str);
     228           0 :         return EIO;
     229             :     }
     230             : 
     231          26 :     talloc_steal(table, key.str);
     232          26 :     talloc_steal(table, value.ptr);
     233             : 
     234          26 :     return EOK;
     235             : }
     236             : 
     237          26 : static errno_t sdap_nested_group_hash_entry(hash_table_t *table,
     238             :                                             struct sysdb_attrs *entry,
     239             :                                             const char *table_name)
     240             : {
     241          26 :     const char *name = NULL;
     242             :     errno_t ret;
     243             : 
     244          26 :     ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name);
     245          26 :     if (ret != EOK) {
     246           0 :         return ret;
     247             :     }
     248             : 
     249          26 :     return sdap_nested_group_hash_insert(table, name, entry, false, table_name);
     250             : }
     251             : 
     252             : static errno_t
     253           7 : sdap_nested_group_hash_user(struct sdap_nested_group_ctx *group_ctx,
     254             :                             struct sysdb_attrs *user)
     255             : {
     256           7 :     return sdap_nested_group_hash_entry(group_ctx->users, user, "users");
     257             : }
     258             : 
     259             : static errno_t
     260          19 : sdap_nested_group_hash_group(struct sdap_nested_group_ctx *group_ctx,
     261             :                              struct sysdb_attrs *group)
     262             : {
     263          19 :     struct sdap_attr_map *map = group_ctx->opts->group_map;
     264             :     gid_t gid;
     265             :     errno_t ret;
     266          19 :     bool posix_group = true;
     267             :     bool use_id_mapping;
     268             :     bool can_find_gid;
     269             :     bool need_filter;
     270             : 
     271          19 :     ret = sdap_check_ad_group_type(group_ctx->domain, group_ctx->opts,
     272             :                                    group, "", &need_filter);
     273          19 :     if (ret != EOK) {
     274           0 :         return ret;
     275             :     }
     276             : 
     277          19 :     if (need_filter) {
     278           0 :         posix_group = false;
     279           0 :         gid = 0;
     280             :     }
     281             : 
     282          19 :     use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
     283          19 :                                                           group_ctx->opts->idmap_ctx,
     284          19 :                                                           group_ctx->domain->name,
     285          19 :                                                           group_ctx->domain->domain_id);
     286             : 
     287          19 :     can_find_gid = posix_group && !use_id_mapping;
     288          19 :     if (can_find_gid) {
     289          19 :         ret = sysdb_attrs_get_uint32_t(group, map[SDAP_AT_GROUP_GID].sys_name,
     290             :                                        &gid);
     291             :     }
     292          19 :     if (!can_find_gid || ret == ENOENT || (ret == EOK && gid == 0)) {
     293           0 :         DEBUG(SSSDBG_TRACE_ALL,
     294             :              "The group's gid was %s\n", ret == ENOENT ? "missing" : "zero");
     295           0 :         DEBUG(SSSDBG_TRACE_INTERNAL,
     296             :              "Marking group as non-posix and setting GID=0!\n");
     297             : 
     298           0 :         if (ret == ENOENT || !posix_group) {
     299           0 :             ret = sysdb_attrs_add_uint32(group,
     300           0 :                                          map[SDAP_AT_GROUP_GID].sys_name, 0);
     301           0 :             if (ret != EOK) {
     302           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     303             :                       "Failed to add a GID to non-posix group!\n");
     304           0 :                 return ret;
     305             :             }
     306             :         }
     307             : 
     308           0 :         ret = sysdb_attrs_add_bool(group, SYSDB_POSIX, false);
     309           0 :         if (ret != EOK) {
     310           0 :             DEBUG(SSSDBG_OP_FAILURE,
     311             :                   "Error: Failed to mark group as non-posix!\n");
     312           0 :             return ret;
     313             :         }
     314          19 :     } else if (ret != EOK) {
     315           0 :         return ret;
     316             :     }
     317             : 
     318          19 :     return sdap_nested_group_hash_entry(group_ctx->groups, group, "groups");
     319             : }
     320             : 
     321           3 : static errno_t sdap_nested_group_external_add(hash_table_t *table,
     322             :                                               const char *ext_member,
     323             :                                               const char *parent_group_dn)
     324             : {
     325             :     hash_key_t key;
     326             :     hash_value_t value;
     327             :     int hret;
     328             :     int ret;
     329             :     struct sdap_external_missing_member *ext_mem;
     330             : 
     331           3 :     key.type = HASH_KEY_STRING;
     332           3 :     key.str = discard_const(ext_member);
     333             : 
     334           3 :     DEBUG(SSSDBG_TRACE_ALL,
     335             :           "Inserting external member [%s] into external members hash table\n",
     336             :           ext_member);
     337             : 
     338           3 :     hret = hash_lookup(table, &key, &value);
     339           3 :     switch (hret) {
     340             :     case HASH_ERROR_KEY_NOT_FOUND:
     341           2 :         ext_mem = talloc_zero(table, struct sdap_external_missing_member);
     342           2 :         if (ext_mem == NULL) {
     343           0 :             return ENOMEM;
     344             :         }
     345           2 :         ext_mem->parent_group_dns = talloc_zero_array(ext_mem,
     346             :                                                       const char *,
     347             :                                                       EXTERNAL_MEMBERS_CHUNK);
     348           2 :         if (ext_mem->parent_group_dns == NULL) {
     349           0 :             talloc_free(ext_mem);
     350           0 :             return ENOMEM;
     351             :         }
     352             : 
     353           2 :         ret = sdap_nested_group_hash_insert(table, ext_member, ext_mem,
     354             :                                             true, "missing external users");
     355           2 :         if (ret != EOK) {
     356           0 :             return ret;
     357             :         }
     358           2 :         break;
     359             : 
     360             :     case HASH_SUCCESS:
     361           1 :         ext_mem = talloc_get_type(value.ptr,
     362             :                                   struct sdap_external_missing_member);
     363           2 :         if (ext_mem->parent_dn_idx == \
     364           1 :                 talloc_array_length(ext_mem->parent_group_dns)) {
     365           1 :             ext_mem->parent_group_dns = talloc_realloc(ext_mem,
     366             :                                                 ext_mem->parent_group_dns,
     367             :                                                 const char *,
     368             :                                                 ext_mem->parent_dn_idx + \
     369             :                                                     EXTERNAL_MEMBERS_CHUNK);
     370           1 :             if (ext_mem->parent_group_dns == NULL) {
     371           0 :                 talloc_free(ext_mem);
     372           0 :                 return ENOMEM;
     373             :             }
     374             :         }
     375           1 :         break;
     376             :     default:
     377           0 :         return EIO;
     378             :     }
     379             : 
     380           6 :     ext_mem->parent_group_dns[ext_mem->parent_dn_idx] = \
     381           3 :                                         talloc_strdup(ext_mem->parent_group_dns,
     382             :                                                       parent_group_dn);
     383           3 :     if (ext_mem->parent_group_dns[ext_mem->parent_dn_idx] == NULL) {
     384           0 :         return ENOMEM;
     385             :     }
     386           3 :     ext_mem->parent_dn_idx++;
     387             : 
     388           3 :     return EOK;
     389             : }
     390             : 
     391          38 : static errno_t sdap_nested_group_sysdb_search(struct sss_domain_info *domain,
     392             :                                               const char *filter,
     393             :                                               bool user)
     394             : {
     395             :     static const char *attrs[] = {SYSDB_CACHE_EXPIRE,
     396             :                                   SYSDB_UIDNUM,
     397             :                                   NULL};
     398          38 :     struct ldb_message **msgs = NULL;
     399             :     size_t count;
     400          38 :     time_t now = time(NULL);
     401             :     uint64_t expire;
     402             :     uid_t uid;
     403             :     errno_t ret;
     404             : 
     405          38 :     if (user) {
     406          19 :         ret = sysdb_search_users(NULL, domain, filter, attrs,
     407             :                                  &count, &msgs);
     408             :     } else {
     409          19 :         ret = sysdb_search_groups(NULL, domain, filter, attrs,
     410             :                                   &count, &msgs);
     411             :     }
     412          38 :     if (ret != EOK) {
     413          38 :         goto done;
     414             :     }
     415             : 
     416           0 :     if (count != 1) {
     417           0 :         DEBUG(SSSDBG_OP_FAILURE, "More than one entry found?\n");
     418           0 :         ret = EFAULT;
     419           0 :         goto done;
     420             :     }
     421             : 
     422             :     /* we found an object with this origDN in the sysdb,
     423             :      * check if it is valid */
     424           0 :     if (user) {
     425           0 :         uid = ldb_msg_find_attr_as_uint64(msgs[0], SYSDB_UIDNUM, 0);
     426           0 :         if (uid == 0) {
     427           0 :             DEBUG(SSSDBG_OP_FAILURE, "User with no UID?\n");
     428           0 :             ret = EINVAL;
     429           0 :             goto done;
     430             :         }
     431             :     }
     432             : 
     433           0 :     expire = ldb_msg_find_attr_as_uint64(msgs[0], SYSDB_CACHE_EXPIRE, 0);
     434           0 :     if (expire != 0 && expire <= now) {
     435             :         /* needs refresh */
     436           0 :         ret = EAGAIN;
     437           0 :         goto done;
     438             :     }
     439             : 
     440             :     /* valid object */
     441           0 :     ret = EOK;
     442             : 
     443             : done:
     444          38 :     talloc_zfree(msgs);
     445          38 :     return ret;
     446             : }
     447             : 
     448             : static errno_t
     449          19 : sdap_nested_group_check_cache(struct sdap_options *opts,
     450             :                               struct sss_domain_info *domain,
     451             :                               const char *member_dn,
     452             :                               enum sdap_nested_group_dn_type *_type)
     453             : {
     454          19 :     TALLOC_CTX *tmp_ctx = NULL;
     455          19 :     struct sdap_domain *sdap_domain = NULL;
     456          19 :     struct sss_domain_info *member_domain = NULL;
     457          19 :     char *sanitized_dn = NULL;
     458          19 :     char *filter = NULL;
     459             :     errno_t ret;
     460             : 
     461          19 :     tmp_ctx = talloc_new(NULL);
     462          19 :     if (tmp_ctx == NULL) {
     463           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
     464           0 :         return ENOMEM;
     465             :     }
     466             : 
     467          19 :     ret = sss_filter_sanitize(tmp_ctx, member_dn, &sanitized_dn);
     468          19 :     if (ret != EOK) {
     469           0 :         goto done;
     470             :     }
     471             : 
     472          19 :     filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_ORIG_DN, sanitized_dn);
     473          19 :     if (filter == NULL) {
     474           0 :         ret = ENOMEM;
     475           0 :         goto done;
     476             :     }
     477             : 
     478             :     /* determine correct domain of this member */
     479          19 :     sdap_domain = sdap_domain_get_by_dn(opts, member_dn);
     480          19 :     member_domain = sdap_domain == NULL ? domain : sdap_domain->dom;
     481             : 
     482             :     /* search in users */
     483             :     PROBE(SDAP_NESTED_GROUP_SYSDB_SEARCH_USERS_PRE);
     484          19 :     ret = sdap_nested_group_sysdb_search_users(member_domain, filter);
     485             :     PROBE(SDAP_NESTED_GROUP_SYSDB_SEARCH_USERS_POST);
     486          19 :     if (ret == EOK || ret == EAGAIN) {
     487             :         /* user found */
     488           0 :         *_type = SDAP_NESTED_GROUP_DN_USER;
     489           0 :         goto done;
     490          19 :     } else if (ret != ENOENT) {
     491             :         /* error */
     492           0 :         goto done;
     493             :     }
     494             : 
     495             :     /* search in groups */
     496             :     PROBE(SDAP_NESTED_GROUP_SYSDB_SEARCH_GROUPS_PRE);
     497          19 :     ret = sdap_nested_group_sysdb_search_groups(member_domain, filter);
     498             :     PROBE(SDAP_NESTED_GROUP_SYSDB_SEARCH_GROUPS_POST);
     499          19 :     if (ret == EOK || ret == EAGAIN) {
     500             :         /* group found */
     501           0 :         *_type = SDAP_NESTED_GROUP_DN_GROUP;
     502           0 :         goto done;
     503          19 :     } else if (ret != ENOENT) {
     504             :         /* error */
     505           0 :         goto done;
     506             :     }
     507             : 
     508             :     /* not found in the sysdb */
     509          19 :     ret = ENOENT;
     510             : 
     511             : done:
     512          19 :     talloc_free(tmp_ctx);
     513          19 :     return ret;
     514             : }
     515             : 
     516             : static bool
     517          38 : sdap_nested_member_is_ent(struct sdap_nested_group_ctx *group_ctx,
     518             :                           const char *dn, char **filter, bool is_user)
     519             : {
     520          38 :     struct sdap_domain *sditer = NULL;
     521          38 :     bool ret = false;
     522             :     struct sdap_search_base **search_bases;
     523             : 
     524          57 :     DLIST_FOR_EACH(sditer, group_ctx->opts->sdom) {
     525          38 :         search_bases = is_user ? sditer->user_search_bases : \
     526             :                                  sditer->group_search_bases;
     527             : 
     528          38 :         ret = sss_ldap_dn_in_search_bases(group_ctx, dn, search_bases,
     529             :                                           filter);
     530          38 :         if (ret == true) {
     531          19 :             break;
     532             :         }
     533             :     }
     534             : 
     535          38 :     return ret;
     536             : }
     537             : 
     538             : static inline bool
     539          19 : sdap_nested_member_is_user(struct sdap_nested_group_ctx *group_ctx,
     540             :                            const char *dn, char **filter)
     541             : {
     542          19 :     return sdap_nested_member_is_ent(group_ctx, dn, filter, true);
     543             : }
     544             : 
     545             : static inline bool
     546          19 : sdap_nested_member_is_group(struct sdap_nested_group_ctx *group_ctx,
     547             :                             const char *dn, char **filter)
     548             : {
     549          19 :     return sdap_nested_member_is_ent(group_ctx, dn, filter, false);
     550             : }
     551             : 
     552             : static errno_t
     553          14 : sdap_nested_group_split_members(TALLOC_CTX *mem_ctx,
     554             :                                 struct sdap_nested_group_ctx *group_ctx,
     555             :                                 int threshold,
     556             :                                 int nesting_level,
     557             :                                 struct ldb_message_element *members,
     558             :                                 struct sdap_nested_group_member **_missing,
     559             :                                 int *_num_missing,
     560             :                                 int *_num_groups)
     561             : {
     562          14 :     TALLOC_CTX *tmp_ctx = NULL;
     563          14 :     struct sdap_nested_group_member *missing = NULL;
     564             :     enum sdap_nested_group_dn_type type;
     565          14 :     char *dn = NULL;
     566          14 :     char *user_filter = NULL;
     567          14 :     char *group_filter = NULL;
     568          14 :     int num_missing = 0;
     569          14 :     int num_groups = 0;
     570             :     hash_key_t key;
     571             :     bool bret;
     572             :     bool is_user;
     573             :     bool is_group;
     574             :     errno_t ret;
     575             :     int i;
     576             : 
     577          14 :     if (members == NULL) {
     578           2 :         *_missing = NULL;
     579           2 :         *_num_missing = 0;
     580           2 :         *_num_groups = 0;
     581           2 :         return EOK;
     582             :     }
     583             : 
     584          12 :     tmp_ctx = talloc_new(NULL);
     585          12 :     if (tmp_ctx == NULL) {
     586           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
     587           0 :         return ENOMEM;
     588             :     }
     589             : 
     590          12 :     missing = talloc_zero_array(tmp_ctx, struct sdap_nested_group_member,
     591             :                                 members->num_values);
     592          12 :     if (missing == NULL) {
     593           0 :         ret = ENOMEM;
     594           0 :         goto done;
     595             :     }
     596             : 
     597             :     /* create list of missing members
     598             :      * skip dn if:
     599             :      * - is present in user or group hash table
     600             :      * - is present in sysdb and not expired
     601             :      * - it is a group and we have reached the maximal nesting level
     602             :      * - it is not under user nor group search bases
     603             :      *
     604             :      * if dn is in sysdb but expired
     605             :      * - we know what object type it is
     606             :      *
     607             :      * if dn is not in hash table or sysdb
     608             :      * - try to determine type of object by search base that match dn
     609             :      */
     610          31 :     for (i = 0; i < members->num_values; i++) {
     611          19 :         dn = (char*)members->values[i].data;
     612          19 :         type = SDAP_NESTED_GROUP_DN_UNKNOWN;
     613             : 
     614             :         /* check hash tables */
     615          19 :         key.type = HASH_KEY_STRING;
     616          19 :         key.str = dn;
     617             : 
     618          19 :         bret = hash_has_key(group_ctx->users, &key);
     619          19 :         if (bret) {
     620           0 :             continue;
     621             :         }
     622             : 
     623          19 :         bret = hash_has_key(group_ctx->groups, &key);
     624          19 :         if (bret) {
     625           0 :             continue;
     626             :         }
     627             : 
     628             :         /* check sysdb */
     629             :         PROBE(SDAP_NESTED_GROUP_CHECK_CACHE_PRE);
     630          19 :         ret = sdap_nested_group_check_cache(group_ctx->opts, group_ctx->domain,
     631             :                                             dn, &type);
     632             :         PROBE(SDAP_NESTED_GROUP_CHECK_CACHE_POST);
     633          19 :         if (ret == EOK) {
     634             :             /* found and valid */
     635           0 :             DEBUG(SSSDBG_TRACE_ALL, "[%s] found in cache, skipping\n", dn);
     636           0 :             continue;
     637          19 :         } else if (ret != EAGAIN && ret != ENOENT) {
     638             :             /* error */
     639           0 :             goto done;
     640             :         }
     641             : 
     642             :         /* try to determine type by dn */
     643          19 :         if (type == SDAP_NESTED_GROUP_DN_UNKNOWN) {
     644             :             /* user */
     645          19 :             is_user = sdap_nested_member_is_user(group_ctx, dn,
     646             :                                                  &user_filter);
     647             : 
     648          19 :             is_group = sdap_nested_member_is_group(group_ctx, dn,
     649             :                                                    &group_filter);
     650             : 
     651          19 :             if (is_user && is_group) {
     652             :                 /* search bases overlap */
     653           0 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is unknown object\n", dn);
     654           0 :                 type = SDAP_NESTED_GROUP_DN_UNKNOWN;
     655          19 :             } else if (is_user) {
     656           8 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is a user\n", dn);
     657           8 :                 type = SDAP_NESTED_GROUP_DN_USER;
     658          11 :             } else if (is_group) {
     659          11 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is a group\n", dn);
     660          11 :                 type = SDAP_NESTED_GROUP_DN_GROUP;
     661             :             } else {
     662             :                 /* dn is outside search bases */
     663           0 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is out of scope of configured "
     664             :                       "search bases, skipping\n", dn);
     665           0 :                 continue;
     666             :             }
     667             :         }
     668             : 
     669             :         /* check nesting level */
     670          19 :         if (type == SDAP_NESTED_GROUP_DN_GROUP) {
     671          11 :             if (nesting_level >= group_ctx->max_nesting_level) {
     672           0 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is outside nesting limit "
     673             :                       "(level %d), skipping\n", dn, nesting_level);
     674           0 :                 talloc_zfree(user_filter);
     675           0 :                 talloc_zfree(group_filter);
     676           0 :                 continue;
     677             :             }
     678             :         }
     679             : 
     680          19 :         missing[num_missing].dn = talloc_strdup(missing, dn);
     681          19 :         if (missing[num_missing].dn == NULL) {
     682           0 :             ret = ENOMEM;
     683           0 :             goto done;
     684             :         }
     685             : 
     686          19 :         missing[num_missing].type = type;
     687          19 :         missing[num_missing].user_filter = talloc_steal(missing, user_filter);
     688          19 :         missing[num_missing].group_filter = talloc_steal(missing, group_filter);
     689             : 
     690          19 :         num_missing++;
     691          19 :         if (threshold > 0 && num_missing > threshold) {
     692           0 :             if (_num_missing) {
     693           0 :                 *_num_missing = num_missing;
     694             :             }
     695             : 
     696           0 :             ret = ERR_DEREF_THRESHOLD;
     697           0 :             goto done;
     698             :         }
     699             : 
     700          19 :         if (type != SDAP_NESTED_GROUP_DN_USER) {
     701          11 :             num_groups++;
     702             :         }
     703             :     }
     704             : 
     705          12 :     missing = talloc_realloc(mem_ctx, missing,
     706             :                              struct sdap_nested_group_member, num_missing);
     707             :     /* talloc_realloc behaves as talloc_free if 3rd parameter (count) is 0,
     708             :      * so it's OK to return NULL then
     709             :      */
     710          12 :     if (missing == NULL && num_missing > 0) {
     711           0 :         ret = ENOMEM;
     712           0 :         goto done;
     713             :     }
     714             : 
     715          12 :     if (_missing) {
     716          12 :         *_missing = talloc_steal(mem_ctx, missing);
     717             :     }
     718             : 
     719          12 :     if (_num_missing) {
     720          12 :         *_num_missing = num_missing;
     721             :     }
     722             : 
     723          12 :     if (_num_groups) {
     724          12 :         *_num_groups = num_groups;
     725             :     }
     726             : 
     727          12 :     ret = EOK;
     728             : 
     729             : done:
     730          12 :     talloc_free(tmp_ctx);
     731             : 
     732          12 :     return ret;
     733             : }
     734             : 
     735             : static errno_t
     736          14 : sdap_nested_group_add_ext_members(struct sdap_nested_group_ctx *group_ctx,
     737             :                                   struct sysdb_attrs *group,
     738             :                                   struct ldb_message_element *ext_members)
     739             : {
     740             :     errno_t ret;
     741             :     const char *ext_member_attr;
     742             :     const char *orig_dn;
     743             : 
     744          14 :     if (ext_members == NULL) {
     745          12 :         return EOK;
     746             :     }
     747             : 
     748           2 :     ret = sysdb_attrs_get_string(group, SYSDB_ORIG_DN, &orig_dn);
     749           2 :     if (ret != EOK) {
     750           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "A group with no originalDN!?!\n");
     751           0 :         return ret;
     752             :     }
     753             : 
     754           5 :     for (size_t i = 0; i < ext_members->num_values; i++) {
     755           3 :         ext_member_attr = (const char *) ext_members->values[i].data;
     756             : 
     757           3 :         ret = sdap_nested_group_external_add(group_ctx->missing_external,
     758             :                                              ext_member_attr,
     759             :                                              orig_dn);
     760           3 :         if (ret != EOK) {
     761           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     762             :                     "Cannot add %s into external members [%d]: %s\n",
     763             :                     ext_member_attr, ret, sss_strerror(ret));
     764           0 :             return ret;
     765             :         }
     766             :     }
     767             : 
     768           2 :     return EOK;
     769             : }
     770             : 
     771             : static struct ldb_message_element *
     772          18 : sdap_nested_group_ext_members(struct sdap_options *opts,
     773             :                               struct sysdb_attrs *group)
     774             : {
     775             :     errno_t ret;
     776          18 :     struct ldb_message_element *ext_members = NULL;
     777             : 
     778          18 :     if (opts->ext_ctx == NULL) {
     779          14 :         return NULL;
     780             :     }
     781             : 
     782           4 :     ret = sysdb_attrs_get_el_ext(group,
     783           4 :                  opts->group_map[SDAP_AT_GROUP_EXT_MEMBER].sys_name,
     784             :                  false, &ext_members);
     785           4 :     if (ret != EOK && ret != ENOENT) {
     786           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve external member list "
     787             :                                    "[%d]: %s\n", ret, sss_strerror(ret));
     788             :     }
     789             : 
     790           4 :     return ext_members;
     791             : }
     792             : 
     793             : 
     794             : struct sdap_nested_group_state {
     795             :     struct sdap_nested_group_ctx *group_ctx;
     796             : };
     797             : 
     798             : static void sdap_nested_group_done(struct tevent_req *subreq);
     799             : 
     800             : struct tevent_req *
     801           8 : sdap_nested_group_send(TALLOC_CTX *mem_ctx,
     802             :                        struct tevent_context *ev,
     803             :                        struct sdap_domain *sdom,
     804             :                        struct sdap_options *opts,
     805             :                        struct sdap_handle *sh,
     806             :                        struct sysdb_attrs *group)
     807             : {
     808           8 :     struct sdap_nested_group_state *state = NULL;
     809           8 :     struct tevent_req *req = NULL;
     810           8 :     struct tevent_req *subreq = NULL;
     811             :     errno_t ret;
     812             :     int i;
     813             : 
     814             :     PROBE(SDAP_NESTED_GROUP_SEND);
     815             : 
     816           8 :     req = tevent_req_create(mem_ctx, &state, struct sdap_nested_group_state);
     817           8 :     if (req == NULL) {
     818           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
     819           0 :         return NULL;
     820             :     }
     821             : 
     822             :     /* create main nested group context */
     823           8 :     state->group_ctx = talloc_zero(state, struct sdap_nested_group_ctx);
     824           8 :     if (state->group_ctx == NULL) {
     825           0 :         ret = ENOMEM;
     826           0 :         goto immediately;
     827             :     }
     828             : 
     829           8 :     ret = sss_hash_create(state->group_ctx, 32, &state->group_ctx->users);
     830           8 :     if (ret != EOK) {
     831           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n",
     832             :                                     ret, strerror(ret));
     833           0 :         goto immediately;
     834             :     }
     835             : 
     836           8 :     ret = sss_hash_create(state->group_ctx, 32, &state->group_ctx->groups);
     837           8 :     if (ret != EOK) {
     838           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n",
     839             :                                     ret, strerror(ret));
     840           0 :         goto immediately;
     841             :     }
     842             : 
     843           8 :     ret = sss_hash_create(state->group_ctx, 32,
     844           8 :                           &state->group_ctx->missing_external);
     845           8 :     if (ret != EOK) {
     846           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n",
     847             :                                     ret, strerror(ret));
     848           0 :         goto immediately;
     849             :     }
     850             : 
     851           8 :     state->group_ctx->try_deref = true;
     852           8 :     state->group_ctx->deref_treshold = dp_opt_get_int(opts->basic,
     853             :                                                       SDAP_DEREF_THRESHOLD);
     854           8 :     state->group_ctx->max_nesting_level = dp_opt_get_int(opts->basic,
     855             :                                                          SDAP_NESTING_LEVEL);
     856           8 :     state->group_ctx->domain = sdom->dom;
     857           8 :     state->group_ctx->opts = opts;
     858           8 :     state->group_ctx->user_search_bases = sdom->user_search_bases;
     859           8 :     state->group_ctx->group_search_bases = sdom->group_search_bases;
     860           8 :     state->group_ctx->sh = sh;
     861           8 :     state->group_ctx->try_deref = sdap_has_deref_support(sh, opts);
     862             : 
     863             :     /* disable deref if threshold <= 0 */
     864           8 :     if (state->group_ctx->deref_treshold <= 0) {
     865           0 :         state->group_ctx->try_deref = false;
     866             :     }
     867             : 
     868             :     /* if any search base contains filter, disable dereference. */
     869           8 :     if (state->group_ctx->try_deref) {
     870           0 :         for (i = 0; opts->sdom->user_search_bases[i] != NULL; i++) {
     871           0 :             if (opts->sdom->user_search_bases[i]->filter != NULL) {
     872           0 :                 DEBUG(SSSDBG_TRACE_FUNC, "User search base contains filter, "
     873             :                                           "dereference will be disabled\n");
     874           0 :                 state->group_ctx->try_deref = false;
     875           0 :                 break;
     876             :             }
     877             :         }
     878             :     }
     879             : 
     880           8 :     if (state->group_ctx->try_deref) {
     881           0 :         for (i = 0; opts->sdom->group_search_bases[i] != NULL; i++) {
     882           0 :             if (opts->sdom->group_search_bases[i]->filter != NULL) {
     883           0 :                 DEBUG(SSSDBG_TRACE_FUNC, "Group search base contains filter, "
     884             :                                           "dereference will be disabled\n");
     885           0 :                 state->group_ctx->try_deref = false;
     886           0 :                 break;
     887             :             }
     888             :         }
     889             :     }
     890             : 
     891             :     /* insert initial group into hash table */
     892           8 :     ret = sdap_nested_group_hash_group(state->group_ctx, group);
     893           8 :     if (ret != EOK) {
     894           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to insert group into hash table "
     895             :                                     "[%d]: %s\n", ret, strerror(ret));
     896           0 :         goto immediately;
     897             :     }
     898             : 
     899             :     /* resolve group */
     900           8 :     subreq = sdap_nested_group_process_send(state, ev, state->group_ctx,
     901             :                                             0, group);
     902           8 :     if (subreq == NULL) {
     903           0 :         ret = ENOMEM;
     904           0 :         goto immediately;
     905             :     }
     906             : 
     907           8 :     tevent_req_set_callback(subreq, sdap_nested_group_done, req);
     908             : 
     909           8 :     return req;
     910             : 
     911             : immediately:
     912           0 :     if (ret == EOK) {
     913           0 :         tevent_req_done(req);
     914             :     } else {
     915           0 :         tevent_req_error(req, ret);
     916             :     }
     917           0 :     tevent_req_post(req, ev);
     918             : 
     919           0 :     return req;
     920             : }
     921             : 
     922           8 : static void sdap_nested_group_done(struct tevent_req *subreq)
     923             : {
     924           8 :     struct tevent_req *req = NULL;
     925             :     errno_t ret;
     926             : 
     927           8 :     req = tevent_req_callback_data(subreq, struct tevent_req);
     928             : 
     929           8 :     ret = sdap_nested_group_process_recv(subreq);
     930           8 :     talloc_zfree(subreq);
     931           8 :     if (ret != EOK) {
     932           1 :         tevent_req_error(req, ret);
     933           9 :         return;
     934             :     }
     935             : 
     936           7 :     tevent_req_done(req);
     937             : }
     938             : 
     939           8 : errno_t sdap_nested_group_recv(TALLOC_CTX *mem_ctx,
     940             :                                struct tevent_req *req,
     941             :                                unsigned long *_num_users,
     942             :                                struct sysdb_attrs ***_users,
     943             :                                unsigned long *_num_groups,
     944             :                                struct sysdb_attrs ***_groups,
     945             :                                hash_table_t **_missing_external)
     946             : {
     947           8 :     struct sdap_nested_group_state *state = NULL;
     948           8 :     struct sysdb_attrs **users = NULL;
     949           8 :     struct sysdb_attrs **groups = NULL;
     950             :     unsigned long num_users;
     951             :     unsigned long num_groups;
     952             :     errno_t ret;
     953             : 
     954           8 :     state = tevent_req_data(req, struct sdap_nested_group_state);
     955             : 
     956             :     PROBE(SDAP_NESTED_GROUP_RECV);
     957           9 :     TEVENT_REQ_RETURN_ON_ERROR(req);
     958             : 
     959           7 :     ret = sdap_nested_group_extract_hash_table(state, state->group_ctx->users,
     960             :                                                &num_users, &users);
     961           7 :     if (ret != EOK) {
     962           0 :         return ret;
     963             :     }
     964             : 
     965           7 :     DEBUG(SSSDBG_TRACE_FUNC, "%lu users found in the hash table\n",
     966             :                               num_users);
     967             : 
     968           7 :     ret = sdap_nested_group_extract_hash_table(state, state->group_ctx->groups,
     969             :                                                &num_groups, &groups);
     970           7 :     if (ret != EOK) {
     971           0 :         return ret;
     972             :     }
     973             : 
     974           7 :     DEBUG(SSSDBG_TRACE_FUNC, "%lu groups found in the hash table\n",
     975             :                               num_groups);
     976             : 
     977           7 :     if (_num_users != NULL) {
     978           7 :         *_num_users = num_users;
     979             :     }
     980             : 
     981           7 :     if (_users != NULL) {
     982           7 :         *_users = talloc_steal(mem_ctx, users);
     983             :     }
     984             : 
     985           7 :     if (_num_groups!= NULL) {
     986           7 :         *_num_groups = num_groups;
     987             :     }
     988             : 
     989           7 :     if (_groups != NULL) {
     990           7 :         *_groups = talloc_steal(mem_ctx, groups);
     991             :     }
     992             : 
     993           7 :     if (_missing_external) {
     994           7 :         *_missing_external = talloc_steal(mem_ctx,
     995             :                                           state->group_ctx->missing_external);
     996             :     }
     997             : 
     998           7 :     return EOK;
     999             : }
    1000             : 
    1001             : struct sdap_nested_group_process_state {
    1002             :     struct tevent_context *ev;
    1003             :     struct sdap_nested_group_ctx *group_ctx;
    1004             :     struct sdap_nested_group_member *missing;
    1005             :     int num_missing_total;
    1006             :     int num_missing_groups;
    1007             :     struct ldb_message_element *ext_members;
    1008             :     struct ldb_message_element *members;
    1009             :     int nesting_level;
    1010             :     char *group_dn;
    1011             :     bool deref;
    1012             :     bool deref_shortcut;
    1013             : };
    1014             : 
    1015             : static void sdap_nested_group_process_done(struct tevent_req *subreq);
    1016             : 
    1017             : static struct tevent_req *
    1018          18 : sdap_nested_group_process_send(TALLOC_CTX *mem_ctx,
    1019             :                                struct tevent_context *ev,
    1020             :                                struct sdap_nested_group_ctx *group_ctx,
    1021             :                                int nesting_level,
    1022             :                                struct sysdb_attrs *group)
    1023             : {
    1024          18 :     struct sdap_nested_group_process_state *state = NULL;
    1025          18 :     struct sdap_attr_map *group_map = NULL;
    1026          18 :     struct tevent_req *req = NULL;
    1027          18 :     struct tevent_req *subreq = NULL;
    1028          18 :     const char *orig_dn = NULL;
    1029             :     errno_t ret;
    1030             :     int split_threshold;
    1031             : 
    1032          18 :     req = tevent_req_create(mem_ctx, &state,
    1033             :                             struct sdap_nested_group_process_state);
    1034          18 :     if (req == NULL) {
    1035           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    1036           0 :         return NULL;
    1037             :     }
    1038             : 
    1039          18 :     state->ev = ev;
    1040          18 :     state->group_ctx = group_ctx;
    1041          18 :     state->nesting_level = nesting_level;
    1042          18 :     group_map = state->group_ctx->opts->group_map;
    1043             : 
    1044             :     /* get original dn */
    1045          18 :     ret = sysdb_attrs_get_string(group, SYSDB_ORIG_DN, &orig_dn);
    1046          18 :     if (ret != EOK) {
    1047           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve original dn "
    1048             :                                     "[%d]: %s\n", ret, strerror(ret));
    1049           0 :         goto immediately;
    1050             :     }
    1051             : 
    1052          18 :     state->group_dn = talloc_strdup(state, orig_dn);
    1053          18 :     if (state->group_dn == NULL) {
    1054           0 :         ret = ENOMEM;
    1055           0 :         goto immediately;
    1056             :     }
    1057             : 
    1058          18 :     DEBUG(SSSDBG_TRACE_INTERNAL, "About to process group [%s]\n", orig_dn);
    1059             :     PROBE(SDAP_NESTED_GROUP_PROCESS_SEND, state->group_dn);
    1060             : 
    1061             :     /* get member list, both direct and external */
    1062          18 :     state->ext_members = sdap_nested_group_ext_members(state->group_ctx->opts,
    1063             :                                                        group);
    1064             : 
    1065          18 :     ret = sysdb_attrs_get_el_ext(group, group_map[SDAP_AT_GROUP_MEMBER].sys_name,
    1066          18 :                                  false, &state->members);
    1067          18 :     if (ret == ENOENT && state->ext_members == NULL) {
    1068           4 :         ret = EOK; /* no members, direct or external */
    1069           4 :         goto immediately;
    1070          14 :     } else if (ret != EOK && ret != ENOENT) {
    1071           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve member list "
    1072             :                                     "[%d]: %s\n", ret, strerror(ret));
    1073           0 :         goto immediately;
    1074             :     }
    1075             : 
    1076          28 :     split_threshold = state->group_ctx->try_deref ? \
    1077          14 :                             state->group_ctx->deref_treshold : \
    1078             :                             -1;
    1079             : 
    1080             :     /* get members that need to be refreshed */
    1081             :     PROBE(SDAP_NESTED_GROUP_PROCESS_SPLIT_PRE);
    1082          70 :     ret = sdap_nested_group_split_members(state, state->group_ctx,
    1083             :                                           split_threshold,
    1084          14 :                                           state->nesting_level,
    1085          14 :                                           state->members,
    1086          14 :                                           &state->missing,
    1087          14 :                                           &state->num_missing_total,
    1088          14 :                                           &state->num_missing_groups);
    1089             :     PROBE(SDAP_NESTED_GROUP_PROCESS_SPLIT_POST);
    1090          14 :     if (ret == ERR_DEREF_THRESHOLD) {
    1091           0 :         DEBUG(SSSDBG_TRACE_FUNC,
    1092             :               "More members were missing than the deref threshold\n");
    1093           0 :         state->deref_shortcut = true;
    1094          14 :     } else if (ret != EOK) {
    1095           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to split member list "
    1096             :                                     "[%d]: %s\n", ret, sss_strerror(ret));
    1097           0 :         goto immediately;
    1098             :     }
    1099             : 
    1100          14 :     ret = sdap_nested_group_add_ext_members(state->group_ctx,
    1101             :                                             group,
    1102          14 :                                             state->ext_members);
    1103          14 :     if (ret != EOK) {
    1104           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to split external member list "
    1105             :                                     "[%d]: %s\n", ret, sss_strerror(ret));
    1106           0 :         goto immediately;
    1107             :     }
    1108             : 
    1109          14 :     if (state->num_missing_total == 0
    1110           2 :             && hash_count(state->group_ctx->missing_external) == 0) {
    1111           0 :         ret = EOK; /* we're done */
    1112           0 :         goto immediately;
    1113             :     }
    1114             : 
    1115             :     /* If there are only indirect members of the group, it's still safe to
    1116             :      * proceed and let the direct lookup code just fall through.
    1117             :      */
    1118             : 
    1119          14 :     DEBUG(SSSDBG_TRACE_INTERNAL,
    1120             :           "Looking up %d/%d members of group [%s]\n",
    1121             :           state->num_missing_total,
    1122             :           state->members ? state->members->num_values : 0,
    1123             :           orig_dn);
    1124             : 
    1125             :     /* process members */
    1126          14 :     if (group_ctx->try_deref
    1127           0 :             && state->num_missing_total > group_ctx->deref_treshold) {
    1128           0 :         DEBUG(SSSDBG_TRACE_INTERNAL, "Dereferencing members of group [%s]\n",
    1129             :                                       orig_dn);
    1130           0 :         state->deref = true;
    1131           0 :         subreq = sdap_nested_group_deref_send(state, ev, group_ctx,
    1132           0 :                                               state->members, orig_dn,
    1133           0 :                                               state->nesting_level);
    1134             :     } else {
    1135          14 :         DEBUG(SSSDBG_TRACE_INTERNAL, "Members of group [%s] will be "
    1136             :                                       "processed individually\n", orig_dn);
    1137          14 :         state->deref = false;
    1138          56 :         subreq = sdap_nested_group_single_send(state, ev, group_ctx,
    1139          14 :                                                state->missing,
    1140          14 :                                                state->num_missing_total,
    1141          14 :                                                state->num_missing_groups,
    1142          14 :                                                state->nesting_level);
    1143             :     }
    1144          14 :     if (subreq == NULL) {
    1145           0 :         ret = ENOMEM;
    1146           0 :         goto immediately;
    1147             :     }
    1148             : 
    1149          14 :     tevent_req_set_callback(subreq, sdap_nested_group_process_done, req);
    1150             : 
    1151          14 :     return req;
    1152             : 
    1153             : immediately:
    1154           4 :     if (ret == EOK) {
    1155           4 :         tevent_req_done(req);
    1156             :     } else {
    1157           0 :         tevent_req_error(req, ret);
    1158             :     }
    1159           4 :     tevent_req_post(req, ev);
    1160             : 
    1161           4 :     return req;
    1162             : }
    1163             : 
    1164          14 : static void sdap_nested_group_process_done(struct tevent_req *subreq)
    1165             : {
    1166          14 :     struct sdap_nested_group_process_state *state = NULL;
    1167          14 :     struct tevent_req *req = NULL;
    1168             :     errno_t ret;
    1169             : 
    1170          14 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1171          14 :     state = tevent_req_data(req, struct sdap_nested_group_process_state);
    1172             : 
    1173          14 :     if (state->deref) {
    1174           0 :         ret = sdap_nested_group_deref_recv(subreq);
    1175           0 :         talloc_zfree(subreq);
    1176           0 :         if (ret == ENOTSUP) {
    1177             :             /* dereference is not supported, try again without dereference */
    1178           0 :             state->group_ctx->try_deref = false;
    1179           0 :             state->deref = false;
    1180             : 
    1181           0 :             DEBUG(SSSDBG_TRACE_INTERNAL, "Members of group [%s] will be "
    1182             :                   "processed individually\n", state->group_dn);
    1183             : 
    1184           0 :             if (state->deref_shortcut == true) {
    1185             :                 /* If we previously short-cut dereference, we need to split the
    1186             :                  * members again to get full list of missing member types
    1187             :                  */
    1188             :                 PROBE(SDAP_NESTED_GROUP_PROCESS_SPLIT_PRE);
    1189           0 :                 ret = sdap_nested_group_split_members(state, state->group_ctx,
    1190             :                                                       -1,
    1191             :                                                       state->nesting_level,
    1192             :                                                       state->members,
    1193             :                                                       &state->missing,
    1194             :                                                       &state->num_missing_total,
    1195             :                                                       &state->num_missing_groups);
    1196             :                 PROBE(SDAP_NESTED_GROUP_PROCESS_SPLIT_POST);
    1197           0 :                 if (ret != EOK) {
    1198           0 :                     DEBUG(SSSDBG_CRIT_FAILURE, "Unable to split member list "
    1199             :                                                 "[%d]: %s\n",
    1200             :                                                 ret, sss_strerror(ret));
    1201           0 :                     goto done;
    1202             :                 }
    1203             :             }
    1204             : 
    1205           0 :             subreq = sdap_nested_group_single_send(state,
    1206             :                                                    state->ev,
    1207             :                                                    state->group_ctx,
    1208             :                                                    state->missing,
    1209             :                                                    state->num_missing_total,
    1210             :                                                    state->num_missing_groups,
    1211             :                                                    state->nesting_level);
    1212           0 :             if (subreq == NULL) {
    1213           0 :                 ret = ENOMEM;
    1214           0 :                 goto done;
    1215             :             }
    1216             : 
    1217           0 :             tevent_req_set_callback(subreq, sdap_nested_group_process_done,
    1218             :                                     req);
    1219             : 
    1220           0 :             ret = EAGAIN;
    1221             :         }
    1222             :     } else {
    1223          14 :         ret = sdap_nested_group_single_recv(subreq);
    1224          14 :         talloc_zfree(subreq);
    1225             :     }
    1226             : 
    1227             : done:
    1228          14 :     if (ret == EOK) {
    1229          11 :         tevent_req_done(req);
    1230           3 :     } else if (ret != EAGAIN) {
    1231           3 :         tevent_req_error(req, ret);
    1232             :     }
    1233          14 : }
    1234             : 
    1235          18 : static errno_t sdap_nested_group_process_recv(struct tevent_req *req)
    1236             : {
    1237             : #ifdef HAVE_SYSTEMTAP
    1238             :     struct sdap_nested_group_process_state *state = NULL;
    1239             :     state = tevent_req_data(req, struct sdap_nested_group_process_state);
    1240             : 
    1241             :     PROBE(SDAP_NESTED_GROUP_PROCESS_RECV, state->group_dn);
    1242             : #endif
    1243             : 
    1244          21 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1245             : 
    1246          15 :     return EOK;
    1247             : }
    1248             : 
    1249             : struct sdap_nested_group_recurse_state {
    1250             :     struct tevent_context *ev;
    1251             :     struct sdap_nested_group_ctx *group_ctx;
    1252             :     struct sysdb_attrs **groups;
    1253             :     int num_groups;
    1254             :     int index;
    1255             :     int nesting_level;
    1256             : };
    1257             : 
    1258             : static errno_t sdap_nested_group_recurse_step(struct tevent_req *req);
    1259             : static void sdap_nested_group_recurse_done(struct tevent_req *subreq);
    1260             : 
    1261             : static struct tevent_req *
    1262          11 : sdap_nested_group_recurse_send(TALLOC_CTX *mem_ctx,
    1263             :                                struct tevent_context *ev,
    1264             :                                struct sdap_nested_group_ctx *group_ctx,
    1265             :                                struct sysdb_attrs **nested_groups,
    1266             :                                int num_groups,
    1267             :                                int nesting_level)
    1268             : {
    1269          11 :     struct sdap_nested_group_recurse_state *state = NULL;
    1270          11 :     struct tevent_req *req = NULL;
    1271             :     errno_t ret;
    1272             : 
    1273          11 :     req = tevent_req_create(mem_ctx, &state,
    1274             :                             struct sdap_nested_group_recurse_state);
    1275          11 :     if (req == NULL) {
    1276           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    1277           0 :         return NULL;
    1278             :     }
    1279             : 
    1280          11 :     state->ev = ev;
    1281          11 :     state->group_ctx = group_ctx;
    1282          11 :     state->groups = nested_groups;
    1283          11 :     state->num_groups = num_groups;
    1284          11 :     state->index = 0;
    1285          11 :     state->nesting_level = nesting_level;
    1286             : 
    1287             :     /* process each group individually */
    1288          11 :     ret = sdap_nested_group_recurse_step(req);
    1289          11 :     if (ret != EAGAIN) {
    1290           3 :         goto immediately;
    1291             :     }
    1292             : 
    1293           8 :     return req;
    1294             : 
    1295             : immediately:
    1296           3 :     if (ret == EOK) {
    1297           3 :         tevent_req_done(req);
    1298             :     } else {
    1299           0 :         tevent_req_error(req, ret);
    1300             :     }
    1301           3 :     tevent_req_post(req, ev);
    1302             : 
    1303           3 :     return req;
    1304             : }
    1305             : 
    1306          19 : static errno_t sdap_nested_group_recurse_step(struct tevent_req *req)
    1307             : {
    1308          19 :     struct sdap_nested_group_recurse_state *state = NULL;
    1309          19 :     struct tevent_req *subreq = NULL;
    1310             : 
    1311          19 :     state = tevent_req_data(req, struct sdap_nested_group_recurse_state);
    1312             : 
    1313          19 :     if (state->index >= state->num_groups) {
    1314             :         /* we're done */
    1315           9 :         return EOK;
    1316             :     }
    1317             : 
    1318          10 :     subreq = sdap_nested_group_process_send(state, state->ev, state->group_ctx,
    1319             :                                             state->nesting_level,
    1320          10 :                                             state->groups[state->index]);
    1321          10 :     if (subreq == NULL) {
    1322           0 :         return ENOMEM;
    1323             :     }
    1324             : 
    1325          10 :     tevent_req_set_callback(subreq, sdap_nested_group_recurse_done, req);
    1326             : 
    1327          10 :     state->index++;
    1328             : 
    1329          10 :     return EAGAIN;
    1330             : }
    1331             : 
    1332          10 : static void sdap_nested_group_recurse_done(struct tevent_req *subreq)
    1333             : {
    1334          10 :     struct tevent_req *req = NULL;
    1335             :     errno_t ret;
    1336             : 
    1337          10 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1338             : 
    1339          10 :     ret = sdap_nested_group_process_recv(subreq);
    1340          10 :     talloc_zfree(subreq);
    1341          10 :     if (ret != EOK) {
    1342           2 :         goto done;
    1343             :     }
    1344             : 
    1345           8 :     ret = sdap_nested_group_recurse_step(req);
    1346             : 
    1347             : done:
    1348          10 :     if (ret == EOK) {
    1349           6 :         tevent_req_done(req);
    1350           4 :     } else if (ret != EAGAIN) {
    1351           2 :         tevent_req_error(req, ret);
    1352             :     }
    1353             : 
    1354          10 :     return;
    1355             : }
    1356             : 
    1357          11 : static errno_t sdap_nested_group_recurse_recv(struct tevent_req *req)
    1358             : {
    1359          13 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1360             : 
    1361           9 :     return EOK;
    1362             : }
    1363             : 
    1364             : struct sdap_nested_group_single_state {
    1365             :     struct tevent_context *ev;
    1366             :     struct sdap_nested_group_ctx *group_ctx;
    1367             :     struct sdap_nested_group_member *members;
    1368             :     int nesting_level;
    1369             : 
    1370             :     struct sdap_nested_group_member *current_member;
    1371             :     int num_members;
    1372             :     int member_index;
    1373             : 
    1374             :     struct sysdb_attrs **nested_groups;
    1375             :     int num_groups;
    1376             : };
    1377             : 
    1378             : static errno_t sdap_nested_group_single_step(struct tevent_req *req);
    1379             : static void sdap_nested_group_single_step_done(struct tevent_req *subreq);
    1380             : static void sdap_nested_group_single_done(struct tevent_req *subreq);
    1381             : 
    1382             : static struct tevent_req *
    1383          14 : sdap_nested_group_single_send(TALLOC_CTX *mem_ctx,
    1384             :                               struct tevent_context *ev,
    1385             :                               struct sdap_nested_group_ctx *group_ctx,
    1386             :                               struct sdap_nested_group_member *members,
    1387             :                               int num_members,
    1388             :                               int num_groups_max,
    1389             :                               int nesting_level)
    1390             : {
    1391          14 :     struct sdap_nested_group_single_state *state = NULL;
    1392          14 :     struct tevent_req *req = NULL;
    1393             :     errno_t ret;
    1394             : 
    1395          14 :     req = tevent_req_create(mem_ctx, &state,
    1396             :                             struct sdap_nested_group_single_state);
    1397          14 :     if (req == NULL) {
    1398           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    1399           0 :         return NULL;
    1400             :     }
    1401             : 
    1402          14 :     state->ev = ev;
    1403          14 :     state->group_ctx = group_ctx;
    1404          14 :     state->members = members;
    1405          14 :     state->nesting_level = nesting_level;
    1406          14 :     state->current_member = NULL;
    1407          14 :     state->num_members = num_members;
    1408          14 :     state->member_index = 0;
    1409          14 :     state->nested_groups = talloc_zero_array(state, struct sysdb_attrs *,
    1410             :                                              num_groups_max);
    1411          14 :     if (state->nested_groups == NULL) {
    1412           0 :         ret = ENOMEM;
    1413           0 :         goto immediately;
    1414             :     }
    1415          14 :     state->num_groups = 0; /* we will count exact number of the groups */
    1416             : 
    1417             :     /* process each member individually */
    1418          14 :     ret = sdap_nested_group_single_step(req);
    1419          14 :     if (ret != EAGAIN) {
    1420           2 :         goto immediately;
    1421             :     }
    1422             : 
    1423          12 :     return req;
    1424             : 
    1425             : immediately:
    1426           2 :     if (ret == EOK) {
    1427           2 :         tevent_req_done(req);
    1428             :     } else {
    1429           0 :         tevent_req_error(req, ret);
    1430             :     }
    1431           2 :     tevent_req_post(req, ev);
    1432             : 
    1433           2 :     return req;
    1434             : }
    1435             : 
    1436          32 : static errno_t sdap_nested_group_single_step(struct tevent_req *req)
    1437             : {
    1438          32 :     struct sdap_nested_group_single_state *state = NULL;
    1439          32 :     struct tevent_req *subreq = NULL;
    1440             : 
    1441          32 :     state = tevent_req_data(req, struct sdap_nested_group_single_state);
    1442             : 
    1443          32 :     if (state->member_index >= state->num_members) {
    1444             :         /* we're done */
    1445          13 :         return EOK;
    1446             :     }
    1447             : 
    1448          19 :     state->current_member = &state->members[state->member_index];
    1449          19 :     state->member_index++;
    1450             : 
    1451          19 :     switch (state->current_member->type) {
    1452             :     case SDAP_NESTED_GROUP_DN_USER:
    1453           8 :         subreq = sdap_nested_group_lookup_user_send(state, state->ev,
    1454             :                                                     state->group_ctx,
    1455             :                                                     state->current_member);
    1456           8 :         break;
    1457             :     case SDAP_NESTED_GROUP_DN_GROUP:
    1458          11 :         subreq = sdap_nested_group_lookup_group_send(state, state->ev,
    1459             :                                                      state->group_ctx,
    1460             :                                                      state->current_member);
    1461          11 :         break;
    1462             :     case SDAP_NESTED_GROUP_DN_UNKNOWN:
    1463           0 :         subreq = sdap_nested_group_lookup_unknown_send(state, state->ev,
    1464             :                                                    state->group_ctx,
    1465             :                                                    state->current_member);
    1466           0 :         break;
    1467             :     }
    1468             : 
    1469          19 :     if (subreq == NULL) {
    1470           0 :         return ENOMEM;
    1471             :     }
    1472             : 
    1473          19 :     tevent_req_set_callback(subreq, sdap_nested_group_single_step_done, req);
    1474             : 
    1475          19 :     return EAGAIN;
    1476             : }
    1477             : 
    1478             : static errno_t
    1479          19 : sdap_nested_group_single_step_process(struct tevent_req *subreq)
    1480             : {
    1481          19 :     struct sdap_nested_group_single_state *state = NULL;
    1482          19 :     struct tevent_req *req = NULL;
    1483          19 :     struct sysdb_attrs *entry = NULL;
    1484          19 :     enum sdap_nested_group_dn_type type = SDAP_NESTED_GROUP_DN_UNKNOWN;
    1485          19 :     const char *orig_dn = NULL;
    1486             :     errno_t ret;
    1487             : 
    1488          19 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1489          19 :     state = tevent_req_data(req, struct sdap_nested_group_single_state);
    1490             : 
    1491             :     /* set correct type if possible */
    1492          19 :     if (state->current_member->type == SDAP_NESTED_GROUP_DN_UNKNOWN) {
    1493           0 :         ret = sdap_nested_group_lookup_unknown_recv(state, subreq,
    1494             :                                                     &entry, &type);
    1495           0 :         if (ret != EOK) {
    1496           0 :             goto done;
    1497             :         }
    1498             : 
    1499           0 :         if (entry != NULL) {
    1500           0 :             state->current_member->type = type;
    1501             :         }
    1502             :     }
    1503             : 
    1504          19 :     switch (state->current_member->type) {
    1505             :     case SDAP_NESTED_GROUP_DN_USER:
    1506           8 :         if (entry == NULL) {
    1507             :             /* type was not unknown, receive data */
    1508           8 :             ret = sdap_nested_group_lookup_user_recv(state, subreq, &entry);
    1509           8 :             if (ret != EOK) {
    1510           1 :                 goto done;
    1511             :             }
    1512             : 
    1513           7 :             if (entry == NULL) {
    1514             :                 /* user not found, continue */
    1515           0 :                 break;
    1516             :             }
    1517             :         }
    1518             : 
    1519             :         /* save user in hash table */
    1520           7 :         ret = sdap_nested_group_hash_user(state->group_ctx, entry);
    1521           7 :         if (ret == EEXIST) {
    1522             :             /* the user is already present, skip it */
    1523           1 :             talloc_zfree(entry);
    1524           1 :             ret = EOK;
    1525           1 :             goto done;
    1526           6 :         } else if (ret != EOK) {
    1527           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Unable to save user in hash table "
    1528             :                                         "[%d]: %s\n", ret, strerror(ret));
    1529           0 :             goto done;
    1530             :         }
    1531           6 :         break;
    1532             :     case SDAP_NESTED_GROUP_DN_GROUP:
    1533          11 :         if (entry == NULL) {
    1534             :             /* type was not unknown, receive data */
    1535          11 :             ret = sdap_nested_group_lookup_group_recv(state, subreq, &entry);
    1536          11 :             if (ret != EOK) {
    1537           0 :                 goto done;
    1538             :             }
    1539             : 
    1540          11 :             if (entry == NULL) {
    1541             :                 /* group not found, continue */
    1542           0 :                 break;
    1543             :             }
    1544             :         } else {
    1545             :             /* the type was unknown so we had to pull the group,
    1546             :              * but we don't want to process it if we have reached
    1547             :              * the nesting level */
    1548           0 :             if (state->nesting_level >= state->group_ctx->max_nesting_level) {
    1549           0 :                 ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &orig_dn);
    1550           0 :                 if (ret != EOK) {
    1551           0 :                     DEBUG(SSSDBG_MINOR_FAILURE,
    1552             :                           "The entry has no originalDN\n");
    1553           0 :                     orig_dn = "invalid";
    1554             :                 }
    1555             : 
    1556           0 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is outside nesting limit "
    1557             :                       "(level %d), skipping\n", orig_dn, state->nesting_level);
    1558           0 :                 break;
    1559             :             }
    1560             :         }
    1561             : 
    1562             :         /* save group in hash table */
    1563          11 :         ret = sdap_nested_group_hash_group(state->group_ctx, entry);
    1564          11 :         if (ret == EEXIST) {
    1565             :             /* the group is already present, skip it */
    1566           1 :             talloc_zfree(entry);
    1567           1 :             ret = EOK;
    1568           1 :             goto done;
    1569          10 :         } else if (ret != EOK) {
    1570           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Unable to save group in hash table "
    1571             :                                         "[%d]: %s\n", ret, strerror(ret));
    1572           0 :             goto done;
    1573             :         }
    1574             : 
    1575             :         /* remember the group for later processing */
    1576          10 :         state->nested_groups[state->num_groups] = entry;
    1577          10 :         state->num_groups++;
    1578             : 
    1579          10 :         break;
    1580             :     case SDAP_NESTED_GROUP_DN_UNKNOWN:
    1581             :         /* not found in users nor nested_groups, continue */
    1582           0 :         break;
    1583             :     }
    1584             : 
    1585          16 :     ret = EOK;
    1586             : 
    1587             : done:
    1588          19 :     return ret;
    1589             : }
    1590             : 
    1591          19 : static void sdap_nested_group_single_step_done(struct tevent_req *subreq)
    1592             : {
    1593          19 :     struct sdap_nested_group_single_state *state = NULL;
    1594          19 :     struct tevent_req *req = NULL;
    1595             :     errno_t ret;
    1596             : 
    1597          19 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1598          19 :     state = tevent_req_data(req, struct sdap_nested_group_single_state);
    1599             : 
    1600             :     /* process direct members */
    1601          19 :     ret = sdap_nested_group_single_step_process(subreq);
    1602          19 :     talloc_zfree(subreq);
    1603          19 :     if (ret != EOK) {
    1604           1 :         DEBUG(SSSDBG_CRIT_FAILURE, "Error processing direct membership "
    1605             :                                     "[%d]: %s\n", ret, strerror(ret));
    1606           1 :         goto done;
    1607             :     }
    1608             : 
    1609          18 :     ret = sdap_nested_group_single_step(req);
    1610          18 :     if (ret == EOK) {
    1611             :         /* we have processed all direct members,
    1612             :          * now recurse and process nested groups */
    1613          11 :         subreq = sdap_nested_group_recurse_send(state, state->ev,
    1614             :                                                 state->group_ctx,
    1615             :                                                 state->nested_groups,
    1616             :                                                 state->num_groups,
    1617          11 :                                                 state->nesting_level + 1);
    1618          11 :         if (subreq == NULL) {
    1619           0 :             ret = ENOMEM;
    1620           0 :             goto done;
    1621             :         }
    1622             : 
    1623          11 :         tevent_req_set_callback(subreq, sdap_nested_group_single_done, req);
    1624           7 :     } else if (ret != EAGAIN) {
    1625             :         /* error */
    1626           0 :         goto done;
    1627             :     }
    1628             : 
    1629             :     /* we're not done yet */
    1630          18 :     ret = EAGAIN;
    1631             : 
    1632             : done:
    1633          19 :     if (ret == EOK) {
    1634             :         /* tevent_req_error() cannot cope with EOK */
    1635           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "We should not get here with EOK\n");
    1636           0 :         tevent_req_error(req, EINVAL);
    1637          19 :     } else if (ret != EAGAIN) {
    1638           1 :         tevent_req_error(req, ret);
    1639             :     }
    1640             : 
    1641          19 :     return;
    1642             : }
    1643             : 
    1644          11 : static void sdap_nested_group_single_done(struct tevent_req *subreq)
    1645             : {
    1646          11 :     struct tevent_req *req = NULL;
    1647             :     errno_t ret;
    1648             : 
    1649          11 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1650             : 
    1651             :     /* all nested groups are completed */
    1652          11 :     ret = sdap_nested_group_recurse_recv(subreq);
    1653          11 :     talloc_zfree(subreq);
    1654          11 :     if (ret != EOK) {
    1655           2 :         DEBUG(SSSDBG_CRIT_FAILURE, "Error processing nested groups "
    1656             :                                     "[%d]: %s.\n", ret, strerror(ret));
    1657           2 :         tevent_req_error(req, ret);
    1658           2 :         return;
    1659             :     }
    1660             : 
    1661           9 :     tevent_req_done(req);
    1662             : 
    1663           9 :     return;
    1664             : }
    1665             : 
    1666          14 : static errno_t sdap_nested_group_single_recv(struct tevent_req *req)
    1667             : {
    1668          17 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1669             : 
    1670          11 :     return EOK;
    1671             : }
    1672             : 
    1673           0 : static errno_t sdap_nested_group_get_ipa_user(TALLOC_CTX *mem_ctx,
    1674             :                                               const char *user_dn,
    1675             :                                               struct sysdb_ctx *sysdb,
    1676             :                                               struct sysdb_attrs **_user)
    1677             : {
    1678             :     TALLOC_CTX *tmp_ctx;
    1679             :     struct sysdb_attrs *user;
    1680             :     char *name;
    1681             :     errno_t ret;
    1682             : 
    1683           0 :     tmp_ctx = talloc_new(NULL);
    1684           0 :     if (tmp_ctx == NULL) {
    1685           0 :         return ENOMEM;
    1686             :     }
    1687             : 
    1688           0 :     ret = ipa_get_rdn(tmp_ctx, sysdb, user_dn, &name, "uid",
    1689             :                       "cn", "users", "cn", "accounts");
    1690           0 :     if (ret != EOK) {
    1691           0 :         goto done;
    1692             :     }
    1693             : 
    1694           0 :     user = sysdb_new_attrs(tmp_ctx);
    1695           0 :     if (user == NULL) {
    1696           0 :         ret = ENOMEM;
    1697           0 :         goto done;
    1698             :     }
    1699             : 
    1700           0 :     ret = sysdb_attrs_add_string(user, SYSDB_NAME, name);
    1701           0 :     if (ret != EOK) {
    1702           0 :         goto done;
    1703             :     }
    1704             : 
    1705           0 :     ret = sysdb_attrs_add_string(user, SYSDB_ORIG_DN, user_dn);
    1706           0 :     if (ret != EOK) {
    1707           0 :         goto done;
    1708             :     }
    1709             : 
    1710           0 :     ret = sysdb_attrs_add_string(user, SYSDB_OBJECTCLASS, SYSDB_USER_CLASS);
    1711           0 :     if (ret != EOK) {
    1712           0 :         goto done;
    1713             :     }
    1714             : 
    1715           0 :     *_user = talloc_steal(mem_ctx, user);
    1716             : 
    1717             : done:
    1718           0 :     talloc_free(tmp_ctx);
    1719           0 :     return ret;
    1720             : }
    1721             : 
    1722             : struct sdap_nested_group_lookup_user_state {
    1723             :     struct sysdb_attrs *user;
    1724             : };
    1725             : 
    1726             : static void sdap_nested_group_lookup_user_done(struct tevent_req *subreq);
    1727             : 
    1728             : static struct tevent_req *
    1729           8 : sdap_nested_group_lookup_user_send(TALLOC_CTX *mem_ctx,
    1730             :                                    struct tevent_context *ev,
    1731             :                                    struct sdap_nested_group_ctx *group_ctx,
    1732             :                                    struct sdap_nested_group_member *member)
    1733             : {
    1734           8 :     struct sdap_nested_group_lookup_user_state *state = NULL;
    1735           8 :     struct tevent_req *req = NULL;
    1736           8 :     struct tevent_req *subreq = NULL;
    1737           8 :     const char **attrs = NULL;
    1738           8 :     const char *base_filter = NULL;
    1739           8 :     const char *filter = NULL;
    1740             :     errno_t ret;
    1741             : 
    1742           8 :     req = tevent_req_create(mem_ctx, &state,
    1743             :                             struct sdap_nested_group_lookup_user_state);
    1744           8 :     if (req == NULL) {
    1745           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    1746           0 :         return NULL;
    1747             :     }
    1748             : 
    1749             :     PROBE(SDAP_NESTED_GROUP_LOOKUP_USER_SEND);
    1750             : 
    1751           8 :     if (group_ctx->opts->schema_type == SDAP_SCHEMA_IPA_V1) {
    1752             :         /* if the schema is IPA, then just shortcut and guess the name */
    1753           0 :         ret = sdap_nested_group_get_ipa_user(state, member->dn,
    1754           0 :                                              group_ctx->domain->sysdb,
    1755           0 :                                              &state->user);
    1756           0 :         if (ret == EOK) {
    1757           0 :             goto immediately;
    1758             :         }
    1759             : 
    1760           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "Couldn't parse out user information "
    1761             :               "based on DN %s, falling back to an LDAP lookup\n", member->dn);
    1762             :     }
    1763             : 
    1764             :     /* only pull down username and originalDN */
    1765           8 :     attrs = talloc_array(state, const char *, 3);
    1766           8 :     if (attrs == NULL) {
    1767           0 :         ret = ENOMEM;
    1768           0 :         goto immediately;
    1769             :     }
    1770             : 
    1771           8 :     attrs[0] = "objectClass";
    1772           8 :     attrs[1] = group_ctx->opts->user_map[SDAP_AT_USER_NAME].name;
    1773           8 :     attrs[2] = NULL;
    1774             : 
    1775             :     /* create filter */
    1776           8 :     base_filter = talloc_asprintf(state, "(objectclass=%s)",
    1777           8 :                                   group_ctx->opts->user_map[SDAP_OC_USER].name);
    1778           8 :     if (base_filter == NULL) {
    1779           0 :         ret = ENOMEM;
    1780           0 :         goto immediately;
    1781             :     }
    1782             : 
    1783             :     /* use search base filter if needed */
    1784           8 :     filter = sdap_combine_filters(state, base_filter, member->user_filter);
    1785           8 :     if (filter == NULL) {
    1786           0 :         ret = ENOMEM;
    1787           0 :         goto immediately;
    1788             :     }
    1789             : 
    1790             :     /* search */
    1791          24 :     subreq = sdap_get_generic_send(state, ev, group_ctx->opts, group_ctx->sh,
    1792             :                                    member->dn, LDAP_SCOPE_BASE, filter, attrs,
    1793           8 :                                    group_ctx->opts->user_map,
    1794           8 :                                    group_ctx->opts->user_map_cnt,
    1795           8 :                                    dp_opt_get_int(group_ctx->opts->basic,
    1796             :                                                   SDAP_SEARCH_TIMEOUT),
    1797             :                                    false);
    1798           8 :     if (subreq == NULL) {
    1799           0 :         ret = ENOMEM;
    1800           0 :         goto immediately;
    1801             :     }
    1802             : 
    1803           8 :     tevent_req_set_callback(subreq, sdap_nested_group_lookup_user_done, req);
    1804             : 
    1805           8 :     return req;
    1806             : 
    1807             : immediately:
    1808           0 :     if (ret == EOK) {
    1809           0 :         tevent_req_done(req);
    1810             :     } else {
    1811           0 :         tevent_req_error(req, ret);
    1812             :     }
    1813           0 :     tevent_req_post(req, ev);
    1814             : 
    1815           0 :     return req;
    1816             : }
    1817             : 
    1818           8 : static void sdap_nested_group_lookup_user_done(struct tevent_req *subreq)
    1819             : {
    1820           8 :     struct sdap_nested_group_lookup_user_state *state = NULL;
    1821           8 :     struct tevent_req *req = NULL;
    1822           8 :     struct sysdb_attrs **user = NULL;
    1823           8 :     size_t count = 0;
    1824             :     errno_t ret;
    1825             : 
    1826           8 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1827           8 :     state = tevent_req_data(req, struct sdap_nested_group_lookup_user_state);
    1828             : 
    1829           8 :     ret = sdap_get_generic_recv(subreq, state, &count, &user);
    1830           8 :     talloc_zfree(subreq);
    1831           8 :     if (ret == ENOENT) {
    1832           0 :         count = 0;
    1833           8 :     } else if (ret != EOK) {
    1834           1 :         goto done;
    1835             :     }
    1836             : 
    1837           7 :     if (count == 1) {
    1838           7 :         state->user = user[0];
    1839           0 :     } else if (count == 0) {
    1840             :         /* group not found */
    1841           0 :         state->user = NULL;
    1842             :     } else {
    1843           0 :         DEBUG(SSSDBG_OP_FAILURE,
    1844             :               "BASE search returned more than one records\n");
    1845           0 :         ret = EIO;
    1846           0 :         goto done;
    1847             :     }
    1848             : 
    1849           7 :     ret = EOK;
    1850             : 
    1851             : done:
    1852           8 :     if (ret != EOK) {
    1853           1 :         tevent_req_error(req, ret);
    1854           9 :         return;
    1855             :     }
    1856             : 
    1857           7 :     tevent_req_done(req);
    1858             : }
    1859             : 
    1860           8 : static errno_t sdap_nested_group_lookup_user_recv(TALLOC_CTX *mem_ctx,
    1861             :                                                   struct tevent_req *req,
    1862             :                                                   struct sysdb_attrs **_user)
    1863             : {
    1864           8 :     struct sdap_nested_group_lookup_user_state *state = NULL;
    1865           8 :     state = tevent_req_data(req, struct sdap_nested_group_lookup_user_state);
    1866             : 
    1867             :     PROBE(SDAP_NESTED_GROUP_LOOKUP_USER_RECV);
    1868             : 
    1869           9 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1870             : 
    1871           7 :     if (_user != NULL) {
    1872           7 :         *_user = talloc_steal(mem_ctx, state->user);
    1873             :     }
    1874             : 
    1875           7 :     return EOK;
    1876             : }
    1877             : 
    1878             : struct sdap_nested_group_lookup_group_state {
    1879             :     struct sysdb_attrs *group;
    1880             : };
    1881             : 
    1882             : static void sdap_nested_group_lookup_group_done(struct tevent_req *subreq);
    1883             : 
    1884             : static struct tevent_req *
    1885          11 : sdap_nested_group_lookup_group_send(TALLOC_CTX *mem_ctx,
    1886             :                                     struct tevent_context *ev,
    1887             :                                     struct sdap_nested_group_ctx *group_ctx,
    1888             :                                     struct sdap_nested_group_member *member)
    1889             : {
    1890          11 :      struct sdap_nested_group_lookup_group_state *state = NULL;
    1891          11 :      struct tevent_req *req = NULL;
    1892          11 :      struct tevent_req *subreq = NULL;
    1893          11 :      struct sdap_attr_map *map = group_ctx->opts->group_map;
    1894          11 :      const char **attrs = NULL;
    1895          11 :      const char *base_filter = NULL;
    1896          11 :      const char *filter = NULL;
    1897             :      char *oc_list;
    1898             :      errno_t ret;
    1899             : 
    1900             :      PROBE(SDAP_NESTED_GROUP_LOOKUP_GROUP_SEND);
    1901             : 
    1902          11 :      req = tevent_req_create(mem_ctx, &state,
    1903             :                              struct sdap_nested_group_lookup_group_state);
    1904          11 :      if (req == NULL) {
    1905           0 :          DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    1906           0 :          return NULL;
    1907             :      }
    1908             : 
    1909          11 :      ret = build_attrs_from_map(state, group_ctx->opts->group_map,
    1910             :                                 SDAP_OPTS_GROUP, NULL, &attrs, NULL);
    1911          11 :      if (ret != EOK) {
    1912           0 :          goto immediately;
    1913             :      }
    1914             : 
    1915             :      /* create filter */
    1916          11 :      oc_list = sdap_make_oc_list(state, map);
    1917          11 :      if (oc_list == NULL) {
    1918           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create objectClass list.\n");
    1919           0 :         ret = ENOMEM;
    1920           0 :         goto immediately;
    1921             :      }
    1922             : 
    1923          11 :      base_filter = talloc_asprintf(attrs, "(&(%s)(%s=*))", oc_list,
    1924          11 :                                    map[SDAP_AT_GROUP_NAME].name);
    1925          11 :      if (base_filter == NULL) {
    1926           0 :          ret = ENOMEM;
    1927           0 :          goto immediately;
    1928             :      }
    1929             : 
    1930             :      /* use search base filter if needed */
    1931          11 :      filter = sdap_combine_filters(state, base_filter, member->group_filter);
    1932          11 :      if (filter == NULL) {
    1933           0 :          ret = ENOMEM;
    1934           0 :          goto immediately;
    1935             :      }
    1936             : 
    1937             :      /* search */
    1938          11 :      subreq = sdap_get_generic_send(state, ev, group_ctx->opts, group_ctx->sh,
    1939             :                                     member->dn, LDAP_SCOPE_BASE, filter, attrs,
    1940             :                                     map, SDAP_OPTS_GROUP,
    1941          11 :                                     dp_opt_get_int(group_ctx->opts->basic,
    1942             :                                                    SDAP_SEARCH_TIMEOUT),
    1943             :                                     false);
    1944          11 :      if (subreq == NULL) {
    1945           0 :          ret = ENOMEM;
    1946           0 :          goto immediately;
    1947             :      }
    1948             : 
    1949          11 :      tevent_req_set_callback(subreq, sdap_nested_group_lookup_group_done, req);
    1950             : 
    1951          11 :      return req;
    1952             : 
    1953             : immediately:
    1954           0 :     if (ret == EOK) {
    1955           0 :         tevent_req_done(req);
    1956             :     } else {
    1957           0 :         tevent_req_error(req, ret);
    1958             :     }
    1959           0 :     tevent_req_post(req, ev);
    1960             : 
    1961           0 :     return req;
    1962             : }
    1963             : 
    1964          11 : static void sdap_nested_group_lookup_group_done(struct tevent_req *subreq)
    1965             : {
    1966          11 :     struct sdap_nested_group_lookup_group_state *state = NULL;
    1967          11 :     struct tevent_req *req = NULL;
    1968          11 :     struct sysdb_attrs **group = NULL;
    1969          11 :     size_t count = 0;
    1970             :     errno_t ret;
    1971             : 
    1972          11 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1973          11 :     state = tevent_req_data(req, struct sdap_nested_group_lookup_group_state);
    1974             : 
    1975          11 :     ret = sdap_get_generic_recv(subreq, state, &count, &group);
    1976          11 :     talloc_zfree(subreq);
    1977          11 :     if (ret == ENOENT) {
    1978           0 :         count = 0;
    1979          11 :     } else if (ret != EOK) {
    1980           0 :         goto done;
    1981             :     }
    1982             : 
    1983          11 :     if (count == 1) {
    1984          11 :         state->group = group[0];
    1985           0 :     } else if (count == 0) {
    1986             :         /* group not found */
    1987           0 :         state->group = NULL;
    1988             :     } else {
    1989           0 :         DEBUG(SSSDBG_OP_FAILURE,
    1990             :               "BASE search returned more than one records\n");
    1991           0 :         ret = EIO;
    1992           0 :         goto done;
    1993             :     }
    1994             : 
    1995          11 :     ret = EOK;
    1996             : 
    1997             : done:
    1998          11 :     if (ret != EOK) {
    1999           0 :         tevent_req_error(req, ret);
    2000          11 :         return;
    2001             :     }
    2002             : 
    2003          11 :     tevent_req_done(req);
    2004             : }
    2005             : 
    2006          11 : static errno_t sdap_nested_group_lookup_group_recv(TALLOC_CTX *mem_ctx,
    2007             :                                                    struct tevent_req *req,
    2008             :                                                    struct sysdb_attrs **_group)
    2009             : {
    2010          11 :      struct sdap_nested_group_lookup_group_state *state = NULL;
    2011          11 :      state = tevent_req_data(req, struct sdap_nested_group_lookup_group_state);
    2012             : 
    2013             :      PROBE(SDAP_NESTED_GROUP_LOOKUP_GROUP_RECV);
    2014             : 
    2015          11 :      TEVENT_REQ_RETURN_ON_ERROR(req);
    2016             : 
    2017          11 :      if (_group != NULL) {
    2018          11 :          *_group = talloc_steal(mem_ctx, state->group);
    2019             :      }
    2020             : 
    2021          11 :      return EOK;
    2022             : }
    2023             : 
    2024             : struct sdap_nested_group_lookup_unknown_state {
    2025             :     struct tevent_context *ev;
    2026             :     struct sdap_nested_group_ctx *group_ctx;
    2027             :     struct sdap_nested_group_member *member;
    2028             :     enum sdap_nested_group_dn_type type;
    2029             :     struct sysdb_attrs *entry;
    2030             : };
    2031             : 
    2032             : static void
    2033             : sdap_nested_group_lookup_unknown_user_done(struct tevent_req *subreq);
    2034             : 
    2035             : static void
    2036             : sdap_nested_group_lookup_unknown_group_done(struct tevent_req *subreq);
    2037             : 
    2038             : static struct tevent_req *
    2039           0 : sdap_nested_group_lookup_unknown_send(TALLOC_CTX *mem_ctx,
    2040             :                                       struct tevent_context *ev,
    2041             :                                       struct sdap_nested_group_ctx *group_ctx,
    2042             :                                       struct sdap_nested_group_member *member)
    2043             : {
    2044           0 :     struct sdap_nested_group_lookup_unknown_state *state = NULL;
    2045           0 :     struct tevent_req *req = NULL;
    2046           0 :     struct tevent_req *subreq = NULL;
    2047             :     errno_t ret;
    2048             : 
    2049           0 :     req = tevent_req_create(mem_ctx, &state,
    2050             :                             struct sdap_nested_group_lookup_unknown_state);
    2051           0 :     if (req == NULL) {
    2052           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    2053           0 :         return NULL;
    2054             :     }
    2055             : 
    2056             :     PROBE(SDAP_NESTED_GROUP_LOOKUP_UNKNOWN_SEND);
    2057             : 
    2058           0 :     state->ev = ev;
    2059           0 :     state->group_ctx = group_ctx;
    2060           0 :     state->member = member;
    2061             : 
    2062             :     /* try users first */
    2063           0 :     subreq = sdap_nested_group_lookup_user_send(state,
    2064           0 :                                                 state->ev,
    2065           0 :                                                 state->group_ctx,
    2066           0 :                                                 state->member);
    2067           0 :     if (subreq == NULL) {
    2068           0 :         ret = ENOMEM;
    2069           0 :         goto immediately;
    2070             :     }
    2071             : 
    2072           0 :     tevent_req_set_callback(subreq, sdap_nested_group_lookup_unknown_user_done,
    2073             :                             req);
    2074             : 
    2075           0 :     return req;
    2076             : 
    2077             : immediately:
    2078           0 :     if (ret == EOK) {
    2079           0 :         tevent_req_done(req);
    2080             :     } else {
    2081           0 :         tevent_req_error(req, ret);
    2082             :     }
    2083           0 :     tevent_req_post(req, ev);
    2084             : 
    2085           0 :     return req;
    2086             : }
    2087             : 
    2088             : static void
    2089           0 : sdap_nested_group_lookup_unknown_user_done(struct tevent_req *subreq)
    2090             : {
    2091           0 :     struct sdap_nested_group_lookup_unknown_state *state = NULL;
    2092           0 :     struct tevent_req *req = NULL;
    2093           0 :     struct sysdb_attrs *entry = NULL;
    2094             :     errno_t ret;
    2095             : 
    2096           0 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    2097           0 :     state = tevent_req_data(req, struct sdap_nested_group_lookup_unknown_state);
    2098             : 
    2099           0 :     ret = sdap_nested_group_lookup_user_recv(state, subreq, &entry);
    2100           0 :     talloc_zfree(subreq);
    2101           0 :     if (ret != EOK) {
    2102           0 :         goto done;
    2103             :     }
    2104             : 
    2105           0 :     if (entry != NULL) {
    2106             :         /* found in users */
    2107           0 :         state->entry = entry;
    2108           0 :         state->type = SDAP_NESTED_GROUP_DN_USER;
    2109           0 :         ret = EOK;
    2110           0 :         goto done;
    2111             :     }
    2112             : 
    2113             :     /* not found in users, try group */
    2114           0 :     subreq = sdap_nested_group_lookup_group_send(state,
    2115             :                                                  state->ev,
    2116             :                                                  state->group_ctx,
    2117             :                                                  state->member);
    2118           0 :     if (subreq == NULL) {
    2119           0 :         ret = ENOMEM;
    2120           0 :         goto done;
    2121             :     }
    2122             : 
    2123           0 :     tevent_req_set_callback(subreq, sdap_nested_group_lookup_unknown_group_done,
    2124             :                             req);
    2125             : 
    2126           0 :     ret = EAGAIN;
    2127             : 
    2128             : done:
    2129           0 :     if (ret == EOK) {
    2130           0 :         tevent_req_done(req);
    2131           0 :     } else if (ret != EAGAIN) {
    2132           0 :         tevent_req_error(req, ret);
    2133             :     }
    2134             : 
    2135           0 :     return;
    2136             : }
    2137             : 
    2138             : static void
    2139           0 : sdap_nested_group_lookup_unknown_group_done(struct tevent_req *subreq)
    2140             : {
    2141           0 :     struct sdap_nested_group_lookup_unknown_state *state = NULL;
    2142           0 :     struct tevent_req *req = NULL;
    2143           0 :     struct sysdb_attrs *entry = NULL;
    2144             :     errno_t ret;
    2145             : 
    2146           0 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    2147           0 :     state = tevent_req_data(req, struct sdap_nested_group_lookup_unknown_state);
    2148             : 
    2149           0 :     ret = sdap_nested_group_lookup_group_recv(state, subreq, &entry);
    2150           0 :     talloc_zfree(subreq);
    2151           0 :     if (ret != EOK) {
    2152           0 :         goto done;
    2153             :     }
    2154             : 
    2155           0 :     if (entry == NULL) {
    2156             :         /* not found, end request */
    2157           0 :         state->entry = NULL;
    2158           0 :         state->type = SDAP_NESTED_GROUP_DN_UNKNOWN;
    2159             :     } else {
    2160             :         /* found in groups */
    2161           0 :         state->entry = entry;
    2162           0 :         state->type = SDAP_NESTED_GROUP_DN_GROUP;
    2163             :     }
    2164             : 
    2165           0 :     ret = EOK;
    2166             : 
    2167             : done:
    2168           0 :     if (ret != EOK) {
    2169           0 :         tevent_req_error(req, ret);
    2170           0 :         return;
    2171             :     }
    2172             : 
    2173           0 :     tevent_req_done(req);
    2174             : }
    2175             : 
    2176             : static errno_t
    2177           0 : sdap_nested_group_lookup_unknown_recv(TALLOC_CTX *mem_ctx,
    2178             :                                       struct tevent_req *req,
    2179             :                                       struct sysdb_attrs **_entry,
    2180             :                                       enum sdap_nested_group_dn_type *_type)
    2181             : {
    2182           0 :     struct sdap_nested_group_lookup_unknown_state *state = NULL;
    2183           0 :     state = tevent_req_data(req, struct sdap_nested_group_lookup_unknown_state);
    2184             : 
    2185             :     PROBE(SDAP_NESTED_GROUP_LOOKUP_UNKNOWN_RECV);
    2186             : 
    2187           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    2188             : 
    2189           0 :     if (_entry != NULL) {
    2190           0 :         *_entry = talloc_steal(mem_ctx, state->entry);
    2191             :     }
    2192             : 
    2193           0 :     if (_type != NULL) {
    2194           0 :         *_type = state->type;
    2195             :     }
    2196             : 
    2197             : 
    2198           0 :     return EOK;
    2199             : }
    2200             : 
    2201             : struct sdap_nested_group_deref_state {
    2202             :     struct tevent_context *ev;
    2203             :     struct sdap_nested_group_ctx *group_ctx;
    2204             :     struct ldb_message_element *members;
    2205             :     int nesting_level;
    2206             : 
    2207             :     struct sysdb_attrs **nested_groups;
    2208             :     int num_groups;
    2209             : };
    2210             : 
    2211             : static void sdap_nested_group_deref_direct_done(struct tevent_req *subreq);
    2212             : static void sdap_nested_group_deref_done(struct tevent_req *subreq);
    2213             : 
    2214             : static struct tevent_req *
    2215           0 : sdap_nested_group_deref_send(TALLOC_CTX *mem_ctx,
    2216             :                              struct tevent_context *ev,
    2217             :                              struct sdap_nested_group_ctx *group_ctx,
    2218             :                              struct ldb_message_element *members,
    2219             :                              const char *group_dn,
    2220             :                              int nesting_level)
    2221             : {
    2222           0 :     struct sdap_nested_group_deref_state *state = NULL;
    2223           0 :     struct tevent_req *req = NULL;
    2224           0 :     struct tevent_req *subreq = NULL;
    2225           0 :     struct sdap_attr_map_info *maps = NULL;
    2226             :     static const int num_maps = 2;
    2227           0 :     struct sdap_options *opts = group_ctx->opts;
    2228           0 :     const char **attrs = NULL;
    2229           0 :     size_t num_attrs = 0;
    2230             :     errno_t ret;
    2231             : 
    2232           0 :     req = tevent_req_create(mem_ctx, &state,
    2233             :                             struct sdap_nested_group_deref_state);
    2234           0 :     if (req == NULL) {
    2235           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    2236           0 :         return NULL;
    2237             :     }
    2238             : 
    2239             :     PROBE(SDAP_NESTED_GROUP_DEREF_SEND);
    2240             : 
    2241           0 :     state->ev = ev;
    2242           0 :     state->group_ctx = group_ctx;
    2243           0 :     state->members = members;
    2244           0 :     state->nesting_level = nesting_level;
    2245           0 :     state->num_groups = 0; /* we will count exact number of the groups */
    2246             : 
    2247           0 :     maps = talloc_array(state, struct sdap_attr_map_info, num_maps);
    2248           0 :     if (maps == NULL) {
    2249           0 :         ret = ENOMEM;
    2250           0 :         goto immediately;
    2251             :     }
    2252             : 
    2253           0 :     maps[0].map = opts->user_map;
    2254           0 :     maps[0].num_attrs = opts->user_map_cnt;
    2255           0 :     maps[1].map = opts->group_map;
    2256           0 :     maps[1].num_attrs = SDAP_OPTS_GROUP;
    2257             : 
    2258             :     /* pull down the whole group map,
    2259             :      * but only pull down username and originalDN for users */
    2260           0 :     ret = build_attrs_from_map(state, opts->group_map, SDAP_OPTS_GROUP,
    2261             :                                NULL, &attrs, &num_attrs);
    2262           0 :     if (ret != EOK) {
    2263           0 :         goto immediately;
    2264             :     }
    2265             : 
    2266           0 :     attrs = talloc_realloc(state, attrs, const char *, num_attrs + 2);
    2267           0 :     if (attrs == NULL) {
    2268           0 :         ret = ENOMEM;
    2269           0 :         goto immediately;
    2270             :     }
    2271             : 
    2272           0 :     attrs[num_attrs] = group_ctx->opts->user_map[SDAP_AT_USER_NAME].name;
    2273           0 :     attrs[num_attrs + 1] = NULL;
    2274             : 
    2275             :     /* send request */
    2276           0 :     subreq = sdap_deref_search_send(state, ev, opts, group_ctx->sh, group_dn,
    2277           0 :                                     opts->group_map[SDAP_AT_GROUP_MEMBER].name,
    2278             :                                     attrs, num_maps, maps,
    2279             :                                     dp_opt_get_int(opts->basic,
    2280             :                                                    SDAP_SEARCH_TIMEOUT));
    2281           0 :     if (subreq == NULL) {
    2282           0 :         ret = ENOMEM;
    2283           0 :         goto immediately;
    2284             :     }
    2285             : 
    2286           0 :     tevent_req_set_callback(subreq, sdap_nested_group_deref_direct_done, req);
    2287             : 
    2288           0 :     return req;
    2289             : 
    2290             : immediately:
    2291           0 :     if (ret == EOK) {
    2292           0 :         tevent_req_done(req);
    2293             :     } else {
    2294           0 :         tevent_req_error(req, ret);
    2295             :     }
    2296           0 :     tevent_req_post(req, ev);
    2297             : 
    2298           0 :     return req;
    2299             : }
    2300             : 
    2301             : static errno_t
    2302           0 : sdap_nested_group_deref_direct_process(struct tevent_req *subreq)
    2303             : {
    2304           0 :     struct sdap_nested_group_deref_state *state = NULL;
    2305           0 :     struct tevent_req *req = NULL;
    2306           0 :     struct sdap_options *opts = NULL;
    2307           0 :     struct sdap_deref_attrs **entries = NULL;
    2308           0 :     struct ldb_message_element *members = NULL;
    2309           0 :     const char *orig_dn = NULL;
    2310           0 :     const char *member_dn = NULL;
    2311           0 :     size_t num_entries = 0;
    2312             :     size_t i, j;
    2313             :     bool member_found;
    2314             :     errno_t ret;
    2315             : 
    2316           0 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    2317           0 :     state = tevent_req_data(req, struct sdap_nested_group_deref_state);
    2318             : 
    2319           0 :     opts = state->group_ctx->opts;
    2320           0 :     members = state->members;
    2321             : 
    2322           0 :     ret = sdap_deref_search_recv(subreq, state, &num_entries, &entries);
    2323           0 :     if (ret != EOK) {
    2324           0 :         goto done;
    2325             :     }
    2326             : 
    2327           0 :     DEBUG(SSSDBG_TRACE_INTERNAL, "Received %zu dereference results, "
    2328             :           "about to process them\n", num_entries);
    2329             : 
    2330             :     /*
    2331             :      * We don't have any knowledge about possible number of groups when
    2332             :      * dereferencing. We expect that every member is a group and we will
    2333             :      * allocate enough space to hold it. We will shrink the memory later.
    2334             :      */
    2335           0 :     state->nested_groups = talloc_zero_array(state, struct sysdb_attrs *,
    2336             :                                              num_entries);
    2337           0 :     if (state->nested_groups == NULL) {
    2338           0 :         ret = ENOMEM;
    2339           0 :         goto done;
    2340             :     }
    2341             : 
    2342             :     PROBE(SDAP_NESTED_GROUP_DEREF_PROCESS_PRE);
    2343           0 :     for (i = 0; i < num_entries; i++) {
    2344           0 :         ret = sysdb_attrs_get_string(entries[i]->attrs,
    2345             :                                      SYSDB_ORIG_DN, &orig_dn);
    2346           0 :         if (ret != EOK) {
    2347           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "The entry has no originalDN\n");
    2348           0 :             goto done;
    2349             :         }
    2350             : 
    2351             :         /* Ensure that all members returned from the deref request are included
    2352             :          * in the member processing. Sometimes we will get more results back
    2353             :          * from deref/asq than we got from the initial lookup, as is the case
    2354             :          * with Active Directory and its range retrieval mechanism.
    2355             :          */
    2356           0 :         member_found = false;
    2357           0 :         for (j = 0; j < members->num_values; j++) {
    2358             :             /* FIXME: This is inefficient for very large sets of groups */
    2359           0 :             member_dn = (const char *)members->values[j].data;
    2360           0 :             if (strcasecmp(orig_dn, member_dn) == 0) {
    2361           0 :                 member_found = true;
    2362           0 :                 break;
    2363             :             }
    2364             :         }
    2365             : 
    2366           0 :         if (!member_found) {
    2367             :             /* Append newly found member to member list.
    2368             :              * Changes in state->members will propagate into sysdb_attrs of
    2369             :              * the group. */
    2370           0 :             state->members->values = talloc_realloc(members, members->values,
    2371             :                                                     struct ldb_val,
    2372             :                                                     members->num_values + 1);
    2373           0 :             if (members->values == NULL) {
    2374           0 :                 ret = ENOMEM;
    2375           0 :                 goto done;
    2376             :             }
    2377             : 
    2378           0 :             members->values[members->num_values].data =
    2379           0 :                     (uint8_t *)talloc_strdup(members->values, orig_dn);
    2380           0 :             if (members->values[members->num_values].data == NULL) {
    2381           0 :                 ret = ENOMEM;
    2382           0 :                 goto done;
    2383             :             }
    2384             : 
    2385           0 :             members->values[members->num_values].length = strlen(orig_dn);
    2386           0 :             members->num_values++;
    2387             :         }
    2388             : 
    2389           0 :         if (entries[i]->map == opts->user_map) {
    2390             :             /* we found a user */
    2391             : 
    2392             :             /* skip the user if it is not amongst configured search bases */
    2393           0 :             if (!sdap_nested_member_is_user(state->group_ctx, orig_dn, NULL)) {
    2394           0 :                 continue;
    2395             :             }
    2396             : 
    2397             :             /* save user in hash table */
    2398           0 :             ret = sdap_nested_group_hash_user(state->group_ctx,
    2399           0 :                                               entries[i]->attrs);
    2400           0 :             if (ret != EOK && ret != EEXIST) {
    2401           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    2402             :                       "Unable to save user in hash table "
    2403             :                        "[%d]: %s\n", ret, strerror(ret));
    2404           0 :                 goto done;
    2405             :             }
    2406             : 
    2407           0 :         } else if (entries[i]->map == opts->group_map) {
    2408             :             /* we found a group */
    2409             : 
    2410             :             /* skip the group if we have reached the nesting limit */
    2411           0 :             if (state->nesting_level >= state->group_ctx->max_nesting_level) {
    2412           0 :                 DEBUG(SSSDBG_TRACE_ALL, "[%s] is outside nesting limit "
    2413             :                       "(level %d), skipping\n", orig_dn, state->nesting_level);
    2414           0 :                 continue;
    2415             :             }
    2416             : 
    2417             :             /* skip the group if it is not amongst configured search bases */
    2418           0 :             if (!sdap_nested_member_is_group(state->group_ctx, orig_dn, NULL)) {
    2419           0 :                 continue;
    2420             :             }
    2421             : 
    2422             :             /* save group in hash table */
    2423           0 :             ret = sdap_nested_group_hash_group(state->group_ctx,
    2424           0 :                                                entries[i]->attrs);
    2425           0 :             if (ret == EEXIST) {
    2426           0 :                 continue;
    2427           0 :             } else if (ret != EOK) {
    2428           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    2429             :                       "Unable to save group in hash table "
    2430             :                        "[%d]: %s\n", ret, strerror(ret));
    2431           0 :                 goto done;
    2432             :             }
    2433             : 
    2434             :             /* remember the group for later processing */
    2435           0 :             state->nested_groups[state->num_groups] = entries[i]->attrs;
    2436           0 :             state->num_groups++;
    2437             : 
    2438             :         } else {
    2439             :             /* this should never happen, but if it does, do not loop forever */
    2440           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
    2441             :                   "Entry does not match any known map, skipping\n");
    2442           0 :             continue;
    2443             :         }
    2444             :     }
    2445             :     PROBE(SDAP_NESTED_GROUP_DEREF_PROCESS_POST);
    2446             : 
    2447             :     /* adjust size of nested groups array */
    2448           0 :     if (state->num_groups > 0) {
    2449           0 :         state->nested_groups = talloc_realloc(state, state->nested_groups,
    2450             :                                               struct sysdb_attrs *,
    2451             :                                               state->num_groups);
    2452           0 :         if (state->nested_groups == NULL) {
    2453           0 :             ret = ENOMEM;
    2454           0 :             goto done;
    2455             :         }
    2456             :     } else {
    2457           0 :         talloc_zfree(state->nested_groups);
    2458             :     }
    2459             : 
    2460           0 :     ret = EOK;
    2461             : 
    2462             : done:
    2463           0 :     return ret;
    2464             : }
    2465             : 
    2466           0 : static void sdap_nested_group_deref_direct_done(struct tevent_req *subreq)
    2467             : {
    2468           0 :     struct sdap_nested_group_deref_state *state = NULL;
    2469           0 :     struct tevent_req *req = NULL;
    2470             :     errno_t ret;
    2471             : 
    2472           0 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    2473           0 :     state = tevent_req_data(req, struct sdap_nested_group_deref_state);
    2474             : 
    2475             :     /* process direct members */
    2476           0 :     ret = sdap_nested_group_deref_direct_process(subreq);
    2477           0 :     talloc_zfree(subreq);
    2478           0 :     if (ret != EOK) {
    2479           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Error processing direct membership "
    2480             :                                     "[%d]: %s\n", ret, strerror(ret));
    2481           0 :         goto done;
    2482             :     }
    2483             : 
    2484             :     /* we have processed all direct members,
    2485             :      * now recurse and process nested groups */
    2486           0 :     subreq = sdap_nested_group_recurse_send(state, state->ev,
    2487             :                                             state->group_ctx,
    2488             :                                             state->nested_groups,
    2489             :                                             state->num_groups,
    2490           0 :                                             state->nesting_level + 1);
    2491           0 :     if (subreq == NULL) {
    2492           0 :         ret = ENOMEM;
    2493           0 :         goto done;
    2494             :     }
    2495             : 
    2496           0 :     tevent_req_set_callback(subreq, sdap_nested_group_deref_done, req);
    2497             : 
    2498           0 :     ret = EAGAIN;
    2499             : 
    2500             : done:
    2501           0 :     if (ret == EOK) {
    2502             :         /* tevent_req_error() cannot cope with EOK */
    2503           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "We should not get here with EOK\n");
    2504           0 :         tevent_req_error(req, EINVAL);
    2505           0 :     } else if (ret != EAGAIN) {
    2506           0 :         tevent_req_error(req, ret);
    2507             :     }
    2508             : 
    2509           0 :     return;
    2510             : 
    2511             : }
    2512             : 
    2513           0 : static void sdap_nested_group_deref_done(struct tevent_req *subreq)
    2514             : {
    2515           0 :     struct tevent_req *req = NULL;
    2516             :     errno_t ret;
    2517             : 
    2518           0 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    2519             : 
    2520             :     /* process nested groups */
    2521           0 :     ret = sdap_nested_group_recurse_recv(subreq);
    2522           0 :     talloc_zfree(subreq);
    2523             : 
    2524           0 :     if (ret == EOK) {
    2525           0 :         tevent_req_done(req);
    2526             :     } else {
    2527           0 :         tevent_req_error(req, ret);
    2528             :     }
    2529             : 
    2530           0 :     return;
    2531             : }
    2532             : 
    2533           0 : static errno_t sdap_nested_group_deref_recv(struct tevent_req *req)
    2534             : {
    2535             :     PROBE(SDAP_NESTED_GROUP_DEREF_RECV);
    2536             : 
    2537           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    2538             : 
    2539           0 :     return EOK;
    2540             : }
    2541             : 
    2542             : struct sdap_ext_member {
    2543             :     struct sdap_external_missing_member *missing_mem;
    2544             :     const char *ext_member_attr;
    2545             : 
    2546             :     enum sysdb_member_type member_type;
    2547             :     struct sss_domain_info *dom;
    2548             :     struct sysdb_attrs *attrs;
    2549             : };
    2550             : 
    2551             : struct sdap_nested_group_lookup_external_state {
    2552             :     struct tevent_context *ev;
    2553             :     struct sdap_ext_member_ctx *ext_ctx;
    2554             :     struct sss_domain_info *group_dom;
    2555             :     hash_table_t *missing_external;
    2556             : 
    2557             :     hash_entry_t *entries;
    2558             :     unsigned long n_entries;
    2559             :     unsigned long eniter;
    2560             : 
    2561             :     struct sdap_ext_member *ext_members;
    2562             : 
    2563             :     ext_member_send_fn_t ext_member_resolve_send;
    2564             :     ext_member_recv_fn_t ext_member_resolve_recv;
    2565             : };
    2566             : 
    2567             : static errno_t
    2568             : sdap_nested_group_lookup_external_step(struct tevent_req *req);
    2569             : static void
    2570             : sdap_nested_group_lookup_external_done(struct tevent_req *subreq);
    2571             : static errno_t
    2572             : sdap_nested_group_lookup_external_link(struct tevent_req *req);
    2573             : static errno_t
    2574             : sdap_nested_group_lookup_external_link_member(
    2575             :                         struct sdap_nested_group_lookup_external_state *state,
    2576             :                         struct sdap_ext_member *member);
    2577             : static errno_t
    2578             : sdap_nested_group_memberof_dn_by_original_dn(
    2579             :                             TALLOC_CTX *mem_ctx,
    2580             :                             struct sss_domain_info *group_dom,
    2581             :                             const char *original_dn,
    2582             :                             const char ***_parents);
    2583             : 
    2584             : struct tevent_req *
    2585           1 : sdap_nested_group_lookup_external_send(TALLOC_CTX *mem_ctx,
    2586             :                                        struct tevent_context *ev,
    2587             :                                        struct sss_domain_info *group_dom,
    2588             :                                        struct sdap_ext_member_ctx *ext_ctx,
    2589             :                                        hash_table_t *missing_external)
    2590             : {
    2591           1 :     struct sdap_nested_group_lookup_external_state *state = NULL;
    2592           1 :     struct tevent_req *req = NULL;
    2593             :     errno_t ret;
    2594             : 
    2595           1 :     req = tevent_req_create(mem_ctx, &state,
    2596             :                             struct sdap_nested_group_lookup_external_state);
    2597           1 :     if (req == NULL) {
    2598           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
    2599           0 :         return NULL;
    2600             :     }
    2601             : 
    2602           1 :     state->ev = ev;
    2603           1 :     state->group_dom = group_dom;
    2604           1 :     state->ext_ctx = ext_ctx;
    2605           1 :     state->missing_external = missing_external;
    2606             : 
    2607           1 :     if (state->ext_ctx->ext_member_resolve_send == NULL
    2608           1 :             || state->ext_ctx->ext_member_resolve_recv == NULL) {
    2609           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Wrong private context\n");
    2610           0 :         ret = EINVAL;
    2611           0 :         goto immediately;
    2612             :     }
    2613             : 
    2614           2 :     ret = hash_entries(state->missing_external,
    2615           2 :                        &state->n_entries, &state->entries);
    2616           1 :     if (ret != HASH_SUCCESS) {
    2617           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "hash_entries returned %d\n", ret);
    2618           0 :         ret = EIO;
    2619           0 :         goto immediately;
    2620             :     }
    2621           1 :     state->eniter = 0;
    2622             : 
    2623           1 :     state->ext_members = talloc_zero_array(state,
    2624             :                                            struct sdap_ext_member,
    2625             :                                            state->n_entries);
    2626           1 :     if (state->ext_members == NULL) {
    2627           0 :         ret = ENOMEM;
    2628           0 :         goto immediately;
    2629             :     }
    2630             : 
    2631           1 :     ret = sdap_nested_group_lookup_external_step(req);
    2632           1 :     if (ret != EAGAIN) {
    2633           0 :         goto immediately;
    2634             :     }
    2635             : 
    2636           1 :     return req;
    2637             : 
    2638             : immediately:
    2639           0 :     if (ret == EOK) {
    2640           0 :         tevent_req_done(req);
    2641             :     } else {
    2642           0 :         tevent_req_error(req, ret);
    2643             :     }
    2644           0 :     tevent_req_post(req, ev);
    2645           0 :     return req;
    2646             : }
    2647             : 
    2648             : static errno_t
    2649           2 : sdap_nested_group_lookup_external_step(struct tevent_req *req)
    2650             : {
    2651           2 :     struct tevent_req *subreq = NULL;
    2652           2 :     struct sdap_nested_group_lookup_external_state *state = NULL;
    2653           2 :     state = tevent_req_data(req,
    2654             :                             struct sdap_nested_group_lookup_external_state);
    2655             : 
    2656           6 :     subreq = state->ext_ctx->ext_member_resolve_send(state,
    2657             :                                         state->ev,
    2658           2 :                                         state->entries[state->eniter].key.str,
    2659           2 :                                         state->ext_ctx->pvt);
    2660           2 :     if (subreq == NULL) {
    2661           0 :         return ENOMEM;
    2662             :     }
    2663           2 :     DEBUG(SSSDBG_TRACE_FUNC, "Refreshing member %lu/%lu\n",
    2664             :                              state->eniter, state->n_entries);
    2665           2 :     tevent_req_set_callback(subreq,
    2666             :                             sdap_nested_group_lookup_external_done,
    2667             :                             req);
    2668             : 
    2669           2 :     return EAGAIN;
    2670             : }
    2671             : 
    2672             : static void
    2673           2 : sdap_nested_group_lookup_external_done(struct tevent_req *subreq)
    2674             : {
    2675             :     errno_t ret;
    2676           2 :     struct tevent_req *req = NULL;
    2677           2 :     struct sdap_nested_group_lookup_external_state *state = NULL;
    2678             :     enum sysdb_member_type member_type;
    2679             :     struct sysdb_attrs *member;
    2680             :     struct sss_domain_info *member_dom;
    2681             : 
    2682           2 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    2683           2 :     state = tevent_req_data(req,
    2684             :                             struct sdap_nested_group_lookup_external_state);
    2685             : 
    2686           2 :     ret = state->ext_ctx->ext_member_resolve_recv(state, subreq,
    2687             :                                                   &member_type,
    2688             :                                                   &member_dom,
    2689             :                                                   &member);
    2690           2 :     talloc_free(subreq);
    2691           2 :     if (ret == EOK) {
    2692           2 :         DEBUG(SSSDBG_TRACE_FUNC, "Refreshed member %lu\n", state->eniter);
    2693           4 :         state->ext_members[state->eniter].missing_mem = \
    2694           2 :                                     state->entries[state->eniter].value.ptr;
    2695           2 :         state->ext_members[state->eniter].dom = member_dom;
    2696             : 
    2697           4 :         state->ext_members[state->eniter].ext_member_attr = \
    2698           2 :                         talloc_steal(state->ext_members,
    2699             :                                      state->entries[state->eniter].key.str);
    2700           2 :         state->ext_members[state->eniter].member_type = member_type;
    2701           4 :         state->ext_members[state->eniter].attrs = \
    2702           2 :                             talloc_steal(state->ext_members, member);
    2703             :     }
    2704             : 
    2705           2 :     state->eniter++;
    2706           2 :     if (state->eniter >= state->n_entries) {
    2707           1 :         DEBUG(SSSDBG_TRACE_FUNC, "All external members processed\n");
    2708           1 :         ret = sdap_nested_group_lookup_external_link(req);
    2709           1 :         if (ret != EOK) {
    2710           0 :             tevent_req_error(req, ret);
    2711           0 :             return;
    2712             :         }
    2713           1 :         tevent_req_done(req);
    2714           1 :         return;
    2715             :     }
    2716             : 
    2717           1 :     ret = sdap_nested_group_lookup_external_step(req);
    2718           1 :     if (ret != EOK && ret != EAGAIN) {
    2719           0 :         tevent_req_error(req, ret);
    2720           0 :         return;
    2721             :     }
    2722             : 
    2723           1 :     return;
    2724             : }
    2725             : 
    2726             : static errno_t
    2727           1 : sdap_nested_group_lookup_external_link(struct tevent_req *req)
    2728             : {
    2729             :     errno_t ret, tret;
    2730           1 :     bool in_transaction = false;
    2731           1 :     struct sdap_nested_group_lookup_external_state *state = NULL;
    2732           1 :     state = tevent_req_data(req,
    2733             :                             struct sdap_nested_group_lookup_external_state);
    2734             : 
    2735           1 :     ret = sysdb_transaction_start(state->group_dom->sysdb);
    2736           1 :     if (ret != EOK) {
    2737           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
    2738           0 :         goto fail;
    2739             :     }
    2740           1 :     in_transaction = true;
    2741             : 
    2742             : 
    2743           3 :     for (size_t i = 0; i < state->eniter; i++) {
    2744           2 :         if (state->ext_members[i].attrs == NULL) {
    2745           0 :             DEBUG(SSSDBG_MINOR_FAILURE, "The member %s could not be resolved\n",
    2746             :                                         state->ext_members[i].ext_member_attr);
    2747           0 :             continue;
    2748             :         }
    2749             : 
    2750           2 :         ret = sdap_nested_group_lookup_external_link_member(state,
    2751           2 :                                                     &state->ext_members[i]);
    2752           2 :         if (ret != EOK) {
    2753           0 :             goto fail;
    2754             :         }
    2755             :     }
    2756             : 
    2757           1 :     ret = sysdb_transaction_commit(state->group_dom->sysdb);
    2758           1 :     if (ret != EOK) {
    2759           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
    2760           0 :         goto fail;
    2761             :     }
    2762           1 :     in_transaction = false;
    2763             : 
    2764           1 :     return EOK;
    2765             : 
    2766             : fail:
    2767           0 :     if (in_transaction) {
    2768           0 :         tret = sysdb_transaction_cancel(state->group_dom->sysdb);
    2769           0 :         if (tret != EOK) {
    2770           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
    2771             :         }
    2772             :     }
    2773           0 :     return EFAULT;
    2774             : }
    2775             : 
    2776             : static errno_t
    2777           2 : sdap_nested_group_lookup_external_link_member(
    2778             :                         struct sdap_nested_group_lookup_external_state *state,
    2779             :                         struct sdap_ext_member *member)
    2780             : {
    2781             :     const char *name;
    2782             :     int ret;
    2783           2 :     const char **parents = NULL;
    2784             :     size_t i;
    2785             :     TALLOC_CTX *tmp_ctx;
    2786             :     const char *orig_dn;
    2787             : 
    2788           2 :     tmp_ctx = talloc_new(state);
    2789           2 :     if (tmp_ctx == NULL) {
    2790           0 :         return ENOMEM;
    2791             :     }
    2792             : 
    2793           2 :     ret = sysdb_attrs_get_string(member->attrs, SYSDB_NAME, &name);
    2794           2 :     if (ret != EOK) {
    2795           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "No name for a user\n");
    2796           0 :         goto done;
    2797             :     }
    2798             : 
    2799             :     /* This only works because the groups were saved in a previous
    2800             :      * transaction */
    2801           5 :     for (i=0; i < member->missing_mem->parent_dn_idx; i++) {
    2802           3 :         orig_dn = member->missing_mem->parent_group_dns[i];
    2803           3 :         DEBUG(SSSDBG_TRACE_INTERNAL,
    2804             :               "Linking external members %s from domain %s to parents of %s\n",
    2805             :               name, member->dom->name, orig_dn);
    2806           3 :         ret = sdap_nested_group_memberof_dn_by_original_dn(tmp_ctx,
    2807             :                                                            state->group_dom,
    2808             :                                                            orig_dn,
    2809             :                                                            &parents);
    2810           3 :         if (ret != EOK) {
    2811           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
    2812             :                   "Cannot find parents of %s\n", orig_dn);
    2813           0 :             continue;
    2814             :         }
    2815             : 
    2816             :         /* We don't have to remove the members here, since all members attributes
    2817             :          * are always written anew
    2818             :          */
    2819           3 :         ret = sysdb_update_members_dn(member->dom, name, member->member_type,
    2820             :                                       parents, NULL);
    2821           3 :         if (ret != EOK) {
    2822           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Cannot link %s@%s to its parents\n",
    2823             :                                        name, member->dom->name);
    2824           0 :             goto done;
    2825             :         }
    2826             : 
    2827             :     }
    2828             : 
    2829           2 :     ret = EOK;
    2830             : done:
    2831           2 :     talloc_free(tmp_ctx);
    2832           2 :     return ret;
    2833             : }
    2834             : 
    2835             : static errno_t
    2836           3 : sdap_nested_group_memberof_dn_by_original_dn(
    2837             :                             TALLOC_CTX *mem_ctx,
    2838             :                             struct sss_domain_info *group_dom,
    2839             :                             const char *original_dn,
    2840             :                             const char ***_parents)
    2841             : {
    2842             :     errno_t ret;
    2843             :     char *sanitized_dn;
    2844             :     char *filter;
    2845           3 :     const char *attrs[] = { SYSDB_NAME,
    2846             :                             SYSDB_MEMBEROF,
    2847             :                             NULL };
    2848           3 :     struct ldb_message **msgs = NULL;
    2849             :     size_t count;
    2850             :     TALLOC_CTX *tmp_ctx;
    2851             :     struct ldb_message_element *memberof;
    2852             :     const char **parents;
    2853             : 
    2854           3 :     tmp_ctx = talloc_new(mem_ctx);
    2855           3 :     if (tmp_ctx == NULL) {
    2856           0 :         return ENOMEM;
    2857             :     }
    2858             : 
    2859           3 :     ret = sss_filter_sanitize(tmp_ctx, original_dn, &sanitized_dn);
    2860           3 :     if (ret != EOK) {
    2861           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    2862             :                 "Cannot sanitize originalDN [%s]\n", original_dn);
    2863           0 :         goto done;
    2864             :     }
    2865             : 
    2866           3 :     filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_ORIG_DN, sanitized_dn);
    2867           3 :     if (filter == NULL) {
    2868           0 :         goto done;
    2869             :     }
    2870             : 
    2871           3 :     ret = sysdb_search_groups(tmp_ctx, group_dom, filter, attrs,
    2872             :                               &count, &msgs);
    2873           3 :     if (ret != EOK) {
    2874           0 :         goto done;
    2875             :     }
    2876             : 
    2877           3 :     if (count != 1) {
    2878           0 :         DEBUG(SSSDBG_OP_FAILURE,
    2879             :               "More than one entry found by originalDN?\n");
    2880           0 :         goto done;
    2881             :     }
    2882             : 
    2883           3 :     memberof = ldb_msg_find_element(msgs[0], SYSDB_MEMBEROF);
    2884           3 :     if (memberof == NULL || memberof->num_values == 0) {
    2885           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
    2886             :               "The external group is not a member of any groups\n");
    2887           0 :         ret = ENOENT;
    2888           0 :         goto done;
    2889             :     }
    2890             : 
    2891           3 :     parents = talloc_zero_array(tmp_ctx,
    2892             :                                 const char *,
    2893             :                                 memberof->num_values + 1);
    2894           3 :     if (parents == NULL) {
    2895           0 :         ret = ENOMEM;
    2896           0 :         goto done;
    2897             :     }
    2898             : 
    2899           8 :     for (size_t i = 0; i < memberof->num_values; i++) {
    2900          10 :         parents[i] = talloc_strdup(parents,
    2901           5 :                                    (const char *) memberof->values[i].data);
    2902           5 :         if (parents[i] == NULL) {
    2903           0 :             ret = ENOMEM;
    2904           0 :             goto done;
    2905             :         }
    2906             :     }
    2907             : 
    2908           3 :     *_parents = talloc_steal(mem_ctx, parents);
    2909           3 :     ret = EOK;
    2910             : done:
    2911           3 :     talloc_free(tmp_ctx);
    2912           3 :     return ret;
    2913             : }
    2914             : 
    2915             : errno_t
    2916           1 : sdap_nested_group_lookup_external_recv(TALLOC_CTX *mem_ctx,
    2917             :                                        struct tevent_req *req)
    2918             : {
    2919           1 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    2920             : 
    2921           1 :     return EOK;
    2922             : }

Generated by: LCOV version 1.10