LCOV - code coverage report
Current view: top level - providers/ldap - sdap_async_groups.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 1188 0.0 %
Date: 2016-06-29 Functions: 0 33 0.0 %

          Line data    Source code
       1             : /*
       2             :     SSSD
       3             : 
       4             :     Async LDAP Helper routines - retrieving groups
       5             : 
       6             :     Copyright (C) Simo Sorce <ssorce@redhat.com> - 2009
       7             :     Copyright (C) 2010, Ralf Haferkamp <rhafer@suse.de>, Novell Inc.
       8             :     Copyright (C) Jan Zeleny <jzeleny@redhat.com> - 2011
       9             : 
      10             :     This program is free software; you can redistribute it and/or modify
      11             :     it under the terms of the GNU General Public License as published by
      12             :     the Free Software Foundation; either version 3 of the License, or
      13             :     (at your option) any later version.
      14             : 
      15             :     This program is distributed in the hope that it will be useful,
      16             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :     GNU General Public License for more details.
      19             : 
      20             :     You should have received a copy of the GNU General Public License
      21             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      22             : */
      23             : 
      24             : #include "util/util.h"
      25             : #include "util/probes.h"
      26             : #include "db/sysdb.h"
      27             : #include "providers/ldap/sdap_async_private.h"
      28             : #include "providers/ldap/ldap_common.h"
      29             : #include "providers/ldap/sdap_idmap.h"
      30             : #include "providers/ad/ad_common.h"
      31             : 
      32             : /* ==Group-Parsing Routines=============================================== */
      33             : 
      34           0 : static int sdap_find_entry_by_origDN(TALLOC_CTX *memctx,
      35             :                                      struct sysdb_ctx *ctx,
      36             :                                      struct sss_domain_info *domain,
      37             :                                      const char *orig_dn,
      38             :                                      char **_localdn,
      39             :                                      bool *_is_group)
      40             : {
      41             :     TALLOC_CTX *tmpctx;
      42           0 :     const char *attrs[] = {SYSDB_OBJECTCLASS,  NULL};
      43             :     struct ldb_dn *base_dn;
      44             :     char *filter;
      45             :     struct ldb_message **msgs;
      46             :     size_t num_msgs;
      47             :     int ret;
      48             :     char *sanitized_dn;
      49             :     const char *objectclass;
      50             : 
      51           0 :     tmpctx = talloc_new(NULL);
      52           0 :     if (!tmpctx) {
      53           0 :         return ENOMEM;
      54             :     }
      55             : 
      56           0 :     ret = sss_filter_sanitize(tmpctx, orig_dn, &sanitized_dn);
      57           0 :     if (ret != EOK) {
      58           0 :         ret = ENOMEM;
      59           0 :         goto done;
      60             :     }
      61             : 
      62           0 :     filter = talloc_asprintf(tmpctx, "%s=%s", SYSDB_ORIG_DN, sanitized_dn);
      63           0 :     if (!filter) {
      64           0 :         ret = ENOMEM;
      65           0 :         goto done;
      66             :     }
      67             : 
      68           0 :     base_dn = sysdb_domain_dn(tmpctx, domain);
      69           0 :     if (!base_dn) {
      70           0 :         ret = ENOMEM;
      71           0 :         goto done;
      72             :     }
      73             : 
      74           0 :     DEBUG(SSSDBG_TRACE_ALL, "Searching cache for [%s].\n", sanitized_dn);
      75           0 :     ret = sysdb_search_entry(tmpctx, ctx,
      76             :                              base_dn, LDB_SCOPE_SUBTREE, filter, attrs,
      77             :                              &num_msgs, &msgs);
      78           0 :     if (ret) {
      79           0 :         goto done;
      80             :     }
      81           0 :     if (num_msgs != 1) {
      82           0 :         ret = ENOENT;
      83           0 :         goto done;
      84             :     }
      85             : 
      86           0 :     *_localdn = talloc_strdup(memctx, ldb_dn_get_linearized(msgs[0]->dn));
      87           0 :     if (!*_localdn) {
      88           0 :         ret = ENOENT;
      89           0 :         goto done;
      90             :     }
      91             : 
      92           0 :     if (_is_group != NULL) {
      93           0 :         objectclass = ldb_msg_find_attr_as_string(msgs[0], SYSDB_OBJECTCLASS,
      94             :                                                   NULL);
      95           0 :         if (objectclass == NULL) {
      96           0 :             DEBUG(SSSDBG_OP_FAILURE, "An antry without a %s?\n",
      97             :                   SYSDB_OBJECTCLASS);
      98           0 :             ret = EINVAL;
      99           0 :             goto done;
     100             :         }
     101             : 
     102           0 :         *_is_group = strcmp(SYSDB_GROUP_CLASS, objectclass) == 0;
     103             :     }
     104             : 
     105           0 :     ret = EOK;
     106             : 
     107             : done:
     108           0 :     talloc_zfree(tmpctx);
     109           0 :     return ret;
     110             : }
     111             : 
     112             : static errno_t
     113           0 : sdap_get_members_with_primary_gid(TALLOC_CTX *mem_ctx,
     114             :                                   struct sss_domain_info *domain,
     115             :                                   gid_t gid, char ***_localdn, size_t *_ndn)
     116             : {
     117             :     static const char *search_attrs[] = { SYSDB_NAME, NULL };
     118             :     char *filter;
     119             :     struct ldb_message **msgs;
     120             :     size_t count;
     121             :     size_t i;
     122             :     errno_t ret;
     123             :     char **localdn;
     124             : 
     125             :     /* Don't search if the group is non-posix */
     126           0 :     if (!gid) return EOK;
     127             : 
     128           0 :     filter = talloc_asprintf(mem_ctx, "(%s=%llu)", SYSDB_GIDNUM,
     129             :                              (unsigned long long) gid);
     130           0 :     if (!filter) {
     131           0 :         return ENOMEM;
     132             :     }
     133             : 
     134           0 :     ret = sysdb_search_users(mem_ctx, domain, filter,
     135             :                              search_attrs, &count, &msgs);
     136           0 :     talloc_free(filter);
     137           0 :     if (ret == ENOENT) {
     138           0 :         *_localdn = NULL;
     139           0 :         *_ndn = 0;
     140           0 :         return EOK;
     141           0 :     } else if (ret != EOK) {
     142           0 :         return ret;
     143             :     }
     144             : 
     145           0 :     localdn = talloc_array(mem_ctx, char *, count);
     146           0 :     if (!localdn) {
     147           0 :         talloc_free(msgs);
     148           0 :         return ENOMEM;
     149             :     }
     150             : 
     151           0 :     for (i=0; i < count; i++) {
     152           0 :         localdn[i] = talloc_strdup(localdn,
     153           0 :                                    ldb_dn_get_linearized(msgs[i]->dn));
     154           0 :         if (!localdn[i]) {
     155           0 :             talloc_free(localdn);
     156           0 :             talloc_free(msgs);
     157           0 :             return ENOMEM;
     158             :         }
     159             :     }
     160             : 
     161           0 :     talloc_free(msgs);
     162           0 :     *_localdn = localdn;
     163           0 :     *_ndn = count;
     164           0 :     return EOK;
     165             : }
     166             : 
     167             : static errno_t
     168           0 : sdap_dn_by_primary_gid(TALLOC_CTX *mem_ctx, struct sysdb_attrs *ldap_attrs,
     169             :                        struct sss_domain_info *domain,
     170             :                        struct sdap_options *opts,
     171             :                        char ***_dn_list, size_t *_count)
     172             : {
     173             :     gid_t gid;
     174             :     errno_t ret;
     175             : 
     176           0 :     ret = sysdb_attrs_get_uint32_t(ldap_attrs,
     177           0 :                                    opts->group_map[SDAP_AT_GROUP_GID].sys_name,
     178             :                                    &gid);
     179           0 :     if (ret == ENOENT) {
     180             :         /* Non-posix AD group. Skip. */
     181           0 :         *_dn_list = NULL;
     182           0 :         *_count = 0;
     183           0 :         return EOK;
     184           0 :     } else if (ret && ret != ENOENT) {
     185           0 :         return ret;
     186             :     }
     187             : 
     188           0 :     ret = sdap_get_members_with_primary_gid(mem_ctx, domain, gid,
     189             :                                             _dn_list, _count);
     190           0 :     if (ret) return ret;
     191             : 
     192           0 :     return EOK;
     193             : }
     194             : 
     195           0 : static bool has_member(struct ldb_message_element *member_el,
     196             :                        char *member)
     197             : {
     198             :     struct ldb_val val;
     199             : 
     200           0 :     val.data = (uint8_t *) member;
     201           0 :     val.length = strlen(member);
     202             : 
     203             :     /* This is bad complexity, but the this loop should only be invoked in
     204             :      * the very rare scenario of AD POSIX group that is primary group of
     205             :      * some users but has user member attributes at the same time
     206             :      */
     207           0 :     if (ldb_msg_find_val(member_el, &val) != NULL) {
     208           0 :         return true;
     209             :     }
     210             : 
     211           0 :     return false;
     212             : }
     213             : 
     214           0 : static void link_pgroup_members(struct sysdb_attrs *group_attrs,
     215             :                                 struct ldb_message_element *member_el,
     216             :                                 char **userdns,
     217             :                                 size_t nuserdns)
     218             : {
     219             :     int i, j;
     220             : 
     221           0 :     j = 0;
     222           0 :     for (i=0; i < nuserdns; i++) {
     223           0 :         if (has_member(member_el, userdns[i])) {
     224           0 :             DEBUG(SSSDBG_TRACE_INTERNAL,
     225             :                   "Member %s already included, skipping\n", userdns[i]);
     226           0 :             continue;
     227             :         }
     228             : 
     229           0 :         member_el->values[member_el->num_values + j].data = (uint8_t *) \
     230           0 :                                          talloc_steal(group_attrs, userdns[i]);
     231           0 :         member_el->values[member_el->num_values + j].length = \
     232           0 :                                          strlen(userdns[i]);
     233           0 :         j++;
     234             :     }
     235           0 :     member_el->num_values += j;
     236           0 : }
     237             : 
     238           0 : static int sdap_fill_memberships(struct sdap_options *opts,
     239             :                                  struct sysdb_attrs *group_attrs,
     240             :                                  struct sysdb_ctx *ctx,
     241             :                                  struct sss_domain_info *domain,
     242             :                                  hash_table_t *ghosts,
     243             :                                  struct ldb_val *values,
     244             :                                  int num_values,
     245             :                                  char **userdns,
     246             :                                  size_t nuserdns)
     247             : {
     248             :     struct ldb_message_element *el;
     249             :     int i, j;
     250             :     int ret;
     251             :     errno_t hret;
     252             :     hash_key_t key;
     253             :     hash_value_t value;
     254             :     struct sdap_domain *sdom;
     255             :     struct sysdb_ctx *member_sysdb;
     256             :     struct sss_domain_info *member_dom;
     257             : 
     258           0 :     ret = sysdb_attrs_get_el(group_attrs, SYSDB_MEMBER, &el);
     259           0 :     if (ret) {
     260           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_attrs_get_el failed\n");
     261           0 :         goto done;
     262             :     }
     263             : 
     264             :     /* Just allocate both big enough to contain all members for now */
     265           0 :     el->values = talloc_realloc(group_attrs, el->values, struct ldb_val,
     266             :                                 el->num_values + num_values + nuserdns);
     267           0 :     if (!el->values) {
     268           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "No memory to allocate group attrs\n");
     269           0 :         ret = ENOMEM;
     270           0 :         goto done;
     271             :     }
     272             : 
     273           0 :     j = el->num_values;
     274           0 :     for (i = 0; i < num_values; i++) {
     275           0 :         if (ghosts == NULL) {
     276           0 :             hret = HASH_ERROR_KEY_NOT_FOUND;
     277             :         } else {
     278           0 :             key.type = HASH_KEY_STRING;
     279           0 :             key.str = (char *)values[i].data;
     280           0 :             hret = hash_lookup(ghosts, &key, &value);
     281             :         }
     282             : 
     283           0 :         if (hret == HASH_ERROR_KEY_NOT_FOUND) {
     284           0 :             sdom = sdap_domain_get_by_dn(opts, (char *)values[i].data);
     285           0 :             if (sdom == NULL) {
     286           0 :                 DEBUG(SSSDBG_MINOR_FAILURE, "Member [%s] is it out of domain "
     287             :                       "scope?\n", (char *)values[i].data);
     288           0 :                 member_sysdb = ctx;
     289           0 :                 member_dom = domain;
     290             :             } else {
     291           0 :                 member_sysdb = sdom->dom->sysdb;
     292           0 :                 member_dom = sdom->dom;
     293             :             }
     294             : 
     295             :             /* sync search entry with this as origDN */
     296           0 :             ret = sdap_find_entry_by_origDN(el->values, member_sysdb,
     297           0 :                                             member_dom, (char *)values[i].data,
     298           0 :                                             (char **)&el->values[j].data,
     299             :                                             NULL);
     300           0 :             if (ret == ENOENT) {
     301             :                 /* member may be outside of the configured search bases
     302             :                  * or out of scope of nesting limit */
     303           0 :                 DEBUG(SSSDBG_MINOR_FAILURE, "Member [%s] was not found in "
     304             :                       "cache. Is it out of scope?\n", (char *)values[i].data);
     305           0 :                 continue;
     306             :             }
     307           0 :             if (ret != EOK) {
     308           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
     309             :                       "'sdap_find_entry_by_origDN' failed for member [%s].\n",
     310             :                       (char *)values[i].data);
     311           0 :                 goto done;
     312             :             }
     313             : 
     314           0 :             DEBUG(SSSDBG_TRACE_LIBS, "    member #%d (%s): [%s]\n",
     315             :                       i, (char *)values[i].data,
     316             :                       (char *)el->values[j].data);
     317             : 
     318           0 :             el->values[j].length = strlen((char *)el->values[j].data);
     319           0 :             j++;
     320           0 :         } else if (hret != HASH_SUCCESS) {
     321           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
     322             :                   "hash_lookup failed: [%d]: %s\n", hret, strerror(hret));
     323           0 :             ret = EFAULT;
     324           0 :             goto done;
     325             :         }
     326             : 
     327             :         /* If the member is in ghost table, it has
     328             :          * already been processed - just skip it */
     329             :     }
     330           0 :     el->num_values = j;
     331             : 
     332           0 :     link_pgroup_members(group_attrs, el, userdns, nuserdns);
     333           0 :     ret = EOK;
     334             : 
     335             : done:
     336           0 :     return ret;
     337             : }
     338             : 
     339             : /* ==Save-Group-Entry===================================================== */
     340             : 
     341             :     /* FIXME: support non legacy */
     342             :     /* FIXME: support storing additional attributes */
     343             : 
     344             : static errno_t
     345           0 : sdap_store_group_with_gid(struct sss_domain_info *domain,
     346             :                           const char *name,
     347             :                           gid_t gid,
     348             :                           struct sysdb_attrs *group_attrs,
     349             :                           uint64_t cache_timeout,
     350             :                           bool posix_group,
     351             :                           time_t now)
     352             : {
     353             :     errno_t ret;
     354             : 
     355             :     /* make sure that non-posix (empty or explicit gid=0) groups have the
     356             :      * gidNumber set to zero even if updating existing group */
     357           0 :     if (!posix_group) {
     358           0 :         ret = sysdb_attrs_add_uint32(group_attrs, SYSDB_GIDNUM, 0);
     359           0 :         if (ret) {
     360           0 :             DEBUG(SSSDBG_OP_FAILURE,
     361             :                   "Could not set explicit GID 0 for %s\n", name);
     362           0 :             return ret;
     363             :         }
     364             :     }
     365             : 
     366           0 :     ret = sysdb_store_group(domain, name, gid, group_attrs,
     367             :                             cache_timeout, now);
     368           0 :     if (ret) {
     369           0 :         DEBUG(SSSDBG_OP_FAILURE, "Could not store group %s\n", name);
     370           0 :         return ret;
     371             :     }
     372             : 
     373           0 :     return ret;
     374             : }
     375             : 
     376             : static errno_t
     377           0 : sdap_process_ghost_members(struct sysdb_attrs *attrs,
     378             :                            struct sdap_options *opts,
     379             :                            hash_table_t *ghosts,
     380             :                            bool populate_members,
     381             :                            bool store_original_member,
     382             :                            struct sysdb_attrs *sysdb_attrs)
     383             : {
     384             :     errno_t ret;
     385             :     struct ldb_message_element *gh;
     386             :     struct ldb_message_element *memberel;
     387             :     struct ldb_message_element *sysdb_memberel;
     388             :     struct ldb_message_element *ghostel;
     389             :     size_t cnt;
     390             :     int i;
     391             :     int hret;
     392             :     hash_key_t key;
     393             :     hash_value_t value;
     394             : 
     395           0 :     ret = sysdb_attrs_get_el(attrs, SYSDB_GHOST, &gh);
     396           0 :     if (ret != EOK) {
     397           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
     398             :               "Error reading ghost attributes: [%s]\n",
     399             :                strerror(ret));
     400           0 :         return ret;
     401             :     }
     402             : 
     403           0 :     ret = sysdb_attrs_get_el_ext(attrs,
     404           0 :                              opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
     405             :                              false, &memberel);
     406           0 :     if (ret == ENOENT) {
     407             :         /* Create a dummy element with no values in order for the loop to just
     408             :          * fall through and make sure the attrs array is not reallocated.
     409             :          */
     410           0 :         memberel = talloc(attrs, struct ldb_message_element);
     411           0 :         if (memberel == NULL) {
     412           0 :             return ENOMEM;
     413             :         }
     414           0 :         memberel->num_values = 0;
     415           0 :         memberel->values = NULL;
     416           0 :     } else if (ret != EOK) {
     417           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
     418             :                 "Error reading members: [%s]\n", strerror(ret));
     419           0 :         return ret;
     420             :     }
     421             : 
     422           0 :     if (store_original_member) {
     423           0 :         DEBUG(SSSDBG_TRACE_FUNC, "The group has %d members\n", memberel->num_values);
     424           0 :         for (i = 0; i < memberel->num_values; i++) {
     425           0 :             ret = sysdb_attrs_add_string(sysdb_attrs, SYSDB_ORIG_MEMBER,
     426           0 :                                         (const char *) memberel->values[i].data);
     427           0 :             if (ret) {
     428           0 :                 DEBUG(SSSDBG_OP_FAILURE, "Could not add member [%s]\n",
     429             :                       (const char *) memberel->values[i].data);
     430           0 :                 return ret;
     431             :             }
     432             :         }
     433             :     }
     434             : 
     435           0 :     if (populate_members) {
     436           0 :         ret = sysdb_attrs_get_el(sysdb_attrs, SYSDB_MEMBER, &sysdb_memberel);
     437           0 :         if (ret != EOK) {
     438           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
     439             :                   "Error reading group members from group_attrs: [%s]\n",
     440             :                    strerror(ret));
     441           0 :             return ret;
     442             :         }
     443           0 :         sysdb_memberel->values = memberel->values;
     444           0 :         sysdb_memberel->num_values = memberel->num_values;
     445             :     }
     446             : 
     447           0 :     ret = sysdb_attrs_get_el(sysdb_attrs, SYSDB_GHOST, &ghostel);
     448           0 :     if (ret != EOK) {
     449           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
     450             :               "Error getting ghost element: [%s]\n", strerror(ret));
     451           0 :         return ret;
     452             :     }
     453           0 :     ghostel->values = gh->values;
     454           0 :     ghostel->num_values = gh->num_values;
     455             : 
     456           0 :     cnt = ghostel->num_values + memberel->num_values;
     457           0 :     DEBUG(SSSDBG_TRACE_FUNC, "Group has %zu members\n", cnt);
     458             : 
     459             :     /* Now process RFC2307bis ghost hash table */
     460           0 :     if (ghosts && cnt > 0) {
     461           0 :         ghostel->values = talloc_realloc(sysdb_attrs, ghostel->values,
     462             :                                          struct ldb_val, cnt);
     463           0 :         if (ghostel->values == NULL) {
     464           0 :             return ENOMEM;
     465             :         }
     466             : 
     467           0 :         for (i = 0; i < memberel->num_values; i++) {
     468           0 :             key.type = HASH_KEY_STRING;
     469           0 :             key.str = (char *) memberel->values[i].data;
     470           0 :             hret = hash_lookup(ghosts, &key, &value);
     471           0 :             if (hret == HASH_ERROR_KEY_NOT_FOUND) {
     472           0 :                 continue;
     473           0 :             } else if (hret != HASH_SUCCESS) {
     474           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
     475             :                       "Error checking hash table: [%s]\n",
     476             :                        hash_error_string(hret));
     477           0 :                 return EFAULT;
     478             :             }
     479             : 
     480           0 :             DEBUG(SSSDBG_TRACE_FUNC,
     481             :                   "Adding ghost member for group [%s]\n", (char *) value.ptr);
     482           0 :             ghostel->values[ghostel->num_values].data = \
     483           0 :                         (uint8_t *) talloc_strdup(ghostel->values, value.ptr);
     484           0 :             if (ghostel->values[ghostel->num_values].data == NULL) {
     485           0 :                 return ENOMEM;
     486             :             }
     487           0 :             ghostel->values[ghostel->num_values].length = strlen(value.ptr);
     488           0 :             ghostel->num_values++;
     489             :         }
     490             :     }
     491             : 
     492           0 :     return EOK;
     493             : }
     494             : 
     495           0 : static int sdap_save_group(TALLOC_CTX *memctx,
     496             :                            struct sdap_options *opts,
     497             :                            struct sss_domain_info *dom,
     498             :                            struct sysdb_attrs *attrs,
     499             :                            bool populate_members,
     500             :                            bool store_original_member,
     501             :                            hash_table_t *ghosts,
     502             :                            char **_usn_value,
     503             :                            time_t now)
     504             : {
     505             :     struct ldb_message_element *el;
     506             :     struct sysdb_attrs *group_attrs;
     507           0 :     const char *group_name = NULL;
     508             :     gid_t gid;
     509             :     errno_t ret;
     510           0 :     char *usn_value = NULL;
     511           0 :     TALLOC_CTX *tmpctx = NULL;
     512             :     bool posix_group;
     513             :     bool use_id_mapping;
     514             :     bool need_filter;
     515             :     char *sid_str;
     516             :     struct sss_domain_info *subdomain;
     517             : 
     518           0 :     tmpctx = talloc_new(NULL);
     519           0 :     if (!tmpctx) {
     520           0 :         ret = ENOMEM;
     521           0 :         goto done;
     522             :     }
     523             : 
     524           0 :     group_attrs = sysdb_new_attrs(tmpctx);
     525           0 :     if (group_attrs == NULL) {
     526           0 :         ret = ENOMEM;
     527           0 :         goto done;
     528             :     }
     529             : 
     530             :     /* Always store SID string if available */
     531           0 :     ret = sdap_attrs_get_sid_str(tmpctx, opts->idmap_ctx, attrs,
     532           0 :                               opts->group_map[SDAP_AT_GROUP_OBJECTSID].sys_name,
     533             :                               &sid_str);
     534           0 :     if (ret == EOK) {
     535           0 :         ret = sysdb_attrs_add_string(group_attrs, SYSDB_SID_STR, sid_str);
     536           0 :         if (ret != EOK) {
     537           0 :             DEBUG(SSSDBG_MINOR_FAILURE, "Could not add SID string: [%s]\n",
     538             :                                          sss_strerror(ret));
     539           0 :             goto done;
     540             :         }
     541           0 :     } else if (ret == ENOENT) {
     542           0 :         DEBUG(SSSDBG_TRACE_ALL, "objectSID: not available for group [%s].\n",
     543             :                                  group_name);
     544           0 :         sid_str = NULL;
     545             :     } else {
     546           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "Could not identify objectSID: [%s]\n",
     547             :                                      sss_strerror(ret));
     548           0 :         sid_str = NULL;
     549             :     }
     550             : 
     551             :     /* Always store UUID if available */
     552           0 :     ret = sysdb_handle_original_uuid(
     553           0 :                                    opts->group_map[SDAP_AT_GROUP_UUID].def_name,
     554             :                                    attrs,
     555           0 :                                    opts->group_map[SDAP_AT_GROUP_UUID].sys_name,
     556             :                                    group_attrs, SYSDB_UUID);
     557           0 :     if (ret != EOK) {
     558           0 :         DEBUG((ret == ENOENT) ? SSSDBG_TRACE_ALL : SSSDBG_MINOR_FAILURE,
     559             :               "Failed to retrieve UUID [%d][%s].\n", ret, sss_strerror(ret));
     560             :     }
     561             : 
     562             :     /* If this object has a SID available, we will determine the correct
     563             :      * domain by its SID. */
     564           0 :     if (sid_str != NULL) {
     565           0 :         subdomain = sss_get_domain_by_sid_ldap_fallback(get_domains_head(dom),
     566             :                                                         sid_str);
     567           0 :         if (subdomain) {
     568           0 :             dom = subdomain;
     569             :         } else {
     570           0 :             DEBUG(SSSDBG_TRACE_FUNC, "SID %s does not belong to any known "
     571             :                                       "domain\n", sid_str);
     572             :         }
     573             :     }
     574             : 
     575           0 :     ret = sdap_get_group_primary_name(tmpctx, opts, attrs, dom, &group_name);
     576           0 :     if (ret != EOK) {
     577           0 :         DEBUG(SSSDBG_OP_FAILURE, "Failed to get group name\n");
     578           0 :         goto done;
     579             :     }
     580           0 :     DEBUG(SSSDBG_TRACE_FUNC, "Processing group %s\n", group_name);
     581             : 
     582           0 :     posix_group = true;
     583           0 :     ret = sdap_check_ad_group_type(dom, opts, attrs, group_name,
     584             :                                    &need_filter);
     585           0 :     if (ret != EOK) {
     586           0 :         goto done;
     587             :     }
     588           0 :     if (need_filter) {
     589           0 :         posix_group = false;
     590           0 :         gid = 0;
     591             : 
     592           0 :         ret = sysdb_attrs_add_bool(group_attrs, SYSDB_POSIX, false);
     593           0 :         if (ret != EOK) {
     594           0 :             DEBUG(SSSDBG_OP_FAILURE,
     595             :                   "Error: Failed to mark group as non-posix!\n");
     596           0 :             goto done;
     597             :         }
     598             :     }
     599             : 
     600           0 :     if (posix_group) {
     601           0 :         use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(opts->idmap_ctx,
     602           0 :                                                                    dom->name,
     603             :                                                                    sid_str);
     604           0 :         if (use_id_mapping) {
     605           0 :             posix_group = true;
     606             : 
     607           0 :             if (sid_str == NULL) {
     608           0 :                 DEBUG(SSSDBG_MINOR_FAILURE, "SID not available, cannot map a " \
     609             :                                              "unix ID to group [%s].\n", group_name);
     610           0 :                 ret = ENOENT;
     611           0 :                 goto done;
     612             :             }
     613             : 
     614           0 :             DEBUG(SSSDBG_TRACE_LIBS,
     615             :                   "Mapping group [%s] objectSID [%s] to unix ID\n",
     616             :                    group_name, sid_str);
     617             : 
     618             :             /* Convert the SID into a UNIX group ID */
     619           0 :             ret = sdap_idmap_sid_to_unix(opts->idmap_ctx, sid_str, &gid);
     620           0 :             if (ret == ENOTSUP) {
     621             :                 /* ENOTSUP is returned if built-in SID was provided
     622             :                  * => do not store the group, but return EOK */
     623           0 :                 DEBUG(SSSDBG_TRACE_FUNC, "Skipping built-in object.\n");
     624           0 :                 ret = EOK;
     625           0 :                 goto done;
     626           0 :             } else if (ret != EOK) {
     627           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
     628             :                       "Could not convert SID string: [%s]\n",
     629             :                        sss_strerror(ret));
     630           0 :                 goto done;
     631             :             }
     632             : 
     633             :             /* Store the GID in the ldap_attrs so it doesn't get
     634             :              * treated as a missing attribute from LDAP and removed.
     635             :              */
     636           0 :             ret = sdap_replace_id(attrs, SYSDB_GIDNUM, gid);
     637           0 :             if (ret) {
     638           0 :                 DEBUG(SSSDBG_OP_FAILURE, "Cannot set the id-mapped GID\n");
     639           0 :                 goto done;
     640             :             }
     641             :         } else {
     642           0 :             ret = sysdb_attrs_get_bool(attrs, SYSDB_POSIX, &posix_group);
     643           0 :             if (ret == ENOENT) {
     644           0 :                 posix_group = true;
     645           0 :             } else if (ret != EOK) {
     646           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
     647             :                       "Error reading posix attribute: [%s]\n",
     648             :                        sss_strerror(ret));
     649           0 :                 goto done;
     650             :             }
     651             : 
     652           0 :             DEBUG(SSSDBG_TRACE_INTERNAL,
     653             :                   "This is%s a posix group\n", (posix_group)?"":" not");
     654           0 :             ret = sysdb_attrs_add_bool(group_attrs, SYSDB_POSIX, posix_group);
     655           0 :             if (ret != EOK) {
     656           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
     657             :                       "Error setting posix attribute: [%s]\n",
     658             :                        sss_strerror(ret));
     659           0 :                 goto done;
     660             :             }
     661             : 
     662           0 :             ret = sysdb_attrs_get_uint32_t(attrs,
     663           0 :                                            opts->group_map[SDAP_AT_GROUP_GID].sys_name,
     664             :                                            &gid);
     665           0 :             if (ret != EOK) {
     666           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     667             :                       "no gid provided for [%s] in domain [%s].\n",
     668             :                           group_name, dom->name);
     669           0 :                 ret = EINVAL;
     670           0 :                 goto done;
     671             :             }
     672             :         }
     673             :     }
     674             : 
     675             :     /* check that the gid is valid for this domain */
     676           0 :     if (posix_group) {
     677           0 :         if (OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) {
     678           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
     679             :                   "Group [%s] filtered out! (id out of range)\n", group_name);
     680           0 :             ret = EINVAL;
     681           0 :             goto done;
     682             :         }
     683             :         /* Group ID OK */
     684             :     }
     685             : 
     686           0 :     ret = sdap_attrs_add_string(attrs, SYSDB_ORIG_DN, "original DN",
     687             :                                 group_name, group_attrs);
     688           0 :     if (ret != EOK) {
     689           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
     690             :               "Error setting original DN: [%s]\n",
     691             :                sss_strerror(ret));
     692           0 :         goto done;
     693             :     }
     694             : 
     695           0 :     ret = sdap_attrs_add_string(attrs,
     696             :                             opts->group_map[SDAP_AT_GROUP_MODSTAMP].sys_name,
     697             :                             "original mod-Timestamp",
     698             :                             group_name, group_attrs);
     699           0 :     if (ret != EOK) {
     700           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
     701             :               "Error setting mod timestamp: [%s]\n",
     702             :                sss_strerror(ret));
     703           0 :         goto done;
     704             :     }
     705             : 
     706           0 :     ret = sysdb_attrs_get_el(attrs,
     707           0 :                       opts->group_map[SDAP_AT_GROUP_USN].sys_name, &el);
     708           0 :     if (ret) {
     709           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
     710             :               "Error looking up group USN: [%s]\n",
     711             :                sss_strerror(ret));
     712           0 :         goto done;
     713             :     }
     714           0 :     if (el->num_values == 0) {
     715           0 :         DEBUG(SSSDBG_TRACE_FUNC,
     716             :               "Original USN value is not available for [%s].\n", group_name);
     717             :     } else {
     718           0 :         ret = sysdb_attrs_add_string(group_attrs,
     719           0 :                           opts->group_map[SDAP_AT_GROUP_USN].sys_name,
     720           0 :                           (const char*)el->values[0].data);
     721           0 :         if (ret) {
     722           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
     723             :                   "Error setting group USN: [%s]\n",
     724             :                    sss_strerror(ret));
     725           0 :             goto done;
     726             :         }
     727           0 :         usn_value = talloc_strdup(tmpctx, (const char*)el->values[0].data);
     728           0 :         if (!usn_value) {
     729           0 :             ret = ENOMEM;
     730           0 :             goto done;
     731             :         }
     732             :     }
     733             : 
     734           0 :     ret = sdap_process_ghost_members(attrs, opts, ghosts,
     735             :                                      populate_members, store_original_member,
     736             :                                      group_attrs);
     737           0 :     if (ret != EOK) {
     738           0 :         DEBUG(SSSDBG_OP_FAILURE, "Failed to save ghost members\n");
     739           0 :         goto done;
     740             :     }
     741             : 
     742           0 :     ret = sdap_save_all_names(group_name, attrs, dom, group_attrs);
     743           0 :     if (ret != EOK) {
     744           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to save group names\n");
     745           0 :         goto done;
     746             :     }
     747           0 :     DEBUG(SSSDBG_TRACE_FUNC, "Storing info for group %s\n", group_name);
     748             : 
     749           0 :     ret = sdap_store_group_with_gid(dom, group_name, gid, group_attrs,
     750           0 :                                     dom->group_timeout,
     751             :                                     posix_group, now);
     752           0 :     if (ret) {
     753           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
     754             :               "Could not store group with GID: [%s]\n",
     755             :                sss_strerror(ret));
     756           0 :         goto done;
     757             :     }
     758             : 
     759           0 :     if (_usn_value) {
     760           0 :         *_usn_value = talloc_steal(memctx, usn_value);
     761             :     }
     762             : 
     763           0 :     talloc_steal(memctx, group_attrs);
     764           0 :     ret = EOK;
     765             : 
     766             : done:
     767           0 :     if (ret) {
     768           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
     769             :               "Failed to save group [%s]: [%s]\n",
     770             :                group_name ? group_name : "Unknown",
     771             :                sss_strerror(ret));
     772             :     }
     773           0 :     talloc_free(tmpctx);
     774           0 :     return ret;
     775             : }
     776             : 
     777             : static errno_t
     778           0 : are_sids_from_same_dom(const char *sid1, const char *sid2, bool *_result)
     779             : {
     780             :     size_t len_prefix_sid1;
     781             :     size_t len_prefix_sid2;
     782             :     char *rid1, *rid2;
     783             :     bool result;
     784             : 
     785           0 :     rid1 = strrchr(sid1, '-');
     786           0 :     if (rid1 == NULL) {
     787           0 :         return EINVAL;
     788             :     }
     789             : 
     790           0 :     rid2 = strrchr(sid2, '-');
     791           0 :     if (rid2 == NULL) {
     792           0 :         return EINVAL;
     793             :     }
     794             : 
     795           0 :     len_prefix_sid1 = rid1 - sid1;
     796           0 :     len_prefix_sid2 = rid2 - sid2;
     797             : 
     798           0 :     result = (len_prefix_sid1 == len_prefix_sid2) &&
     799           0 :         (strncmp(sid1, sid2, len_prefix_sid1) == 0);
     800             : 
     801           0 :     *_result = result;
     802             : 
     803           0 :     return EOK;
     804             : }
     805             : 
     806             : static errno_t
     807           0 : retain_extern_members(TALLOC_CTX *mem_ctx,
     808             :                       struct sss_domain_info *dom,
     809             :                       const char *group_name,
     810             :                       const char *group_sid,
     811             :                       char ***_userdns,
     812             :                       size_t *_nuserdns)
     813             : {
     814             :     TALLOC_CTX *tmp_ctx;
     815             :     const char **sids, **dns;
     816             :     bool same_domain;
     817             :     errno_t ret;
     818             :     size_t i, n;
     819           0 :     size_t nuserdns = 0;
     820           0 :     const char **userdns = NULL;
     821             : 
     822           0 :     tmp_ctx = talloc_new(NULL);
     823           0 :     if (tmp_ctx == NULL) {
     824           0 :         return ENOMEM;
     825             :     }
     826             : 
     827           0 :     ret = sysdb_get_sids_of_members(tmp_ctx, dom, group_name, &sids, &dns, &n);
     828           0 :     if (ret != EOK) {
     829           0 :         if (ret != ENOENT) {
     830           0 :             DEBUG(SSSDBG_TRACE_ALL,
     831             :                   "get_sids_of_members failed: %d [%s]\n",
     832             :                   ret, sss_strerror(ret));
     833             :         }
     834           0 :         goto done;
     835             :     }
     836             : 
     837           0 :     for (i=0; i < n; i++) {
     838           0 :         ret = are_sids_from_same_dom(group_sid, sids[i], &same_domain);
     839           0 :         if (ret == EOK && !same_domain) {
     840           0 :             DEBUG(SSSDBG_TRACE_ALL, "extern member: %s\n", dns[i]);
     841           0 :             nuserdns++;
     842           0 :             userdns = talloc_realloc(tmp_ctx, userdns, const char*, nuserdns);
     843           0 :             if (userdns == NULL) {
     844           0 :                 ret = ENOMEM;
     845           0 :                 goto done;
     846             :             }
     847           0 :             userdns[nuserdns-1] = talloc_steal(userdns, dns[i]);
     848             :         }
     849             :     }
     850           0 :     *_nuserdns = nuserdns;
     851           0 :     *_userdns = discard_const(talloc_steal(mem_ctx, userdns));
     852           0 :     ret = EOK;
     853             : 
     854             : done:
     855           0 :     talloc_free(tmp_ctx);
     856           0 :     return ret;
     857             : }
     858             : 
     859             : /* ==Save-Group-Memebrs=================================================== */
     860             : 
     861             :     /* FIXME: support non legacy */
     862             :     /* FIXME: support storing additional attributes */
     863             : 
     864           0 : static int sdap_save_grpmem(TALLOC_CTX *memctx,
     865             :                             struct sysdb_ctx *ctx,
     866             :                             struct sdap_options *opts,
     867             :                             struct sss_domain_info *dom,
     868             :                             struct sysdb_attrs *attrs,
     869             :                             hash_table_t *ghosts,
     870             :                             time_t now)
     871             : {
     872             :     struct ldb_message_element *el;
     873           0 :     struct sysdb_attrs *group_attrs = NULL;
     874             :     const char *group_sid;
     875             :     const char *group_name;
     876           0 :     char **userdns = NULL;
     877           0 :     size_t nuserdns = 0;
     878           0 :     struct sss_domain_info *group_dom = NULL;
     879             :     int ret;
     880             : 
     881           0 :     if (dom->ignore_group_members) {
     882           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     883             :               "Group members are ignored, nothing to do. If you see this " \
     884             :               "message it might indicate an error in the group processing " \
     885             :               "logic.\n");
     886           0 :         return EOK;
     887             :     }
     888             : 
     889           0 :     ret = sysdb_attrs_get_string(attrs, SYSDB_SID_STR, &group_sid);
     890           0 :     if (ret != EOK) {
     891             :         /* Try harder. */
     892           0 :         ret = sdap_attrs_get_sid_str(memctx, opts->idmap_ctx, attrs,
     893           0 :                               opts->group_map[SDAP_AT_GROUP_OBJECTSID].sys_name,
     894             :                               discard_const(&group_sid));
     895           0 :         if (ret != EOK) {
     896           0 :             DEBUG(SSSDBG_TRACE_FUNC, "Failed to get group sid\n");
     897           0 :             group_sid = NULL;
     898             :         }
     899             :     }
     900             : 
     901           0 :     if (group_sid != NULL) {
     902           0 :         group_dom = sss_get_domain_by_sid_ldap_fallback(get_domains_head(dom),
     903             :                                                         group_sid);
     904           0 :         if (group_dom == NULL) {
     905           0 :             DEBUG(SSSDBG_TRACE_FUNC, "SID [%s] does not belong to any known "
     906             :                                      "domain, using [%s].\n", group_sid,
     907             :                                                               dom->name);
     908             :         }
     909             :     }
     910             : 
     911           0 :     if (group_dom == NULL) {
     912           0 :         group_dom = dom;
     913             :     }
     914             : 
     915           0 :     ret = sdap_get_group_primary_name(memctx, opts, attrs, group_dom,
     916             :                                       &group_name);
     917           0 :     if (ret != EOK) {
     918           0 :         DEBUG(SSSDBG_OP_FAILURE, "Failed to get group name\n");
     919           0 :         goto fail;
     920             :     }
     921           0 :     DEBUG(SSSDBG_TRACE_FUNC, "Processing group %s\n", group_name);
     922             : 
     923             :     /* With AD we also want to merge in parent groups of primary GID as they
     924             :      * are reported with tokenGroups, too
     925             :      */
     926           0 :     if (opts->schema_type == SDAP_SCHEMA_AD) {
     927           0 :         ret = sdap_dn_by_primary_gid(memctx, attrs, group_dom, opts,
     928             :                                      &userdns, &nuserdns);
     929           0 :         if (ret != EOK) {
     930           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
     931             :                   "sdap_dn_by_primary_gid failed: [%d][%s].\n",
     932             :                   ret, strerror(ret));
     933           0 :             goto fail;
     934             :         }
     935             :     }
     936             : 
     937             :     /* This is a temporal solution until the IPA provider is able to
     938             :      * resolve external group membership.
     939             :      * https://fedorahosted.org/sssd/ticket/2522
     940             :      */
     941           0 :     if (opts->schema_type == SDAP_SCHEMA_IPA_V1) {
     942           0 :         if (group_sid != NULL) {
     943           0 :             ret = retain_extern_members(memctx, group_dom, group_name,
     944             :                                         group_sid, &userdns, &nuserdns);
     945           0 :             if (ret != EOK) {
     946           0 :                 DEBUG(SSSDBG_TRACE_INTERNAL,
     947             :                       "retain_extern_members failed: %d:[%s].\n",
     948             :                       ret, sss_strerror(ret));
     949             :             }
     950             :         }
     951             :     }
     952             : 
     953           0 :     ret = sysdb_attrs_get_el(attrs,
     954           0 :                     opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el);
     955           0 :     if (ret != EOK) {
     956           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_attrs_get_el failed: [%d][%s].\n",
     957             :               ret, strerror(ret));
     958           0 :         goto fail;
     959             :     }
     960             : 
     961           0 :     if (el->num_values == 0 && nuserdns == 0) {
     962           0 :         DEBUG(SSSDBG_TRACE_FUNC,
     963             :               "No members for group [%s]\n", group_name);
     964             :     } else {
     965           0 :         DEBUG(SSSDBG_TRACE_FUNC,
     966             :               "Adding member users to group [%s]\n", group_name);
     967             : 
     968           0 :         group_attrs = sysdb_new_attrs(memctx);
     969           0 :         if (!group_attrs) {
     970           0 :             DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_new_attrs failed\n");
     971           0 :             ret = ENOMEM;
     972           0 :             goto fail;
     973             :         }
     974             : 
     975           0 :         ret = sdap_fill_memberships(opts, group_attrs, ctx, group_dom, ghosts,
     976           0 :                                     el->values, el->num_values,
     977             :                                     userdns, nuserdns);
     978           0 :         if (ret) {
     979           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     980             :                   "sdap_fill_memberships failed with [%d]: %s\n", ret,
     981             :                    strerror(ret));
     982           0 :             goto fail;
     983             :         }
     984             :     }
     985             : 
     986           0 :     ret = sysdb_store_group(group_dom, group_name, 0, group_attrs,
     987           0 :                             group_dom->group_timeout, now);
     988           0 :     if (ret) {
     989           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_store_group failed: [%d][%s].\n",
     990             :               ret, strerror(ret));
     991           0 :         goto fail;
     992             :     }
     993             : 
     994           0 :     return EOK;
     995             : 
     996             : fail:
     997           0 :     DEBUG(SSSDBG_OP_FAILURE,
     998             :            "Failed to save members of group %s\n", group_name);
     999           0 :     return ret;
    1000             : }
    1001             : 
    1002             : 
    1003             : /* ==Generic-Function-to-save-multiple-groups============================= */
    1004             : 
    1005           0 : static int sdap_save_groups(TALLOC_CTX *memctx,
    1006             :                             struct sysdb_ctx *sysdb,
    1007             :                             struct sss_domain_info *dom,
    1008             :                             struct sdap_options *opts,
    1009             :                             struct sysdb_attrs **groups,
    1010             :                             int num_groups,
    1011             :                             bool populate_members,
    1012             :                             hash_table_t *ghosts,
    1013             :                             bool save_orig_member,
    1014             :                             char **_usn_value)
    1015             : {
    1016             :     TALLOC_CTX *tmpctx;
    1017           0 :     char *higher_usn = NULL;
    1018             :     char *usn_value;
    1019             :     bool twopass;
    1020           0 :     bool has_nesting = false;
    1021             :     int ret;
    1022             :     errno_t sret;
    1023             :     int i;
    1024           0 :     struct sysdb_attrs **saved_groups = NULL;
    1025           0 :     int nsaved_groups = 0;
    1026             :     time_t now;
    1027           0 :     bool in_transaction = false;
    1028             : 
    1029           0 :     switch (opts->schema_type) {
    1030             :     case SDAP_SCHEMA_RFC2307:
    1031           0 :         twopass = false;
    1032           0 :         break;
    1033             : 
    1034             :     case SDAP_SCHEMA_RFC2307BIS:
    1035             :     case SDAP_SCHEMA_IPA_V1:
    1036             :     case SDAP_SCHEMA_AD:
    1037           0 :         twopass = true;
    1038           0 :         has_nesting = true;
    1039           0 :         break;
    1040             : 
    1041             :     default:
    1042           0 :         return EINVAL;
    1043             :     }
    1044             : 
    1045           0 :     tmpctx = talloc_new(memctx);
    1046           0 :     if (!tmpctx) {
    1047           0 :         return ENOMEM;
    1048             :     }
    1049             : 
    1050           0 :     ret = sysdb_transaction_start(sysdb);
    1051           0 :     if (ret) {
    1052           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
    1053           0 :         goto done;
    1054             :     }
    1055           0 :     in_transaction = true;
    1056             : 
    1057           0 :     if (twopass && !populate_members) {
    1058           0 :         saved_groups = talloc_array(tmpctx, struct sysdb_attrs *,
    1059             :                                     num_groups);
    1060           0 :         if (!saved_groups) {
    1061           0 :             ret = ENOMEM;
    1062           0 :             goto done;
    1063             :         }
    1064             :     }
    1065             : 
    1066           0 :     now = time(NULL);
    1067           0 :     for (i = 0; i < num_groups; i++) {
    1068           0 :         usn_value = NULL;
    1069             : 
    1070             :         /* if 2 pass savemembers = false */
    1071           0 :         ret = sdap_save_group(tmpctx, opts, dom, groups[i],
    1072             :                               populate_members,
    1073           0 :                               has_nesting && save_orig_member,
    1074             :                               ghosts, &usn_value, now);
    1075             : 
    1076             :         /* Do not fail completely on errors.
    1077             :          * Just report the failure to save and go on */
    1078           0 :         if (ret) {
    1079           0 :             DEBUG(SSSDBG_OP_FAILURE,
    1080             :                   "Failed to store group %d. Ignoring.\n", i);
    1081             :         } else {
    1082           0 :             DEBUG(SSSDBG_TRACE_ALL, "Group %d processed!\n", i);
    1083           0 :             if (twopass && !populate_members) {
    1084           0 :                 saved_groups[nsaved_groups] = groups[i];
    1085           0 :                 nsaved_groups++;
    1086             :             }
    1087             :         }
    1088             : 
    1089           0 :         if (usn_value) {
    1090           0 :             if (higher_usn) {
    1091           0 :                 if ((strlen(usn_value) > strlen(higher_usn)) ||
    1092           0 :                     (strcmp(usn_value, higher_usn) > 0)) {
    1093           0 :                     talloc_zfree(higher_usn);
    1094           0 :                     higher_usn = usn_value;
    1095             :                 } else {
    1096           0 :                     talloc_zfree(usn_value);
    1097             :                 }
    1098             :             } else {
    1099           0 :                 higher_usn = usn_value;
    1100             :             }
    1101             :         }
    1102             :     }
    1103             : 
    1104           0 :     if (twopass && !populate_members) {
    1105             : 
    1106           0 :         for (i = 0; i < nsaved_groups; i++) {
    1107             : 
    1108           0 :             ret = sdap_save_grpmem(tmpctx, sysdb, opts, dom, saved_groups[i],
    1109             :                                    ghosts, now);
    1110             :             /* Do not fail completely on errors.
    1111             :              * Just report the failure to save and go on */
    1112           0 :             if (ret) {
    1113           0 :                 DEBUG(SSSDBG_OP_FAILURE,
    1114             :                       "Failed to store group %d members.\n", i);
    1115             :             } else {
    1116           0 :                 DEBUG(SSSDBG_TRACE_ALL, "Group %d members processed!\n", i);
    1117             :             }
    1118             :         }
    1119             :     }
    1120             : 
    1121           0 :     ret = sysdb_transaction_commit(sysdb);
    1122           0 :     if (ret) {
    1123           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction!\n");
    1124           0 :         goto done;
    1125             :     }
    1126           0 :     in_transaction = false;
    1127             : 
    1128           0 :     if (_usn_value) {
    1129           0 :         *_usn_value = talloc_steal(memctx, higher_usn);
    1130             :     }
    1131             : 
    1132             : done:
    1133           0 :     if (in_transaction) {
    1134           0 :         sret = sysdb_transaction_cancel(sysdb);
    1135           0 :         if (sret != EOK) {
    1136           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
    1137             :         }
    1138             :     }
    1139           0 :     talloc_zfree(tmpctx);
    1140           0 :     return ret;
    1141             : }
    1142             : 
    1143             : 
    1144             : /* ==Process-Groups======================================================= */
    1145             : 
    1146             : struct sdap_process_group_state {
    1147             :     struct tevent_context *ev;
    1148             :     struct sdap_options *opts;
    1149             :     struct sdap_handle *sh;
    1150             :     struct sss_domain_info *dom;
    1151             :     struct sysdb_ctx *sysdb;
    1152             : 
    1153             :     struct sysdb_attrs *group;
    1154             :     struct ldb_message_element* sysdb_dns;
    1155             :     struct ldb_message_element* ghost_dns;
    1156             :     char **queued_members;
    1157             :     int queue_len;
    1158             :     const char **attrs;
    1159             :     const char *filter;
    1160             :     size_t queue_idx;
    1161             :     size_t count;
    1162             :     size_t check_count;
    1163             : 
    1164             :     bool enumeration;
    1165             : };
    1166             : 
    1167             : #define GROUPMEMBER_REQ_PARALLEL 50
    1168             : static void sdap_process_group_members(struct tevent_req *subreq);
    1169             : 
    1170             : static int sdap_process_group_members_2307bis(struct tevent_req *req,
    1171             :                                    struct sdap_process_group_state *state,
    1172             :                                    struct ldb_message_element *memberel);
    1173             : static int sdap_process_group_members_2307(struct sdap_process_group_state *state,
    1174             :                                    struct ldb_message_element *memberel,
    1175             :                                    struct ldb_message_element *ghostel);
    1176             : 
    1177           0 : static errno_t sdap_process_group_create_dns(TALLOC_CTX *mem_ctx,
    1178             :                                              size_t num_values,
    1179             :                                              struct ldb_message_element **_dns)
    1180             : {
    1181             :     struct ldb_message_element *dns;
    1182             : 
    1183           0 :     dns = talloc(mem_ctx, struct ldb_message_element);
    1184           0 :     if (dns == NULL) {
    1185           0 :         return ENOMEM;
    1186             :     }
    1187             : 
    1188           0 :     dns->num_values = 0;
    1189           0 :     dns->values = talloc_array(dns, struct ldb_val,
    1190             :                                num_values);
    1191           0 :     if (dns->values == NULL) {
    1192           0 :         talloc_zfree(dns);
    1193           0 :         return ENOMEM;
    1194             :     }
    1195             : 
    1196           0 :     *_dns = dns;
    1197             : 
    1198           0 :     return EOK;
    1199             : }
    1200             : 
    1201             : static struct tevent_req *
    1202           0 : sdap_process_group_send(TALLOC_CTX *memctx,
    1203             :                         struct tevent_context *ev,
    1204             :                         struct sss_domain_info *dom,
    1205             :                         struct sysdb_ctx *sysdb,
    1206             :                         struct sdap_options *opts,
    1207             :                         struct sdap_handle *sh,
    1208             :                         struct sysdb_attrs *group,
    1209             :                         bool enumeration)
    1210             : {
    1211             :     struct ldb_message_element *el;
    1212             :     struct ldb_message_element *ghostel;
    1213             :     struct sdap_process_group_state *grp_state;
    1214           0 :     struct tevent_req *req = NULL;
    1215             :     const char **attrs;
    1216             :     char* filter;
    1217             :     int ret;
    1218             : 
    1219           0 :     req = tevent_req_create(memctx, &grp_state,
    1220             :                             struct sdap_process_group_state);
    1221           0 :     if (!req) return NULL;
    1222             : 
    1223           0 :     ret = build_attrs_from_map(grp_state, opts->user_map, opts->user_map_cnt,
    1224             :                                NULL, &attrs, NULL);
    1225           0 :     if (ret) {
    1226           0 :         goto done;
    1227             :     }
    1228             : 
    1229             :     /* FIXME: we ignore nested rfc2307bis groups for now */
    1230           0 :     filter = talloc_asprintf(grp_state, "(objectclass=%s)",
    1231           0 :                              opts->user_map[SDAP_OC_USER].name);
    1232           0 :     if (!filter) {
    1233           0 :         talloc_zfree(req);
    1234           0 :         return NULL;
    1235             :     }
    1236             : 
    1237           0 :     grp_state->ev = ev;
    1238           0 :     grp_state->opts = opts;
    1239           0 :     grp_state->dom = dom;
    1240           0 :     grp_state->sh = sh;
    1241           0 :     grp_state->sysdb = sysdb;
    1242           0 :     grp_state->group =  group;
    1243           0 :     grp_state->check_count = 0;
    1244           0 :     grp_state->queue_idx = 0;
    1245           0 :     grp_state->queued_members = NULL;
    1246           0 :     grp_state->queue_len = 0;
    1247           0 :     grp_state->filter = filter;
    1248           0 :     grp_state->attrs = attrs;
    1249           0 :     grp_state->enumeration = enumeration;
    1250             : 
    1251           0 :     ret = sysdb_attrs_get_el(group,
    1252           0 :                              opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
    1253             :                              &el);
    1254           0 :     if (ret) {
    1255           0 :         goto done;
    1256             :     }
    1257             : 
    1258             :     /* Group without members */
    1259           0 :     if (el->num_values == 0) {
    1260           0 :         DEBUG(SSSDBG_OP_FAILURE, "No Members. Done!\n");
    1261           0 :         ret = EOK;
    1262           0 :         goto done;
    1263             :     }
    1264             : 
    1265           0 :     ret = sysdb_attrs_get_el(group,
    1266             :                              SYSDB_GHOST,
    1267             :                              &ghostel);
    1268           0 :     if (ret) {
    1269           0 :         goto done;
    1270             :     }
    1271             : 
    1272           0 :     if (ghostel->num_values == 0) {
    1273             :         /* Element was probably newly created, look for "member" again */
    1274           0 :         ret = sysdb_attrs_get_el(group,
    1275           0 :                                  opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
    1276             :                                  &el);
    1277           0 :         if (ret != EOK) {
    1278           0 :             goto done;
    1279             :         }
    1280             :     }
    1281             : 
    1282             : 
    1283           0 :     ret = sdap_process_group_create_dns(grp_state, el->num_values,
    1284           0 :                                         &grp_state->sysdb_dns);
    1285           0 :     if (ret != EOK) {
    1286           0 :         goto done;
    1287             :     }
    1288             : 
    1289           0 :     ret = sdap_process_group_create_dns(grp_state, el->num_values,
    1290           0 :                                         &grp_state->ghost_dns);
    1291           0 :     if (ret != EOK) {
    1292           0 :         goto done;
    1293             :     }
    1294             : 
    1295           0 :     switch (opts->schema_type) {
    1296             :         case SDAP_SCHEMA_RFC2307:
    1297           0 :             ret = sdap_process_group_members_2307(grp_state, el, ghostel);
    1298           0 :             break;
    1299             : 
    1300             :         case SDAP_SCHEMA_IPA_V1:
    1301             :         case SDAP_SCHEMA_AD:
    1302             :         case SDAP_SCHEMA_RFC2307BIS:
    1303             :             /* Note that this code branch will be used only if
    1304             :              * ldap_nesting_level = 0 is set in config file
    1305             :              */
    1306           0 :             ret = sdap_process_group_members_2307bis(req, grp_state, el);
    1307           0 :             break;
    1308             : 
    1309             :         default:
    1310           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1311             :                   "Unknown schema type %d\n", opts->schema_type);
    1312           0 :             ret = EINVAL;
    1313           0 :             break;
    1314             :     }
    1315             : 
    1316             : done:
    1317             :     /* We managed to process all the entries */
    1318             :     /* EBUSY means we need to wait for entries in LDAP */
    1319           0 :     if (ret == EOK) {
    1320           0 :         DEBUG(SSSDBG_TRACE_LIBS, "All group members processed\n");
    1321           0 :         tevent_req_done(req);
    1322           0 :         tevent_req_post(req, ev);
    1323             :     }
    1324             : 
    1325           0 :     if (ret != EOK && ret != EBUSY) {
    1326           0 :         tevent_req_error(req, ret);
    1327           0 :         tevent_req_post(req, ev);
    1328             :     }
    1329           0 :     return req;
    1330             : }
    1331             : 
    1332             : static int
    1333           0 : sdap_process_missing_member_2307bis(struct tevent_req *req,
    1334             :                                     char *user_dn,
    1335             :                                     unsigned num_users)
    1336             : {
    1337           0 :     struct sdap_process_group_state *grp_state =
    1338           0 :         tevent_req_data(req, struct sdap_process_group_state);
    1339             :     struct tevent_req *subreq;
    1340             : 
    1341             :     /*
    1342             :      * Issue at most GROUPMEMBER_REQ_PARALLEL LDAP searches at once.
    1343             :      * The rest is sent while the results are being processed.
    1344             :      * We limit the number as of request here, as the Server might
    1345             :      * enforce limits on the number of pending operations per
    1346             :      * connection.
    1347             :      */
    1348           0 :     if (grp_state->check_count > GROUPMEMBER_REQ_PARALLEL) {
    1349           0 :         DEBUG(SSSDBG_TRACE_LIBS, " queueing search for: %s\n", user_dn);
    1350           0 :         if (!grp_state->queued_members) {
    1351           0 :             DEBUG(SSSDBG_TRACE_LIBS,
    1352             :                   "Allocating queue for %zu members\n",
    1353             :                    num_users - grp_state->check_count);
    1354             : 
    1355           0 :             grp_state->queued_members = talloc_array(grp_state, char *,
    1356             :                     num_users - grp_state->check_count + 1);
    1357           0 :             if (!grp_state->queued_members) {
    1358           0 :                 return ENOMEM;
    1359             :             }
    1360             :         }
    1361           0 :         grp_state->queued_members[grp_state->queue_len] = user_dn;
    1362           0 :         grp_state->queue_len++;
    1363             :     } else {
    1364           0 :         subreq = sdap_get_generic_send(grp_state,
    1365             :                                        grp_state->ev,
    1366             :                                        grp_state->opts,
    1367             :                                        grp_state->sh,
    1368             :                                        user_dn,
    1369             :                                        LDAP_SCOPE_BASE,
    1370             :                                        grp_state->filter,
    1371             :                                        grp_state->attrs,
    1372           0 :                                        grp_state->opts->user_map,
    1373           0 :                                        grp_state->opts->user_map_cnt,
    1374           0 :                                        dp_opt_get_int(grp_state->opts->basic,
    1375             :                                                       SDAP_SEARCH_TIMEOUT),
    1376             :                                        false);
    1377           0 :         if (!subreq) {
    1378           0 :             return ENOMEM;
    1379             :         }
    1380           0 :         tevent_req_set_callback(subreq, sdap_process_group_members, req);
    1381             :     }
    1382             : 
    1383           0 :     grp_state->check_count++;
    1384           0 :     return EOK;
    1385             : }
    1386             : 
    1387             : static int
    1388           0 : sdap_process_group_members_2307bis(struct tevent_req *req,
    1389             :                                    struct sdap_process_group_state *state,
    1390             :                                    struct ldb_message_element *memberel)
    1391             : {
    1392             :     char *member_dn;
    1393             :     char *strdn;
    1394             :     int ret;
    1395             :     int i;
    1396             :     int nesting_level;
    1397             :     bool is_group;
    1398             : 
    1399           0 :     nesting_level = dp_opt_get_int(state->opts->basic, SDAP_NESTING_LEVEL);
    1400             : 
    1401           0 :     for (i=0; i < memberel->num_values; i++) {
    1402           0 :         member_dn = (char *)memberel->values[i].data;
    1403             : 
    1404           0 :         ret = sdap_find_entry_by_origDN(state->sysdb_dns->values,
    1405             :                                         state->sysdb,
    1406             :                                         state->dom,
    1407             :                                         member_dn,
    1408             :                                         &strdn,
    1409             :                                         &is_group);
    1410             : 
    1411           0 :         if (ret == EOK) {
    1412           0 :             if (nesting_level == 0 && is_group) {
    1413             :                 /* Ignore group members which are groups themselves. */
    1414           0 :                 continue;
    1415             :             }
    1416             : 
    1417             :             /*
    1418             :              * User already cached in sysdb. Remember the sysdb DN for later
    1419             :              * use by sdap_save_groups()
    1420             :              */
    1421           0 :             DEBUG(SSSDBG_TRACE_LIBS, "sysdbdn: %s\n", strdn);
    1422           0 :             state->sysdb_dns->values[state->sysdb_dns->num_values].data =
    1423             :                 (uint8_t*) strdn;
    1424           0 :             state->sysdb_dns->values[state->sysdb_dns->num_values].length =
    1425           0 :                 strlen(strdn);
    1426           0 :             state->sysdb_dns->num_values++;
    1427           0 :         } else if (ret == ENOENT) {
    1428           0 :             if (!state->enumeration) {
    1429             :                 /* The user is not in sysdb, need to add it
    1430             :                  * We don't need to do this if we're in an enumeration,
    1431             :                  * because all real members should all be populated
    1432             :                  * already by the first pass of the enumeration.
    1433             :                  * Also, we don't want to be holding the sysdb
    1434             :                  * transaction while we're performing LDAP lookups.
    1435             :                  */
    1436           0 :                 DEBUG(SSSDBG_TRACE_LIBS,
    1437             :                       "Searching LDAP for missing user entry\n");
    1438           0 :                 ret = sdap_process_missing_member_2307bis(req,
    1439             :                                                           member_dn,
    1440             :                                                           memberel->num_values);
    1441           0 :                 if (ret != EOK) {
    1442           0 :                     DEBUG(SSSDBG_CRIT_FAILURE,
    1443             :                           "Error processing missing member #%d (%s):\n",
    1444             :                               i, member_dn);
    1445           0 :                     return ret;
    1446             :                 }
    1447             :             }
    1448             :         } else {
    1449           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1450             :                   "Error checking cache for member #%d (%s):\n",
    1451             :                        i, (char *)memberel->values[i].data);
    1452           0 :             return ret;
    1453             :         }
    1454             :     }
    1455             : 
    1456           0 :     if (state->queue_len > 0) {
    1457           0 :         state->queued_members[state->queue_len]=NULL;
    1458             :     }
    1459             : 
    1460           0 :     if (state->check_count == 0) {
    1461             :         /*
    1462             :          * All group members are already cached in sysdb, we are done
    1463             :          * with this group. To avoid redundant sysdb lookups, populate the
    1464             :          * "member" attribute of the group entry with the sysdb DNs of
    1465             :          * the members.
    1466             :          */
    1467           0 :         ret = EOK;
    1468           0 :         memberel->values = talloc_steal(state->group, state->sysdb_dns->values);
    1469           0 :         memberel->num_values = state->sysdb_dns->num_values;
    1470             :     } else {
    1471           0 :         state->count = state->check_count;
    1472           0 :         ret = EBUSY;
    1473             :     }
    1474             : 
    1475           0 :     return ret;
    1476             : }
    1477             : 
    1478             : static int
    1479           0 : sdap_add_group_member_2307(struct ldb_message_element *sysdb_dns,
    1480             :                            const char *username)
    1481             : {
    1482           0 :     sysdb_dns->values[sysdb_dns->num_values].data =
    1483           0 :             (uint8_t *) talloc_strdup(sysdb_dns->values, username);
    1484           0 :     if (sysdb_dns->values[sysdb_dns->num_values].data == NULL) {
    1485           0 :         return ENOMEM;
    1486             :     }
    1487           0 :     sysdb_dns->values[sysdb_dns->num_values].length =
    1488           0 :             strlen(username);
    1489           0 :     sysdb_dns->num_values++;
    1490             : 
    1491           0 :     return EOK;
    1492             : }
    1493             : 
    1494             : static int
    1495           0 : sdap_process_missing_member_2307(struct sdap_process_group_state *state,
    1496             :                                  char *member_name)
    1497             : {
    1498             :     int ret;
    1499             :     TALLOC_CTX *tmp_ctx;
    1500             :     const char *filter;
    1501             :     const char *username;
    1502             :     const char *user_dn;
    1503             :     size_t count;
    1504           0 :     struct ldb_message **msgs = NULL;
    1505             :     static const char *attrs[] = { SYSDB_NAME, NULL };
    1506             : 
    1507           0 :     tmp_ctx = talloc_new(NULL);
    1508           0 :     if (!tmp_ctx) return ENOMEM;
    1509             : 
    1510             :     /* Check for the alias in the sysdb */
    1511           0 :     filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_NAME_ALIAS, member_name);
    1512           0 :     if (!filter) {
    1513           0 :         ret = ENOMEM;
    1514           0 :         goto done;
    1515             :     }
    1516             : 
    1517           0 :     ret = sysdb_search_users(tmp_ctx, state->dom, filter,
    1518             :                              attrs, &count, &msgs);
    1519           0 :     if (ret == EOK && count > 0) {
    1520             :         /* Entry exists but the group references it with an alias. */
    1521             : 
    1522           0 :         if (count != 1) {
    1523           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1524             :                   "More than one entry with this alias?\n");
    1525           0 :             ret = EIO;
    1526           0 :             goto done;
    1527             :         }
    1528             : 
    1529             :         /* fill username with primary name */
    1530           0 :         username = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL);
    1531           0 :         if (username == NULL) {
    1532           0 :             ret = EINVAL;
    1533           0 :             DEBUG(SSSDBG_MINOR_FAILURE, "Inconsistent sysdb: user "
    1534             :                                          "without primary name?\n");
    1535           0 :             goto done;
    1536             :         }
    1537           0 :         user_dn = sysdb_user_strdn(tmp_ctx, state->dom->name, username);
    1538           0 :         if (user_dn == NULL) {
    1539           0 :             return ENOMEM;
    1540             :         }
    1541             : 
    1542           0 :         ret = sdap_add_group_member_2307(state->sysdb_dns, user_dn);
    1543           0 :         if (ret != EOK) {
    1544           0 :             DEBUG(SSSDBG_OP_FAILURE, "Could not add group member %s\n", username);
    1545             :         }
    1546           0 :     } else if (ret == ENOENT || count == 0) {
    1547             :         /* The entry really does not exist, add a ghost */
    1548           0 :         DEBUG(SSSDBG_TRACE_FUNC, "Adding a ghost entry\n");
    1549           0 :         ret = sdap_add_group_member_2307(state->ghost_dns, member_name);
    1550           0 :         if (ret != EOK) {
    1551           0 :             DEBUG(SSSDBG_OP_FAILURE, "Could not add group member %s\n", member_name);
    1552             :         }
    1553             :     } else {
    1554           0 :         ret = EIO;
    1555             :     }
    1556             : 
    1557             : done:
    1558           0 :     talloc_free(tmp_ctx);
    1559           0 :     return ret;
    1560             : }
    1561             : 
    1562             : static int
    1563           0 : sdap_process_group_members_2307(struct sdap_process_group_state *state,
    1564             :                                 struct ldb_message_element *memberel,
    1565             :                                 struct ldb_message_element *ghostel)
    1566             : {
    1567             :     struct ldb_message *msg;
    1568             :     char *member_name;
    1569             :     char *userdn;
    1570             :     int ret;
    1571             :     int i;
    1572             : 
    1573           0 :     for (i=0; i < memberel->num_values; i++) {
    1574           0 :         member_name = (char *)memberel->values[i].data;
    1575             : 
    1576             :         /* We need to skip over zero-length usernames */
    1577           0 :         if (member_name[0] == '\0') continue;
    1578             : 
    1579           0 :         ret = sysdb_search_user_by_name(state, state->dom, member_name,
    1580             :                                         NULL, &msg);
    1581           0 :         if (ret == EOK) {
    1582             :             /*
    1583             :              * User already cached in sysdb. Remember the sysdb DN for later
    1584             :              * use by sdap_save_groups()
    1585             :              */
    1586           0 :             DEBUG(SSSDBG_TRACE_LIBS,
    1587             :                   "Member already cached in sysdb: %s\n", member_name);
    1588             : 
    1589           0 :             userdn = sysdb_user_strdn(state->sysdb_dns, state->dom->name, member_name);
    1590           0 :             if (userdn == NULL) {
    1591           0 :                 return ENOMEM;
    1592             :             }
    1593             : 
    1594           0 :             ret = sdap_add_group_member_2307(state->sysdb_dns, userdn);
    1595           0 :             if (ret != EOK) {
    1596           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    1597             :                       "Could not add member %s into sysdb\n", member_name);
    1598           0 :                 goto done;
    1599             :             }
    1600           0 :         } else if (ret == ENOENT) {
    1601             :             /* The user is not in sysdb, need to add it */
    1602           0 :             DEBUG(SSSDBG_TRACE_LIBS, "member #%d (%s): not found in sysdb\n",
    1603             :                        i, member_name);
    1604             : 
    1605           0 :             ret = sdap_process_missing_member_2307(state, member_name);
    1606           0 :             if (ret != EOK) {
    1607           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    1608             :                       "Error processing missing member #%d (%s):\n",
    1609             :                           i, member_name);
    1610           0 :                 goto done;
    1611             :             }
    1612             :         } else {
    1613           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1614             :                   "Error checking cache for member #%d (%s):\n",
    1615             :                        i, (char *) memberel->values[i].data);
    1616           0 :             goto done;
    1617             :         }
    1618             :     }
    1619             : 
    1620           0 :     ret = EOK;
    1621           0 :     talloc_free(memberel->values);
    1622           0 :     memberel->values = talloc_steal(state->group, state->sysdb_dns->values);
    1623           0 :     memberel->num_values = state->sysdb_dns->num_values;
    1624           0 :     talloc_free(ghostel->values);
    1625           0 :     ghostel->values = talloc_steal(state->group, state->ghost_dns->values);
    1626           0 :     ghostel->num_values = state->ghost_dns->num_values;
    1627             : 
    1628             : done:
    1629           0 :     return ret;
    1630             : }
    1631             : 
    1632           0 : static void sdap_process_group_members(struct tevent_req *subreq)
    1633             : {
    1634             :     struct sysdb_attrs **usr_attrs;
    1635             :     size_t count;
    1636             :     int ret;
    1637           0 :     struct tevent_req *req =
    1638           0 :                         tevent_req_callback_data(subreq, struct tevent_req);
    1639           0 :     struct sdap_process_group_state *state =
    1640           0 :                         tevent_req_data(req, struct sdap_process_group_state);
    1641             :     struct ldb_message_element *el;
    1642             :     uint8_t* name_string;
    1643             : 
    1644           0 :     state->check_count--;
    1645           0 :     DEBUG(SSSDBG_TRACE_ALL, "Members remaining: %zu\n", state->check_count);
    1646             : 
    1647           0 :     ret = sdap_get_generic_recv(subreq, state, &count, &usr_attrs);
    1648           0 :     talloc_zfree(subreq);
    1649           0 :     if (ret) {
    1650           0 :         goto next;
    1651             :     }
    1652           0 :     if (count != 1) {
    1653           0 :         ret = EINVAL;
    1654           0 :         DEBUG(SSSDBG_TRACE_LIBS,
    1655             :               "Expected one user entry and got %zu\n", count);
    1656           0 :         goto next;
    1657             :     }
    1658           0 :     ret = sysdb_attrs_get_el(usr_attrs[0],
    1659           0 :             state->opts->user_map[SDAP_AT_USER_NAME].sys_name, &el);
    1660           0 :     if (el->num_values == 0) {
    1661           0 :         ret = EINVAL;
    1662             :     }
    1663           0 :     if (ret) {
    1664           0 :         DEBUG(SSSDBG_OP_FAILURE, "Failed to get the member's name\n");
    1665           0 :         goto next;
    1666             :     }
    1667             : 
    1668           0 :     name_string = el[0].values[0].data;
    1669           0 :     state->ghost_dns->values[state->ghost_dns->num_values].data =
    1670           0 :             talloc_steal(state->ghost_dns->values, name_string);
    1671           0 :     state->ghost_dns->values[state->ghost_dns->num_values].length =
    1672           0 :             strlen((char *)name_string);
    1673           0 :     state->ghost_dns->num_values++;
    1674             : 
    1675             : next:
    1676           0 :     if (ret) {
    1677           0 :         DEBUG(SSSDBG_TRACE_FUNC,
    1678             :               "Error reading group member[%d]: %s. Skipping\n",
    1679             :                ret, strerror(ret));
    1680           0 :         state->count--;
    1681             :     }
    1682             :     /* Are there more searches for uncached users to submit ? */
    1683           0 :     if (state->queued_members && state->queued_members[state->queue_idx]) {
    1684           0 :         subreq = sdap_get_generic_send(state,
    1685             :                                        state->ev, state->opts, state->sh,
    1686           0 :                                        state->queued_members[state->queue_idx],
    1687             :                                        LDAP_SCOPE_BASE,
    1688             :                                        state->filter,
    1689             :                                        state->attrs,
    1690           0 :                                        state->opts->user_map,
    1691           0 :                                        state->opts->user_map_cnt,
    1692           0 :                                        dp_opt_get_int(state->opts->basic,
    1693             :                                                       SDAP_SEARCH_TIMEOUT),
    1694             :                                        false);
    1695           0 :         if (!subreq) {
    1696           0 :             tevent_req_error(req, ENOMEM);
    1697           0 :             return;
    1698             :         }
    1699             : 
    1700           0 :         tevent_req_set_callback(subreq,
    1701             :                                 sdap_process_group_members, req);
    1702           0 :         state->queue_idx++;
    1703             :     }
    1704             : 
    1705           0 :     if (state->check_count == 0) {
    1706             :         /*
    1707             :          * To avoid redundant sysdb lookups, populate the "member" attribute
    1708             :          * of the group entry with the sysdb DNs of the members.
    1709             :          */
    1710           0 :         ret = sysdb_attrs_get_el(state->group,
    1711           0 :                         state->opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
    1712             :                         &el);
    1713           0 :         if (ret != EOK) {
    1714           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1715             :                   "Failed to get the group member attribute [%d]: %s\n",
    1716             :                   ret, strerror(ret));
    1717           0 :             tevent_req_error(req, ret);
    1718           0 :             return;
    1719             :         }
    1720           0 :         el->values = talloc_steal(state->group, state->sysdb_dns->values);
    1721           0 :         el->num_values = state->sysdb_dns->num_values;
    1722             : 
    1723           0 :         ret = sysdb_attrs_get_el(state->group, SYSDB_GHOST, &el);
    1724           0 :         if (ret != EOK) {
    1725           0 :             tevent_req_error(req, ret);
    1726           0 :             return;
    1727             :         }
    1728           0 :         el->values = talloc_steal(state->group, state->ghost_dns->values);
    1729           0 :         el->num_values = state->ghost_dns->num_values;
    1730           0 :         DEBUG(SSSDBG_TRACE_ALL, "Processed Group - Done\n");
    1731           0 :         tevent_req_done(req);
    1732             :     }
    1733             : }
    1734             : 
    1735           0 : static int sdap_process_group_recv(struct tevent_req *req)
    1736             : {
    1737           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1738             : 
    1739           0 :     return EOK;
    1740             : }
    1741             : 
    1742             : 
    1743             : /* ==Search-Groups-with-filter============================================ */
    1744             : 
    1745             : struct sdap_get_groups_state {
    1746             :     struct tevent_context *ev;
    1747             :     struct sdap_options *opts;
    1748             :     struct sdap_handle *sh;
    1749             :     struct sss_domain_info *dom;
    1750             :     struct sdap_domain *sdom;
    1751             :     struct sysdb_ctx *sysdb;
    1752             :     const char **attrs;
    1753             :     const char *base_filter;
    1754             :     char *filter;
    1755             :     int timeout;
    1756             :     enum sdap_entry_lookup_type lookup_type;
    1757             :     bool no_members;
    1758             : 
    1759             :     char *higher_usn;
    1760             :     struct sysdb_attrs **groups;
    1761             :     size_t count;
    1762             :     size_t check_count;
    1763             :     hash_table_t *missing_external;
    1764             : 
    1765             :     hash_table_t *user_hash;
    1766             :     hash_table_t *group_hash;
    1767             : 
    1768             :     size_t base_iter;
    1769             :     struct sdap_search_base **search_bases;
    1770             : 
    1771             :     struct sdap_handle *ldap_sh;
    1772             :     struct sdap_id_op *op;
    1773             : };
    1774             : 
    1775             : static errno_t sdap_get_groups_next_base(struct tevent_req *req);
    1776             : static void sdap_get_groups_ldap_connect_done(struct tevent_req *subreq);
    1777             : static void sdap_get_groups_process(struct tevent_req *subreq);
    1778             : static void sdap_get_groups_done(struct tevent_req *subreq);
    1779             : 
    1780           0 : struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
    1781             :                                        struct tevent_context *ev,
    1782             :                                        struct sdap_domain *sdom,
    1783             :                                        struct sdap_options *opts,
    1784             :                                        struct sdap_handle *sh,
    1785             :                                        const char **attrs,
    1786             :                                        const char *filter,
    1787             :                                        int timeout,
    1788             :                                        enum sdap_entry_lookup_type lookup_type,
    1789             :                                        bool no_members)
    1790             : {
    1791             :     errno_t ret;
    1792             :     struct tevent_req *req;
    1793             :     struct tevent_req *subreq;
    1794             :     struct sdap_get_groups_state *state;
    1795             :     struct ad_id_ctx *subdom_id_ctx;
    1796             : 
    1797           0 :     req = tevent_req_create(memctx, &state, struct sdap_get_groups_state);
    1798           0 :     if (!req) return NULL;
    1799             : 
    1800           0 :     state->ev = ev;
    1801           0 :     state->opts = opts;
    1802           0 :     state->sdom = sdom;
    1803           0 :     state->dom = sdom->dom;
    1804           0 :     state->sh = sh;
    1805           0 :     state->sysdb = sdom->dom->sysdb;
    1806           0 :     state->attrs = attrs;
    1807           0 :     state->higher_usn = NULL;
    1808           0 :     state->groups =  NULL;
    1809           0 :     state->count = 0;
    1810           0 :     state->timeout = timeout;
    1811           0 :     state->lookup_type = lookup_type;
    1812           0 :     state->no_members = no_members;
    1813           0 :     state->base_filter = filter;
    1814           0 :     state->base_iter = 0;
    1815           0 :     state->search_bases = sdom->group_search_bases;
    1816             : 
    1817           0 :     if (!state->search_bases) {
    1818           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1819             :               "Group lookup request without a search base\n");
    1820           0 :         ret = EINVAL;
    1821           0 :         goto done;
    1822             :     }
    1823             : 
    1824             :     /* With AD by default the Global Catalog is used for lookup. But the GC
    1825             :      * group object might not have full group membership data. To make sure we
    1826             :      * connect to an LDAP server of the group's domain. */
    1827           0 :     if (state->opts->schema_type == SDAP_SCHEMA_AD && sdom->pvt != NULL) {
    1828           0 :         subdom_id_ctx = talloc_get_type(sdom->pvt, struct ad_id_ctx);
    1829           0 :         state->op = sdap_id_op_create(state, subdom_id_ctx->ldap_ctx->conn_cache);
    1830           0 :         if (!state->op) {
    1831           0 :             DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
    1832           0 :             ret = ENOMEM;
    1833           0 :             goto done;
    1834             :         }
    1835             : 
    1836           0 :         subreq = sdap_id_op_connect_send(state->op, state, &ret);
    1837           0 :         if (subreq == NULL) {
    1838           0 :             ret = ENOMEM;
    1839           0 :             goto done;
    1840             :         }
    1841             : 
    1842           0 :         tevent_req_set_callback(subreq,
    1843             :                                 sdap_get_groups_ldap_connect_done,
    1844             :                                 req);
    1845           0 :         return req;
    1846             :     }
    1847             : 
    1848           0 :     ret = sdap_get_groups_next_base(req);
    1849             : 
    1850             : done:
    1851           0 :     if (ret != EOK) {
    1852           0 :         tevent_req_error(req, ret);
    1853           0 :         tevent_req_post(req, ev);
    1854             :     }
    1855             : 
    1856           0 :     return req;
    1857             : }
    1858             : 
    1859           0 : static void sdap_get_groups_ldap_connect_done(struct tevent_req *subreq)
    1860             : {
    1861             :     struct tevent_req *req;
    1862             :     struct sdap_get_groups_state *state;
    1863             :     int ret;
    1864             :     int dp_error;
    1865             : 
    1866           0 :     req = tevent_req_callback_data(subreq, struct tevent_req);
    1867           0 :     state = tevent_req_data(req, struct sdap_get_groups_state);
    1868             : 
    1869           0 :     ret = sdap_id_op_connect_recv(subreq, &dp_error);
    1870           0 :     talloc_zfree(subreq);
    1871             : 
    1872           0 :     if (ret != EOK) {
    1873           0 :         tevent_req_error(req, ret);
    1874           0 :         return;
    1875             :     }
    1876             : 
    1877           0 :     state->ldap_sh = sdap_id_op_handle(state->op);
    1878             : 
    1879           0 :     ret = sdap_get_groups_next_base(req);
    1880           0 :     if (ret != EOK) {
    1881           0 :         tevent_req_error(req, ret);
    1882             :     }
    1883             : 
    1884           0 :     return;
    1885             : }
    1886             : 
    1887           0 : static errno_t sdap_get_groups_next_base(struct tevent_req *req)
    1888             : {
    1889             :     struct tevent_req *subreq;
    1890             :     struct sdap_get_groups_state *state;
    1891           0 :     bool need_paging = false;
    1892           0 :     int sizelimit = 0;
    1893             : 
    1894           0 :     state = tevent_req_data(req, struct sdap_get_groups_state);
    1895             : 
    1896           0 :     talloc_zfree(state->filter);
    1897           0 :     state->filter = sdap_combine_filters(state, state->base_filter,
    1898           0 :                         state->search_bases[state->base_iter]->filter);
    1899           0 :     if (!state->filter) {
    1900           0 :         return ENOMEM;
    1901             :     }
    1902             : 
    1903           0 :     DEBUG(SSSDBG_TRACE_FUNC,
    1904             :           "Searching for groups with base [%s]\n",
    1905             :            state->search_bases[state->base_iter]->basedn);
    1906             : 
    1907           0 :     switch (state->lookup_type) {
    1908             :     case SDAP_LOOKUP_SINGLE:
    1909           0 :         break;
    1910             :     /* Only requests that can return multiple entries should require
    1911             :      * the paging control
    1912             :      */
    1913             :     case SDAP_LOOKUP_WILDCARD:
    1914           0 :         sizelimit = dp_opt_get_int(state->opts->basic, SDAP_WILDCARD_LIMIT);
    1915           0 :         need_paging = true;
    1916           0 :         break;
    1917             :     case SDAP_LOOKUP_ENUMERATE:
    1918           0 :         need_paging = true;
    1919           0 :         break;
    1920             :     }
    1921             : 
    1922           0 :     subreq = sdap_get_and_parse_generic_send(
    1923             :             state, state->ev, state->opts,
    1924           0 :             state->ldap_sh != NULL ? state->ldap_sh : state->sh,
    1925           0 :             state->search_bases[state->base_iter]->basedn,
    1926           0 :             state->search_bases[state->base_iter]->scope,
    1927           0 :             state->filter, state->attrs,
    1928           0 :             state->opts->group_map, SDAP_OPTS_GROUP,
    1929             :             0, NULL, NULL, sizelimit, state->timeout,
    1930             :             need_paging);
    1931           0 :     if (!subreq) {
    1932           0 :         return ENOMEM;
    1933             :     }
    1934           0 :     tevent_req_set_callback(subreq, sdap_get_groups_process, req);
    1935             : 
    1936           0 :     return EOK;
    1937             : }
    1938             : 
    1939             : static void sdap_nested_done(struct tevent_req *req);
    1940             : static void sdap_search_group_copy_batch(struct sdap_get_groups_state *state,
    1941             :                                          struct sysdb_attrs **groups,
    1942             :                                          size_t count);
    1943             : static void sdap_ad_match_rule_members_process(struct tevent_req *subreq);
    1944             : 
    1945           0 : static void sdap_get_groups_process(struct tevent_req *subreq)
    1946             : {
    1947           0 :     struct tevent_req *req =
    1948           0 :                         tevent_req_callback_data(subreq, struct tevent_req);
    1949           0 :     struct sdap_get_groups_state *state =
    1950           0 :                         tevent_req_data(req, struct sdap_get_groups_state);
    1951             :     int ret;
    1952             :     int i;
    1953           0 :     bool next_base = false;
    1954             :     size_t count;
    1955             :     struct sysdb_attrs **groups;
    1956             :     char **groupnamelist;
    1957             : 
    1958           0 :     ret = sdap_get_and_parse_generic_recv(subreq, state,
    1959             :                                           &count, &groups);
    1960           0 :     talloc_zfree(subreq);
    1961           0 :     if (ret) {
    1962           0 :         tevent_req_error(req, ret);
    1963           0 :         return;
    1964             :     }
    1965             : 
    1966           0 :     DEBUG(SSSDBG_TRACE_FUNC,
    1967             :           "Search for groups, returned %zu results.\n", count);
    1968             : 
    1969           0 :     if (state->lookup_type == SDAP_LOOKUP_WILDCARD || \
    1970           0 :             state->lookup_type == SDAP_LOOKUP_ENUMERATE || \
    1971           0 :         count == 0) {
    1972             :         /* No users found in this search or looking up multiple entries */
    1973           0 :         next_base = true;
    1974             :     }
    1975             : 
    1976             :     /* Add this batch of groups to the list */
    1977           0 :     if (count > 0) {
    1978           0 :         state->groups =
    1979           0 :                 talloc_realloc(state,
    1980             :                                state->groups,
    1981             :                                struct sysdb_attrs *,
    1982             :                                state->count + count + 1);
    1983           0 :         if (!state->groups) {
    1984           0 :             tevent_req_error(req, ENOMEM);
    1985           0 :             return;
    1986             :         }
    1987             : 
    1988           0 :         sdap_search_group_copy_batch(state, groups, count);
    1989             :     }
    1990             : 
    1991           0 :     if (next_base) {
    1992           0 :         state->base_iter++;
    1993           0 :         if (state->search_bases[state->base_iter]) {
    1994             :             /* There are more search bases to try */
    1995           0 :             ret = sdap_get_groups_next_base(req);
    1996           0 :             if (ret != EOK) {
    1997           0 :                 tevent_req_error(req, ret);
    1998             :             }
    1999           0 :             return;
    2000             :         }
    2001             :     }
    2002             : 
    2003             :     /* No more search bases
    2004             :      * Return ENOENT if no groups were found
    2005             :      */
    2006           0 :     if (state->count == 0) {
    2007           0 :         tevent_req_error(req, ENOENT);
    2008           0 :         return;
    2009             :     }
    2010             : 
    2011           0 :     if (state->no_members) {
    2012           0 :         ret = sysdb_attrs_primary_name_list(state->sysdb, state,
    2013             :                                 state->groups, state->count,
    2014           0 :                                 state->opts->group_map[SDAP_AT_GROUP_NAME].name,
    2015             :                                 &groupnamelist);
    2016           0 :         if (ret != EOK) {
    2017           0 :             DEBUG(SSSDBG_OP_FAILURE,
    2018             :                   "sysdb_attrs_primary_name_list failed.\n");
    2019           0 :             tevent_req_error(req, ret);
    2020           0 :             return;
    2021             :         }
    2022             : 
    2023           0 :         ret = sdap_add_incomplete_groups(state->sysdb, state->dom, state->opts,
    2024             :                                          groupnamelist, state->groups,
    2025           0 :                                          state->count);
    2026           0 :         if (ret == EOK) {
    2027           0 :             DEBUG(SSSDBG_TRACE_LIBS,
    2028             :                   "Writing only group data without members was successful.\n");
    2029           0 :             tevent_req_done(req);
    2030             :         } else {
    2031           0 :             DEBUG(SSSDBG_OP_FAILURE, "sdap_add_incomplete_groups failed.\n");
    2032           0 :             tevent_req_error(req, ret);
    2033             :         }
    2034           0 :         return;
    2035             :     }
    2036             : 
    2037             :     /* Check whether we need to do nested searches
    2038             :      * for RFC2307bis/FreeIPA/ActiveDirectory
    2039             :      * We don't need to do this for enumeration,
    2040             :      * because all groups will be picked up anyway.
    2041             :      *
    2042             :      * We can also skip this if we're using the
    2043             :      * LDAP_MATCHING_RULE_IN_CHAIN available in
    2044             :      * AD 2008 and later
    2045             :      */
    2046           0 :     if (state->lookup_type == SDAP_LOOKUP_SINGLE) {
    2047           0 :         if ((state->opts->schema_type != SDAP_SCHEMA_RFC2307)
    2048           0 :                 && (dp_opt_get_int(state->opts->basic, SDAP_NESTING_LEVEL) != 0)
    2049           0 :                 && !dp_opt_get_bool(state->opts->basic, SDAP_AD_MATCHING_RULE_GROUPS)) {
    2050           0 :             subreq = sdap_nested_group_send(state, state->ev, state->sdom,
    2051             :                                             state->opts, state->sh,
    2052           0 :                                             state->groups[0]);
    2053           0 :             if (!subreq) {
    2054           0 :                 tevent_req_error(req, EIO);
    2055           0 :                 return;
    2056             :             }
    2057             : 
    2058           0 :             tevent_req_set_callback(subreq, sdap_nested_done, req);
    2059           0 :             return;
    2060             :         }
    2061             :     }
    2062             : 
    2063             :     /* We have all of the groups. Save them to the sysdb */
    2064           0 :     state->check_count = state->count;
    2065             : 
    2066             :     /* If we're using LDAP_MATCHING_RULE_IN_CHAIN, start a subreq to
    2067             :      * retrieve the members so we can save them in a single step.
    2068             :      */
    2069           0 :     if (state->lookup_type == SDAP_LOOKUP_SINGLE
    2070           0 :             && (state->opts->schema_type != SDAP_SCHEMA_RFC2307)
    2071           0 :             && state->opts->support_matching_rule
    2072           0 :             && dp_opt_get_bool(state->opts->basic, SDAP_AD_MATCHING_RULE_GROUPS)) {
    2073           0 :         subreq = sdap_get_ad_match_rule_members_send(
    2074             :                 state, state->ev, state->opts, state->sh,
    2075           0 :                 state->groups[0], state->timeout);
    2076           0 :         if (!subreq) {
    2077           0 :             tevent_req_error(req, ENOMEM);
    2078           0 :             return;
    2079             :         }
    2080           0 :         tevent_req_set_callback(subreq,
    2081             :                                 sdap_ad_match_rule_members_process,
    2082             :                                 req);
    2083           0 :         return;
    2084             :     }
    2085             : 
    2086           0 :     ret = sysdb_transaction_start(state->sysdb);
    2087           0 :     if (ret != EOK) {
    2088           0 :         DEBUG(SSSDBG_FATAL_FAILURE, "Failed to start transaction\n");
    2089           0 :         tevent_req_error(req, ret);
    2090           0 :         return;
    2091             :     }
    2092             : 
    2093           0 :     if ((state->lookup_type == SDAP_LOOKUP_ENUMERATE
    2094           0 :                 || state->lookup_type == SDAP_LOOKUP_WILDCARD)
    2095           0 :             && state->opts->schema_type != SDAP_SCHEMA_RFC2307
    2096           0 :             && dp_opt_get_int(state->opts->basic, SDAP_NESTING_LEVEL) != 0) {
    2097           0 :         DEBUG(SSSDBG_TRACE_ALL, "Saving groups without members first "
    2098             :                   "to allow unrolling of nested groups.\n");
    2099           0 :         ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
    2100           0 :                                state->groups, state->count, false,
    2101             :                                NULL, true, NULL);
    2102           0 :         if (ret) {
    2103           0 :             DEBUG(SSSDBG_OP_FAILURE, "Failed to store groups.\n");
    2104           0 :             tevent_req_error(req, ret);
    2105           0 :             return;
    2106             :         }
    2107             :     }
    2108             : 
    2109           0 :     for (i = 0; i < state->count; i++) {
    2110           0 :         subreq = sdap_process_group_send(state, state->ev, state->dom,
    2111             :                                          state->sysdb, state->opts,
    2112           0 :                                          state->sh, state->groups[i],
    2113           0 :                                          state->lookup_type == SDAP_LOOKUP_ENUMERATE);
    2114             : 
    2115           0 :         if (!subreq) {
    2116           0 :             tevent_req_error(req, ENOMEM);
    2117           0 :             return;
    2118             :         }
    2119           0 :         tevent_req_set_callback(subreq, sdap_get_groups_done, req);
    2120             :     }
    2121             : }
    2122             : 
    2123           0 : static void sdap_search_group_copy_batch(struct sdap_get_groups_state *state,
    2124             :                                          struct sysdb_attrs **groups,
    2125             :                                          size_t count)
    2126             : {
    2127             :     size_t copied;
    2128             :     bool filter;
    2129             : 
    2130             :     /* Always copy all objects for wildcard lookups. */
    2131           0 :     filter = state->lookup_type == SDAP_LOOKUP_SINGLE ? true : false;
    2132             : 
    2133           0 :     copied = sdap_steal_objects_in_dom(state->opts,
    2134             :                                        state->groups,
    2135             :                                        state->count,
    2136             :                                        state->dom,
    2137             :                                        groups, count, filter);
    2138             : 
    2139           0 :     state->count += copied;
    2140           0 :     state->groups[state->count] = NULL;
    2141           0 : }
    2142             : 
    2143           0 : static void sdap_get_groups_done(struct tevent_req *subreq)
    2144             : {
    2145           0 :     struct tevent_req *req =
    2146           0 :                         tevent_req_callback_data(subreq, struct tevent_req);
    2147           0 :     struct sdap_get_groups_state *state =
    2148           0 :                         tevent_req_data(req, struct sdap_get_groups_state);
    2149             : 
    2150             :     int ret;
    2151             :     errno_t sysret;
    2152             : 
    2153           0 :     ret = sdap_process_group_recv(subreq);
    2154           0 :     talloc_zfree(subreq);
    2155           0 :     if (ret) {
    2156           0 :         sysret = sysdb_transaction_cancel(state->sysdb);
    2157           0 :         if (sysret != EOK) {
    2158           0 :             DEBUG(SSSDBG_FATAL_FAILURE, "Could not cancel sysdb transaction\n");
    2159             :         }
    2160           0 :         tevent_req_error(req, ret);
    2161           0 :         return;
    2162             :     }
    2163             : 
    2164           0 :     state->check_count--;
    2165           0 :     DEBUG(SSSDBG_TRACE_ALL, "Groups remaining: %zu\n", state->check_count);
    2166             : 
    2167             : 
    2168           0 :     if (state->check_count == 0) {
    2169           0 :         DEBUG(SSSDBG_TRACE_ALL, "All groups processed\n");
    2170             : 
    2171             :         /* If ignore_group_members is set for the domain, don't update
    2172             :          * group memberships in the cache.
    2173             :          *
    2174             :          * If enumeration is on, don't overwrite orig_members as they've been
    2175             :          * saved earlier.
    2176             :          */
    2177           0 :         ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
    2178           0 :                                state->groups, state->count,
    2179           0 :                                !state->dom->ignore_group_members, NULL,
    2180           0 :                                state->lookup_type == SDAP_LOOKUP_SINGLE,
    2181           0 :                                &state->higher_usn);
    2182           0 :         if (ret) {
    2183           0 :             DEBUG(SSSDBG_OP_FAILURE, "Failed to store groups.\n");
    2184           0 :             tevent_req_error(req, ret);
    2185           0 :             return;
    2186             :         }
    2187           0 :         DEBUG(SSSDBG_TRACE_ALL, "Saving %zu Groups - Done\n", state->count);
    2188           0 :         sysret = sysdb_transaction_commit(state->sysdb);
    2189           0 :         if (sysret != EOK) {
    2190           0 :             DEBUG(SSSDBG_FATAL_FAILURE, "Couldn't commit transaction\n");
    2191           0 :             tevent_req_error(req, sysret);
    2192             :         } else {
    2193           0 :             tevent_req_done(req);
    2194             :         }
    2195             :     }
    2196             : }
    2197             : 
    2198             : static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx,
    2199             :                                                 struct sysdb_ctx *sysdb,
    2200             :                                                 struct sss_domain_info *domain,
    2201             :                                                 struct sdap_options *opts,
    2202             :                                                 struct sysdb_attrs **users,
    2203             :                                                 int num_users,
    2204             :                                                 hash_table_t **_ghosts);
    2205             : 
    2206           0 : static void sdap_ad_match_rule_members_process(struct tevent_req *subreq)
    2207             : {
    2208             :     errno_t ret;
    2209           0 :     TALLOC_CTX *tmp_ctx = NULL;
    2210           0 :     struct tevent_req *req =
    2211           0 :             tevent_req_callback_data(subreq, struct tevent_req);
    2212           0 :     struct sdap_get_groups_state *state = tevent_req_data(req,
    2213             :                                             struct sdap_get_groups_state);
    2214             :     struct sysdb_attrs **users;
    2215           0 :     struct sysdb_attrs *group = state->groups[0];
    2216             :     struct ldb_message_element *member_el;
    2217             :     struct ldb_message_element *orig_dn_el;
    2218           0 :     size_t count = 0;
    2219             :     size_t i;
    2220             :     hash_table_t *ghosts;
    2221             : 
    2222           0 :     ret = sdap_get_ad_match_rule_members_recv(subreq, state,
    2223             :                                               &count, &users);
    2224           0 :     talloc_zfree(subreq);
    2225           0 :     if (ret != EOK && ret != ENOENT) {
    2226           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
    2227             :               "Could not retrieve members using AD match rule. [%s]\n",
    2228             :                strerror(ret));
    2229             : 
    2230           0 :         goto done;
    2231             :     }
    2232             : 
    2233             :     /* Save the group and users to the cache */
    2234             : 
    2235             :     /* Truncate the member attribute of the group.
    2236             :      * It will be repopulated below, and it may currently
    2237             :      * be incomplete anyway, thanks to the range extension.
    2238             :      */
    2239             : 
    2240           0 :     ret = sysdb_attrs_get_el(group, SYSDB_MEMBER, &member_el);
    2241           0 :     if (ret != EOK) {
    2242           0 :         goto done;
    2243             :     }
    2244             : 
    2245           0 :     member_el->num_values = 0;
    2246           0 :     talloc_zfree(member_el->values);
    2247             : 
    2248           0 :     tmp_ctx = talloc_new(NULL);
    2249           0 :     if (!tmp_ctx) {
    2250           0 :         ret = ENOMEM;
    2251           0 :         goto done;
    2252             :     }
    2253             : 
    2254             :     /* Figure out which users are already cached in the sysdb and
    2255             :      * which ones need to be added as ghost users.
    2256             :      */
    2257           0 :     ret = sdap_nested_group_populate_users(tmp_ctx, state->sysdb, state->dom,
    2258             :                                            state->opts, users, count,
    2259             :                                            &ghosts);
    2260           0 :     if (ret != EOK) {
    2261           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
    2262             :               "Could not determine which users are ghosts: [%s]\n",
    2263             :                strerror(ret));
    2264           0 :         goto done;
    2265             :     }
    2266             : 
    2267             :     /* Add any entries that aren't in the ghost hash table to the
    2268             :      * member element of the group. This will get converted to a
    2269             :      * native sysdb representation later in sdap_save_groups().
    2270             :      */
    2271             : 
    2272             :     /* Add all of the users as members
    2273             :      */
    2274           0 :     member_el->values = talloc_zero_array(tmp_ctx, struct ldb_val, count);
    2275           0 :     if (!member_el->values) {
    2276           0 :         ret = ENOMEM;
    2277           0 :         goto done;
    2278             :     }
    2279             : 
    2280             :     /* Copy the origDN values of the users into the member element */
    2281           0 :     for (i = 0; i < count; i++) {
    2282           0 :         ret = sysdb_attrs_get_el(users[i], SYSDB_ORIG_DN,
    2283             :                                  &orig_dn_el);
    2284           0 :         if (ret != EOK) {
    2285             :             /* This should never happen. Every entry should have
    2286             :              * an originalDN.
    2287             :              */
    2288           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
    2289             :                   "BUG: Missing originalDN for user?\n");
    2290           0 :             goto done;
    2291             :         }
    2292             : 
    2293             :         /* These values will have the same lifespan, so instead
    2294             :          * of copying them, just point at the data.
    2295             :          */
    2296           0 :         member_el->values[i].data = orig_dn_el->values[0].data;
    2297           0 :         member_el->values[i].length = orig_dn_el->values[0].length;
    2298             :     }
    2299           0 :     member_el->num_values = count;
    2300             : 
    2301             :     /* Now save the group, users and ghosts to the cache */
    2302           0 :     ret = sdap_save_groups(tmp_ctx, state->sysdb, state->dom,
    2303             :                            state->opts, state->groups, 1,
    2304             :                            false, ghosts, true, NULL);
    2305           0 :     if (ret != EOK) {
    2306           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
    2307             :               "Could not save group to the cache: [%s]\n",
    2308             :                strerror(ret));
    2309           0 :         goto done;
    2310             :     }
    2311             : 
    2312           0 :     ret = EOK;
    2313             : 
    2314             : done:
    2315           0 :     talloc_free(tmp_ctx);
    2316             : 
    2317           0 :     if (ret == EOK) {
    2318           0 :         tevent_req_done(req);
    2319             :     } else {
    2320           0 :         tevent_req_error(req, ret);
    2321             :     }
    2322           0 : }
    2323             : 
    2324           0 : int sdap_get_groups_recv(struct tevent_req *req,
    2325             :                          TALLOC_CTX *mem_ctx, char **usn_value)
    2326             : {
    2327           0 :     struct sdap_get_groups_state *state = tevent_req_data(req,
    2328             :                                             struct sdap_get_groups_state);
    2329             : 
    2330           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    2331             : 
    2332           0 :     if (usn_value) {
    2333           0 :         *usn_value = talloc_steal(mem_ctx, state->higher_usn);
    2334             :     }
    2335             : 
    2336           0 :     return EOK;
    2337             : }
    2338             : 
    2339             : static void sdap_nested_ext_done(struct tevent_req *subreq);
    2340             : 
    2341           0 : static void sdap_nested_done(struct tevent_req *subreq)
    2342             : {
    2343             :     errno_t ret, tret;
    2344             :     unsigned long user_count;
    2345             :     unsigned long group_count;
    2346           0 :     bool in_transaction = false;
    2347           0 :     struct sysdb_attrs **users = NULL;
    2348           0 :     struct sysdb_attrs **groups = NULL;
    2349             :     hash_table_t *ghosts;
    2350           0 :     struct tevent_req *req = tevent_req_callback_data(subreq,
    2351             :                                                       struct tevent_req);
    2352           0 :     struct sdap_get_groups_state *state = tevent_req_data(req,
    2353             :                                             struct sdap_get_groups_state);
    2354             : 
    2355           0 :     ret = sdap_nested_group_recv(state, subreq, &user_count, &users,
    2356             :                                  &group_count, &groups,
    2357             :                                  &state->missing_external);
    2358           0 :     talloc_zfree(subreq);
    2359           0 :     if (ret != EOK) {
    2360           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Nested group processing failed: [%d][%s]\n",
    2361             :                   ret, strerror(ret));
    2362           0 :         goto fail;
    2363             :     }
    2364             : 
    2365             :     /* Save all of the users first so that they are in
    2366             :      * place for the groups to add them.
    2367             :      */
    2368           0 :     ret = sysdb_transaction_start(state->sysdb);
    2369           0 :     if (ret != EOK) {
    2370           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
    2371           0 :         goto fail;
    2372             :     }
    2373           0 :     in_transaction = true;
    2374             : 
    2375             :     PROBE(SDAP_NESTED_GROUP_POPULATE_PRE);
    2376           0 :     ret = sdap_nested_group_populate_users(state, state->sysdb,
    2377             :                                            state->dom, state->opts,
    2378             :                                            users, user_count, &ghosts);
    2379             :     PROBE(SDAP_NESTED_GROUP_POPULATE_POST);
    2380           0 :     if (ret != EOK) {
    2381           0 :         goto fail;
    2382             :     }
    2383             : 
    2384             :     PROBE(SDAP_NESTED_GROUP_SAVE_PRE);
    2385           0 :     ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
    2386             :                            groups, group_count, false, ghosts, true,
    2387             :                            &state->higher_usn);
    2388             :     PROBE(SDAP_NESTED_GROUP_SAVE_POST);
    2389           0 :     if (ret != EOK) {
    2390           0 :         goto fail;
    2391             :     }
    2392             : 
    2393           0 :     ret = sysdb_transaction_commit(state->sysdb);
    2394           0 :     if (ret != EOK) {
    2395           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
    2396           0 :         goto fail;
    2397             :     }
    2398           0 :     in_transaction = false;
    2399             : 
    2400           0 :     if (hash_count(state->missing_external) == 0) {
    2401             :         /* No external members. Processing complete */
    2402           0 :         DEBUG(SSSDBG_TRACE_INTERNAL, "No external members, done");
    2403           0 :         tevent_req_done(req);
    2404           0 :         return;
    2405             :     }
    2406             : 
    2407             :     /* At the moment, we need to save the direct groups & members in one
    2408             :      * transaction and then query the others in a separate requests
    2409             :      */
    2410           0 :     subreq = sdap_nested_group_lookup_external_send(state, state->ev,
    2411             :                                                     state->dom,
    2412           0 :                                                     state->opts->ext_ctx,
    2413             :                                                     state->missing_external);
    2414           0 :     if (subreq == NULL) {
    2415           0 :         ret = ENOMEM;
    2416           0 :         goto fail;
    2417             :     }
    2418           0 :     tevent_req_set_callback(subreq, sdap_nested_ext_done, req);
    2419           0 :     return;
    2420             : 
    2421             : fail:
    2422           0 :     if (in_transaction) {
    2423           0 :         tret = sysdb_transaction_cancel(state->sysdb);
    2424           0 :         if (tret != EOK) {
    2425           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
    2426             :         }
    2427             :     }
    2428           0 :     tevent_req_error(req, ret);
    2429             : }
    2430             : 
    2431           0 : static void sdap_nested_ext_done(struct tevent_req *subreq)
    2432             : {
    2433             :     errno_t ret;
    2434           0 :     struct tevent_req *req = tevent_req_callback_data(subreq,
    2435             :                                                       struct tevent_req);
    2436           0 :     struct sdap_get_groups_state *state = tevent_req_data(req,
    2437             :                                             struct sdap_get_groups_state);
    2438             : 
    2439           0 :     ret = sdap_nested_group_lookup_external_recv(state, subreq);
    2440           0 :     talloc_free(subreq);
    2441           0 :     if (ret != EOK) {
    2442           0 :         DEBUG(SSSDBG_OP_FAILURE,
    2443             :               "Cannot resolve external members [%d]: %s\n",
    2444             :               ret, sss_strerror(ret));
    2445           0 :         tevent_req_error(req, ret);
    2446           0 :         return;
    2447             :     }
    2448             : 
    2449           0 :     tevent_req_done(req);
    2450           0 :     return;
    2451             : }
    2452             : 
    2453           0 : static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx,
    2454             :                                                 struct sysdb_ctx *sysdb,
    2455             :                                                 struct sss_domain_info *domain,
    2456             :                                                 struct sdap_options *opts,
    2457             :                                                 struct sysdb_attrs **users,
    2458             :                                                 int num_users,
    2459             :                                                 hash_table_t **_ghosts)
    2460             : {
    2461             :     int i;
    2462             :     errno_t ret, sret;
    2463             :     struct ldb_message_element *el;
    2464             :     const char *username;
    2465             :     char *clean_orig_dn;
    2466             :     const char *original_dn;
    2467             :     struct sss_domain_info *user_dom;
    2468             :     struct sdap_domain *sdap_dom;
    2469             : 
    2470             :     TALLOC_CTX *tmp_ctx;
    2471             :     struct ldb_message **msgs;
    2472             :     char *filter;
    2473             :     const char *sysdb_name;
    2474             :     struct sysdb_attrs *attrs;
    2475             :     static const char *search_attrs[] = { SYSDB_NAME, NULL };
    2476             :     hash_table_t *ghosts;
    2477             :     hash_key_t key;
    2478             :     hash_value_t value;
    2479             :     size_t count;
    2480           0 :     bool in_transaction = false;
    2481             : 
    2482           0 :     if (_ghosts == NULL) {
    2483           0 :         return EINVAL;
    2484             :     }
    2485             : 
    2486           0 :     if (num_users == 0) {
    2487             :         /* Nothing to do if there are no users */
    2488           0 :         *_ghosts = NULL;
    2489           0 :         return EOK;
    2490             :     }
    2491             : 
    2492           0 :     tmp_ctx = talloc_new(NULL);
    2493           0 :     if (!tmp_ctx) return ENOMEM;
    2494             : 
    2495           0 :     ret = sss_hash_create(tmp_ctx, num_users, &ghosts);
    2496           0 :     if (ret != HASH_SUCCESS) {
    2497           0 :         ret = ENOMEM;
    2498           0 :         goto done;
    2499             :     }
    2500             : 
    2501           0 :     ret = sysdb_transaction_start(sysdb);
    2502           0 :     if (ret) {
    2503           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction!\n");
    2504           0 :         goto done;
    2505             :     }
    2506           0 :     in_transaction = true;
    2507             : 
    2508           0 :     for (i = 0; i < num_users; i++) {
    2509           0 :         ret = sysdb_attrs_get_el(users[i], SYSDB_ORIG_DN, &el);
    2510           0 :         if (el->num_values == 0) {
    2511           0 :             ret = EINVAL;
    2512             :         }
    2513           0 :         if (ret != EOK) {
    2514           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    2515             :                   "User entry %d has no originalDN attribute\n", i);
    2516           0 :             goto done;
    2517             :         }
    2518           0 :         original_dn = (const char *) el->values[0].data;
    2519             : 
    2520           0 :         ret = sss_filter_sanitize(tmp_ctx, original_dn,
    2521             :                                   &clean_orig_dn);
    2522           0 :         if (ret != EOK) {
    2523           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    2524             :                   "Cannot sanitize originalDN [%s]\n", original_dn);
    2525           0 :             goto done;
    2526             :         }
    2527             : 
    2528           0 :         sdap_dom = sdap_domain_get_by_dn(opts, original_dn);
    2529           0 :         user_dom = sdap_dom == NULL ? domain : sdap_dom->dom;
    2530             : 
    2531           0 :         ret = sdap_get_user_primary_name(tmp_ctx, opts, users[i],
    2532             :                                          user_dom, &username);
    2533           0 :         if (ret != EOK) {
    2534           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
    2535             :                   "User entry %d has no name attribute. Skipping\n", i);
    2536           0 :             continue;
    2537             :         }
    2538             : 
    2539             :         /* Check for the specified origDN in the sysdb */
    2540           0 :         filter = talloc_asprintf(tmp_ctx, "(%s=%s)",
    2541             :                                  SYSDB_ORIG_DN,
    2542             :                                  clean_orig_dn);
    2543           0 :         if (!filter) {
    2544           0 :             ret = ENOMEM;
    2545           0 :             goto done;
    2546             :         }
    2547             :         PROBE(SDAP_NESTED_GROUP_POPULATE_SEARCH_USERS_PRE);
    2548           0 :         ret = sysdb_search_users(tmp_ctx, user_dom, filter,
    2549             :                                  search_attrs, &count, &msgs);
    2550             :         PROBE(SDAP_NESTED_GROUP_POPULATE_SEARCH_USERS_POST);
    2551           0 :         talloc_zfree(filter);
    2552           0 :         talloc_zfree(clean_orig_dn);
    2553           0 :         if (ret != EOK && ret != ENOENT) {
    2554           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Error checking cache for user entry\n");
    2555           0 :             goto done;
    2556           0 :         } else if (ret == EOK) {
    2557             :             /* The entry is cached but expired. Update the username
    2558             :              * if needed. */
    2559           0 :             if (count != 1) {
    2560           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    2561             :                       "More than one entry with this origDN? Skipping\n");
    2562           0 :                 continue;
    2563             :             }
    2564             : 
    2565           0 :             sysdb_name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL);
    2566           0 :             if (strcmp(sysdb_name, username) == 0) {
    2567             :                 /* Username is correct, continue */
    2568           0 :                 continue;
    2569             :             }
    2570             : 
    2571           0 :             attrs = sysdb_new_attrs(tmp_ctx);
    2572           0 :             if (!attrs) {
    2573           0 :                 ret = ENOMEM;
    2574           0 :                 goto done;
    2575             :             }
    2576             : 
    2577           0 :             ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, username);
    2578           0 :             if (ret) goto done;
    2579           0 :             ret = sysdb_set_entry_attr(user_dom->sysdb, msgs[0]->dn, attrs,
    2580             :                                        SYSDB_MOD_REP);
    2581           0 :             if (ret != EOK) goto done;
    2582             :         } else {
    2583           0 :             key.type = HASH_KEY_STRING;
    2584           0 :             key.str = talloc_steal(ghosts, discard_const(original_dn));
    2585           0 :             value.type = HASH_VALUE_PTR;
    2586           0 :             value.ptr = talloc_steal(ghosts, discard_const(username));
    2587           0 :             ret = hash_enter(ghosts, &key, &value);
    2588           0 :             if (ret != HASH_SUCCESS) {
    2589           0 :                 talloc_free(key.str);
    2590           0 :                 talloc_free(value.ptr);
    2591           0 :                 ret = ENOMEM;
    2592           0 :                 goto done;
    2593             :             }
    2594             :         }
    2595             :     }
    2596             : 
    2597           0 :     ret = sysdb_transaction_commit(sysdb);
    2598           0 :     if (ret) {
    2599           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction!\n");
    2600           0 :         goto done;
    2601             :     }
    2602           0 :     in_transaction = false;
    2603             : 
    2604           0 :     ret = EOK;
    2605             : done:
    2606           0 :     if (in_transaction) {
    2607           0 :         sret = sysdb_transaction_cancel(sysdb);
    2608           0 :         if (sret != EOK) {
    2609           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
    2610             :         }
    2611             :     }
    2612             : 
    2613           0 :     if (ret != EOK) {
    2614           0 :         *_ghosts = NULL;
    2615             :     } else {
    2616           0 :         *_ghosts = talloc_steal(mem_ctx, ghosts);
    2617             :     }
    2618           0 :     talloc_zfree(tmp_ctx);
    2619           0 :     return ret;
    2620             : }

Generated by: LCOV version 1.10