LCOV - code coverage report
Current view: top level - responder/nss - nsssrv_cmd.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 1105 2572 43.0 %
Date: 2015-10-19 Functions: 49 85 57.6 %

          Line data    Source code
       1             : /*
       2             :    SSSD
       3             : 
       4             :    NSS Responder
       5             : 
       6             :    Copyright (C) Simo Sorce <ssorce@redhat.com>   2008
       7             : 
       8             :    This program is free software; you can redistribute it and/or modify
       9             :    it under the terms of the GNU General Public License as published by
      10             :    the Free Software Foundation; either version 3 of the License, or
      11             :    (at your option) any later version.
      12             : 
      13             :    This program is distributed in the hope that it will be useful,
      14             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :    GNU General Public License for more details.
      17             : 
      18             :    You should have received a copy of the GNU General Public License
      19             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             : */
      21             : 
      22             : #include "util/util.h"
      23             : #include "util/sss_nss.h"
      24             : #include "util/sss_cli_cmd.h"
      25             : #include "responder/nss/nsssrv.h"
      26             : #include "responder/nss/nsssrv_private.h"
      27             : #include "responder/nss/nsssrv_netgroup.h"
      28             : #include "responder/nss/nsssrv_services.h"
      29             : #include "responder/nss/nsssrv_mmap_cache.h"
      30             : #include "responder/common/negcache.h"
      31             : #include "providers/data_provider.h"
      32             : #include "confdb/confdb.h"
      33             : #include "db/sysdb.h"
      34             : #include "sss_client/idmap/sss_nss_idmap.h"
      35             : #include <time.h>
      36             : 
      37           4 : static int nss_cmd_send_error(struct nss_cmd_ctx *cmdctx, int err)
      38             : {
      39           4 :     return sss_cmd_send_error(cmdctx->cctx, err);
      40             : }
      41             : 
      42          12 : static int nss_cmd_send_empty(struct nss_cmd_ctx *cmdctx)
      43             : {
      44          12 :     struct cli_ctx *cctx = cmdctx->cctx;
      45          12 :     return sss_cmd_send_empty(cctx, cmdctx);
      46             : }
      47             : 
      48         105 : int nss_cmd_done(struct nss_cmd_ctx *cmdctx, int ret)
      49             : {
      50         105 :     switch (ret) {
      51             :     case EOK:
      52             :         /* all fine, just return here */
      53          37 :         break;
      54             : 
      55             :     case ENOENT:
      56          12 :         ret = nss_cmd_send_empty(cmdctx);
      57          12 :         if (ret) {
      58           0 :             return EFAULT;
      59             :         }
      60          12 :         break;
      61             : 
      62             :     case EAGAIN:
      63             :         /* async processing, just return here */
      64          52 :         break;
      65             : 
      66             :     case EFAULT:
      67             :         /* very bad error */
      68           0 :         return EFAULT;
      69             : 
      70             :     default:
      71           4 :         ret = nss_cmd_send_error(cmdctx, ret);
      72           4 :         if (ret) {
      73           0 :             return EFAULT;
      74             :         }
      75           4 :         sss_cmd_done(cmdctx->cctx, cmdctx);
      76           4 :         break;
      77             :     }
      78             : 
      79         105 :     return EOK;
      80             : }
      81             : 
      82             : /***************************
      83             :  *  Enumeration procedures *
      84             :  ***************************/
      85           0 : errno_t nss_setent_add_ref(TALLOC_CTX *memctx,
      86             :                            struct getent_ctx *getent_ctx,
      87             :                            struct tevent_req *req)
      88             : {
      89           0 :     return setent_add_ref(memctx, getent_ctx, &getent_ctx->reqs, req);
      90             : }
      91             : 
      92           0 : void nss_setent_notify_error(struct getent_ctx *getent_ctx, errno_t ret)
      93             : {
      94           0 :     return setent_notify(&getent_ctx->reqs, ret);
      95             : }
      96             : 
      97           0 : void nss_setent_notify_done(struct getent_ctx *getent_ctx)
      98             : {
      99           0 :     return setent_notify_done(&getent_ctx->reqs);
     100             : }
     101             : 
     102             : struct setent_ctx {
     103             :     struct cli_ctx *client;
     104             :     struct nss_ctx *nctx;
     105             :     struct nss_dom_ctx *dctx;
     106             :     struct getent_ctx *getent_ctx;
     107             : };
     108             : 
     109          32 : static int nss_reset_negcache(struct resp_ctx *rctx)
     110             : {
     111             :     struct nss_ctx *nss_ctx;
     112             : 
     113          32 :     nss_ctx = talloc_get_type(rctx->pvt_ctx, struct nss_ctx);
     114          32 :     if (nss_ctx == NULL) {
     115           0 :         return EIO;
     116             :     }
     117             : 
     118          32 :     return sss_ncache_reset_repopulate_permanent(rctx, nss_ctx->ncache);
     119             : }
     120             : 
     121             : /****************************************************************************
     122             :  * PASSWD db related functions
     123             :  ***************************************************************************/
     124             : 
     125           0 : void nss_update_pw_memcache(struct nss_ctx *nctx)
     126             : {
     127             :     struct sss_domain_info *dom;
     128             :     struct ldb_result *res;
     129             :     uint64_t exp;
     130             :     struct sized_string key;
     131             :     const char *id;
     132             :     time_t now;
     133             :     int ret;
     134             :     int i;
     135             : 
     136           0 :     now = time(NULL);
     137             : 
     138           0 :     for (dom = nctx->rctx->domains; dom; dom = get_next_domain(dom, false)) {
     139           0 :         ret = sysdb_enumpwent_with_views(nctx, dom, &res);
     140           0 :         if (ret != EOK) {
     141           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     142             :                   "Failed to enumerate users for domain [%s]\n", dom->name);
     143           0 :             continue;
     144             :         }
     145             : 
     146           0 :         for (i = 0; i < res->count; i++) {
     147           0 :             exp = ldb_msg_find_attr_as_uint64(res->msgs[i],
     148             :                                               SYSDB_CACHE_EXPIRE, 0);
     149           0 :             if (exp >= now) {
     150           0 :                 continue;
     151             :             }
     152             : 
     153             :             /* names require more manipulation (build up fqname conditionally),
     154             :              * but uidNumber is unique and always resolvable too, so we use
     155             :              * that to update the cache, as it points to the same entry */
     156           0 :             id = sss_view_ldb_msg_find_attr_as_string(dom, res->msgs[i],
     157             :                                                       SYSDB_UIDNUM, NULL);
     158           0 :             if (!id) {
     159           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     160             :                       "Failed to find uidNumber in %s.\n",
     161             :                        ldb_dn_get_linearized(res->msgs[i]->dn));
     162           0 :                 continue;
     163             :             }
     164           0 :             to_sized_string(&key, id);
     165             : 
     166           0 :             ret = sss_mmap_cache_pw_invalidate(nctx->pwd_mc_ctx, &key);
     167           0 :             if (ret != EOK && ret != ENOENT) {
     168           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     169             :                       "Internal failure in memory cache code: %d [%s]\n",
     170             :                        ret, strerror(ret));
     171             :             }
     172             : 
     173           0 :             ret = sss_mmap_cache_pw_invalidate(nctx->initgr_mc_ctx, &key);
     174           0 :             if (ret != EOK && ret != ENOENT) {
     175           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     176             :                       "Internal failure in memory cache code: %d [%s]\n",
     177             :                       ret, strerror(ret));
     178             :             }
     179             :         }
     180             : 
     181           0 :         talloc_zfree(res);
     182             :     }
     183           0 : }
     184             : 
     185          12 : static gid_t get_gid_override(struct ldb_message *msg,
     186             :                               struct sss_domain_info *dom)
     187             : {
     188          24 :     return dom->override_gid ?
     189             :         dom->override_gid :
     190          12 :         ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
     191             : }
     192             : 
     193          12 : static const char *get_homedir_override(TALLOC_CTX *mem_ctx,
     194             :                                         struct ldb_message *msg,
     195             :                                         struct nss_ctx *nctx,
     196             :                                         struct sss_domain_info *dom,
     197             :                                         struct sss_nss_homedir_ctx *homedir_ctx)
     198             : {
     199             :     const char *homedir;
     200          12 :     const char *orig_name = homedir_ctx->username;
     201             :     errno_t ret;
     202             : 
     203          12 :     homedir = sss_view_ldb_msg_find_attr_as_string(dom, msg, SYSDB_HOMEDIR,
     204             :                                                    NULL);
     205          12 :     homedir_ctx->original = homedir;
     206             : 
     207             :     /* Subdomain users store FQDN in their name attribute */
     208          12 :     ret = sss_parse_name_const(mem_ctx, dom->names, orig_name,
     209             :                                NULL, &homedir_ctx->username);
     210          12 :     if (ret != EOK) {
     211           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "Could not parse [%s] into "
     212             :               "name-value components.\n", orig_name);
     213           0 :         return NULL;
     214             :     }
     215             : 
     216             :     /* Check to see which homedir_prefix to use. */
     217          12 :     if (dom->homedir_substr != NULL) {
     218           0 :         homedir_ctx->config_homedir_substr = dom->homedir_substr;
     219          12 :     } else if (nctx->homedir_substr != NULL) {
     220           0 :         homedir_ctx->config_homedir_substr = nctx->homedir_substr;
     221             :     }
     222             : 
     223             :     /* Check whether we are unconditionally overriding the server
     224             :      * for home directory locations.
     225             :      */
     226          12 :     if (dom->override_homedir) {
     227           0 :         return expand_homedir_template(mem_ctx, dom->override_homedir,
     228             :                                        homedir_ctx);
     229          12 :     } else if (nctx->override_homedir) {
     230           0 :         return expand_homedir_template(mem_ctx, nctx->override_homedir,
     231             :                                        homedir_ctx);
     232             :     }
     233             : 
     234          12 :     if (!homedir || *homedir == '\0') {
     235             :         /* In the case of a NULL or empty homedir, check to see if
     236             :          * we have a fallback homedir to use.
     237             :          */
     238           0 :         if (dom->fallback_homedir) {
     239           0 :             return expand_homedir_template(mem_ctx, dom->fallback_homedir,
     240             :                                            homedir_ctx);
     241           0 :         } else if (nctx->fallback_homedir) {
     242           0 :             return expand_homedir_template(mem_ctx, nctx->fallback_homedir,
     243             :                                            homedir_ctx);
     244             :         }
     245             :     }
     246             : 
     247             :     /* Provider can also return template, try to expand it.*/
     248          12 :     return expand_homedir_template(mem_ctx, homedir, homedir_ctx);
     249             : }
     250             : 
     251          12 : static const char *get_shell_override(TALLOC_CTX *mem_ctx,
     252             :                                       struct ldb_message *msg,
     253             :                                       struct nss_ctx *nctx,
     254             :                                       struct sss_domain_info *dom)
     255             : {
     256             :     const char *user_shell;
     257             :     int i;
     258             : 
     259             :     /* Check whether we are unconditionally overriding the server
     260             :      * for the login shell.
     261             :      */
     262          12 :     if (dom->override_shell) {
     263           0 :         return dom->override_shell;
     264          12 :     } else if (nctx->override_shell) {
     265           0 :         return nctx->override_shell;
     266             :     }
     267             : 
     268          12 :     user_shell = sss_view_ldb_msg_find_attr_as_string(dom, msg, SYSDB_SHELL,
     269             :                                                       NULL);
     270          12 :     if (!user_shell) {
     271             :         /* Check whether there is a default shell specified */
     272           0 :         if (dom->default_shell) {
     273           0 :             return talloc_strdup(mem_ctx, dom->default_shell);
     274           0 :         } else if (nctx->default_shell) {
     275           0 :             return talloc_strdup(mem_ctx, nctx->default_shell);
     276             :         }
     277           0 :         return NULL;
     278             :     }
     279          12 :     if (!nctx->allowed_shells && !nctx->vetoed_shells) return talloc_strdup(mem_ctx, user_shell);
     280             : 
     281           0 :     if (nctx->vetoed_shells) {
     282           0 :         for (i=0; nctx->vetoed_shells[i]; i++) {
     283           0 :             if (strcmp(nctx->vetoed_shells[i], user_shell) == 0) {
     284           0 :                 DEBUG(SSSDBG_FUNC_DATA, "The shell '%s' is vetoed. "
     285             :                          "Using fallback\n", user_shell);
     286           0 :                 return talloc_strdup(mem_ctx, nctx->shell_fallback);
     287             :             }
     288             :         }
     289             :     }
     290             : 
     291           0 :     if (nctx->etc_shells) {
     292           0 :         for (i=0; nctx->etc_shells[i]; i++) {
     293           0 :             if (strcmp(user_shell, nctx->etc_shells[i]) == 0) {
     294           0 :                 DEBUG(SSSDBG_TRACE_ALL, "Shell %s found in /etc/shells\n",
     295             :                         nctx->etc_shells[i]);
     296           0 :                 break;
     297             :             }
     298             :         }
     299             : 
     300           0 :         if (nctx->etc_shells[i]) {
     301           0 :             DEBUG(SSSDBG_TRACE_ALL, "Using original shell '%s'\n", user_shell);
     302           0 :             return talloc_strdup(mem_ctx, user_shell);
     303             :         }
     304             :     }
     305             : 
     306           0 :     if (nctx->allowed_shells) {
     307           0 :         if (strcmp(nctx->allowed_shells[0], "*") == 0) {
     308           0 :             DEBUG(SSSDBG_FUNC_DATA,
     309             :                   "The shell '%s' is allowed but does not exist. "
     310             :                   "Using fallback\n", user_shell);
     311           0 :             return talloc_strdup(mem_ctx, nctx->shell_fallback);
     312             :         } else {
     313           0 :             for (i=0; nctx->allowed_shells[i]; i++) {
     314           0 :                 if (strcmp(nctx->allowed_shells[i], user_shell) == 0) {
     315           0 :                     DEBUG(SSSDBG_FUNC_DATA,
     316             :                           "The shell '%s' is allowed but does not exist. "
     317             :                           "Using fallback\n", user_shell);
     318           0 :                     return talloc_strdup(mem_ctx, nctx->shell_fallback);
     319             :                 }
     320             :             }
     321             :         }
     322             :     }
     323             : 
     324           0 :     DEBUG(SSSDBG_FUNC_DATA,
     325             :           "The shell '%s' is not allowed and does not exist.\n",
     326             :               user_shell);
     327           0 :     return talloc_strdup(mem_ctx, NOLOGIN_SHELL);
     328             : }
     329             : 
     330          12 : static int fill_pwent(struct sss_packet *packet,
     331             :                       struct sss_domain_info *dom,
     332             :                       struct nss_ctx *nctx,
     333             :                       bool filter_users, bool pw_mmap_cache,
     334             :                       struct ldb_message **msgs,
     335             :                       int *count)
     336             : {
     337             :     struct ldb_message *msg;
     338             :     uint8_t *body;
     339             :     const char *upn;
     340             :     const char *tmpstr;
     341             :     const char *orig_name;
     342             :     struct sized_string name;
     343             :     struct sized_string gecos;
     344             :     struct sized_string homedir;
     345             :     struct sized_string shell;
     346             :     struct sized_string pwfield;
     347             :     struct sized_string fullname;
     348             :     uint32_t uid;
     349             :     uint32_t gid;
     350             :     size_t rsize, rp, blen;
     351          12 :     int fq_len = 0;
     352             :     int i, ret, num;
     353          12 :     bool add_domain = (!IS_SUBDOMAIN(dom) && dom->fqnames);
     354          12 :     const char *domain = dom->name;
     355          12 :     bool packet_initialized = false;
     356             :     int ncret;
     357          12 :     TALLOC_CTX *tmp_ctx = NULL;
     358             :     struct sss_nss_homedir_ctx homedir_ctx;
     359             : 
     360          12 :     to_sized_string(&pwfield, nctx->pwfield);
     361             : 
     362          12 :     rp = 2*sizeof(uint32_t);
     363             : 
     364          12 :     num = 0;
     365          24 :     for (i = 0; i < *count; i++) {
     366          12 :         talloc_zfree(tmp_ctx);
     367          12 :         tmp_ctx = talloc_new(NULL);
     368             : 
     369          12 :         msg = msgs[i];
     370             : 
     371          12 :         upn = ldb_msg_find_attr_as_string(msg, SYSDB_UPN, NULL);
     372             : 
     373          12 :         if (DOM_HAS_VIEWS(dom)) {
     374           0 :             orig_name = ldb_msg_find_attr_as_string(msg,
     375             :                                                     OVERRIDE_PREFIX SYSDB_NAME,
     376             :                                                     NULL);
     377           0 :             if (orig_name != NULL && IS_SUBDOMAIN(dom)) {
     378             :                 /* Override names are not fully qualified */
     379           0 :                 add_domain = true;
     380             :             }
     381             : 
     382           0 :             gid = ldb_msg_find_attr_as_uint64(msg,
     383             :                                               OVERRIDE_PREFIX SYSDB_GIDNUM, 0);
     384             :         } else {
     385          12 :             orig_name = NULL;
     386          12 :             gid = 0;
     387             :         }
     388             : 
     389          12 :         if (orig_name == NULL) {
     390          12 :             orig_name = ldb_msg_find_attr_as_string(msg,
     391             :                                                     SYSDB_DEFAULT_OVERRIDE_NAME,
     392             :                                                     NULL);
     393          12 :             if (orig_name == NULL) {
     394          12 :                 orig_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
     395             :             }
     396             :         }
     397             : 
     398          12 :         uid = sss_view_ldb_msg_find_attr_as_uint64(dom, msg, SYSDB_UIDNUM, 0);
     399             : 
     400          12 :         if (gid == 0) {
     401          12 :             gid = get_gid_override(msg, dom);
     402             :         }
     403             : 
     404          12 :         if (!orig_name || !uid || !gid) {
     405           0 :             DEBUG(SSSDBG_OP_FAILURE, "Incomplete user object for %s[%llu]! Skipping\n",
     406             :                       orig_name?orig_name:"<NULL>", (unsigned long long int)uid);
     407           0 :             continue;
     408             :         }
     409             : 
     410          12 :         if (filter_users) {
     411           3 :             ncret = sss_ncache_check_user(nctx->ncache,
     412             :                                         nctx->neg_timeout,
     413             :                                         dom, orig_name);
     414           3 :             if (ncret == EEXIST) {
     415           0 :                 DEBUG(SSSDBG_TRACE_FUNC,
     416             :                       "User [%s@%s] filtered out! (negative cache)\n",
     417             :                        orig_name, domain);
     418           0 :                 continue;
     419             :             }
     420             :         }
     421             : 
     422          12 :         if (!packet_initialized) {
     423             :             /* first 2 fields (len and reserved), filled up later */
     424          12 :             ret = sss_packet_grow(packet, 2*sizeof(uint32_t));
     425          12 :             if (ret != EOK) return ret;
     426          12 :             packet_initialized = true;
     427             :         }
     428             : 
     429          12 :         tmpstr = sss_get_cased_name(tmp_ctx, orig_name, dom->case_preserve);
     430          12 :         if (tmpstr == NULL) {
     431           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     432             :                   "sss_get_cased_name failed, skipping\n");
     433           0 :             continue;
     434             :         }
     435             : 
     436          12 :         tmpstr = sss_replace_space(tmp_ctx, tmpstr,
     437          12 :                                    nctx->rctx->override_space);
     438          12 :         if (tmpstr == NULL) {
     439           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     440             :                   "sss_replace_space failed, skipping\n");
     441           0 :             continue;
     442             :         }
     443             : 
     444          12 :         to_sized_string(&name, tmpstr);
     445             : 
     446          12 :         tmpstr = sss_view_ldb_msg_find_attr_as_string(dom, msg, SYSDB_GECOS,
     447             :                                                       NULL);
     448          12 :         if (!tmpstr) {
     449           0 :             to_sized_string(&gecos, "");
     450             :         } else {
     451          12 :             to_sized_string(&gecos, tmpstr);
     452             :         }
     453             : 
     454          12 :         ZERO_STRUCT(homedir_ctx);
     455             : 
     456          12 :         homedir_ctx.username = name.str;
     457          12 :         homedir_ctx.uid = uid;
     458          12 :         homedir_ctx.domain = dom->name;
     459          12 :         homedir_ctx.upn = upn;
     460             : 
     461          12 :         tmpstr = get_homedir_override(tmp_ctx, msg, nctx, dom, &homedir_ctx);
     462          12 :         if (!tmpstr) {
     463           0 :             to_sized_string(&homedir, "/");
     464             :         } else {
     465          12 :             to_sized_string(&homedir, tmpstr);
     466             :         }
     467             : 
     468          12 :         tmpstr = get_shell_override(tmp_ctx, msg, nctx, dom);
     469          12 :         if (!tmpstr) {
     470           0 :             to_sized_string(&shell, "");
     471             :         } else {
     472          12 :             to_sized_string(&shell, tmpstr);
     473             :         }
     474             : 
     475          36 :         rsize = 2 * sizeof(uint32_t) + name.len + gecos.len +
     476          24 :                                        homedir.len + shell.len + pwfield.len;
     477             : 
     478          12 :         if (add_domain) {
     479           2 :             fq_len = sss_fqname(NULL, 0, dom->names, dom, name.str);
     480           2 :             if (fq_len >= 0) {
     481           2 :                 fq_len += 1;
     482           2 :                 rsize -= name.len;
     483           2 :                 rsize += fq_len;
     484             :             } else {
     485           0 :                 fq_len = 0;
     486             :             }
     487             :         }
     488             : 
     489          12 :         ret = sss_packet_grow(packet, rsize);
     490          12 :         if (ret != EOK) {
     491           0 :             num = 0;
     492           0 :             goto done;
     493             :         }
     494          12 :         sss_packet_get_body(packet, &body, &blen);
     495             : 
     496          12 :         SAFEALIGN_SET_UINT32(&body[rp], uid, &rp);
     497          12 :         SAFEALIGN_SET_UINT32(&body[rp], gid, &rp);
     498             : 
     499          12 :         if (add_domain) {
     500           2 :             ret = sss_fqname((char *) &body[rp], fq_len, dom->names, dom, name.str);
     501           2 :             if (ret < 0 || ret != fq_len - 1) {
     502           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     503             :                       "Failed to generate a fully qualified name for user "
     504             :                           "[%s] in [%s]! Skipping user.\n", name.str, domain);
     505           0 :                 continue;
     506             :             }
     507             :         } else {
     508          10 :             memcpy(&body[rp], name.str, name.len);
     509             :         }
     510          12 :         to_sized_string(&fullname, (const char *)&body[rp]);
     511          12 :         rp += fullname.len;
     512             : 
     513          12 :         memcpy(&body[rp], pwfield.str, pwfield.len);
     514          12 :         rp += pwfield.len;
     515          12 :         memcpy(&body[rp], gecos.str, gecos.len);
     516          12 :         rp += gecos.len;
     517          12 :         memcpy(&body[rp], homedir.str, homedir.len);
     518          12 :         rp += homedir.len;
     519          12 :         memcpy(&body[rp], shell.str, shell.len);
     520          12 :         rp += shell.len;
     521             : 
     522          12 :         num++;
     523             : 
     524          12 :         if (pw_mmap_cache && nctx->pwd_mc_ctx) {
     525           0 :             ret = sss_mmap_cache_pw_store(&nctx->pwd_mc_ctx,
     526             :                                           &fullname, &pwfield,
     527             :                                           uid, gid,
     528             :                                           &gecos, &homedir, &shell);
     529           0 :             if (ret != EOK && ret != ENOMEM) {
     530           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     531             :                       "Failed to store user %s(%s) in mmap cache!\n",
     532             :                         name.str, domain);
     533             :             }
     534             :         }
     535             :     }
     536          12 :     talloc_zfree(tmp_ctx);
     537             : 
     538             : done:
     539          12 :     *count = i;
     540             : 
     541             :     /* if there are no results just return ENOENT,
     542             :      * let the caller decide if this is the last packet or not */
     543          12 :     if (!packet_initialized) return ENOENT;
     544             : 
     545          12 :     sss_packet_get_body(packet, &body, &blen);
     546          12 :     SAFEALIGN_COPY_UINT32(body, &num, NULL); /* num results */
     547          12 :     SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); /* reserved */
     548             : 
     549          12 :     return EOK;
     550             : }
     551             : 
     552          12 : static int nss_cmd_getpw_send_reply(struct nss_dom_ctx *dctx, bool filter)
     553             : {
     554          12 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
     555          12 :     struct cli_ctx *cctx = cmdctx->cctx;
     556             :     struct nss_ctx *nctx;
     557             :     int ret;
     558             :     int i;
     559             : 
     560          12 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
     561             : 
     562          24 :     ret = sss_packet_new(cctx->creq, 0,
     563          12 :                          sss_packet_get_cmd(cctx->creq->in),
     564          12 :                          &cctx->creq->out);
     565          12 :     if (ret != EOK) {
     566           0 :         return EFAULT;
     567             :     }
     568          12 :     i = dctx->res->count;
     569             : 
     570          12 :     ret = fill_pwent(cctx->creq->out,
     571             :                      dctx->domain,
     572             :                      nctx, filter, true,
     573          12 :                      dctx->res->msgs, &i);
     574          12 :     if (ret) {
     575           0 :         return ret;
     576             :     }
     577          12 :     sss_packet_set_error(cctx->creq->out, EOK);
     578          12 :     sss_cmd_done(cctx, cmdctx);
     579          12 :     return EOK;
     580             : }
     581             : 
     582             : /* Currently only refreshing expired netgroups is supported. */
     583             : static bool
     584          28 : is_refreshed_on_bg(enum sss_dp_acct_type req_type,
     585             :                    enum sss_dp_acct_type refresh_expired_interval)
     586             : {
     587          28 :     if (refresh_expired_interval == 0) {
     588          28 :         return false;
     589             :     }
     590             : 
     591           0 :     switch (req_type) {
     592             :     case SSS_DP_NETGR:
     593           0 :         return true;
     594             :     default:
     595           0 :         return false;
     596             :     }
     597             : 
     598             :     return false;
     599             : }
     600             : 
     601             : static void nsssrv_dp_send_acct_req_done(struct tevent_req *req);
     602             : 
     603          37 : static void get_dp_name_and_id(TALLOC_CTX *mem_ctx,
     604             :                               struct sss_domain_info *dom,
     605             :                               enum sss_dp_acct_type req_type,
     606             :                               const char *opt_name,
     607             :                               uint32_t opt_id,
     608             :                               const char **_name,
     609             :                               uint32_t *_id)
     610             : {
     611             :     TALLOC_CTX *tmp_ctx;
     612          37 :     struct ldb_result *res = NULL;
     613             :     const char *attr;
     614             :     const char *name;
     615             :     uint32_t id;
     616             :     errno_t ret;
     617             : 
     618             :     /* First set the same values to make things easier. */
     619          37 :     *_name = opt_name;
     620          37 :     *_id = opt_id;
     621             : 
     622          37 :     if (!DOM_HAS_VIEWS(dom) || !is_local_view(dom->view_name)) {
     623          37 :         DEBUG(SSSDBG_TRACE_FUNC, "Not a LOCAL view, continuing with "
     624             :               "provided values.\n");
     625          74 :         return;
     626             :     }
     627             : 
     628           0 :     tmp_ctx = talloc_new(NULL);
     629           0 :     if (tmp_ctx == NULL) {
     630           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
     631           0 :         return;
     632             :     }
     633             : 
     634           0 :     if (opt_name != NULL) {
     635           0 :         switch (req_type) {
     636             :         case SSS_DP_USER:
     637             :         case SSS_DP_INITGROUPS:
     638           0 :             ret = sysdb_getpwnam_with_views(tmp_ctx, dom, opt_name, &res);
     639           0 :             if (ret != EOK) {
     640           0 :                 DEBUG(SSSDBG_CONF_SETTINGS,
     641             :                       "sysdb_getpwnam_with_views() failed [%d]: %s\n",
     642             :                       ret, sss_strerror(ret));
     643           0 :                 goto done;
     644             :             }
     645           0 :             break;
     646             :         case SSS_DP_GROUP:
     647           0 :             ret = sysdb_getgrnam_with_views(tmp_ctx, dom, opt_name, &res);
     648           0 :             if (ret != EOK) {
     649           0 :                 DEBUG(SSSDBG_CONF_SETTINGS,
     650             :                       "sysdb_getgrnam_with_views() failed [%d]: %s\n",
     651             :                       ret, sss_strerror(ret));
     652           0 :                 goto done;
     653             :             }
     654           0 :             break;
     655             :         default:
     656           0 :             goto done;
     657             :         }
     658             : 
     659           0 :         if (res == NULL || res->count != 1) {
     660             :             /* This should not happen with LOCAL view and overridden value. */
     661           0 :             DEBUG(SSSDBG_TRACE_FUNC, "Entry is missing?! Continuing with "
     662             :                   "provided values.\n");
     663           0 :             goto done;
     664             :         }
     665             : 
     666           0 :         name = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL);
     667           0 :         if (name == NULL) {
     668           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL\n");
     669           0 :             goto done;
     670             :         }
     671             : 
     672           0 :         *_name = talloc_steal(mem_ctx, name);
     673           0 :     } else if (opt_id != 0) {
     674           0 :         switch (req_type) {
     675             :         case SSS_DP_USER:
     676           0 :             ret = sysdb_getpwuid_with_views(tmp_ctx, dom, opt_id, &res);
     677           0 :             if (ret != EOK) {
     678           0 :                 DEBUG(SSSDBG_CONF_SETTINGS,
     679             :                       "sysdb_getpwuid_with_views() failed [%d]: %s\n",
     680             :                       ret, sss_strerror(ret));
     681           0 :                 goto done;
     682             :             }
     683             : 
     684           0 :             attr = SYSDB_UIDNUM;
     685           0 :             break;
     686             :         case SSS_DP_GROUP:
     687           0 :             ret = sysdb_getgrgid_with_views(tmp_ctx, dom, opt_id, &res);
     688           0 :             if (ret != EOK) {
     689           0 :                 DEBUG(SSSDBG_CONF_SETTINGS,
     690             :                       "sysdb_getgrgid_with_views() failed [%d]: %s\n",
     691             :                       ret, sss_strerror(ret));
     692           0 :                 goto done;
     693             :             }
     694             : 
     695           0 :             attr = SYSDB_GIDNUM;
     696           0 :             break;
     697             :         default:
     698           0 :             goto done;
     699             :         }
     700             : 
     701           0 :         if (res == NULL || res->count != 1) {
     702             :             /* This should not happen with LOCAL view and overridden value. */
     703           0 :             DEBUG(SSSDBG_TRACE_FUNC, "Entry is missing?! Continuing with "
     704             :                   "provided values.\n");
     705           0 :             goto done;
     706             :         }
     707             : 
     708           0 :         id = ldb_msg_find_attr_as_uint64(res->msgs[0], attr, 0);
     709           0 :         if (id == 0) {
     710           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Bug: id cannot be 0\n");
     711           0 :             goto done;
     712             :         }
     713             : 
     714           0 :         *_id = id;
     715             :     }
     716             : 
     717             : done:
     718           0 :     talloc_free(tmp_ctx);
     719             : }
     720             : 
     721             : /* FIXME: do not check res->count, but get in a msgs and check in parent */
     722          37 : errno_t check_cache(struct nss_dom_ctx *dctx,
     723             :                     struct nss_ctx *nctx,
     724             :                     struct ldb_result *res,
     725             :                     enum sss_dp_acct_type req_type,
     726             :                     const char *opt_name,
     727             :                     uint32_t opt_id,
     728             :                     const char *extra,
     729             :                     sss_dp_callback_t callback,
     730             :                     void *pvt)
     731             : {
     732             :     errno_t ret;
     733          37 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
     734          37 :     struct cli_ctx *cctx = cmdctx->cctx;
     735          37 :     struct tevent_req *req = NULL;
     736          37 :     struct dp_callback_ctx *cb_ctx = NULL;
     737          37 :     uint64_t cacheExpire = 0;
     738          37 :     const char *name = opt_name;
     739          37 :     uint32_t id = opt_id;
     740             : 
     741             :     /* when searching for a user or netgroup, more than one reply is a
     742             :      * db error
     743             :      */
     744          55 :     if ((req_type == SSS_DP_USER || req_type == SSS_DP_NETGR) &&
     745          18 :             (res->count > 1)) {
     746           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     747             :               "getpwXXX call returned more than one result! DB Corrupted?\n");
     748           0 :         return ENOENT;
     749             :     }
     750             : 
     751             :     /* In case of local view we have to always contant DP with the original
     752             :      * name or id. */
     753          37 :     get_dp_name_and_id(dctx->cmdctx, dctx->domain, req_type, opt_name, opt_id,
     754             :                        &name, &id);
     755             : 
     756             :     /* if we have any reply let's check cache validity, but ignore netgroups
     757             :      * if refresh_expired_interval is set (which implies that another method
     758             :      * is used to refresh netgroups)
     759             :      */
     760          37 :     if (res->count > 0) {
     761          28 :         if (is_refreshed_on_bg(req_type,
     762          28 :                                dctx->domain->refresh_expired_interval)) {
     763           0 :             ret = EOK;
     764             :         } else {
     765          28 :             if (req_type == SSS_DP_INITGROUPS) {
     766           4 :                 cacheExpire = ldb_msg_find_attr_as_uint64(res->msgs[0],
     767             :                                                           SYSDB_INITGR_EXPIRE,
     768             :                                                           0);
     769             :             } else {
     770          24 :                 cacheExpire = ldb_msg_find_attr_as_uint64(res->msgs[0],
     771             :                                                           SYSDB_CACHE_EXPIRE,
     772             :                                                           0);
     773             :             }
     774             : 
     775             :             /* if we have any reply let's check cache validity */
     776          28 :             ret = sss_cmd_check_cache(res->msgs[0],
     777             :                                       nctx->cache_refresh_percent,
     778             :                                       cacheExpire);
     779             :         }
     780          28 :         if (ret == EOK) {
     781          23 :             DEBUG(SSSDBG_TRACE_FUNC, "Cached entry is valid, returning..\n");
     782          23 :             return EOK;
     783           5 :         } else if (ret != EAGAIN && ret != ENOENT) {
     784           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Error checking cache: %d\n", ret);
     785           0 :             goto error;
     786             :         }
     787             :     } else {
     788             :         /* No replies */
     789           9 :         ret = ENOENT;
     790             :     }
     791             : 
     792             :     /* EAGAIN (off band) or ENOENT (cache miss) -> check cache */
     793          14 :     if (ret == EAGAIN) {
     794             :         /* No callback required
     795             :          * This was an out-of-band update. We'll return EOK
     796             :          * so the calling function can return the cached entry
     797             :          * immediately.
     798             :          */
     799           0 :         DEBUG(SSSDBG_TRACE_FUNC,
     800             :              "Performing midpoint cache update on [%s]\n", name);
     801             : 
     802           0 :         req = sss_dp_get_account_send(cctx, cctx->rctx, dctx->domain, true,
     803             :                                       req_type, name, id, extra);
     804           0 :         if (!req) {
     805           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     806             :                   "Out of memory sending out-of-band data provider "
     807             :                    "request\n");
     808             :             /* This is non-fatal, so we'll continue here */
     809             :         } else {
     810           0 :             DEBUG(SSSDBG_TRACE_FUNC, "Updating cache out-of-band\n");
     811             :         }
     812             : 
     813             :         /* We don't need to listen for a reply, so we will free the
     814             :          * request here.
     815             :          */
     816           0 :         talloc_zfree(req);
     817             : 
     818             :     } else {
     819             :        /* This is a cache miss. Or the cache is expired.
     820             :         * We need to get the updated user information before returning it.
     821             :         */
     822             : 
     823             :         /* dont loop forever :-) */
     824          14 :         dctx->check_provider = false;
     825             : 
     826             :         /* keep around current data in case backend is offline */
     827          14 :         if (res->count) {
     828           5 :             dctx->res = talloc_steal(dctx, res);
     829             :         }
     830             : 
     831          14 :         req = sss_dp_get_account_send(cctx, cctx->rctx, dctx->domain, true,
     832             :                                       req_type, name, id, extra);
     833          14 :         if (!req) {
     834           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     835             :                   "Out of memory sending data provider request\n");
     836           0 :             ret = ENOMEM;
     837           0 :             goto error;
     838             :         }
     839             : 
     840          14 :         cb_ctx = talloc_zero(dctx, struct dp_callback_ctx);
     841          14 :         if(!cb_ctx) {
     842           0 :             talloc_zfree(req);
     843           0 :             ret = ENOMEM;
     844           0 :             goto error;
     845             :         }
     846          14 :         cb_ctx->callback = callback;
     847          14 :         cb_ctx->ptr = pvt;
     848          14 :         cb_ctx->cctx = dctx->cmdctx->cctx;
     849          14 :         cb_ctx->mem_ctx = dctx;
     850             : 
     851          14 :         tevent_req_set_callback(req, nsssrv_dp_send_acct_req_done, cb_ctx);
     852             : 
     853          14 :         return EAGAIN;
     854             :     }
     855             : 
     856           0 :     return EOK;
     857             : 
     858             : error:
     859           0 :     ret = nss_cmd_send_error(cmdctx, ret);
     860           0 :     if (ret != EOK) {
     861           0 :         NSS_CMD_FATAL_ERROR_CODE(cctx, ret);
     862             :     }
     863           0 :     sss_cmd_done(cctx, cmdctx);
     864           0 :     return EOK;
     865             : }
     866             : 
     867          14 : static void nsssrv_dp_send_acct_req_done(struct tevent_req *req)
     868             : {
     869          14 :     struct dp_callback_ctx *cb_ctx =
     870          14 :             tevent_req_callback_data(req, struct dp_callback_ctx);
     871             : 
     872             :     errno_t ret;
     873             :     dbus_uint16_t err_maj;
     874             :     dbus_uint32_t err_min;
     875             :     char *err_msg;
     876             : 
     877          14 :     ret = sss_dp_get_account_recv(cb_ctx->mem_ctx, req,
     878             :                                   &err_maj, &err_min,
     879             :                                   &err_msg);
     880          14 :     talloc_zfree(req);
     881          14 :     if (ret != EOK) {
     882           0 :         NSS_CMD_FATAL_ERROR(cb_ctx->cctx);
     883             :     }
     884             : 
     885          14 :     cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr);
     886             : }
     887             : 
     888           4 : static int delete_entry_from_memcache(struct sss_domain_info *dom, char *name,
     889             :                                       struct sss_mc_ctx *mc_ctx,
     890             :                                       enum sss_mc_type type)
     891             : {
     892           4 :     TALLOC_CTX *tmp_ctx = NULL;
     893             :     struct sized_string delete_name;
     894           4 :     char *fqdn = NULL;
     895             :     int ret;
     896             : 
     897           4 :     tmp_ctx = talloc_new(NULL);
     898           4 :     if (tmp_ctx == NULL) {
     899           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory.\n");
     900           0 :         return ENOMEM;
     901             :     }
     902             : 
     903           4 :     if (dom->fqnames) {
     904           0 :         fqdn = sss_tc_fqname(tmp_ctx, dom->names, dom, name);
     905           0 :         if (fqdn == NULL) {
     906           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory.\n");
     907           0 :             ret = ENOMEM;
     908           0 :             goto done;
     909             :         }
     910           0 :         to_sized_string(&delete_name, fqdn);
     911             :     } else {
     912           4 :         to_sized_string(&delete_name, name);
     913             :     }
     914             : 
     915           4 :     switch (type) {
     916             :     case SSS_MC_PASSWD:
     917           2 :         ret = sss_mmap_cache_pw_invalidate(mc_ctx, &delete_name);
     918           2 :         if (ret != EOK && ret != ENOENT) {
     919           2 :             DEBUG(SSSDBG_CRIT_FAILURE,
     920             :                   "Internal failure in memory cache code: %d [%s]\n",
     921             :                   ret, strerror(ret));
     922           2 :             goto done;
     923             :         }
     924           0 :         break;
     925             :     case SSS_MC_GROUP:
     926           0 :         ret = sss_mmap_cache_gr_invalidate(mc_ctx, &delete_name);
     927           0 :         if (ret != EOK && ret != ENOENT) {
     928           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     929             :                   "Internal failure in memory cache code: %d [%s]\n",
     930             :                   ret, strerror(ret));
     931           0 :             goto done;
     932             :         }
     933           0 :         break;
     934             :     case SSS_MC_INITGROUPS:
     935           2 :         ret = sss_mmap_cache_initgr_invalidate(mc_ctx, &delete_name);
     936           2 :         if (ret != EOK && ret != ENOENT) {
     937           2 :             DEBUG(SSSDBG_CRIT_FAILURE,
     938             :                   "Internal failure in memory cache code: %d [%s]\n",
     939             :                   ret, strerror(ret));
     940           2 :             goto done;
     941             :         }
     942           0 :         break;
     943             :     default:
     944           0 :         ret = EINVAL;
     945           0 :         goto done;
     946             :     }
     947             : 
     948           0 :     ret = EOK;
     949             : done:
     950           4 :     talloc_free(tmp_ctx);
     951           4 :     return ret;
     952             : 
     953             : }
     954             : 
     955             : static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min,
     956             :                                       const char *err_msg, void *ptr);
     957             : 
     958             : /* search for a user.
     959             :  * Returns:
     960             :  *   ENOENT, if user is definitely not found
     961             :  *   EAGAIN, if user is being fetched from backend via async operations
     962             :  *   EOK, if found
     963             :  *   anything else on a fatal error
     964             :  */
     965             : 
     966          18 : static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
     967             : {
     968          18 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
     969          18 :     struct sss_domain_info *dom = dctx->domain;
     970          18 :     struct cli_ctx *cctx = cmdctx->cctx;
     971          18 :     char *name = NULL;
     972             :     struct nss_ctx *nctx;
     973             :     int ret;
     974             :     static const char *user_attrs[] = SYSDB_PW_ATTRS;
     975             :     struct ldb_message *msg;
     976          18 :     const char *extra_flag = NULL;
     977             : 
     978          18 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
     979             : 
     980          18 :     while (dom) {
     981             :        /* if it is a domainless search, skip domains that require fully
     982             :          * qualified names instead */
     983          36 :         while (dom && cmdctx->check_next && dom->fqnames
     984           0 :                 && !cmdctx->name_is_upn) {
     985           0 :             dom = get_next_domain(dom, false);
     986             :         }
     987             : 
     988          18 :         if (!dom) break;
     989             : 
     990          18 :         if (dom != dctx->domain) {
     991             :             /* make sure we reset the check_provider flag when we check
     992             :              * a new domain */
     993           0 :             dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
     994             :         }
     995             : 
     996             :         /* make sure to update the dctx if we changed domain */
     997          18 :         dctx->domain = dom;
     998             : 
     999          18 :         talloc_free(name);
    1000          18 :         name = sss_get_cased_name(cmdctx, cmdctx->name, dom->case_sensitive);
    1001          18 :         if (!name) return ENOMEM;
    1002             : 
    1003          18 :         name = sss_reverse_replace_space(dctx, name,
    1004          18 :                                          nctx->rctx->override_space);
    1005          18 :         if (name == NULL) {
    1006           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1007             :                   "sss_reverse_replace_space failed\n");
    1008           0 :             return ENOMEM;
    1009             :         }
    1010             : 
    1011             :         /* verify this user has not yet been negatively cached,
    1012             :         * or has been permanently filtered */
    1013          18 :         ret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
    1014             :                                     dom, name);
    1015             : 
    1016             :         /* if neg cached, return we didn't find it */
    1017          18 :         if (ret == EEXIST) {
    1018           3 :             DEBUG(SSSDBG_TRACE_FUNC,
    1019             :                   "User [%s] does not exist in [%s]! (negative cache)\n",
    1020             :                    name, dom->name);
    1021             :             /* if a multidomain search, try with next */
    1022           3 :             if (cmdctx->check_next) {
    1023           3 :                 if (cmdctx->name_is_upn) {
    1024           2 :                     dom = get_next_domain(dom, true);
    1025             :                 } else {
    1026           1 :                     dom = get_next_domain(dom, false);
    1027             :                 }
    1028           3 :                 continue;
    1029             :             }
    1030             :             /* There are no further domains or this was a
    1031             :              * fully-qualified user request.
    1032             :              */
    1033           0 :             return ENOENT;
    1034             :         }
    1035             : 
    1036          15 :         DEBUG(SSSDBG_CONF_SETTINGS,
    1037             :               "Requesting info for [%s@%s]\n", name, dom->name);
    1038             : 
    1039          15 :         if (dom->sysdb == NULL) {
    1040           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    1041             :                   "Fatal: Sysdb CTX not found for this domain!\n");
    1042           0 :             return EIO;
    1043             :         }
    1044             : 
    1045          15 :         if (cmdctx->name_is_upn) {
    1046           3 :             ret = sysdb_search_user_by_upn(cmdctx, dom, name, user_attrs, &msg);
    1047           3 :             if (ret != EOK && ret != ENOENT) {
    1048           0 :                 DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_upn failed.\n");
    1049           0 :                 return ret;
    1050             :             }
    1051             : 
    1052           3 :             dctx->res = talloc_zero(cmdctx, struct ldb_result);
    1053           3 :             if (dctx->res == NULL) {
    1054           0 :                 DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
    1055           0 :                 return ENOMEM;
    1056             :             }
    1057             : 
    1058           3 :             if (ret == ENOENT) {
    1059           2 :                 dctx->res->count = 0;
    1060           2 :                 dctx->res->msgs = NULL;
    1061           2 :                 ret = EOK;
    1062             :             } else {
    1063           1 :                 dctx->res->count = 1;
    1064           1 :                 dctx->res->msgs = talloc_array(dctx->res,
    1065             :                                                struct ldb_message *, 1);
    1066           1 :                 if (dctx->res->msgs == NULL) {
    1067           0 :                     DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
    1068           0 :                     return ENOMEM;
    1069             :                 }
    1070             : 
    1071           1 :                 dctx->res->msgs[0] = talloc_steal(dctx->res->msgs, msg);
    1072             :             }
    1073             :         } else {
    1074          12 :             ret = sysdb_getpwnam_with_views(cmdctx, dom, name, &dctx->res);
    1075             :         }
    1076          15 :         if (ret != EOK) {
    1077           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1078             :                   "Failed to make request to our cache!\n");
    1079           0 :             return EIO;
    1080             :         }
    1081             : 
    1082          15 :         if (dctx->res->count > 1) {
    1083           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    1084             :                   "getpwnam call returned more than one result !?!\n");
    1085           0 :             sss_log(SSS_LOG_ERR,
    1086             :                     "More users have the same name [%s@%s] in SSSD cache. "
    1087             :                     "SSSD will not work correctly.\n",
    1088             :                     name, dom->name);
    1089           0 :             return ENOENT;
    1090             :         }
    1091             : 
    1092          15 :         if (dctx->res->count == 0 && !dctx->check_provider) {
    1093             :             /* set negative cache only if not result of cache check */
    1094           2 :             ret = sss_ncache_set_user(nctx->ncache, false, dom, name);
    1095           2 :             if (ret != EOK) {
    1096           0 :                 DEBUG(SSSDBG_MINOR_FAILURE, "Cannot set negcache for %s@%s\n",
    1097             :                       name, dom->name);
    1098             :             }
    1099             : 
    1100             :             /* if a multidomain search, try with next */
    1101           2 :             if (cmdctx->check_next) {
    1102           2 :                 if (cmdctx->name_is_upn) {
    1103           1 :                     dom = get_next_domain(dom, true);
    1104             :                 } else {
    1105           1 :                     dom = get_next_domain(dom, false);
    1106             :                 }
    1107           2 :                 if (dom) continue;
    1108             :             }
    1109             : 
    1110           2 :             DEBUG(SSSDBG_OP_FAILURE, "No results for getpwnam call\n");
    1111             : 
    1112             :             /* User not found in ldb -> delete user from memory cache. */
    1113           2 :             ret = delete_entry_from_memcache(dctx->domain, name,
    1114             :                                              nctx->pwd_mc_ctx, SSS_MC_PASSWD);
    1115           2 :             if (ret != EOK) {
    1116           2 :                 DEBUG(SSSDBG_MINOR_FAILURE,
    1117             :                       "Deleting user from memcache failed.\n");
    1118             :             }
    1119             : 
    1120           2 :             ret = delete_entry_from_memcache(dctx->domain, name,
    1121             :                                              nctx->initgr_mc_ctx,
    1122             :                                              SSS_MC_INITGROUPS);
    1123           2 :             if (ret != EOK) {
    1124           2 :                 DEBUG(SSSDBG_MINOR_FAILURE,
    1125             :                       "Deleting user from memcache failed.\n");
    1126             :             }
    1127             : 
    1128           2 :             return ENOENT;
    1129             :         }
    1130             : 
    1131             :         /* if this is a caching provider (or if we haven't checked the cache
    1132             :          * yet) then verify that the cache is uptodate */
    1133          13 :         if (dctx->check_provider) {
    1134             : 
    1135          11 :             if (cmdctx->name_is_upn) {
    1136           2 :                 extra_flag = EXTRA_NAME_IS_UPN;
    1137           9 :             } else if (DOM_HAS_VIEWS(dom) && (dctx->res->count == 0
    1138           0 :                     || ldb_msg_find_attr_as_string(dctx->res->msgs[0],
    1139             :                                                    OVERRIDE_PREFIX SYSDB_NAME,
    1140             :                                                    NULL) != NULL)) {
    1141           0 :                 extra_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
    1142             :             } else {
    1143           9 :                 extra_flag = NULL;
    1144             :             }
    1145             : 
    1146          11 :             ret = check_cache(dctx, nctx, dctx->res, SSS_DP_USER, name, 0,
    1147             :                               extra_flag, nss_cmd_getby_dp_callback, dctx);
    1148          11 :             if (ret != EOK) {
    1149             :                 /* Anything but EOK means we should reenter the mainloop
    1150             :                  * because we may be refreshing the cache
    1151             :                  */
    1152           4 :                 return ret;
    1153             :             }
    1154             :         }
    1155             : 
    1156             :         /* One result found */
    1157           9 :         DEBUG(SSSDBG_TRACE_FUNC,
    1158             :               "Returning info for user [%s@%s]\n", name, dom->name);
    1159             : 
    1160           9 :         return EOK;
    1161             :     }
    1162             : 
    1163           3 :     DEBUG(SSSDBG_MINOR_FAILURE,
    1164             :           "No matching domain found for [%s], fail!\n", cmdctx->name);
    1165           3 :     return ENOENT;
    1166             : }
    1167             : 
    1168             : static int nss_cmd_getgrnam_search(struct nss_dom_ctx *dctx);
    1169             : static int nss_cmd_getgr_send_reply(struct nss_dom_ctx *dctx, bool filter);
    1170             : static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx);
    1171             : static int nss_cmd_initgr_send_reply(struct nss_dom_ctx *dctx);
    1172             : static int nss_cmd_getpwuid_search(struct nss_dom_ctx *dctx);
    1173             : static int nss_cmd_getgrgid_search(struct nss_dom_ctx *dctx);
    1174             : static errno_t nss_cmd_getbysid_search(struct nss_dom_ctx *dctx);
    1175             : static errno_t nss_cmd_getbysid_send_reply(struct nss_dom_ctx *dctx);
    1176             : static errno_t nss_cmd_getsidby_search(struct nss_dom_ctx *dctx);
    1177             : 
    1178           8 : static int nss_cmd_assume_upn(struct nss_dom_ctx *dctx)
    1179             : {
    1180             :     int ret;
    1181             : 
    1182           8 :     if (dctx->domain == NULL) {
    1183           6 :         dctx->domain = dctx->cmdctx->cctx->rctx->domains;
    1184           6 :         dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
    1185           6 :         dctx->cmdctx->check_next = true;
    1186           6 :         dctx->cmdctx->name = talloc_strdup(dctx->cmdctx, dctx->rawname);
    1187           6 :         dctx->cmdctx->name_is_upn = true;
    1188           6 :         if (dctx->cmdctx->name == NULL) {
    1189           0 :             DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
    1190           0 :             return ENOMEM;
    1191             :         }
    1192             :     }
    1193             : 
    1194           8 :     switch (dctx->cmdctx->cmd) {
    1195             :     case SSS_NSS_GETPWNAM:
    1196           4 :         ret = nss_cmd_getpwnam_search(dctx);
    1197           4 :         if (ret == EOK) {
    1198           1 :             ret = nss_cmd_getpw_send_reply(dctx, false);
    1199             :         }
    1200           4 :         break;
    1201             :     case SSS_NSS_INITGR:
    1202           4 :         ret = nss_cmd_initgroups_search(dctx);
    1203           4 :         if (ret == EOK) {
    1204           1 :             ret = nss_cmd_initgr_send_reply(dctx);
    1205             :         }
    1206           4 :         break;
    1207             :     default:
    1208           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
    1209             :               dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
    1210           0 :         ret = EINVAL;
    1211             :     }
    1212             : 
    1213           8 :     return ret;
    1214             : }
    1215             : 
    1216          14 : static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min,
    1217             :                                       const char *err_msg, void *ptr)
    1218             : {
    1219          14 :     struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx);
    1220          14 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
    1221          14 :     struct cli_ctx *cctx = cmdctx->cctx;
    1222             :     int ret;
    1223             :     bool check_subdomains;
    1224          14 :     struct nss_ctx *nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    1225             : 
    1226          14 :     if (err_maj) {
    1227           0 :         DEBUG(SSSDBG_OP_FAILURE,
    1228             :               "Unable to get information from Data Provider\n"
    1229             :                   "Error: %u, %u, %s\n"
    1230             :                   "Will try to return what we have in cache\n",
    1231             :                   (unsigned int)err_maj, (unsigned int)err_min, err_msg);
    1232             : 
    1233           0 :         if ((dctx->res && dctx->res->count == 1) ||
    1234           0 :             (dctx->cmdctx->cmd == SSS_NSS_INITGR &&
    1235           0 :              dctx->res && dctx->res->count != 0)) {
    1236           0 :             switch (dctx->cmdctx->cmd) {
    1237             :             case SSS_NSS_GETPWNAM:
    1238           0 :                 ret = nss_cmd_getpw_send_reply(dctx, false);
    1239           0 :                 break;
    1240             :             case SSS_NSS_GETGRNAM:
    1241           0 :                 ret = nss_cmd_getgr_send_reply(dctx, false);
    1242           0 :                 break;
    1243             :             case SSS_NSS_INITGR:
    1244           0 :                 ret = nss_cmd_initgr_send_reply(dctx);
    1245           0 :                 break;
    1246             :             case SSS_NSS_GETPWUID:
    1247           0 :                 ret = nss_cmd_getpw_send_reply(dctx, true);
    1248           0 :                 break;
    1249             :             case SSS_NSS_GETGRGID:
    1250           0 :                 ret = nss_cmd_getgr_send_reply(dctx, true);
    1251           0 :                 break;
    1252             :             case SSS_NSS_GETNAMEBYSID:
    1253             :             case SSS_NSS_GETIDBYSID:
    1254             :             case SSS_NSS_GETSIDBYNAME:
    1255             :             case SSS_NSS_GETORIGBYNAME:
    1256             :             case SSS_NSS_GETSIDBYID:
    1257           0 :                 ret = nss_cmd_getbysid_send_reply(dctx);
    1258           0 :                 break;
    1259             :             default:
    1260           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
    1261             :                       dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
    1262           0 :                 ret = EINVAL;
    1263             :             }
    1264           0 :             goto done;
    1265             :         }
    1266             : 
    1267             :         /* Since subdomain users and groups are fully qualified they are
    1268             :          * typically not subject of multi-domain searches. But since POSIX
    1269             :          * ID do not contain a domain name we have to decend to subdomains
    1270             :          * here. */
    1271           0 :         switch (dctx->cmdctx->cmd) {
    1272             :         case SSS_NSS_GETPWUID:
    1273           0 :             ret = sss_ncache_set_uid(nctx->ncache, false, dctx->domain,
    1274             :                                      cmdctx->id);
    1275           0 :             if (ret != EOK) {
    1276           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
    1277             :                       "Cannot set negative cache for UID %"PRIu32"\n",
    1278             :                       cmdctx->id);
    1279             :             }
    1280           0 :             check_subdomains = true;
    1281           0 :             break;
    1282             :         case SSS_NSS_GETGRGID:
    1283           0 :             ret = sss_ncache_set_gid(nctx->ncache, false, dctx->domain,
    1284             :                                      cmdctx->id);
    1285           0 :             if (ret != EOK) {
    1286           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
    1287             :                       "Cannot set negative cache for GID %"PRIu32"\n",
    1288             :                       cmdctx->id);
    1289             :             }
    1290           0 :             check_subdomains = true;
    1291           0 :             break;
    1292             :         case SSS_NSS_GETSIDBYID:
    1293           0 :             ret = sss_ncache_set_uid(nctx->ncache, false, dctx->domain,
    1294             :                                      cmdctx->id);
    1295           0 :             if (ret != EOK) {
    1296           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
    1297             :                       "Cannot set negative cache for UID %"PRIu32"\n",
    1298             :                        cmdctx->id);
    1299             :             }
    1300           0 :             ret = sss_ncache_set_gid(nctx->ncache, false, dctx->domain,
    1301             :                                      cmdctx->id);
    1302           0 :             if (ret != EOK) {
    1303           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
    1304             :                       "Cannot set negative cache for GID %"PRIu32"\n",
    1305             :                       cmdctx->id);
    1306             :             }
    1307           0 :             check_subdomains = true;
    1308           0 :             break;
    1309             :         default:
    1310           0 :             check_subdomains = false;
    1311             :         }
    1312             : 
    1313             :         /* no previous results, just loop to next domain if possible */
    1314           0 :         if (cmdctx->check_next &&
    1315           0 :             get_next_domain(dctx->domain, check_subdomains)) {
    1316           0 :             dctx->domain = get_next_domain(dctx->domain, check_subdomains);
    1317           0 :             dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
    1318             :         } else {
    1319             :             /* nothing available */
    1320           0 :             ret = ENOENT;
    1321           0 :             goto done;
    1322             :         }
    1323             :     }
    1324             : 
    1325             :     /* ok the backend returned, search to see if we have updated results */
    1326          14 :     switch (dctx->cmdctx->cmd) {
    1327             :     case SSS_NSS_GETPWNAM:
    1328           4 :         ret = nss_cmd_getpwnam_search(dctx);
    1329           4 :         if (ret == EOK) {
    1330             :             /* we have results to return */
    1331           2 :             ret = nss_cmd_getpw_send_reply(dctx, false);
    1332             :         }
    1333           4 :         break;
    1334             :     case SSS_NSS_GETGRNAM:
    1335           0 :         ret = nss_cmd_getgrnam_search(dctx);
    1336           0 :         if (ret == EOK) {
    1337             :             /* we have results to return */
    1338           0 :             ret = nss_cmd_getgr_send_reply(dctx, false);
    1339             :         }
    1340           0 :         break;
    1341             :     case SSS_NSS_INITGR:
    1342           5 :         ret = nss_cmd_initgroups_search(dctx);
    1343           5 :         if (ret == EOK) {
    1344             :             /* we have results to return */
    1345           3 :             ret = nss_cmd_initgr_send_reply(dctx);
    1346             :         }
    1347           5 :         break;
    1348             :     case SSS_NSS_GETPWUID:
    1349           3 :         ret = nss_cmd_getpwuid_search(dctx);
    1350           3 :         if (ret == EOK) {
    1351             :             /* we have results to return */
    1352           2 :             ret = nss_cmd_getpw_send_reply(dctx, true);
    1353             :         }
    1354           3 :         break;
    1355             :     case SSS_NSS_GETGRGID:
    1356           0 :         ret = nss_cmd_getgrgid_search(dctx);
    1357           0 :         if (ret == EOK) {
    1358             :             /* we have results to return */
    1359           0 :             ret = nss_cmd_getgr_send_reply(dctx, true);
    1360             :         }
    1361           0 :         break;
    1362             :     case SSS_NSS_GETNAMEBYSID:
    1363             :     case SSS_NSS_GETIDBYSID:
    1364           2 :         ret = nss_cmd_getbysid_search(dctx);
    1365           2 :         if (ret == EOK) {
    1366           1 :             ret = nss_cmd_getbysid_send_reply(dctx);
    1367             :         }
    1368           2 :         break;
    1369             :     case SSS_NSS_GETSIDBYNAME:
    1370             :     case SSS_NSS_GETORIGBYNAME:
    1371           0 :         ret = nss_cmd_getsidby_search(dctx);
    1372           0 :         if (ret == EOK) {
    1373           0 :             ret = nss_cmd_getbysid_send_reply(dctx);
    1374             :         }
    1375           0 :         break;
    1376             :     case SSS_NSS_GETSIDBYID:
    1377           0 :         ret = nss_cmd_getsidby_search(dctx);
    1378           0 :         if (ret == EOK) {
    1379           0 :             ret = nss_cmd_getbysid_send_reply(dctx);
    1380             :         }
    1381           0 :         break;
    1382             :     default:
    1383           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
    1384             :               dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
    1385           0 :         ret = EINVAL;
    1386             :     }
    1387             : 
    1388             : done:
    1389          14 :     if (ret == ENOENT
    1390           6 :         && (dctx->cmdctx->cmd == SSS_NSS_GETPWNAM
    1391           4 :                 || dctx->cmdctx->cmd == SSS_NSS_INITGR)
    1392           4 :         && dctx->rawname != NULL && strchr(dctx->rawname, '@') != NULL) {
    1393             :         /* assume Kerberos principal */
    1394           2 :         ret = nss_cmd_assume_upn(dctx);
    1395             :     }
    1396             : 
    1397          14 :     ret = nss_cmd_done(cmdctx, ret);
    1398          14 :     if (ret) {
    1399           0 :         NSS_CMD_FATAL_ERROR(cctx);
    1400             :     }
    1401             : }
    1402             : 
    1403           6 : static int nss_check_name_of_well_known_sid(struct nss_cmd_ctx *cmdctx,
    1404             :                                             const char *full_name)
    1405             : {
    1406           6 :     char *wk_name = NULL;
    1407           6 :     char *wk_dom_name = NULL;
    1408             :     const char *wk_sid;
    1409             :     int ret;
    1410             :     struct sized_string sid;
    1411             :     uint8_t *body;
    1412             :     size_t blen;
    1413             :     struct cli_ctx *cctx;
    1414             :     struct nss_ctx *nss_ctx;
    1415           6 :     size_t pctr = 0;
    1416             : 
    1417           6 :     nss_ctx = talloc_get_type(cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx);
    1418           6 :     ret = sss_parse_name(cmdctx, nss_ctx->global_names, full_name,
    1419             :                          &wk_dom_name, &wk_name);
    1420           6 :     if (ret != EOK) {
    1421           0 :         DEBUG(SSSDBG_OP_FAILURE, "sss_parse_name failed.\n");
    1422           0 :         return ret;
    1423             :     }
    1424             : 
    1425           6 :     if (wk_dom_name == NULL || wk_name == NULL) {
    1426           0 :         DEBUG(SSSDBG_OP_FAILURE,
    1427             :               "Unable to split [%s] in name and domain part. " \
    1428             :               "Skipping check for well-known name.\n", full_name);
    1429             : 
    1430           0 :         return ENOENT;
    1431             :     }
    1432             : 
    1433           6 :     ret = name_to_well_known_sid(wk_dom_name, wk_name, &wk_sid);
    1434           6 :     talloc_free(wk_dom_name);
    1435           6 :     talloc_free(wk_name);
    1436           6 :     if (ret != EOK) {
    1437           2 :         DEBUG(SSSDBG_TRACE_ALL, "Name [%s] is not the name of a " \
    1438             :                                  "Well-Known SID.\n", full_name);
    1439           2 :         return ret;
    1440             :     }
    1441             : 
    1442           4 :     to_sized_string(&sid, wk_sid);
    1443             : 
    1444           4 :     cctx = cmdctx->cctx;
    1445           8 :     ret = sss_packet_new(cctx->creq, sid.len + 3 * sizeof(uint32_t),
    1446           4 :                          sss_packet_get_cmd(cctx->creq->in),
    1447           4 :                          &cctx->creq->out);
    1448           4 :     if (ret != EOK) {
    1449           0 :         return ENOMEM;
    1450             :     }
    1451             : 
    1452           4 :     sss_packet_get_body(cctx->creq->out, &body, &blen);
    1453           4 :     SAFEALIGN_SETMEM_UINT32(body, 1, &pctr);  /* num results */
    1454           4 :     SAFEALIGN_SETMEM_UINT32(body + pctr, 0, &pctr); /* reserved */
    1455           4 :     SAFEALIGN_SETMEM_UINT32(body + pctr, SSS_ID_TYPE_GID, &pctr);
    1456           4 :     memcpy(&body[pctr], sid.str, sid.len);
    1457             : 
    1458           4 :     sss_packet_set_error(cctx->creq->out, EOK);
    1459           4 :     sss_cmd_done(cctx, cmdctx);
    1460           4 :     return EOK;
    1461             : }
    1462             : 
    1463             : static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx);
    1464             : static void nss_cmd_getbynam_done(struct tevent_req *req);
    1465          13 : static int nss_cmd_getpwnam(struct cli_ctx *cctx)
    1466             : {
    1467          13 :     return nss_cmd_getbynam(SSS_NSS_GETPWNAM, cctx);
    1468             : }
    1469             : 
    1470          40 : static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
    1471             : {
    1472             : 
    1473             :     struct tevent_req *req;
    1474             :     struct nss_cmd_ctx *cmdctx;
    1475             :     struct nss_dom_ctx *dctx;
    1476             :     const char *rawname;
    1477             :     char *domname;
    1478             :     uint8_t *body;
    1479             :     size_t blen;
    1480             :     int ret;
    1481             : 
    1482          40 :     switch(cmd) {
    1483             :     case SSS_NSS_GETPWNAM:
    1484             :     case SSS_NSS_GETGRNAM:
    1485             :     case SSS_NSS_INITGR:
    1486             :     case SSS_NSS_GETSIDBYNAME:
    1487             :     case SSS_NSS_GETORIGBYNAME:
    1488          40 :         break;
    1489             :     default:
    1490           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command type [%d][%s].\n",
    1491             :               cmd, sss_cmd2str(cmd));
    1492           0 :         return EINVAL;
    1493             :     }
    1494             : 
    1495          40 :     cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
    1496          40 :     if (!cmdctx) {
    1497           0 :         return ENOMEM;
    1498             :     }
    1499          40 :     cmdctx->cctx = cctx;
    1500          40 :     cmdctx->cmd = cmd;
    1501             : 
    1502          40 :     dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
    1503          40 :     if (!dctx) {
    1504           0 :         ret = ENOMEM;
    1505           0 :         goto done;
    1506             :     }
    1507          40 :     dctx->cmdctx = cmdctx;
    1508             : 
    1509             :     /* get user name to query */
    1510          40 :     sss_packet_get_body(cctx->creq->in, &body, &blen);
    1511             : 
    1512             :     /* if not terminated fail */
    1513          40 :     if (body[blen -1] != '\0') {
    1514           0 :         ret = EINVAL;
    1515           0 :         goto done;
    1516             :     }
    1517             : 
    1518             :     /* If the body isn't valid UTF-8, fail */
    1519          40 :     if (!sss_utf8_check(body, blen -1)) {
    1520           0 :         ret = EINVAL;
    1521           0 :         goto done;
    1522             :     }
    1523             : 
    1524          40 :     rawname = (const char *)body;
    1525          40 :     dctx->mc_name = rawname;
    1526             : 
    1527          40 :     DEBUG(SSSDBG_TRACE_FUNC, "Running command [%d][%s] with input [%s].\n",
    1528             :           cmd, sss_cmd2str(dctx->cmdctx->cmd), rawname);
    1529             : 
    1530          40 :     if (dctx->cmdctx->cmd == SSS_NSS_GETSIDBYNAME) {
    1531           6 :         ret = nss_check_name_of_well_known_sid(cmdctx, rawname);
    1532           6 :         if (ret != ENOENT) {
    1533           6 :             if (ret == EOK) {
    1534           4 :                 DEBUG(SSSDBG_TRACE_ALL, "Name [%s] is the name of a " \
    1535             :                                          "Well-Known SID.\n", rawname);
    1536             :             } else {
    1537           2 :                 DEBUG(SSSDBG_OP_FAILURE,
    1538             :                       "nss_check_name_of_well_known_sid failed.\n");
    1539             :             }
    1540           6 :             goto done;
    1541             :         }
    1542             :     }
    1543             : 
    1544             :     /* We need to attach to subdomain request, if the first one is not
    1545             :      * finished yet. We may not be able to lookup object in AD otherwise. */
    1546          34 :     if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
    1547          34 :         req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, NULL);
    1548          34 :         if (req == NULL) {
    1549           0 :             ret = ENOMEM;
    1550             :         } else {
    1551          34 :             dctx->rawname = rawname;
    1552          34 :             tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx);
    1553          34 :             ret = EAGAIN;
    1554             :         }
    1555          34 :         goto done;
    1556             :     }
    1557             : 
    1558           0 :     domname = NULL;
    1559           0 :     ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains,
    1560           0 :                                      cctx->rctx->default_domain, rawname,
    1561             :                                      &domname, &cmdctx->name);
    1562           0 :     if (ret == EAGAIN) {
    1563           0 :         req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, domname);
    1564           0 :         if (req == NULL) {
    1565           0 :             ret = ENOMEM;
    1566             :         } else {
    1567           0 :             dctx->rawname = rawname;
    1568           0 :             tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx);
    1569           0 :             ret = EAGAIN;
    1570             :         }
    1571           0 :         goto done;
    1572           0 :     } else if (ret != EOK) {
    1573           0 :         DEBUG(SSSDBG_OP_FAILURE, "Invalid name received [%s]\n", rawname);
    1574           0 :         ret = ENOENT;
    1575           0 :         goto done;
    1576             :     }
    1577             : 
    1578           0 :     DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for [%s] from [%s]\n",
    1579             :               cmdctx->name, domname?domname:"<ALL>");
    1580             : 
    1581           0 :     if (domname) {
    1582           0 :         dctx->domain = responder_get_domain(cctx->rctx, domname);
    1583           0 :         if (!dctx->domain) {
    1584           0 :             ret = ENOENT;
    1585           0 :             goto done;
    1586             :         }
    1587             :     } else {
    1588             :         /* this is a multidomain search */
    1589           0 :         dctx->rawname = rawname;
    1590           0 :         dctx->domain = cctx->rctx->domains;
    1591           0 :         cmdctx->check_next = true;
    1592           0 :         if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
    1593           0 :             req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, false, NULL);
    1594           0 :             if (req == NULL) {
    1595           0 :                 ret = ENOMEM;
    1596             :             } else {
    1597           0 :                 tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx);
    1598           0 :                 ret = EAGAIN;
    1599             :             }
    1600           0 :             goto done;
    1601             :         }
    1602             :     }
    1603             : 
    1604           0 :     dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
    1605             : 
    1606             :     /* ok, find it ! */
    1607           0 :     switch (dctx->cmdctx->cmd) {
    1608             :     case SSS_NSS_GETPWNAM:
    1609           0 :         ret = nss_cmd_getpwnam_search(dctx);
    1610           0 :         if (ret == EOK) {
    1611             :             /* we have results to return */
    1612           0 :             ret = nss_cmd_getpw_send_reply(dctx, false);
    1613           0 :         } else if (ret == ENOENT
    1614           0 :                 && dctx->rawname != NULL
    1615           0 :                 && strchr(dctx->rawname, '@') != NULL) {
    1616             :             /* assume Kerberos principal */
    1617           0 :             ret = nss_cmd_assume_upn(dctx);
    1618             :         }
    1619           0 :         break;
    1620             :     case SSS_NSS_GETGRNAM:
    1621           0 :         ret = nss_cmd_getgrnam_search(dctx);
    1622           0 :         if (ret == EOK) {
    1623             :             /* we have results to return */
    1624           0 :             ret = nss_cmd_getgr_send_reply(dctx, false);
    1625             :         }
    1626           0 :         break;
    1627             :     case SSS_NSS_INITGR:
    1628           0 :         ret = nss_cmd_initgroups_search(dctx);
    1629           0 :         if (ret == EOK) {
    1630             :             /* we have results to return */
    1631           0 :             ret = nss_cmd_initgr_send_reply(dctx);
    1632           0 :         } else if (ret == ENOENT
    1633           0 :                 && dctx->rawname != NULL
    1634           0 :                 && strchr(dctx->rawname, '@') != NULL) {
    1635             :             /* assume Kerberos principal */
    1636           0 :             ret = nss_cmd_assume_upn(dctx);
    1637             :         }
    1638           0 :         break;
    1639             :     case SSS_NSS_GETSIDBYNAME:
    1640             :     case SSS_NSS_GETORIGBYNAME:
    1641           0 :         ret = nss_cmd_getsidby_search(dctx);
    1642           0 :         if (ret == EOK) {
    1643           0 :             ret = nss_cmd_getbysid_send_reply(dctx);
    1644             :         }
    1645           0 :         break;
    1646             :     default:
    1647           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
    1648             :               dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
    1649           0 :         ret = EINVAL;
    1650             :     }
    1651             : 
    1652             : done:
    1653          40 :     return nss_cmd_done(cmdctx, ret);
    1654             : }
    1655             : 
    1656          34 : static void nss_cmd_getbynam_done(struct tevent_req *req)
    1657             : {
    1658          34 :     struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx);
    1659          34 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
    1660          34 :     struct cli_ctx *cctx = cmdctx->cctx;
    1661          34 :     char *domname = NULL;
    1662          34 :     const char *rawname = dctx->rawname;
    1663             :     errno_t ret;
    1664             : 
    1665          34 :     ret = sss_dp_get_domains_recv(req);
    1666          34 :     talloc_free(req);
    1667          34 :     if (ret != EOK) {
    1668           0 :         goto done;
    1669             :     }
    1670             : 
    1671          68 :     ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains,
    1672          34 :                                      cctx->rctx->default_domain, rawname,
    1673             :                                      &domname, &cmdctx->name);
    1674          34 :     if (ret == EAGAIN && (dctx->cmdctx->cmd == SSS_NSS_GETPWNAM
    1675           3 :                             || dctx->cmdctx->cmd == SSS_NSS_INITGR)) {
    1676             :         /* assume Kerberos principal */
    1677           6 :         ret = nss_cmd_assume_upn(dctx);
    1678           6 :         goto done;
    1679          28 :     } else if (ret != EOK) {
    1680           0 :         DEBUG(SSSDBG_OP_FAILURE, "Invalid name received [%s]\n", rawname);
    1681           0 :         ret = ENOENT;
    1682           0 :         goto done;
    1683             :     }
    1684             : 
    1685          28 :     ret = nss_reset_negcache(cctx->rctx);
    1686          28 :     if (ret != EOK) {
    1687           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "Cannot reset negcache records\n");
    1688             :         /* Not fatal */
    1689             :     }
    1690             : 
    1691          28 :     DEBUG(SSSDBG_TRACE_FUNC, "Requesting info for [%s] from [%s]\n",
    1692             :               cmdctx->name, domname?domname:"<ALL>");
    1693             : 
    1694          28 :     if (domname) {
    1695           6 :         dctx->domain = responder_get_domain(cctx->rctx, domname);
    1696           6 :         if (dctx->domain == NULL) {
    1697           0 :             ret = ENOENT;
    1698           0 :             goto done;
    1699             :         }
    1700             :     } else {
    1701             :         /* this is a multidomain search */
    1702          22 :         dctx->domain = cctx->rctx->domains;
    1703          22 :         cmdctx->check_next = true;
    1704             :     }
    1705             : 
    1706          28 :     dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
    1707             : 
    1708             :     /* ok, find it ! */
    1709          28 :     switch (dctx->cmdctx->cmd) {
    1710             :     case SSS_NSS_GETPWNAM:
    1711          10 :         ret = nss_cmd_getpwnam_search(dctx);
    1712          10 :         if (ret == EOK) {
    1713             :             /* we have results to return */
    1714           6 :             ret = nss_cmd_getpw_send_reply(dctx, false);
    1715           4 :         } else if (ret == ENOENT
    1716           1 :                     && dctx->rawname != NULL
    1717           1 :                     && strchr(dctx->rawname, '@') != NULL) {
    1718             :             /* assume Kerberos principal */
    1719           0 :             ret = nss_cmd_assume_upn(dctx);
    1720             :         }
    1721          10 :         break;
    1722             :     case SSS_NSS_GETGRNAM:
    1723           9 :         ret = nss_cmd_getgrnam_search(dctx);
    1724           9 :         if (ret == EOK) {
    1725             :             /* we have results to return */
    1726           9 :             ret = nss_cmd_getgr_send_reply(dctx, false);
    1727             :         }
    1728           9 :         break;
    1729             :     case SSS_NSS_INITGR:
    1730           6 :         ret = nss_cmd_initgroups_search(dctx);
    1731           6 :         if (ret == EOK) {
    1732             :             /* we have results to return */
    1733           1 :             ret = nss_cmd_initgr_send_reply(dctx);
    1734           5 :         } else if (ret == ENOENT
    1735           1 :                     && dctx->rawname != NULL
    1736           1 :                     && strchr(dctx->rawname, '@') != NULL) {
    1737             :             /* assume Kerberos principal */
    1738           0 :             ret = nss_cmd_assume_upn(dctx);
    1739             :         }
    1740           6 :         break;
    1741             :     case SSS_NSS_GETSIDBYNAME:
    1742             :     case SSS_NSS_GETORIGBYNAME:
    1743           3 :         ret = nss_cmd_getsidby_search(dctx);
    1744           3 :         if (ret == EOK) {
    1745           3 :             ret = nss_cmd_getbysid_send_reply(dctx);
    1746             :         }
    1747           3 :         break;
    1748             :     default:
    1749           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
    1750             :               dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
    1751           0 :         ret = EINVAL;
    1752             :     }
    1753             : 
    1754             : done:
    1755          34 :     nss_cmd_done(cmdctx, ret);
    1756          34 : }
    1757             : 
    1758             : /* search for a uid.
    1759             :  * Returns:
    1760             :  *   ENOENT, if uid is definitely not found
    1761             :  *   EAGAIN, if uid is being fetched from backend via async operations
    1762             :  *   EOK, if found
    1763             :  *   anything else on a fatal error
    1764             :  */
    1765             : 
    1766           7 : static int nss_cmd_getpwuid_search(struct nss_dom_ctx *dctx)
    1767             : {
    1768           7 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
    1769           7 :     struct sss_domain_info *dom = dctx->domain;
    1770           7 :     struct cli_ctx *cctx = cmdctx->cctx;
    1771             :     struct nss_ctx *nctx;
    1772             :     int ret;
    1773             :     int err;
    1774           7 :     const char *extra_flag = NULL;
    1775             : 
    1776           7 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    1777             : 
    1778           7 :     while (dom) {
    1779             : 
    1780             :         /* check that the uid is valid for this domain */
    1781          14 :         if ((dom->id_min && (cmdctx->id < dom->id_min)) ||
    1782           7 :             (dom->id_max && (cmdctx->id > dom->id_max))) {
    1783           0 :             DEBUG(SSSDBG_CONF_SETTINGS,
    1784             :                   "Uid [%"PRIu32"] does not exist in domain [%s]! "
    1785             :                       "(id out of range)\n",
    1786             :                       cmdctx->id, dom->name);
    1787           0 :             if (cmdctx->check_next) {
    1788           0 :                 dom = get_next_domain(dom, true);
    1789           0 :                 continue;
    1790             :             }
    1791           0 :             ret = ENOENT;
    1792           0 :             goto done;
    1793             :         }
    1794             : 
    1795           7 :         if (dom != dctx->domain) {
    1796             :             /* make sure we reset the check_provider flag when we check
    1797             :              * a new domain */
    1798           0 :             dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
    1799             :         }
    1800             : 
    1801             :         /* make sure to update the dctx if we changed domain */
    1802           7 :         dctx->domain = dom;
    1803             : 
    1804           7 :         DEBUG(SSSDBG_CONF_SETTINGS,
    1805             :               "Requesting info for [%"PRIu32"@%s]\n", cmdctx->id, dom->name);
    1806             : 
    1807           7 :         if (dom->sysdb == NULL) {
    1808           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    1809             :                   "Fatal: Sysdb CTX not found for this domain!\n");
    1810           0 :             ret = EIO;
    1811           0 :             goto done;
    1812             :         }
    1813             : 
    1814           7 :         ret = sysdb_getpwuid_with_views(cmdctx, dom, cmdctx->id, &dctx->res);
    1815           7 :         if (ret != EOK) {
    1816           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1817             :                   "Failed to make request to our cache!\n");
    1818           0 :             ret = EIO;
    1819           0 :             goto done;
    1820             :         }
    1821             : 
    1822           7 :         if (dctx->res->count > 1) {
    1823           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    1824             :                   "getpwuid call returned more than one result !?!\n");
    1825           0 :             sss_log(SSS_LOG_ERR,
    1826             :                     "More users have the same UID [%"PRIu32"] in directory "
    1827             :                     "server. SSSD will not work correctly.\n", cmdctx->id);
    1828           0 :             ret = ENOENT;
    1829           0 :             goto done;
    1830             :         }
    1831             : 
    1832           7 :         if (dctx->res->count == 0 && !dctx->check_provider) {
    1833             :             /* if a multidomain search, try with next */
    1834           1 :             if (cmdctx->check_next) {
    1835           1 :                 dom = get_next_domain(dom, true);
    1836           1 :                 continue;
    1837             :             }
    1838             : 
    1839             :             /* set negative cache only if not result of cache check */
    1840           0 :             DEBUG(SSSDBG_MINOR_FAILURE, "No results for getpwuid call\n");
    1841           0 :             ret = ENOENT;
    1842           0 :             goto done;
    1843             :         }
    1844             : 
    1845             :         /* if this is a caching provider (or if we haven't checked the cache
    1846             :          * yet) then verify that the cache is uptodate */
    1847           6 :         if (dctx->check_provider) {
    1848             : 
    1849           4 :             if (DOM_HAS_VIEWS(dom) && (dctx->res->count == 0
    1850           0 :                     || ldb_msg_find_attr_as_uint64(dctx->res->msgs[0],
    1851             :                                                    OVERRIDE_PREFIX SYSDB_UIDNUM,
    1852             :                                                    0) != 0)) {
    1853           0 :                 extra_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
    1854             :             } else {
    1855           4 :                 extra_flag = NULL;
    1856             :             }
    1857             : 
    1858           4 :             ret = check_cache(dctx, nctx, dctx->res, SSS_DP_USER, NULL,
    1859             :                               cmdctx->id, extra_flag, nss_cmd_getby_dp_callback,
    1860             :                               dctx);
    1861           4 :             if (ret != EOK) {
    1862             :                 /* Anything but EOK means we should reenter the mainloop
    1863             :                  * because we may be refreshing the cache
    1864             :                  */
    1865           3 :                 goto done;
    1866             :             }
    1867             :         }
    1868             : 
    1869             :         /* One result found */
    1870           3 :         DEBUG(SSSDBG_TRACE_FUNC,
    1871             :               "Returning info for uid [%"PRIu32"@%s]\n", cmdctx->id, dom->name);
    1872             : 
    1873           3 :         ret = EOK;
    1874           3 :         goto done;
    1875             :     }
    1876             : 
    1877             :     /* All domains were tried and none had the entry. */
    1878           1 :     ret = ENOENT;
    1879             : done:
    1880           7 :     if (ret == ENOENT) {
    1881             :         /* The entry was not found, need to set result in negative cache */
    1882           1 :         err = sss_ncache_set_uid(nctx->ncache, false, NULL, cmdctx->id);
    1883           1 :         if (err != EOK) {
    1884           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
    1885             :                 "Cannot set negative cache for UID %"PRIu32"\n", cmdctx->id);
    1886             :         }
    1887             :     }
    1888             : 
    1889           7 :     DEBUG(SSSDBG_MINOR_FAILURE, "No matching domain found for [%"PRIu32"]\n", cmdctx->id);
    1890           7 :     return ret;
    1891             : }
    1892             : 
    1893             : static int nss_cmd_getgrgid_search(struct nss_dom_ctx *dctx);
    1894             : static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx);
    1895             : static void nss_cmd_getbyid_done(struct tevent_req *req);
    1896           5 : static int nss_cmd_getpwuid(struct cli_ctx *cctx)
    1897             : {
    1898           5 :     return nss_cmd_getbyid(SSS_NSS_GETPWUID, cctx);
    1899             : }
    1900             : 
    1901           5 : static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx)
    1902             : {
    1903             :     struct nss_cmd_ctx *cmdctx;
    1904             :     struct nss_dom_ctx *dctx;
    1905             :     struct nss_ctx *nctx;
    1906             :     uint8_t *body;
    1907             :     size_t blen;
    1908             :     int ret;
    1909             :     struct tevent_req *req;
    1910             : 
    1911           5 :     switch (cmd) {
    1912             :     case SSS_NSS_GETPWUID:
    1913             :     case SSS_NSS_GETGRGID:
    1914             :     case SSS_NSS_GETSIDBYID:
    1915           5 :         break;
    1916             :     default:
    1917           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command type [%d][%s].\n",
    1918             :               cmd, sss_cmd2str(cmd));
    1919           0 :         return EINVAL;
    1920             :     }
    1921             : 
    1922           5 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    1923             : 
    1924           5 :     cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
    1925           5 :     if (!cmdctx) {
    1926           0 :         return ENOMEM;
    1927             :     }
    1928           5 :     cmdctx->cctx = cctx;
    1929           5 :     cmdctx->cmd = cmd;
    1930             : 
    1931           5 :     dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
    1932           5 :     if (!dctx) {
    1933           0 :         ret = ENOMEM;
    1934           0 :         goto done;
    1935             :     }
    1936           5 :     dctx->cmdctx = cmdctx;
    1937             : 
    1938             :     /* get id to query */
    1939           5 :     sss_packet_get_body(cctx->creq->in, &body, &blen);
    1940             : 
    1941           5 :     if (blen != sizeof(uint32_t)) {
    1942           0 :         ret = EINVAL;
    1943           0 :         goto done;
    1944             :     }
    1945           5 :     SAFEALIGN_COPY_UINT32(&cmdctx->id, body, NULL);
    1946             : 
    1947           5 :     DEBUG(SSSDBG_TRACE_FUNC, "Running command [%d][%s] with id [%"PRIu32"].\n",
    1948             :           dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd), cmdctx->id);
    1949             : 
    1950           5 :     switch(dctx->cmdctx->cmd) {
    1951             :     case SSS_NSS_GETPWUID:
    1952           5 :         ret = sss_ncache_check_uid(nctx->ncache, nctx->neg_timeout, NULL,
    1953             :                                    cmdctx->id);
    1954           5 :         if (ret == EEXIST) {
    1955           1 :             DEBUG(SSSDBG_TRACE_FUNC,
    1956             :                   "Uid [%"PRIu32"] does not exist! (negative cache)\n",
    1957             :                    cmdctx->id);
    1958           1 :             ret = ENOENT;
    1959           1 :             goto done;
    1960             :         }
    1961           4 :         break;
    1962             :     case SSS_NSS_GETGRGID:
    1963           0 :         ret = sss_ncache_check_gid(nctx->ncache, nctx->neg_timeout, NULL,
    1964             :                                    cmdctx->id);
    1965           0 :         if (ret == EEXIST) {
    1966           0 :             DEBUG(SSSDBG_TRACE_FUNC,
    1967             :                   "Gid [%"PRIu32"] does not exist! (negative cache)\n",
    1968             :                    cmdctx->id);
    1969           0 :             ret = ENOENT;
    1970           0 :             goto done;
    1971             :         }
    1972           0 :         break;
    1973             :     case SSS_NSS_GETSIDBYID:
    1974           0 :         ret = sss_ncache_check_uid(nctx->ncache, nctx->neg_timeout, NULL,
    1975             :                                    cmdctx->id);
    1976           0 :         if (ret != EEXIST) {
    1977           0 :             ret = sss_ncache_check_gid(nctx->ncache, nctx->neg_timeout,
    1978             :                                        NULL, cmdctx->id);
    1979             :         }
    1980           0 :         if (ret == EEXIST) {
    1981           0 :             DEBUG(SSSDBG_TRACE_FUNC,
    1982             :                   "Id [%"PRIu32"] does not exist! (negative cache)\n",
    1983             :                    cmdctx->id);
    1984           0 :             ret = ENOENT;
    1985           0 :             goto done;
    1986             :         }
    1987           0 :         break;
    1988             :     default:
    1989           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
    1990             :               dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
    1991           0 :         ret = EINVAL;
    1992           0 :         goto done;
    1993             :     }
    1994             : 
    1995             :     /* id searches are always multidomain */
    1996           4 :     dctx->domain = cctx->rctx->domains;
    1997           4 :     cmdctx->check_next = true;
    1998             : 
    1999           4 :     dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
    2000             : 
    2001           4 :     if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
    2002           4 :         req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, false, NULL);
    2003           4 :         if (req == NULL) {
    2004           0 :             ret = ENOMEM;
    2005             :         } else {
    2006           4 :             tevent_req_set_callback(req, nss_cmd_getbyid_done, dctx);
    2007           4 :             ret = EAGAIN;
    2008             :         }
    2009           4 :         goto done;
    2010             :     }
    2011             : 
    2012             :     /* ok, find it ! */
    2013           0 :     switch(dctx->cmdctx->cmd) {
    2014             :     case SSS_NSS_GETPWUID:
    2015           0 :         ret = nss_cmd_getpwuid_search(dctx);
    2016           0 :         if (ret == EOK) {
    2017             :             /* we have results to return */
    2018           0 :             ret = nss_cmd_getpw_send_reply(dctx, true);
    2019             :         }
    2020           0 :         break;
    2021             :     case SSS_NSS_GETGRGID:
    2022           0 :         ret = nss_cmd_getgrgid_search(dctx);
    2023           0 :         if (ret == EOK) {
    2024             :             /* we have results to return */
    2025           0 :             ret = nss_cmd_getgr_send_reply(dctx, true);
    2026             :         }
    2027           0 :         break;
    2028             :     case SSS_NSS_GETSIDBYID:
    2029           0 :         ret = nss_cmd_getsidby_search(dctx);
    2030           0 :         if (ret == EOK) {
    2031           0 :             ret = nss_cmd_getbysid_send_reply(dctx);
    2032             :         }
    2033           0 :         break;
    2034             :     default:
    2035           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
    2036             :               dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
    2037           0 :         ret = EINVAL;
    2038             :     }
    2039             : 
    2040             : done:
    2041           5 :     return nss_cmd_done(cmdctx, ret);
    2042             : }
    2043             : 
    2044           4 : static void nss_cmd_getbyid_done(struct tevent_req *req)
    2045             : {
    2046           4 :     struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx);
    2047           4 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
    2048             :     errno_t ret;
    2049             : 
    2050           4 :     ret = sss_dp_get_domains_recv(req);
    2051           4 :     talloc_free(req);
    2052           4 :     if (ret != EOK) {
    2053           0 :         goto done;
    2054             :     }
    2055             : 
    2056           4 :     ret = nss_reset_negcache(cmdctx->cctx->rctx);
    2057           4 :     if (ret != EOK) {
    2058           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "Cannot reset negcache records\n");
    2059             :         /* Not fatal */
    2060             :     }
    2061             : 
    2062             :     /* ok, find it ! */
    2063           4 :     switch(dctx->cmdctx->cmd) {
    2064             :     case SSS_NSS_GETPWUID:
    2065           4 :         ret = nss_cmd_getpwuid_search(dctx);
    2066           4 :         if (ret == EOK) {
    2067             :             /* we have results to return */
    2068           1 :             ret = nss_cmd_getpw_send_reply(dctx, true);
    2069             :         }
    2070           4 :         break;
    2071             :     case SSS_NSS_GETGRGID:
    2072           0 :         ret = nss_cmd_getgrgid_search(dctx);
    2073           0 :         if (ret == EOK) {
    2074             :             /* we have results to return */
    2075           0 :             ret = nss_cmd_getgr_send_reply(dctx, true);
    2076             :         }
    2077           0 :         break;
    2078             :     case SSS_NSS_GETNAMEBYSID:
    2079             :     case SSS_NSS_GETIDBYSID:
    2080           0 :         ret = responder_get_domain_by_id(cmdctx->cctx->rctx, cmdctx->secid,
    2081             :                                          &dctx->domain);
    2082           0 :         if (ret != EOK) {
    2083           0 :             DEBUG(SSSDBG_OP_FAILURE, "Cannot find domain for SID [%s].\n",
    2084             :                                       cmdctx->secid);
    2085           0 :             ret = ENOENT;
    2086           0 :             goto done;
    2087             :         }
    2088             : 
    2089           0 :         dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
    2090             : 
    2091           0 :         ret = nss_cmd_getbysid_search(dctx);
    2092           0 :         if (ret == EOK) {
    2093           0 :             ret = nss_cmd_getbysid_send_reply(dctx);
    2094             :         }
    2095           0 :         break;
    2096             :     case SSS_NSS_GETSIDBYID:
    2097           0 :         ret = nss_cmd_getsidby_search(dctx);
    2098           0 :         if (ret == EOK) {
    2099           0 :             ret = nss_cmd_getbysid_send_reply(dctx);
    2100             :         }
    2101           0 :         break;
    2102             :     default:
    2103           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
    2104             :               dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
    2105           0 :         ret = EINVAL;
    2106             :     }
    2107             : 
    2108             : done:
    2109           4 :     nss_cmd_done(cmdctx, ret);
    2110           4 : }
    2111             : 
    2112             : /* to keep it simple at this stage we are retrieving the
    2113             :  * full enumeration again for each request for each process
    2114             :  * and we also block on setpwent() for the full time needed
    2115             :  * to retrieve the data. And endpwent() frees all the data.
    2116             :  * Next steps are:
    2117             :  * - use an nsssrv wide cache with data already structured
    2118             :  *   so that it can be immediately returned (see nscd way)
    2119             :  * - use mutexes so that setpwent() can return immediately
    2120             :  *   even if the data is still being fetched
    2121             :  * - make getpwent() wait on the mutex
    2122             :  *
    2123             :  * Alternatively:
    2124             :  * - use a smarter search mechanism that keeps track of the
    2125             :  *   last user searched and return the next X users doing
    2126             :  *   an alphabetic sort and starting from the user following
    2127             :  *   the last returned user.
    2128             :  */
    2129             : static int nss_cmd_getpwent_immediate(struct nss_cmd_ctx *cmdctx);
    2130             : struct tevent_req * nss_cmd_setpwent_send(TALLOC_CTX *mem_ctx,
    2131             :                                           struct cli_ctx *client);
    2132             : static void nss_cmd_setpwent_done(struct tevent_req *req);
    2133           0 : static int nss_cmd_setpwent(struct cli_ctx *cctx)
    2134             : {
    2135             :     struct nss_cmd_ctx *cmdctx;
    2136             :     struct tevent_req *req;
    2137           0 :     errno_t ret = EOK;
    2138             : 
    2139           0 :     cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
    2140           0 :     if (!cmdctx) {
    2141           0 :         return ENOMEM;
    2142             :     }
    2143           0 :     cmdctx->cctx = cctx;
    2144             : 
    2145           0 :     req = nss_cmd_setpwent_send(cmdctx, cctx);
    2146           0 :     if (!req) {
    2147           0 :         DEBUG(SSSDBG_FATAL_FAILURE,
    2148             :               "Fatal error calling nss_cmd_setpwent_send\n");
    2149           0 :         ret = EIO;
    2150           0 :         goto done;
    2151             :     }
    2152           0 :     tevent_req_set_callback(req, nss_cmd_setpwent_done, cmdctx);
    2153             : 
    2154             : done:
    2155           0 :     return nss_cmd_done(cmdctx, ret);
    2156             : }
    2157             : 
    2158             : static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx);
    2159           0 : struct tevent_req *nss_cmd_setpwent_send(TALLOC_CTX *mem_ctx,
    2160             :                                          struct cli_ctx *client)
    2161             : {
    2162             :     errno_t ret;
    2163             :     struct nss_ctx *nctx;
    2164             :     struct tevent_req *req;
    2165             :     struct setent_ctx *state;
    2166             :     struct sss_domain_info *dom;
    2167             :     struct setent_step_ctx *step_ctx;
    2168             : 
    2169           0 :     DEBUG(SSSDBG_CONF_SETTINGS, "Received setpwent request\n");
    2170           0 :     nctx = talloc_get_type(client->rctx->pvt_ctx, struct nss_ctx);
    2171             : 
    2172             :     /* Reset the read pointers */
    2173           0 :     client->pwent_dom_idx = 0;
    2174           0 :     client->pwent_cur = 0;
    2175             : 
    2176           0 :     req = tevent_req_create(mem_ctx, &state, struct setent_ctx);
    2177           0 :     if (!req) {
    2178           0 :         DEBUG(SSSDBG_FATAL_FAILURE,
    2179             :               "Could not create tevent request for setpwent\n");
    2180           0 :         return NULL;
    2181             :     }
    2182             : 
    2183           0 :     state->nctx = nctx;
    2184           0 :     state->client = client;
    2185             : 
    2186           0 :     state->dctx = talloc_zero(state, struct nss_dom_ctx);
    2187           0 :     if (!state->dctx) {
    2188           0 :         ret = ENOMEM;
    2189           0 :         goto error;
    2190             :     }
    2191             : 
    2192             :     /* check if enumeration is enabled in any domain */
    2193           0 :     for (dom = client->rctx->domains; dom; dom = get_next_domain(dom, true)) {
    2194           0 :         if (dom->enumerate == true) break;
    2195             :     }
    2196           0 :     state->dctx->domain = dom;
    2197             : 
    2198           0 :     if (state->dctx->domain == NULL) {
    2199           0 :         DEBUG(SSSDBG_OP_FAILURE, "Enumeration disabled on all domains!\n");
    2200           0 :         ret = ENOENT;
    2201           0 :         goto error;
    2202             :     }
    2203             : 
    2204           0 :     state->dctx->check_provider =
    2205           0 :             NEED_CHECK_PROVIDER(state->dctx->domain->provider);
    2206             : 
    2207             :     /* Is the result context already available */
    2208           0 :     if (state->nctx->pctx) {
    2209           0 :         if (state->nctx->pctx->ready) {
    2210             :             /* All of the necessary data is in place
    2211             :              * We can return now, getpwent requests will work at this point
    2212             :              */
    2213           0 :             tevent_req_done(req);
    2214           0 :             tevent_req_post(req, state->nctx->rctx->ev);
    2215             :         }
    2216             :         else {
    2217             :             /* Object is still being constructed
    2218             :              * Register for notification when it's
    2219             :              * ready.
    2220             :              */
    2221           0 :             ret = nss_setent_add_ref(state, state->nctx->pctx, req);
    2222           0 :             if (ret != EOK) {
    2223           0 :                 talloc_free(req);
    2224           0 :                 return NULL;
    2225             :             }
    2226             :         }
    2227           0 :         return req;
    2228             :     }
    2229             : 
    2230             :     /* Create a new result context
    2231             :      * We are creating it on the nss_ctx so that it doesn't
    2232             :      * go away if the original request does. We will delete
    2233             :      * it when the refcount goes to zero;
    2234             :      */
    2235           0 :     state->nctx->pctx = talloc_zero(nctx, struct getent_ctx);
    2236           0 :     if (!state->nctx->pctx) {
    2237           0 :         ret = ENOMEM;
    2238           0 :         goto error;
    2239             :     }
    2240           0 :     state->getent_ctx = nctx->pctx;
    2241             : 
    2242             :     /* Add a callback reference for ourselves */
    2243           0 :     ret = nss_setent_add_ref(state, state->nctx->pctx, req);
    2244           0 :     if (ret) goto error;
    2245             : 
    2246             :     /* ok, start the searches */
    2247           0 :     step_ctx = talloc_zero(state->getent_ctx, struct setent_step_ctx);
    2248           0 :     if (!step_ctx) {
    2249           0 :         ret = ENOMEM;
    2250           0 :         goto error;
    2251             :     }
    2252             : 
    2253             :     /* Steal the dom_ctx onto the step_ctx so it doesn't go out of scope if
    2254             :      * this request is canceled while other requests are in-progress.
    2255             :      */
    2256           0 :     step_ctx->dctx = talloc_steal(step_ctx, state->dctx);
    2257           0 :     step_ctx->nctx = state->nctx;
    2258           0 :     step_ctx->getent_ctx = state->getent_ctx;
    2259           0 :     step_ctx->rctx = client->rctx;
    2260           0 :     step_ctx->cctx = client;
    2261           0 :     step_ctx->returned_to_mainloop = false;
    2262             : 
    2263           0 :     ret = nss_cmd_setpwent_step(step_ctx);
    2264           0 :     if (ret != EOK && ret != EAGAIN) goto error;
    2265             : 
    2266           0 :     if (ret == EOK) {
    2267           0 :         tevent_req_post(req, client->rctx->ev);
    2268             :     }
    2269             : 
    2270           0 :     return req;
    2271             : 
    2272             :  error:
    2273           0 :      tevent_req_error(req, ret);
    2274           0 :      tevent_req_post(req, client->rctx->ev);
    2275           0 :      return req;
    2276             : }
    2277             : 
    2278             : static void nss_cmd_setpwent_dp_callback(uint16_t err_maj, uint32_t err_min,
    2279             :                                          const char *err_msg, void *ptr);
    2280             : static void setpwent_result_timeout(struct tevent_context *ev,
    2281             :                                     struct tevent_timer *te,
    2282             :                                     struct timeval current_time,
    2283             :                                     void *pvt);
    2284             : 
    2285             : /* nss_cmd_setpwent_step returns
    2286             :  *   EOK if everything is done and the request needs to be posted explicitly
    2287             :  *   EAGAIN if the caller can safely return to the main loop
    2288             :  */
    2289           0 : static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx)
    2290             : {
    2291             :     errno_t ret;
    2292           0 :     struct sss_domain_info *dom = step_ctx->dctx->domain;
    2293           0 :     struct resp_ctx *rctx = step_ctx->rctx;
    2294           0 :     struct nss_dom_ctx *dctx = step_ctx->dctx;
    2295           0 :     struct getent_ctx *pctx = step_ctx->getent_ctx;
    2296           0 :     struct nss_ctx *nctx = step_ctx->nctx;
    2297             :     struct ldb_result *res;
    2298             :     struct timeval tv;
    2299             :     struct tevent_timer *te;
    2300             :     struct tevent_req *dpreq;
    2301             :     struct dp_callback_ctx *cb_ctx;
    2302             : 
    2303           0 :     while (dom) {
    2304           0 :         while (dom && dom->enumerate == false) {
    2305           0 :             dom = get_next_domain(dom, true);
    2306             :         }
    2307             : 
    2308           0 :         if (!dom) break;
    2309             : 
    2310           0 :         if (dom != dctx->domain) {
    2311             :             /* make sure we reset the check_provider flag when we check
    2312             :              * a new domain */
    2313           0 :             dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
    2314             :         }
    2315             : 
    2316             :         /* make sure to update the dctx if we changed domain */
    2317           0 :         dctx->domain = dom;
    2318             : 
    2319           0 :         DEBUG(SSSDBG_TRACE_FUNC,
    2320             :               "Requesting info for domain [%s]\n", dom->name);
    2321             : 
    2322           0 :         if (dom->sysdb == NULL) {
    2323           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    2324             :                   "Fatal: Sysdb CTX not found for this domain!\n");
    2325           0 :             return EIO;
    2326             :         }
    2327             : 
    2328             :         /* if this is a caching provider (or if we haven't checked the cache
    2329             :          * yet) then verify that the cache is uptodate */
    2330           0 :         if (dctx->check_provider) {
    2331           0 :             step_ctx->returned_to_mainloop = true;
    2332             :             /* Only do this once per provider */
    2333           0 :             dctx->check_provider = false;
    2334             : 
    2335           0 :             dpreq = sss_dp_get_account_send(step_ctx, rctx, dctx->domain, true,
    2336             :                                           SSS_DP_USER, NULL, 0, NULL);
    2337           0 :             if (!dpreq) {
    2338           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
    2339             :                       "Enum Cache refresh for domain [%s] failed."
    2340             :                        " Trying to return what we have in cache!\n",
    2341             :                        dom->name);
    2342             :             } else {
    2343           0 :                 cb_ctx = talloc_zero(step_ctx, struct dp_callback_ctx);
    2344           0 :                 if(!cb_ctx) {
    2345           0 :                     talloc_zfree(dpreq);
    2346           0 :                     return ENOMEM;
    2347             :                 }
    2348             : 
    2349           0 :                 cb_ctx->callback = nss_cmd_setpwent_dp_callback;
    2350           0 :                 cb_ctx->ptr = step_ctx;
    2351           0 :                 cb_ctx->cctx = step_ctx->cctx;
    2352           0 :                 cb_ctx->mem_ctx = step_ctx;
    2353             : 
    2354           0 :                 tevent_req_set_callback(dpreq, nsssrv_dp_send_acct_req_done, cb_ctx);
    2355             : 
    2356           0 :                 return EAGAIN;
    2357             :             }
    2358             :         }
    2359             : 
    2360           0 :         ret = sysdb_enumpwent_with_views(dctx, dom, &res);
    2361           0 :         if (ret != EOK) {
    2362           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    2363             :                   "Enum from cache failed, skipping domain [%s]\n",
    2364             :                       dom->name);
    2365           0 :             dom = get_next_domain(dom, true);
    2366           0 :             continue;
    2367             :         }
    2368             : 
    2369           0 :         if (res->count == 0) {
    2370           0 :             DEBUG(SSSDBG_CONF_SETTINGS,
    2371             :                   "Domain [%s] has no users, skipping.\n", dom->name);
    2372           0 :             dom = get_next_domain(dom, true);
    2373           0 :             continue;
    2374             :         }
    2375             : 
    2376           0 :         nctx->pctx->doms = talloc_realloc(pctx, pctx->doms,
    2377             :                                     struct dom_ctx, pctx->num +1);
    2378           0 :         if (!pctx->doms) {
    2379           0 :             talloc_free(pctx);
    2380           0 :             nctx->pctx = NULL;
    2381           0 :             return ENOMEM;
    2382             :         }
    2383             : 
    2384           0 :         nctx->pctx->doms[pctx->num].domain = dctx->domain;
    2385           0 :         nctx->pctx->doms[pctx->num].res = talloc_steal(pctx->doms, res);
    2386             : 
    2387           0 :         nctx->pctx->num++;
    2388             : 
    2389             :         /* do not reply until all domain searches are done */
    2390           0 :         dom = get_next_domain(dom, true);
    2391             :     }
    2392             : 
    2393             :     /* We've finished all our lookups
    2394             :      * The result object is now safe to read.
    2395             :      */
    2396           0 :     nctx->pctx->ready = true;
    2397             : 
    2398             :     /* Set up a lifetime timer for this result object
    2399             :      * We don't want this result object to outlive the
    2400             :      * enum cache refresh timeout
    2401             :      */
    2402           0 :     tv = tevent_timeval_current_ofs(nctx->enum_cache_timeout, 0);
    2403           0 :     te = tevent_add_timer(rctx->ev, nctx->pctx, tv,
    2404             :                           setpwent_result_timeout, nctx);
    2405           0 :     if (!te) {
    2406           0 :         DEBUG(SSSDBG_FATAL_FAILURE,
    2407             :               "Could not set up life timer for setpwent result object. "
    2408             :                   "Entries may become stale.\n");
    2409             :     }
    2410             : 
    2411             :     /* Notify the waiting clients */
    2412           0 :     nss_setent_notify_done(nctx->pctx);
    2413             : 
    2414           0 :     if (step_ctx->returned_to_mainloop) {
    2415           0 :         return EAGAIN;
    2416             :     } else {
    2417           0 :         return EOK;
    2418             :     }
    2419             : }
    2420             : 
    2421           0 : static void setpwent_result_timeout(struct tevent_context *ev,
    2422             :                                     struct tevent_timer *te,
    2423             :                                     struct timeval current_time,
    2424             :                                     void *pvt)
    2425             : {
    2426           0 :     struct nss_ctx *nctx = talloc_get_type(pvt, struct nss_ctx);
    2427             : 
    2428           0 :     DEBUG(SSSDBG_CRIT_FAILURE,
    2429             :           "setpwent result object has expired. Cleaning up.\n");
    2430             : 
    2431             :     /* Free the passwd enumeration context.
    2432             :      * If additional getpwent requests come in, they will invoke
    2433             :      * an implicit setpwent and refresh the result object.
    2434             :      */
    2435           0 :     talloc_zfree(nctx->pctx);
    2436           0 : }
    2437             : 
    2438           0 : static void nss_cmd_setpwent_dp_callback(uint16_t err_maj, uint32_t err_min,
    2439             :                                          const char *err_msg, void *ptr)
    2440             : {
    2441           0 :     struct setent_step_ctx *step_ctx =
    2442             :             talloc_get_type(ptr, struct setent_step_ctx);
    2443             :     int ret;
    2444             : 
    2445           0 :     if (err_maj) {
    2446           0 :         DEBUG(SSSDBG_OP_FAILURE,
    2447             :               "Unable to get information from Data Provider\n"
    2448             :                   "Error: %u, %u, %s\n"
    2449             :                   "Will try to return what we have in cache\n",
    2450             :                   (unsigned int)err_maj, (unsigned int)err_min, err_msg);
    2451             :     }
    2452             : 
    2453           0 :     ret = nss_cmd_setpwent_step(step_ctx);
    2454           0 :     if (ret != EOK && ret != EAGAIN) {
    2455             :         /* Notify any waiting processes of failure */
    2456           0 :         nss_setent_notify_error(step_ctx->nctx->pctx, ret);
    2457             :     }
    2458           0 : }
    2459             : 
    2460           0 : static errno_t nss_cmd_setpwent_recv(struct tevent_req *req)
    2461             : {
    2462           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    2463           0 :     return EOK;
    2464             : }
    2465             : 
    2466           0 : static void nss_cmd_setpwent_done(struct tevent_req *req)
    2467             : {
    2468             :     errno_t ret;
    2469           0 :     struct nss_cmd_ctx *cmdctx =
    2470           0 :             tevent_req_callback_data(req, struct nss_cmd_ctx);
    2471             : 
    2472           0 :     ret = nss_cmd_setpwent_recv(req);
    2473           0 :     talloc_zfree(req);
    2474           0 :     if (ret == EOK || ret == ENOENT) {
    2475             :         /* Either we succeeded or no domains were eligible */
    2476           0 :         ret = sss_packet_new(cmdctx->cctx->creq, 0,
    2477           0 :                              sss_packet_get_cmd(cmdctx->cctx->creq->in),
    2478           0 :                              &cmdctx->cctx->creq->out);
    2479           0 :         if (ret == EOK) {
    2480           0 :             sss_cmd_done(cmdctx->cctx, cmdctx);
    2481           0 :             return;
    2482             :         }
    2483             :     }
    2484             : 
    2485             :     /* Something bad happened */
    2486           0 :     nss_cmd_done(cmdctx, ret);
    2487             : }
    2488             : 
    2489             : static void nss_cmd_implicit_setpwent_done(struct tevent_req *req);
    2490           0 : static int nss_cmd_getpwent(struct cli_ctx *cctx)
    2491             : {
    2492             :     struct nss_ctx *nctx;
    2493             :     struct nss_cmd_ctx *cmdctx;
    2494             :     struct tevent_req *req;
    2495             : 
    2496           0 :     DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for all accounts\n");
    2497             : 
    2498           0 :     cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
    2499           0 :     if (!cmdctx) {
    2500           0 :         return ENOMEM;
    2501             :     }
    2502           0 :     cmdctx->cctx = cctx;
    2503             : 
    2504             :     /* Save the current index and cursor locations
    2505             :      * If we end up calling setpwent implicitly, because the response object
    2506             :      * expired and has to be recreated, we want to resume from the same
    2507             :      * location.
    2508             :      */
    2509           0 :     cmdctx->saved_dom_idx = cctx->pwent_dom_idx;
    2510           0 :     cmdctx->saved_cur = cctx->pwent_cur;
    2511             : 
    2512           0 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    2513           0 :     if(!nctx->pctx || !nctx->pctx->ready) {
    2514             :         /* Make sure we invoke setpwent if it hasn't been run or is still
    2515             :          * processing from another client
    2516             :          */
    2517           0 :         req = nss_cmd_setpwent_send(cctx, cctx);
    2518           0 :         if (!req) {
    2519           0 :             return EIO;
    2520             :         }
    2521           0 :         tevent_req_set_callback(req, nss_cmd_implicit_setpwent_done, cmdctx);
    2522           0 :         return EOK;
    2523             :     }
    2524             : 
    2525           0 :     return nss_cmd_getpwent_immediate(cmdctx);
    2526             : }
    2527             : 
    2528             : static int nss_cmd_retpwent(struct cli_ctx *cctx, int num);
    2529           0 : static int nss_cmd_getpwent_immediate(struct nss_cmd_ctx *cmdctx)
    2530             : {
    2531           0 :     struct cli_ctx *cctx = cmdctx->cctx;
    2532             :     uint8_t *body;
    2533             :     size_t blen;
    2534             :     uint32_t num;
    2535             :     int ret;
    2536             : 
    2537             :     /* get max num of entries to return in one call */
    2538           0 :     sss_packet_get_body(cctx->creq->in, &body, &blen);
    2539           0 :     if (blen != sizeof(uint32_t)) {
    2540           0 :         return EINVAL;
    2541             :     }
    2542           0 :     SAFEALIGN_COPY_UINT32(&num, body, NULL);
    2543             : 
    2544             :     /* create response packet */
    2545           0 :     ret = sss_packet_new(cctx->creq, 0,
    2546           0 :                          sss_packet_get_cmd(cctx->creq->in),
    2547           0 :                          &cctx->creq->out);
    2548           0 :     if (ret != EOK) {
    2549           0 :         return ret;
    2550             :     }
    2551             : 
    2552           0 :     ret = nss_cmd_retpwent(cctx, num);
    2553             : 
    2554           0 :     sss_packet_set_error(cctx->creq->out, ret);
    2555           0 :     sss_cmd_done(cctx, cmdctx);
    2556             : 
    2557           0 :     return EOK;
    2558             : }
    2559             : 
    2560           0 : static int nss_cmd_retpwent(struct cli_ctx *cctx, int num)
    2561             : {
    2562             :     struct nss_ctx *nctx;
    2563             :     struct getent_ctx *pctx;
    2564           0 :     struct ldb_message **msgs = NULL;
    2565           0 :     struct dom_ctx *pdom = NULL;
    2566           0 :     int n = 0;
    2567           0 :     int ret = ENOENT;
    2568             : 
    2569           0 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    2570           0 :     if (!nctx->pctx) goto none;
    2571             : 
    2572           0 :     pctx = nctx->pctx;
    2573             : 
    2574           0 :     while (ret == ENOENT) {
    2575           0 :         if (cctx->pwent_dom_idx >= pctx->num) break;
    2576             : 
    2577           0 :         pdom = &pctx->doms[cctx->pwent_dom_idx];
    2578             : 
    2579           0 :         n = pdom->res->count - cctx->pwent_cur;
    2580           0 :         if (n <= 0 && (cctx->pwent_dom_idx+1 < pctx->num)) {
    2581           0 :             cctx->pwent_dom_idx++;
    2582           0 :             pdom = &pctx->doms[cctx->pwent_dom_idx];
    2583           0 :             n = pdom->res->count;
    2584           0 :             cctx->pwent_cur = 0;
    2585             :         }
    2586             : 
    2587           0 :         if (!n) break;
    2588             : 
    2589           0 :         if (n < 0) {
    2590           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Negative difference"
    2591             :                   "[%d - %d = %d]\n", pdom->res->count, cctx->pwent_cur, n);
    2592           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Domain: %d (total %d)\n",
    2593             :                                         cctx->pwent_dom_idx, pctx->num);
    2594           0 :             break;
    2595             :         }
    2596             : 
    2597           0 :         if (n > num) n = num;
    2598             : 
    2599           0 :         msgs = &(pdom->res->msgs[cctx->pwent_cur]);
    2600             : 
    2601           0 :         ret = fill_pwent(cctx->creq->out, pdom->domain, nctx,
    2602             :                          true, false, msgs, &n);
    2603             : 
    2604           0 :         cctx->pwent_cur += n;
    2605             :     }
    2606             : 
    2607             : none:
    2608           0 :     if (ret == ENOENT) {
    2609           0 :         ret = sss_cmd_empty_packet(cctx->creq->out);
    2610             :     }
    2611           0 :     return ret;
    2612             : }
    2613             : 
    2614           0 : static void nss_cmd_implicit_setpwent_done(struct tevent_req *req)
    2615             : {
    2616             :     errno_t ret;
    2617           0 :     struct nss_cmd_ctx *cmdctx =
    2618           0 :             tevent_req_callback_data(req, struct nss_cmd_ctx);
    2619             : 
    2620           0 :     ret = nss_cmd_setpwent_recv(req);
    2621           0 :     talloc_zfree(req);
    2622             : 
    2623             :     /* ENOENT is acceptable, as it just means that there were no entries
    2624             :      * to be returned. This will be handled gracefully in nss_cmd_retpwent
    2625             :      * later.
    2626             :      */
    2627           0 :     if (ret != EOK && ret != ENOENT) {
    2628           0 :         DEBUG(SSSDBG_FATAL_FAILURE,
    2629             :               "Implicit setpwent failed with unexpected error [%d][%s]\n",
    2630             :                   ret, strerror(ret));
    2631           0 :         NSS_CMD_FATAL_ERROR(cmdctx);
    2632             :     }
    2633             : 
    2634             :     /* Restore the saved index and cursor locations */
    2635           0 :     cmdctx->cctx->pwent_dom_idx = cmdctx->saved_dom_idx;
    2636           0 :     cmdctx->cctx->pwent_cur = cmdctx->saved_cur;
    2637             : 
    2638           0 :     ret = nss_cmd_getpwent_immediate(cmdctx);
    2639           0 :     if (ret != EOK) {
    2640           0 :         DEBUG(SSSDBG_FATAL_FAILURE,
    2641             :               "Immediate retrieval failed with unexpected error "
    2642             :                   "[%d][%s]\n", ret, strerror(ret));
    2643           0 :         NSS_CMD_FATAL_ERROR(cmdctx);
    2644             :     }
    2645             : }
    2646             : 
    2647           0 : static int nss_cmd_endpwent(struct cli_ctx *cctx)
    2648             : {
    2649             :     struct nss_ctx *nctx;
    2650             :     int ret;
    2651             : 
    2652           0 :     DEBUG(SSSDBG_CONF_SETTINGS, "Terminating request info for all accounts\n");
    2653             : 
    2654           0 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    2655             : 
    2656             :     /* create response packet */
    2657           0 :     ret = sss_packet_new(cctx->creq, 0,
    2658           0 :                          sss_packet_get_cmd(cctx->creq->in),
    2659           0 :                          &cctx->creq->out);
    2660             : 
    2661           0 :     if (ret != EOK) {
    2662           0 :         return ret;
    2663             :     }
    2664           0 :     if (nctx->pctx == NULL) goto done;
    2665             : 
    2666             :     /* Reset the indices so that subsequent requests start at zero */
    2667           0 :     cctx->pwent_dom_idx = 0;
    2668           0 :     cctx->pwent_cur = 0;
    2669             : 
    2670             : done:
    2671           0 :     sss_cmd_done(cctx, NULL);
    2672           0 :     return EOK;
    2673             : }
    2674             : 
    2675             : /****************************************************************************
    2676             :  * GROUP db related functions
    2677             :  ***************************************************************************/
    2678             : 
    2679           0 : void nss_update_gr_memcache(struct nss_ctx *nctx)
    2680             : {
    2681             :     struct sss_domain_info *dom;
    2682             :     struct ldb_result *res;
    2683             :     uint64_t exp;
    2684             :     struct sized_string key;
    2685             :     const char *id;
    2686             :     time_t now;
    2687             :     int ret;
    2688             :     int i;
    2689             : 
    2690           0 :     now = time(NULL);
    2691             : 
    2692           0 :     for (dom = nctx->rctx->domains; dom; dom = get_next_domain(dom, false)) {
    2693           0 :         ret = sysdb_enumgrent_with_views(nctx, dom, &res);
    2694           0 :         if (ret != EOK) {
    2695           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    2696             :                   "Failed to enumerate users for domain [%s]\n", dom->name);
    2697           0 :             continue;
    2698             :         }
    2699             : 
    2700           0 :         for (i = 0; i < res->count; i++) {
    2701           0 :             exp = ldb_msg_find_attr_as_uint64(res->msgs[i],
    2702             :                                               SYSDB_CACHE_EXPIRE, 0);
    2703           0 :             if (exp >= now) {
    2704           0 :                 continue;
    2705             :             }
    2706             : 
    2707             :             /* names require more manipulation (build up fqname conditionally),
    2708             :              * but uidNumber is unique and always resolvable too, so we use
    2709             :              * that to update the cache, as it points to the same entry */
    2710           0 :             id = sss_view_ldb_msg_find_attr_as_string(dom, res->msgs[i],
    2711             :                                                       SYSDB_GIDNUM, NULL);
    2712           0 :             if (!id) {
    2713           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    2714             :                       "Failed to find gidNumber in %s.\n",
    2715             :                        ldb_dn_get_linearized(res->msgs[i]->dn));
    2716           0 :                 continue;
    2717             :             }
    2718           0 :             to_sized_string(&key, id);
    2719             : 
    2720           0 :             ret = sss_mmap_cache_gr_invalidate(nctx->grp_mc_ctx, &key);
    2721           0 :             if (ret != EOK && ret != ENOENT) {
    2722           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    2723             :                       "Internal failure in memory cache code: %d [%s]\n",
    2724             :                        ret, strerror(ret));
    2725             :             }
    2726             :         }
    2727           0 :         talloc_zfree(res);
    2728             :     }
    2729           0 : }
    2730             : 
    2731             : #define GID_ROFFSET 0
    2732             : #define MNUM_ROFFSET sizeof(uint32_t)
    2733             : #define STRS_ROFFSET 2*sizeof(uint32_t)
    2734             : 
    2735          15 : static int parse_member(TALLOC_CTX *mem_ctx, struct sss_domain_info *group_dom,
    2736             :                         const char *member, struct sss_domain_info **_member_dom,
    2737             :                         struct sized_string *_name, bool *_add_domain)
    2738             : {
    2739             :     errno_t ret;
    2740             :     char *username;
    2741             :     char *domname;
    2742             :     const char *use_member;
    2743             :     struct sss_domain_info *member_dom;
    2744             :     bool add_domain;
    2745             : 
    2746          15 :     ret = sss_parse_name(mem_ctx, group_dom->names, member, &domname, &username);
    2747          15 :     if (ret != EOK) {
    2748           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "Could not parse [%s] into "
    2749             :               "name-value components.\n", member);
    2750           0 :         return ret;
    2751             :     }
    2752             : 
    2753          15 :     add_domain = (!IS_SUBDOMAIN(group_dom) && group_dom->fqnames);
    2754          15 :     use_member = member;
    2755          15 :     member_dom = group_dom;
    2756             : 
    2757          15 :     if (IS_SUBDOMAIN(group_dom) == false && domname != NULL) {
    2758             :         /* The group is stored in the parent domain, but the member comes from.
    2759             :          * a subdomain. No need to add the domain component, it's already
    2760             :          * present in the memberuid/ghost attribute
    2761             :          */
    2762           2 :         add_domain = false;
    2763             :     }
    2764             : 
    2765          15 :     if (IS_SUBDOMAIN(group_dom) == true && domname == NULL) {
    2766             :         /* The group is stored in a subdomain, but the member comes
    2767             :          * from the parent domain. Need to add the domain component
    2768             :          * of the parent domain
    2769             :          */
    2770           1 :         add_domain = true;
    2771           1 :         use_member = username;
    2772           1 :         member_dom = group_dom->parent;
    2773             :     }
    2774             : 
    2775          15 :     to_sized_string(_name, use_member);
    2776          15 :     *_add_domain = add_domain;
    2777          15 :     *_member_dom = member_dom;
    2778          15 :     return EOK;
    2779             : }
    2780             : 
    2781           6 : static int fill_members(struct sss_packet *packet,
    2782             :                         struct sss_domain_info *dom,
    2783             :                         struct nss_ctx *nctx,
    2784             :                         struct ldb_message_element *el,
    2785             :                         size_t *_rzero,
    2786             :                         size_t *_rsize,
    2787             :                         int *_memnum)
    2788             : {
    2789           6 :     int i, ret = EOK;
    2790           6 :     int memnum = *_memnum;
    2791           6 :     size_t rzero= *_rzero;
    2792           6 :     size_t rsize = *_rsize;
    2793             :     const char *tmpstr;
    2794             :     struct sized_string name;
    2795           6 :     TALLOC_CTX *tmp_ctx = NULL;
    2796             : 
    2797           6 :     int nlen = 0;
    2798             : 
    2799             :     uint8_t *body;
    2800             :     size_t blen;
    2801             : 
    2802           6 :     const char *domain = dom->name;
    2803             :     bool add_domain;
    2804             :     struct sss_domain_info *member_dom;
    2805             : 
    2806           6 :     tmp_ctx = talloc_new(NULL);
    2807           6 :     if (tmp_ctx == NULL) {
    2808           0 :         return ENOMEM;
    2809             :     }
    2810             : 
    2811           6 :     sss_packet_get_body(packet, &body, &blen);
    2812          21 :     for (i = 0; i < el->num_values; i++) {
    2813          15 :         tmpstr = sss_get_cased_name(tmp_ctx, (char *)el->values[i].data,
    2814          15 :                                     dom->case_preserve);
    2815          15 :         if (tmpstr == NULL) {
    2816           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    2817             :                   "sss_get_cased_name failed, skipping\n");
    2818           0 :             continue;
    2819             :         }
    2820             : 
    2821          15 :         tmpstr = sss_replace_space(tmp_ctx, tmpstr,
    2822          15 :                                    nctx->rctx->override_space);
    2823          15 :         if (tmpstr == NULL) {
    2824           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    2825             :                   "sss_replace_space failed\n");
    2826           0 :             ret = ENOMEM;
    2827           0 :             goto done;
    2828             :         }
    2829             : 
    2830          15 :         if (nctx->filter_users_in_groups) {
    2831           0 :             ret = sss_ncache_check_user(nctx->ncache,
    2832             :                                         nctx->neg_timeout,
    2833             :                                         dom, tmpstr);
    2834           0 :             if (ret == EEXIST) {
    2835           0 :                 DEBUG(SSSDBG_TRACE_FUNC,
    2836             :                       "Group [%s] member [%s@%s] filtered out!"
    2837             :                        " (negative cache)\n",
    2838             :                        (char *)&body[rzero+STRS_ROFFSET], tmpstr, domain);
    2839           0 :                 continue;
    2840             :             }
    2841             :         }
    2842             : 
    2843          15 :         ret = parse_member(tmp_ctx, dom, tmpstr, &member_dom, &name, &add_domain);
    2844          15 :         if (ret != EOK) {
    2845           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
    2846             :                   "Could not process member %s, skipping\n", tmpstr);
    2847           0 :             continue;
    2848             :         }
    2849             : 
    2850          15 :         if (add_domain) {
    2851           5 :             nlen = sss_fqname(NULL, 0, member_dom->names, member_dom,
    2852             :                               name.str);
    2853           5 :             if (nlen >= 0) {
    2854           5 :                 nlen += 1;
    2855             :             } else {
    2856             :                 /* Other failures caught below */
    2857           0 :                 nlen = 0;
    2858             :             }
    2859             :         } else {
    2860          10 :             nlen = name.len;
    2861             :         }
    2862             : 
    2863          15 :         ret = sss_packet_grow(packet, nlen);
    2864          15 :         if (ret != EOK) {
    2865           0 :             goto done;
    2866             :         }
    2867          15 :         sss_packet_get_body(packet, &body, &blen);
    2868             : 
    2869          15 :         if (add_domain) {
    2870          10 :             ret = sss_fqname((char *)&body[rzero + rsize], nlen,
    2871           5 :                              member_dom->names, member_dom, name.str);
    2872           5 :             if (ret < 0 || ret != nlen - 1) {
    2873           0 :                 DEBUG(SSSDBG_OP_FAILURE, "Failed to generate a fully qualified name"
    2874             :                                           " for member [%s@%s] of group [%s]!"
    2875             :                                           " Skipping\n", name.str, domain,
    2876             :                                           (char *)&body[rzero+STRS_ROFFSET]);
    2877             :                 /* reclaim space */
    2878           0 :                 ret = sss_packet_shrink(packet, nlen);
    2879           0 :                 if (ret != EOK) {
    2880           0 :                     goto done;
    2881             :                 }
    2882           0 :                 continue;
    2883             :             }
    2884             : 
    2885             :         } else {
    2886          10 :             memcpy(&body[rzero + rsize], name.str, name.len);
    2887             :         }
    2888             : 
    2889          15 :         rsize += nlen;
    2890          15 :         memnum++;
    2891             :     }
    2892             : 
    2893           6 :     ret = 0;
    2894             : 
    2895             : done:
    2896           6 :     *_memnum = memnum;
    2897           6 :     *_rzero = rzero;
    2898           6 :     *_rsize = rsize;
    2899           6 :     talloc_zfree(tmp_ctx);
    2900           6 :     return ret;
    2901             : }
    2902             : 
    2903           9 : static int fill_grent(struct sss_packet *packet,
    2904             :                       struct sss_domain_info *dom,
    2905             :                       struct nss_ctx *nctx,
    2906             :                       bool filter_groups, bool gr_mmap_cache,
    2907             :                       struct ldb_message **msgs,
    2908             :                       int *count)
    2909             : {
    2910             :     struct ldb_message *msg;
    2911             :     struct ldb_message_element *el;
    2912             :     uint8_t *body;
    2913             :     size_t blen;
    2914             :     uint32_t gid;
    2915             :     const char *tmpstr;
    2916           9 :     const char *orig_name = NULL;
    2917             :     struct sized_string name;
    2918             :     struct sized_string pwfield;
    2919             :     struct sized_string fullname;
    2920           9 :     int fq_len = 0;
    2921           9 :     int i = 0;
    2922             :     int ret, num, memnum;
    2923             :     size_t rzero, rsize;
    2924           9 :     bool add_domain = (!IS_SUBDOMAIN(dom) && dom->fqnames);
    2925           9 :     const char *domain = dom->name;
    2926           9 :     TALLOC_CTX *tmp_ctx = NULL;
    2927             : 
    2928           9 :     to_sized_string(&pwfield, nctx->pwfield);
    2929             : 
    2930           9 :     num = 0;
    2931             : 
    2932             :     /* first 2 fields (len and reserved), filled up later */
    2933           9 :     ret = sss_packet_grow(packet, 2*sizeof(uint32_t));
    2934           9 :     if (ret != EOK) {
    2935           0 :         goto done;
    2936             :     }
    2937           9 :     sss_packet_get_body(packet, &body, &blen);
    2938           9 :     rzero = 2*sizeof(uint32_t);
    2939           9 :     rsize = 0;
    2940             : 
    2941          18 :     for (i = 0; i < *count; i++) {
    2942           9 :         talloc_zfree(tmp_ctx);
    2943           9 :         tmp_ctx = talloc_new(NULL);
    2944           9 :         msg = msgs[i];
    2945             : 
    2946             :         /* new group */
    2947           9 :         if (!ldb_msg_check_string_attribute(msg, "objectClass",
    2948             :                                             SYSDB_GROUP_CLASS)) {
    2949           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Wrong object (%s) found on stack!\n",
    2950             :                       ldb_dn_get_linearized(msg->dn));
    2951           0 :             continue;
    2952             :         }
    2953             : 
    2954             :         /* new result starts at end of previous result */
    2955           9 :         rzero += rsize;
    2956           9 :         rsize = 0;
    2957             : 
    2958             :         /* find group name/gid */
    2959             : 
    2960             :         /* start with an empty name for each iteration */
    2961           9 :         orig_name = NULL;
    2962           9 :         if (DOM_HAS_VIEWS(dom)) {
    2963           0 :             orig_name = ldb_msg_find_attr_as_string(msg,
    2964             :                                                     OVERRIDE_PREFIX SYSDB_NAME,
    2965             :                                                     NULL);
    2966           0 :             if (orig_name != NULL && IS_SUBDOMAIN(dom)) {
    2967             :                 /* Override names are not fully qualified */
    2968           0 :                 add_domain = true;
    2969             :             }
    2970             :         }
    2971           9 :         if (orig_name == NULL) {
    2972           9 :             orig_name = ldb_msg_find_attr_as_string(msg,
    2973             :                                                     SYSDB_DEFAULT_OVERRIDE_NAME,
    2974             :                                                     NULL);
    2975           9 :             if (orig_name == NULL) {
    2976           9 :                 orig_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
    2977             :             }
    2978             :         }
    2979             : 
    2980           9 :         gid = sss_view_ldb_msg_find_attr_as_uint64(dom, msg, SYSDB_GIDNUM, 0);
    2981           9 :         if (!orig_name || !gid) {
    2982           0 :             DEBUG(SSSDBG_OP_FAILURE,
    2983             :                   "Incomplete group object for %s[%llu]! Skipping\n",
    2984             :                       orig_name?orig_name:"<NULL>", (unsigned long long int)gid);
    2985           0 :             continue;
    2986             :         }
    2987             : 
    2988           9 :         if (filter_groups) {
    2989           0 :             ret = sss_ncache_check_group(nctx->ncache,
    2990             :                                          nctx->neg_timeout, dom, orig_name);
    2991           0 :             if (ret == EEXIST) {
    2992           0 :                 DEBUG(SSSDBG_TRACE_FUNC,
    2993             :                       "Group [%s@%s] filtered out! (negative cache)\n",
    2994             :                        orig_name, domain);
    2995           0 :                 continue;
    2996             :             }
    2997             :         }
    2998             : 
    2999           9 :         tmpstr = sss_get_cased_name(tmp_ctx, orig_name, dom->case_preserve);
    3000           9 :         if (tmpstr == NULL) {
    3001           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    3002             :                   "sss_get_cased_name failed, skipping\n");
    3003           0 :             continue;
    3004             :         }
    3005             : 
    3006           9 :         tmpstr = sss_replace_space(tmp_ctx, tmpstr,
    3007           9 :                                    nctx->rctx->override_space);
    3008           9 :         if (tmpstr == NULL) {
    3009           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    3010             :                   "sss_replace_space failed, skipping\n");
    3011           0 :             continue;
    3012             :         }
    3013             : 
    3014           9 :         to_sized_string(&name, tmpstr);
    3015             : 
    3016             :         /* fill in gid and name and set pointer for number of members */
    3017           9 :         rsize = STRS_ROFFSET + name.len + pwfield.len; /* name\0x\0 */
    3018             : 
    3019           9 :         if (add_domain) {
    3020           2 :             fq_len = sss_fqname(NULL, 0, dom->names, dom, name.str);
    3021           2 :             if (fq_len >= 0) {
    3022           2 :                 fq_len += 1;
    3023           2 :                 rsize -= name.len;
    3024           2 :                 rsize += fq_len;
    3025             :             } else {
    3026             :                 /* Other failures caught below */
    3027           0 :                 fq_len = 0;
    3028             :             }
    3029             :         }
    3030             : 
    3031           9 :         ret = sss_packet_grow(packet, rsize);
    3032           9 :         if (ret != EOK) {
    3033           0 :             num = 0;
    3034           0 :             goto done;
    3035             :         }
    3036           9 :         sss_packet_get_body(packet, &body, &blen);
    3037             : 
    3038             :         /*  0-3: 32bit number gid */
    3039           9 :         SAFEALIGN_SET_UINT32(&body[rzero+GID_ROFFSET], gid, NULL);
    3040             : 
    3041             :         /*  4-7: 32bit unsigned number of members */
    3042           9 :         SAFEALIGN_SET_UINT32(&body[rzero+MNUM_ROFFSET], 0, NULL);
    3043             : 
    3044             :         /*  8-X: sequence of strings (name, passwd, mem..) */
    3045           9 :         if (add_domain) {
    3046           2 :             ret = sss_fqname((char *)&body[rzero+STRS_ROFFSET], fq_len,
    3047             :                              dom->names, dom, name.str);
    3048           2 :             if (ret < 0 || ret != fq_len - 1) {
    3049           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    3050             :                       "Failed to generate a fully qualified name for"
    3051             :                           " group [%s] in [%s]! Skipping\n", name.str, domain);
    3052             :                 /* reclaim space */
    3053           0 :                 ret = sss_packet_shrink(packet, rsize);
    3054           0 :                 if (ret != EOK) {
    3055           0 :                     num = 0;
    3056           0 :                     goto done;
    3057             :                 }
    3058           0 :                 rsize = 0;
    3059           0 :                 continue;
    3060             :             }
    3061             :         } else {
    3062           7 :             memcpy(&body[rzero+STRS_ROFFSET], name.str, name.len);
    3063             :         }
    3064           9 :         to_sized_string(&fullname, (const char *)&body[rzero+STRS_ROFFSET]);
    3065             : 
    3066             :         /* group passwd field */
    3067          18 :         memcpy(&body[rzero+STRS_ROFFSET + fullname.len],
    3068           9 :                                             pwfield.str, pwfield.len);
    3069             : 
    3070           9 :         memnum = 0;
    3071           9 :         if (!dom->ignore_group_members) {
    3072           9 :             el = sss_view_ldb_msg_find_element(dom, msg, SYSDB_MEMBERUID);
    3073           9 :             if (el) {
    3074           6 :                 ret = fill_members(packet, dom, nctx, el, &rzero, &rsize,
    3075             :                                    &memnum);
    3076           6 :                 if (ret != EOK) {
    3077           0 :                     num = 0;
    3078           0 :                     goto done;
    3079             :                 }
    3080           6 :                 sss_packet_get_body(packet, &body, &blen);
    3081             :             }
    3082           9 :             el = ldb_msg_find_element(msg, SYSDB_GHOST);
    3083           9 :             if (el) {
    3084           0 :                 if (DOM_HAS_VIEWS(dom) && !is_local_view(dom->view_name)
    3085           0 :                         && el->num_values != 0) {
    3086           0 :                     DEBUG(SSSDBG_CRIT_FAILURE,
    3087             :                           "Domain has a view [%s] but group [%s] still has " \
    3088             :                           "ghost members.\n", dom->view_name, orig_name);
    3089           0 :                     num = 0;
    3090           0 :                     goto done;
    3091             :                 }
    3092           0 :                 ret = fill_members(packet, dom, nctx, el, &rzero, &rsize,
    3093             :                                    &memnum);
    3094           0 :                 if (ret != EOK) {
    3095           0 :                     num = 0;
    3096           0 :                     goto done;
    3097             :                 }
    3098           0 :                 sss_packet_get_body(packet, &body, &blen);
    3099             :             }
    3100             :         }
    3101           9 :         if (memnum) {
    3102             :             /* set num of members */
    3103           6 :             SAFEALIGN_SET_UINT32(&body[rzero+MNUM_ROFFSET], memnum, NULL);
    3104             :         }
    3105             : 
    3106           9 :         num++;
    3107             : 
    3108           9 :         if (gr_mmap_cache && nctx->grp_mc_ctx) {
    3109             :             /* body was reallocated, so fullname might be pointing to
    3110             :              * where body used to be, not where it is */
    3111           0 :             to_sized_string(&fullname, (const char *)&body[rzero+STRS_ROFFSET]);
    3112           0 :             ret = sss_mmap_cache_gr_store(&nctx->grp_mc_ctx,
    3113             :                                           &fullname, &pwfield, gid, memnum,
    3114           0 :                                           (char *)&body[rzero] + STRS_ROFFSET +
    3115           0 :                                             fullname.len + pwfield.len,
    3116           0 :                                           rsize - STRS_ROFFSET -
    3117           0 :                                             fullname.len - pwfield.len);
    3118           0 :             if (ret != EOK && ret != ENOMEM) {
    3119           0 :                 DEBUG(SSSDBG_OP_FAILURE,
    3120             :                       "Failed to store group %s(%s) in mmap cache!\n",
    3121             :                       name.str, domain);
    3122             :             }
    3123             :         }
    3124             : 
    3125           9 :         continue;
    3126             :     }
    3127           9 :     talloc_zfree(tmp_ctx);
    3128             : 
    3129             : done:
    3130           9 :     *count = i;
    3131             : 
    3132           9 :     if (num == 0) {
    3133             :         /* if num is 0 most probably something went wrong,
    3134             :          * reset packet and return ENOENT */
    3135           0 :         ret = sss_packet_set_size(packet, 0);
    3136           0 :         if (ret != EOK) return ret;
    3137           0 :         return ENOENT;
    3138             :     }
    3139             : 
    3140           9 :     SAFEALIGN_COPY_UINT32(body, &num, NULL); /* num results */
    3141           9 :     SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); /* reserved */
    3142             : 
    3143           9 :     return EOK;
    3144             : }
    3145             : 
    3146           9 : static int nss_cmd_getgr_send_reply(struct nss_dom_ctx *dctx, bool filter)
    3147             : {
    3148           9 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
    3149           9 :     struct cli_ctx *cctx = cmdctx->cctx;
    3150             :     struct nss_ctx *nctx;
    3151             :     int ret;
    3152             :     int i;
    3153             : 
    3154           9 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    3155             : 
    3156          18 :     ret = sss_packet_new(cctx->creq, 0,
    3157           9 :                          sss_packet_get_cmd(cctx->creq->in),
    3158           9 :                          &cctx->creq->out);
    3159           9 :     if (ret != EOK) {
    3160           0 :         return EFAULT;
    3161             :     }
    3162           9 :     i = dctx->res->count;
    3163           9 :     ret = fill_grent(cctx->creq->out,
    3164             :                      dctx->domain,
    3165             :                      nctx, filter, true,
    3166           9 :                      dctx->res->msgs, &i);
    3167           9 :     if (ret) {
    3168           0 :         return ret;
    3169             :     }
    3170           9 :     sss_packet_set_error(cctx->creq->out, EOK);
    3171           9 :     sss_cmd_done(cctx, cmdctx);
    3172           9 :     return EOK;
    3173             : }
    3174             : 
    3175             : /* search for a group.
    3176             :  * Returns:
    3177             :  *   ENOENT, if group is definitely not found
    3178             :  *   EAGAIN, if group is being fetched from backend via async operations
    3179             :  *   EOK, if found
    3180             :  *   anything else on a fatal error
    3181             :  */
    3182             : 
    3183           9 : static int nss_cmd_getgrnam_search(struct nss_dom_ctx *dctx)
    3184             : {
    3185           9 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
    3186           9 :     struct sss_domain_info *dom = dctx->domain;
    3187           9 :     struct cli_ctx *cctx = cmdctx->cctx;
    3188           9 :     char *name = NULL;
    3189             :     struct nss_ctx *nctx;
    3190             :     int ret;
    3191           9 :     const char *extra_flag = NULL;
    3192             : 
    3193           9 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    3194             : 
    3195           9 :     while (dom) {
    3196             :        /* if it is a domainless search, skip domains that require fully
    3197             :          * qualified names instead */
    3198          18 :         while (dom && cmdctx->check_next && dom->fqnames) {
    3199           0 :             dom = get_next_domain(dom, false);
    3200             :         }
    3201             : 
    3202           9 :         if (!dom) break;
    3203             : 
    3204           9 :         if (dom != dctx->domain) {
    3205             :             /* make sure we reset the check_provider flag when we check
    3206             :              * a new domain */
    3207           0 :             dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
    3208             :         }
    3209             : 
    3210             :         /* make sure to update the dctx if we changed domain */
    3211           9 :         dctx->domain = dom;
    3212             : 
    3213           9 :         talloc_free(name);
    3214           9 :         name = sss_get_cased_name(dctx, cmdctx->name, dom->case_sensitive);
    3215           9 :         if (!name) return ENOMEM;
    3216             : 
    3217           9 :         name = sss_reverse_replace_space(dctx, name,
    3218           9 :                                          nctx->rctx->override_space);
    3219           9 :         if (name == NULL) {
    3220           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    3221             :                   "sss_reverse_replace_space failed\n");
    3222           0 :             return ENOMEM;
    3223             :         }
    3224             : 
    3225             :         /* verify this group has not yet been negatively cached,
    3226             :         * or has been permanently filtered */
    3227           9 :         ret = sss_ncache_check_group(nctx->ncache, nctx->neg_timeout,
    3228             :                                      dom, name);
    3229             : 
    3230             :         /* if neg cached, return we didn't find it */
    3231           9 :         if (ret == EEXIST) {
    3232           0 :             DEBUG(SSSDBG_TRACE_FUNC,
    3233             :                   "Group [%s] does not exist in [%s]! (negative cache)\n",
    3234             :                    name, dom->name);
    3235             :             /* if a multidomain search, try with next */
    3236           0 :             if (cmdctx->check_next) {
    3237           0 :                 dom = get_next_domain(dom, false);
    3238           0 :                 continue;
    3239             :             }
    3240             :             /* There are no further domains or this was a
    3241             :              * fully-qualified user request.
    3242             :              */
    3243           0 :             return ENOENT;
    3244             :         }
    3245             : 
    3246           9 :         DEBUG(SSSDBG_CONF_SETTINGS,
    3247             :               "Requesting info for [%s@%s]\n", name, dom->name);
    3248             : 
    3249           9 :         if (dom->sysdb == NULL) {
    3250           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    3251             :                   "Fatal: Sysdb CTX not found for this domain!\n");
    3252           0 :             return EIO;
    3253             :         }
    3254             : 
    3255           9 :         ret = sysdb_getgrnam_with_views(cmdctx, dom, name, &dctx->res);
    3256           9 :         if (ret != EOK) {
    3257           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    3258             :                   "Failed to make request to our cache!\n");
    3259           0 :             return EIO;
    3260             :         }
    3261             : 
    3262           9 :         if (dctx->res->count > 1) {
    3263           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    3264             :                   "getgrnam call returned more than one result !?!\n");
    3265           0 :             sss_log(SSS_LOG_ERR,
    3266             :                     "More groups have the same name [%s@%s] in SSSD cache. "
    3267             :                     "SSSD will not work correctly.\n",
    3268             :                     name, dom->name);
    3269           0 :             return ENOENT;
    3270             :         }
    3271             : 
    3272           9 :         if (dctx->res->count == 0 && !dctx->check_provider) {
    3273             :             /* set negative cache only if not result of cache check */
    3274           0 :             ret = sss_ncache_set_group(nctx->ncache, false, dom, name);
    3275           0 :             if (ret != EOK) {
    3276           0 :                 DEBUG(SSSDBG_MINOR_FAILURE, "Cannot set negcache for %s@%s\n",
    3277             :                       name, dom->name);
    3278             :             }
    3279             : 
    3280             :             /* if a multidomain search, try with next */
    3281           0 :             if (cmdctx->check_next) {
    3282           0 :                 dom = get_next_domain(dom, false);
    3283           0 :                 if (dom) continue;
    3284             :             }
    3285             : 
    3286           0 :             DEBUG(SSSDBG_OP_FAILURE, "No results for getgrnam call\n");
    3287             : 
    3288             :             /* Group not found in ldb -> delete group from memory cache. */
    3289           0 :             ret = delete_entry_from_memcache(dctx->domain, name,
    3290             :                                              nctx->grp_mc_ctx, SSS_MC_GROUP);
    3291           0 :             if (ret != EOK) {
    3292           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
    3293             :                       "Deleting group from memcache failed.\n");
    3294             :             }
    3295             : 
    3296             : 
    3297           0 :             return ENOENT;
    3298             :         }
    3299             : 
    3300             :         /* if this is a caching provider (or if we haven't checked the cache
    3301             :          * yet) then verify that the cache is uptodate */
    3302           9 :         if (dctx->check_provider) {
    3303             : 
    3304           9 :             if (DOM_HAS_VIEWS(dom) && (dctx->res->count == 0
    3305           0 :                     || ldb_msg_find_attr_as_string(dctx->res->msgs[0],
    3306             :                                                    OVERRIDE_PREFIX SYSDB_NAME,
    3307             :                                                    NULL) != NULL)) {
    3308           0 :                 extra_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
    3309             :             } else {
    3310           9 :                 extra_flag = NULL;
    3311             :             }
    3312             : 
    3313           9 :             ret = check_cache(dctx, nctx, dctx->res, SSS_DP_GROUP, name, 0,
    3314             :                               extra_flag, nss_cmd_getby_dp_callback, dctx);
    3315           9 :             if (ret != EOK) {
    3316             :                 /* Anything but EOK means we should reenter the mainloop
    3317             :                  * because we may be refreshing the cache
    3318             :                  */
    3319           0 :                 return ret;
    3320             :             }
    3321             :         }
    3322             : 
    3323             :         /* One result found */
    3324           9 :         DEBUG(SSSDBG_TRACE_FUNC,
    3325             :               "Returning info for group [%s@%s]\n", name, dom->name);
    3326             : 
    3327           9 :         return EOK;
    3328             :     }
    3329             : 
    3330           0 :     DEBUG(SSSDBG_MINOR_FAILURE,
    3331             :           "No matching domain found for [%s], fail!\n", cmdctx->name);
    3332           0 :     return ENOENT;
    3333             : }
    3334             : 
    3335           9 : static int nss_cmd_getgrnam(struct cli_ctx *cctx)
    3336             : {
    3337           9 :     return nss_cmd_getbynam(SSS_NSS_GETGRNAM, cctx);
    3338             : }
    3339             : 
    3340             : /* search for a gid.
    3341             :  * Returns:
    3342             :  *   ENOENT, if gid is definitely not found
    3343             :  *   EAGAIN, if gid is being fetched from backend via async operations
    3344             :  *   EOK, if found
    3345             :  *   anything else on a fatal error
    3346             :  */
    3347             : 
    3348           0 : static int nss_cmd_getgrgid_search(struct nss_dom_ctx *dctx)
    3349             : {
    3350           0 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
    3351           0 :     struct sss_domain_info *dom = dctx->domain;
    3352           0 :     struct cli_ctx *cctx = cmdctx->cctx;
    3353             :     struct nss_ctx *nctx;
    3354             :     int ret;
    3355             :     int err;
    3356           0 :     const char *extra_flag = NULL;
    3357             : 
    3358           0 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    3359             : 
    3360           0 :     while (dom) {
    3361             : 
    3362             :         /* check that the gid is valid for this domain */
    3363           0 :         if ((dom->id_min && (cmdctx->id < dom->id_min)) ||
    3364           0 :             (dom->id_max && (cmdctx->id > dom->id_max))) {
    3365           0 :             DEBUG(SSSDBG_CONF_SETTINGS,
    3366             :                   "Gid [%"PRIu32"] does not exist in domain [%s]! "
    3367             :                       "(id out of range)\n",
    3368             :                       cmdctx->id, dom->name);
    3369           0 :             if (cmdctx->check_next) {
    3370           0 :                 dom = get_next_domain(dom, true);
    3371           0 :                 continue;
    3372             :             }
    3373           0 :             ret = ENOENT;
    3374           0 :             goto done;
    3375             :         }
    3376             : 
    3377           0 :         if (dom != dctx->domain) {
    3378             :             /* make sure we reset the check_provider flag when we check
    3379             :              * a new domain */
    3380           0 :             dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
    3381             :         }
    3382             : 
    3383             :         /* make sure to update the dctx if we changed domain */
    3384           0 :         dctx->domain = dom;
    3385             : 
    3386           0 :         DEBUG(SSSDBG_CONF_SETTINGS,
    3387             :               "Requesting info for [%"PRIu32"@%s]\n", cmdctx->id, dom->name);
    3388             : 
    3389           0 :         if (dom->sysdb == NULL) {
    3390           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    3391             :                   "Fatal: Sysdb CTX not found for this domain!\n");
    3392           0 :             ret = EIO;
    3393           0 :             goto done;
    3394             :         }
    3395             : 
    3396           0 :         ret = sysdb_getgrgid_with_views(cmdctx, dom, cmdctx->id, &dctx->res);
    3397           0 :         if (ret != EOK) {
    3398           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    3399             :                   "Failed to make request to our cache!\n");
    3400           0 :             ret = EIO;
    3401           0 :             goto done;
    3402             :         }
    3403             : 
    3404           0 :         if (dctx->res->count > 1) {
    3405           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    3406             :                   "getgrgid call returned more than one result !?!\n");
    3407           0 :             sss_log(SSS_LOG_ERR,
    3408             :                     "More groups have the same GID [%"PRIu32"] in directory "
    3409             :                     "server. SSSD will not work correctly.\n", cmdctx->id);
    3410           0 :             ret = ENOENT;
    3411           0 :             goto done;
    3412             :         }
    3413             : 
    3414           0 :         if (dctx->res->count == 0 && !dctx->check_provider) {
    3415             :             /* if a multidomain search, try with next */
    3416           0 :             if (cmdctx->check_next) {
    3417           0 :                 dom = get_next_domain(dom, true);
    3418           0 :                 continue;
    3419             :             }
    3420             : 
    3421             :             /* set negative cache only if not result of cache check */
    3422           0 :             DEBUG(SSSDBG_MINOR_FAILURE, "No results for getgrgid call\n");
    3423           0 :             ret = ENOENT;
    3424           0 :             goto done;
    3425             :         }
    3426             : 
    3427             :         /* if this is a caching provider (or if we haven't checked the cache
    3428             :          * yet) then verify that the cache is uptodate */
    3429           0 :         if (dctx->check_provider) {
    3430             : 
    3431           0 :             if (DOM_HAS_VIEWS(dom) && (dctx->res->count == 0
    3432           0 :                     || ldb_msg_find_attr_as_uint64(dctx->res->msgs[0],
    3433             :                                                    OVERRIDE_PREFIX SYSDB_GIDNUM,
    3434             :                                                    0) != 0)) {
    3435           0 :                 extra_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
    3436             :             } else {
    3437           0 :                 extra_flag = NULL;
    3438             :             }
    3439             : 
    3440           0 :             ret = check_cache(dctx, nctx, dctx->res, SSS_DP_GROUP, NULL,
    3441             :                               cmdctx->id, extra_flag, nss_cmd_getby_dp_callback,
    3442             :                               dctx);
    3443           0 :             if (ret != EOK) {
    3444             :                 /* Anything but EOK means we should reenter the mainloop
    3445             :                  * because we may be refreshing the cache
    3446             :                  */
    3447           0 :                 goto done;
    3448             :             }
    3449             :         }
    3450             : 
    3451             :         /* One result found */
    3452           0 :         DEBUG(SSSDBG_TRACE_FUNC,
    3453             :               "Returning info for gid [%"PRIu32"@%s]\n", cmdctx->id, dom->name);
    3454             : 
    3455             :         /* Success. Break from the loop and return EOK */
    3456           0 :         ret = EOK;
    3457           0 :         goto done;
    3458             :     }
    3459             : 
    3460             :     /* All domains were tried and none had the entry. */
    3461           0 :     ret = ENOENT;
    3462             : done:
    3463           0 :     if (ret == ENOENT) {
    3464             :         /* The entry was not found, need to set result in negative cache */
    3465           0 :         err = sss_ncache_set_gid(nctx->ncache, false, NULL, cmdctx->id);
    3466           0 :         if (err != EOK) {
    3467           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
    3468             :                 "Cannot set negative cache for GID %"PRIu32"\n", cmdctx->id);
    3469             :         }
    3470             :     }
    3471             : 
    3472           0 :     DEBUG(SSSDBG_MINOR_FAILURE, "No matching domain found for [%"PRIu32"]\n", cmdctx->id);
    3473           0 :     return ret;
    3474             : }
    3475             : 
    3476           0 : static int nss_cmd_getgrgid(struct cli_ctx *cctx)
    3477             : {
    3478           0 :     return nss_cmd_getbyid(SSS_NSS_GETGRGID, cctx);
    3479             : }
    3480             : 
    3481             : /* to keep it simple at this stage we are retrieving the
    3482             :  * full enumeration again for each request for each process
    3483             :  * and we also block on setgrent() for the full time needed
    3484             :  * to retrieve the data. And endgrent() frees all the data.
    3485             :  * Next steps are:
    3486             :  * - use and nsssrv wide cache with data already structured
    3487             :  *   so that it can be immediately returned (see nscd way)
    3488             :  * - use mutexes so that setgrent() can return immediately
    3489             :  *   even if the data is still being fetched
    3490             :  * - make getgrent() wait on the mutex
    3491             :  */
    3492             : struct tevent_req *nss_cmd_setgrent_send(TALLOC_CTX *mem_ctx,
    3493             :                                          struct cli_ctx *client);
    3494             : static void nss_cmd_setgrent_done(struct tevent_req *req);
    3495           0 : static int nss_cmd_setgrent(struct cli_ctx *cctx)
    3496             : {
    3497             :     struct nss_cmd_ctx *cmdctx;
    3498             :     struct tevent_req *req;
    3499           0 :     errno_t ret = EOK;
    3500             : 
    3501           0 :     cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
    3502           0 :     if (!cmdctx) {
    3503           0 :         return ENOMEM;
    3504             :     }
    3505           0 :     cmdctx->cctx = cctx;
    3506             : 
    3507           0 :     req = nss_cmd_setgrent_send(cmdctx, cctx);
    3508           0 :     if (!req) {
    3509           0 :         DEBUG(SSSDBG_FATAL_FAILURE,
    3510             :               "Fatal error calling nss_cmd_setgrent_send\n");
    3511           0 :         ret = EIO;
    3512           0 :         goto done;
    3513             :     }
    3514           0 :     tevent_req_set_callback(req, nss_cmd_setgrent_done, cmdctx);
    3515             : 
    3516             : done:
    3517           0 :     return nss_cmd_done(cmdctx, ret);
    3518             : }
    3519             : 
    3520             : static errno_t nss_cmd_setgrent_step(struct setent_step_ctx *step_ctx);
    3521           0 : struct tevent_req *nss_cmd_setgrent_send(TALLOC_CTX *mem_ctx,
    3522             :                                          struct cli_ctx *client)
    3523             : {
    3524             :     errno_t ret;
    3525             :     struct nss_ctx *nctx;
    3526             :     struct tevent_req *req;
    3527             :     struct setent_ctx *state;
    3528             :     struct sss_domain_info *dom;
    3529             :     struct setent_step_ctx *step_ctx;
    3530             : 
    3531           0 :     DEBUG(SSSDBG_CONF_SETTINGS, "Received setgrent request\n");
    3532           0 :     nctx = talloc_get_type(client->rctx->pvt_ctx, struct nss_ctx);
    3533             : 
    3534             :     /* Reset the read pointers */
    3535           0 :     client->grent_dom_idx = 0;
    3536           0 :     client->grent_cur = 0;
    3537             : 
    3538           0 :     req = tevent_req_create(mem_ctx, &state, struct setent_ctx);
    3539           0 :     if (!req) {
    3540           0 :         DEBUG(SSSDBG_FATAL_FAILURE,
    3541             :               "Could not create tevent request for setgrent\n");
    3542           0 :         return NULL;
    3543             :     }
    3544             : 
    3545           0 :     state->nctx = nctx;
    3546           0 :     state->client = client;
    3547             : 
    3548           0 :     state->dctx = talloc_zero(state, struct nss_dom_ctx);
    3549           0 :     if (!state->dctx) {
    3550           0 :         ret = ENOMEM;
    3551           0 :         goto error;
    3552             :     }
    3553             : 
    3554             :     /* check if enumeration is enabled in any domain */
    3555           0 :     for (dom = client->rctx->domains; dom; dom = get_next_domain(dom, true)) {
    3556           0 :         if (dom->enumerate == true) break;
    3557             :     }
    3558           0 :     state->dctx->domain = dom;
    3559             : 
    3560           0 :     if (state->dctx->domain == NULL) {
    3561           0 :         DEBUG(SSSDBG_OP_FAILURE, "Enumeration disabled on all domains!\n");
    3562           0 :         ret = ENOENT;
    3563           0 :         goto error;
    3564             :     }
    3565             : 
    3566           0 :     state->dctx->check_provider =
    3567           0 :             NEED_CHECK_PROVIDER(state->dctx->domain->provider);
    3568             : 
    3569             :     /* Is the result context already available */
    3570           0 :     if (state->nctx->gctx) {
    3571           0 :         if (state->nctx->gctx->ready) {
    3572             :             /* All of the necessary data is in place
    3573             :              * We can return now, getgrent requests will work at this point
    3574             :              */
    3575           0 :             tevent_req_done(req);
    3576           0 :             tevent_req_post(req, state->nctx->rctx->ev);
    3577             :         }
    3578             :         else {
    3579             :             /* Object is still being constructed
    3580             :              * Register for notification when it's
    3581             :              * ready.
    3582             :              */
    3583           0 :             ret = nss_setent_add_ref(state, state->nctx->gctx, req);
    3584           0 :             if (ret != EOK) {
    3585           0 :                 talloc_free(req);
    3586           0 :                 return NULL;
    3587             :             }
    3588             :         }
    3589           0 :         return req;
    3590             :     }
    3591             : 
    3592             :     /* Create a new result context
    3593             :      * We are creating it on the nss_ctx so that it doesn't
    3594             :      * go away if the original request does. We will delete
    3595             :      * it when the refcount goes to zero;
    3596             :      */
    3597           0 :     state->nctx->gctx = talloc_zero(nctx, struct getent_ctx);
    3598           0 :     if (!state->nctx->gctx) {
    3599           0 :         ret = ENOMEM;
    3600           0 :         goto error;
    3601             :     }
    3602           0 :     state->getent_ctx = nctx->gctx;
    3603             : 
    3604             :     /* Add a callback reference for ourselves */
    3605           0 :     ret = nss_setent_add_ref(state, state->nctx->gctx, req);
    3606           0 :     if (ret) goto error;
    3607             : 
    3608             :     /* ok, start the searches */
    3609           0 :     step_ctx = talloc_zero(state->getent_ctx, struct setent_step_ctx);
    3610           0 :     if (!step_ctx) {
    3611           0 :         ret = ENOMEM;
    3612           0 :         goto error;
    3613             :     }
    3614             : 
    3615             :     /* Steal the dom_ctx onto the step_ctx so it doesn't go out of scope if
    3616             :      * this request is canceled while other requests are in-progress.
    3617             :      */
    3618           0 :     step_ctx->dctx = talloc_steal(step_ctx, state->dctx);
    3619           0 :     step_ctx->nctx = state->nctx;
    3620           0 :     step_ctx->getent_ctx = state->getent_ctx;
    3621           0 :     step_ctx->rctx = client->rctx;
    3622           0 :     step_ctx->cctx = client;
    3623           0 :     step_ctx->returned_to_mainloop = false;
    3624             : 
    3625           0 :     ret = nss_cmd_setgrent_step(step_ctx);
    3626           0 :     if (ret != EOK && ret != EAGAIN) goto error;
    3627             : 
    3628           0 :     if (ret == EOK) {
    3629           0 :         tevent_req_post(req, client->rctx->ev);
    3630             :     }
    3631             : 
    3632           0 :     return req;
    3633             : 
    3634             :  error:
    3635           0 :      tevent_req_error(req, ret);
    3636           0 :      tevent_req_post(req, client->rctx->ev);
    3637           0 :      return req;
    3638             : }
    3639             : 
    3640             : static void nss_cmd_setgrent_dp_callback(uint16_t err_maj, uint32_t err_min,
    3641             :                                          const char *err_msg, void *ptr);
    3642             : static void setgrent_result_timeout(struct tevent_context *ev,
    3643             :                                     struct tevent_timer *te,
    3644             :                                     struct timeval current_time,
    3645             :                                     void *pvt);
    3646             : 
    3647             : /* nss_cmd_setgrent_step returns
    3648             :  *   EOK if everything is done and the request needs to be posted explicitly
    3649             :  *   EAGAIN if the caller can safely return to the main loop
    3650             :  */
    3651           0 : static errno_t nss_cmd_setgrent_step(struct setent_step_ctx *step_ctx)
    3652             : {
    3653             :     errno_t ret;
    3654           0 :     struct sss_domain_info *dom = step_ctx->dctx->domain;
    3655           0 :     struct resp_ctx *rctx = step_ctx->rctx;
    3656           0 :     struct nss_dom_ctx *dctx = step_ctx->dctx;
    3657           0 :     struct getent_ctx *gctx = step_ctx->getent_ctx;
    3658           0 :     struct nss_ctx *nctx = step_ctx->nctx;
    3659             :     struct ldb_result *res;
    3660             :     struct timeval tv;
    3661             :     struct tevent_timer *te;
    3662             :     struct tevent_req *dpreq;
    3663             :     struct dp_callback_ctx *cb_ctx;
    3664             : 
    3665           0 :     while (dom) {
    3666           0 :         while (dom && dom->enumerate == false) {
    3667           0 :             dom = get_next_domain(dom, true);
    3668             :         }
    3669             : 
    3670           0 :         if (!dom) break;
    3671             : 
    3672           0 :         if (dom != dctx->domain) {
    3673             :             /* make sure we reset the check_provider flag when we check
    3674             :              * a new domain */
    3675           0 :             dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
    3676             :         }
    3677             : 
    3678             :         /* make sure to update the dctx if we changed domain */
    3679           0 :         dctx->domain = dom;
    3680             : 
    3681           0 :         DEBUG(SSSDBG_TRACE_FUNC,
    3682             :               "Requesting info for domain [%s]\n", dom->name);
    3683             : 
    3684           0 :         if (dom->sysdb == NULL) {
    3685           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    3686             :                   "Fatal: Sysdb CTX not found for this domain!\n");
    3687           0 :             return EIO;
    3688             :         }
    3689             : 
    3690             :         /* if this is a caching provider (or if we haven't checked the cache
    3691             :          * yet) then verify that the cache is uptodate */
    3692           0 :         if (dctx->check_provider) {
    3693           0 :             step_ctx->returned_to_mainloop = true;
    3694             :             /* Only do this once per provider */
    3695           0 :             dctx->check_provider = false;
    3696             : 
    3697           0 :             dpreq = sss_dp_get_account_send(step_ctx, rctx, dctx->domain, true,
    3698             :                                             SSS_DP_GROUP, NULL, 0, NULL);
    3699           0 :             if (!dpreq) {
    3700           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
    3701             :                       "Enum Cache refresh for domain [%s] failed."
    3702             :                        " Trying to return what we have in cache!\n",
    3703             :                        dom->name);
    3704             :             } else {
    3705           0 :                 cb_ctx = talloc_zero(step_ctx, struct dp_callback_ctx);
    3706           0 :                 if(!cb_ctx) {
    3707           0 :                     talloc_zfree(dpreq);
    3708           0 :                     return ENOMEM;
    3709             :                 }
    3710             : 
    3711           0 :                 cb_ctx->callback = nss_cmd_setgrent_dp_callback;
    3712           0 :                 cb_ctx->ptr = step_ctx;
    3713           0 :                 cb_ctx->cctx = step_ctx->cctx;
    3714           0 :                 cb_ctx->mem_ctx = step_ctx;
    3715             : 
    3716           0 :                 tevent_req_set_callback(dpreq, nsssrv_dp_send_acct_req_done, cb_ctx);
    3717             : 
    3718           0 :                 return EAGAIN;
    3719             :             }
    3720             :         }
    3721             : 
    3722           0 :         ret = sysdb_enumgrent_with_views(dctx, dom, &res);
    3723           0 :         if (ret != EOK) {
    3724           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    3725             :                   "Enum from cache failed, skipping domain [%s]\n",
    3726             :                       dom->name);
    3727           0 :             dom = get_next_domain(dom, true);
    3728           0 :             continue;
    3729             :         }
    3730             : 
    3731           0 :         if (res->count == 0) {
    3732           0 :             DEBUG(SSSDBG_CONF_SETTINGS,
    3733             :                   "Domain [%s] has no groups, skipping.\n", dom->name);
    3734           0 :             dom = get_next_domain(dom, true);
    3735           0 :             continue;
    3736             :         }
    3737             : 
    3738           0 :         nctx->gctx->doms = talloc_realloc(gctx, gctx->doms,
    3739             :                                     struct dom_ctx, gctx->num +1);
    3740           0 :         if (!gctx->doms) {
    3741           0 :             talloc_free(gctx);
    3742           0 :             nctx->gctx = NULL;
    3743           0 :             return ENOMEM;
    3744             :         }
    3745             : 
    3746           0 :         nctx->gctx->doms[gctx->num].domain = dctx->domain;
    3747           0 :         nctx->gctx->doms[gctx->num].res = talloc_steal(gctx->doms, res);
    3748             : 
    3749           0 :         nctx->gctx->num++;
    3750             : 
    3751             :         /* do not reply until all domain searches are done */
    3752           0 :         dom = get_next_domain(dom, true);
    3753             :     }
    3754             : 
    3755             :     /* We've finished all our lookups
    3756             :      * The result object is now safe to read.
    3757             :      */
    3758           0 :     nctx->gctx->ready = true;
    3759             : 
    3760             :     /* Set up a lifetime timer for this result object
    3761             :      * We don't want this result object to outlive the
    3762             :      * enum cache refresh timeout
    3763             :      */
    3764           0 :     tv = tevent_timeval_current_ofs(nctx->enum_cache_timeout, 0);
    3765           0 :     te = tevent_add_timer(rctx->ev, nctx->gctx, tv,
    3766             :                           setgrent_result_timeout, nctx);
    3767           0 :     if (!te) {
    3768           0 :         DEBUG(SSSDBG_FATAL_FAILURE,
    3769             :               "Could not set up life timer for setgrent result object. "
    3770             :                   "Entries may become stale.\n");
    3771             :     }
    3772             : 
    3773             :     /* Notify the waiting clients */
    3774           0 :     nss_setent_notify_done(nctx->gctx);
    3775             : 
    3776           0 :     if (step_ctx->returned_to_mainloop) {
    3777           0 :         return EAGAIN;
    3778             :     } else {
    3779           0 :         return EOK;
    3780             :     }
    3781             : 
    3782             : }
    3783             : 
    3784           0 : static void setgrent_result_timeout(struct tevent_context *ev,
    3785             :                                     struct tevent_timer *te,
    3786             :                                     struct timeval current_time,
    3787             :                                     void *pvt)
    3788             : {
    3789           0 :     struct nss_ctx *nctx = talloc_get_type(pvt, struct nss_ctx);
    3790             : 
    3791           0 :     DEBUG(SSSDBG_CRIT_FAILURE,
    3792             :           "setgrent result object has expired. Cleaning up.\n");
    3793             : 
    3794             :     /* Free the group enumeration context.
    3795             :      * If additional getgrent requests come in, they will invoke
    3796             :      * an implicit setgrent and refresh the result object.
    3797             :      */
    3798           0 :     talloc_zfree(nctx->gctx);
    3799           0 : }
    3800             : 
    3801           0 : static void nss_cmd_setgrent_dp_callback(uint16_t err_maj, uint32_t err_min,
    3802             :                                          const char *err_msg, void *ptr)
    3803             : {
    3804           0 :     struct setent_step_ctx *step_ctx =
    3805             :             talloc_get_type(ptr, struct setent_step_ctx);
    3806             :     int ret;
    3807             : 
    3808           0 :     if (err_maj) {
    3809           0 :         DEBUG(SSSDBG_OP_FAILURE,
    3810             :               "Unable to get information from Data Provider\n"
    3811             :                   "Error: %u, %u, %s\n"
    3812             :                   "Will try to return what we have in cache\n",
    3813             :                   (unsigned int)err_maj, (unsigned int)err_min, err_msg);
    3814             :     }
    3815             : 
    3816           0 :     ret = nss_cmd_setgrent_step(step_ctx);
    3817           0 :     if (ret != EOK && ret != EAGAIN) {
    3818             :         /* Notify any waiting processes of failure */
    3819           0 :         nss_setent_notify_error(step_ctx->nctx->gctx, ret);
    3820             :     }
    3821           0 : }
    3822             : 
    3823           0 : static errno_t nss_cmd_setgrent_recv(struct tevent_req *req)
    3824             : {
    3825           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    3826           0 :     return EOK;
    3827             : }
    3828             : 
    3829           0 : static void nss_cmd_setgrent_done(struct tevent_req *req)
    3830             : {
    3831             :     errno_t ret;
    3832           0 :     struct nss_cmd_ctx *cmdctx =
    3833           0 :             tevent_req_callback_data(req, struct nss_cmd_ctx);
    3834             : 
    3835           0 :     ret = nss_cmd_setgrent_recv(req);
    3836           0 :     talloc_zfree(req);
    3837           0 :     if (ret == EOK || ret == ENOENT) {
    3838             :         /* Either we succeeded or no domains were eligible */
    3839           0 :         ret = sss_packet_new(cmdctx->cctx->creq, 0,
    3840           0 :                              sss_packet_get_cmd(cmdctx->cctx->creq->in),
    3841           0 :                              &cmdctx->cctx->creq->out);
    3842           0 :         if (ret == EOK) {
    3843           0 :             sss_cmd_done(cmdctx->cctx, cmdctx);
    3844           0 :             return;
    3845             :         }
    3846             :     }
    3847             : 
    3848             :     /* Something bad happened */
    3849           0 :     nss_cmd_done(cmdctx, ret);
    3850             : }
    3851             : 
    3852           0 : static int nss_cmd_retgrent(struct cli_ctx *cctx, int num)
    3853             : {
    3854             :     struct nss_ctx *nctx;
    3855             :     struct getent_ctx *gctx;
    3856           0 :     struct ldb_message **msgs = NULL;
    3857           0 :     struct dom_ctx *gdom = NULL;
    3858           0 :     int n = 0;
    3859           0 :     int ret = ENOENT;
    3860             : 
    3861           0 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    3862           0 :     if (!nctx->gctx) goto none;
    3863             : 
    3864           0 :     gctx = nctx->gctx;
    3865             : 
    3866           0 :     while (ret == ENOENT) {
    3867           0 :         if (cctx->grent_dom_idx >= gctx->num) break;
    3868             : 
    3869           0 :         gdom = &gctx->doms[cctx->grent_dom_idx];
    3870             : 
    3871           0 :         n = gdom->res->count - cctx->grent_cur;
    3872           0 :         if (n <= 0 && (cctx->grent_dom_idx+1 < gctx->num)) {
    3873           0 :             cctx->grent_dom_idx++;
    3874           0 :             gdom = &gctx->doms[cctx->grent_dom_idx];
    3875           0 :             n = gdom->res->count;
    3876           0 :             cctx->grent_cur = 0;
    3877             :         }
    3878             : 
    3879           0 :         if (!n) break;
    3880             : 
    3881           0 :         if (n > num) n = num;
    3882             : 
    3883           0 :         msgs = &(gdom->res->msgs[cctx->grent_cur]);
    3884             : 
    3885           0 :         ret = fill_grent(cctx->creq->out,
    3886             :                          gdom->domain,
    3887             :                          nctx, true, false, msgs, &n);
    3888             : 
    3889           0 :         cctx->grent_cur += n;
    3890             :     }
    3891             : 
    3892             : none:
    3893           0 :     if (ret == ENOENT) {
    3894           0 :         ret = sss_cmd_empty_packet(cctx->creq->out);
    3895             :     }
    3896           0 :     return ret;
    3897             : }
    3898             : 
    3899           0 : static int nss_cmd_getgrent_immediate(struct nss_cmd_ctx *cmdctx)
    3900             : {
    3901           0 :     struct cli_ctx *cctx = cmdctx->cctx;
    3902             :     uint8_t *body;
    3903             :     size_t blen;
    3904             :     uint32_t num;
    3905             :     int ret;
    3906             : 
    3907             :     /* get max num of entries to return in one call */
    3908           0 :     sss_packet_get_body(cctx->creq->in, &body, &blen);
    3909           0 :     if (blen != sizeof(uint32_t)) {
    3910           0 :         return EINVAL;
    3911             :     }
    3912           0 :     SAFEALIGN_COPY_UINT32(&num, body, NULL);
    3913             : 
    3914             :     /* create response packet */
    3915           0 :     ret = sss_packet_new(cctx->creq, 0,
    3916           0 :                          sss_packet_get_cmd(cctx->creq->in),
    3917           0 :                          &cctx->creq->out);
    3918           0 :     if (ret != EOK) {
    3919           0 :         return ret;
    3920             :     }
    3921             : 
    3922           0 :     ret = nss_cmd_retgrent(cctx, num);
    3923             : 
    3924           0 :     sss_packet_set_error(cctx->creq->out, ret);
    3925           0 :     sss_cmd_done(cctx, cmdctx);
    3926             : 
    3927           0 :     return EOK;
    3928             : }
    3929             : 
    3930             : static void nss_cmd_implicit_setgrent_done(struct tevent_req *req);
    3931           0 : static int nss_cmd_getgrent(struct cli_ctx *cctx)
    3932             : {
    3933             :     struct nss_ctx *nctx;
    3934             :     struct nss_cmd_ctx *cmdctx;
    3935             :     struct tevent_req *req;
    3936             : 
    3937           0 :     DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for all groups\n");
    3938             : 
    3939           0 :     cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
    3940           0 :     if (!cmdctx) {
    3941           0 :         return ENOMEM;
    3942             :     }
    3943           0 :     cmdctx->cctx = cctx;
    3944             : 
    3945             :     /* Save the current index and cursor locations
    3946             :      * If we end up calling setgrent implicitly, because the response object
    3947             :      * expired and has to be recreated, we want to resume from the same
    3948             :      * location.
    3949             :      */
    3950           0 :     cmdctx->saved_dom_idx = cctx->grent_dom_idx;
    3951           0 :     cmdctx->saved_cur = cctx->grent_cur;
    3952             : 
    3953           0 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    3954           0 :     if(!nctx->gctx || !nctx->gctx->ready) {
    3955             :         /* Make sure we invoke setgrent if it hasn't been run or is still
    3956             :          * processing from another client
    3957             :          */
    3958           0 :         req = nss_cmd_setgrent_send(cctx, cctx);
    3959           0 :         if (!req) {
    3960           0 :             return EIO;
    3961             :         }
    3962           0 :         tevent_req_set_callback(req, nss_cmd_implicit_setgrent_done, cmdctx);
    3963           0 :         return EOK;
    3964             :     }
    3965             : 
    3966           0 :     return nss_cmd_getgrent_immediate(cmdctx);
    3967             : }
    3968             : 
    3969           0 : static void nss_cmd_implicit_setgrent_done(struct tevent_req *req)
    3970             : {
    3971             :     errno_t ret;
    3972           0 :     struct nss_cmd_ctx *cmdctx =
    3973           0 :             tevent_req_callback_data(req, struct nss_cmd_ctx);
    3974             : 
    3975           0 :     ret = nss_cmd_setgrent_recv(req);
    3976           0 :     talloc_zfree(req);
    3977             : 
    3978             :     /* ENOENT is acceptable, as it just means that there were no entries
    3979             :      * to be returned. This will be handled gracefully in nss_cmd_retpwent
    3980             :      * later.
    3981             :      */
    3982           0 :     if (ret != EOK && ret != ENOENT) {
    3983           0 :         DEBUG(SSSDBG_FATAL_FAILURE,
    3984             :               "Implicit setgrent failed with unexpected error [%d][%s]\n",
    3985             :                   ret, strerror(ret));
    3986           0 :         NSS_CMD_FATAL_ERROR(cmdctx);
    3987             :     }
    3988             : 
    3989             :     /* Restore the saved index and cursor locations */
    3990           0 :     cmdctx->cctx->grent_dom_idx = cmdctx->saved_dom_idx;
    3991           0 :     cmdctx->cctx->grent_cur = cmdctx->saved_cur;
    3992             : 
    3993           0 :     ret = nss_cmd_getgrent_immediate(cmdctx);
    3994           0 :     if (ret != EOK) {
    3995           0 :         DEBUG(SSSDBG_FATAL_FAILURE,
    3996             :               "Immediate retrieval failed with unexpected error "
    3997             :                   "[%d][%s]\n", ret, strerror(ret));
    3998           0 :         NSS_CMD_FATAL_ERROR(cmdctx);
    3999             :     }
    4000             : }
    4001             : 
    4002           0 : static int nss_cmd_endgrent(struct cli_ctx *cctx)
    4003             : {
    4004             :     struct nss_ctx *nctx;
    4005             :     int ret;
    4006             : 
    4007           0 :     DEBUG(SSSDBG_CONF_SETTINGS, "Terminating request info for all groups\n");
    4008             : 
    4009           0 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    4010             : 
    4011             :     /* create response packet */
    4012           0 :     ret = sss_packet_new(cctx->creq, 0,
    4013           0 :                          sss_packet_get_cmd(cctx->creq->in),
    4014           0 :                          &cctx->creq->out);
    4015             : 
    4016           0 :     if (ret != EOK) {
    4017           0 :         return ret;
    4018             :     }
    4019           0 :     if (nctx->gctx == NULL) goto done;
    4020             : 
    4021             :     /* Reset the indices so that subsequent requests start at zero */
    4022           0 :     cctx->grent_dom_idx = 0;
    4023           0 :     cctx->grent_cur = 0;
    4024             : 
    4025             : done:
    4026           0 :     sss_cmd_done(cctx, NULL);
    4027           0 :     return EOK;
    4028             : }
    4029             : 
    4030           0 : void nss_update_initgr_memcache(struct nss_ctx *nctx,
    4031             :                                 const char *name, const char *domain,
    4032             :                                 int gnum, uint32_t *groups)
    4033           0 : {
    4034           0 :     TALLOC_CTX *tmp_ctx = NULL;
    4035             :     struct sss_domain_info *dom;
    4036             :     struct ldb_result *res;
    4037             :     struct sized_string delete_name;
    4038           0 :     bool changed = false;
    4039             :     uint32_t id;
    4040           0 :     uint32_t gids[gnum];
    4041             :     int ret;
    4042             :     int i, j;
    4043             : 
    4044           0 :     for (dom = nctx->rctx->domains; dom; dom = get_next_domain(dom, false)) {
    4045           0 :         if (strcasecmp(dom->name, domain) == 0) {
    4046           0 :             break;
    4047             :         }
    4048             :     }
    4049             : 
    4050           0 :     if (dom == NULL) {
    4051           0 :         DEBUG(SSSDBG_OP_FAILURE,
    4052             :               "Unknown domain (%s) requested by provider\n", domain);
    4053           0 :         return;
    4054             :     }
    4055             : 
    4056           0 :     tmp_ctx = talloc_new(NULL);
    4057             : 
    4058           0 :     ret = sysdb_initgroups(tmp_ctx, dom, name, &res);
    4059           0 :     if (ret != EOK && ret != ENOENT) {
    4060           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    4061             :               "Failed to make request to our cache! [%d][%s]\n",
    4062             :                ret, strerror(ret));
    4063           0 :         goto done;
    4064             :     }
    4065             : 
    4066             :     /* copy, we need the original intact in case we need to invalidate
    4067             :      * all the original groups */
    4068           0 :     memcpy(gids, groups, gnum * sizeof(uint32_t));
    4069             : 
    4070           0 :     if (ret == ENOENT || res->count == 0) {
    4071             :         /* The user is gone. Invalidate the mc record */
    4072           0 :         to_sized_string(&delete_name, name);
    4073           0 :         ret = sss_mmap_cache_pw_invalidate(nctx->pwd_mc_ctx, &delete_name);
    4074           0 :         if (ret != EOK && ret != ENOENT) {
    4075           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    4076             :                   "Internal failure in memory cache code: %d [%s]\n",
    4077             :                   ret, strerror(ret));
    4078             :         }
    4079             : 
    4080             :         /* Also invalidate his groups */
    4081           0 :         changed = true;
    4082             :     } else {
    4083             :         /* we skip the first entry, it's the user itself */
    4084           0 :         for (i = 0; i < res->count; i++) {
    4085           0 :             id = ldb_msg_find_attr_as_uint(res->msgs[i], SYSDB_GIDNUM, 0);
    4086           0 :             if (id == 0) {
    4087             :                 /* probably non-posix group, skip */
    4088           0 :                 continue;
    4089             :             }
    4090           0 :             for (j = 0; j < gnum; j++) {
    4091           0 :                 if (gids[j] == id) {
    4092           0 :                     gids[j] = 0;
    4093           0 :                     break;
    4094             :                 }
    4095             :             }
    4096           0 :             if (j >= gnum) {
    4097             :                 /* we couldn't find a match, this means the groups have
    4098             :                  * changed after the refresh */
    4099           0 :                 changed = true;
    4100           0 :                 break;
    4101             :             }
    4102             :         }
    4103             : 
    4104           0 :         if (!changed) {
    4105           0 :             for (j = 0; j < gnum; j++) {
    4106           0 :                 if (gids[j] != 0) {
    4107             :                     /* we found an un-cleared groups, this means the groups
    4108             :                      * have changed after the refresh (some got deleted) */
    4109           0 :                     changed = true;
    4110           0 :                     break;
    4111             :                 }
    4112             :             }
    4113             :         }
    4114             :     }
    4115             : 
    4116           0 :     if (changed) {
    4117           0 :         char *fq_name = sss_tc_fqname(tmp_ctx, dom->names, dom, name);
    4118           0 :         if (!fq_name) {
    4119           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    4120             :                   "Could not create fq name\n");
    4121           0 :             goto done;
    4122             :         }
    4123             : 
    4124           0 :         for (i = 0; i < gnum; i++) {
    4125           0 :             id = groups[i];
    4126             : 
    4127           0 :             ret = sss_mmap_cache_gr_invalidate_gid(nctx->grp_mc_ctx, id);
    4128           0 :             if (ret != EOK && ret != ENOENT) {
    4129           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    4130             :                       "Internal failure in memory cache code: %d [%s]\n",
    4131             :                        ret, strerror(ret));
    4132             :             }
    4133             :         }
    4134             : 
    4135           0 :         to_sized_string(&delete_name, fq_name);
    4136           0 :         ret = sss_mmap_cache_initgr_invalidate(nctx->initgr_mc_ctx,
    4137             :                                                &delete_name);
    4138           0 :         if (ret != EOK && ret != ENOENT) {
    4139           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    4140             :                   "Internal failure in memory cache code: %d [%s]\n",
    4141             :                   ret, strerror(ret));
    4142             :         }
    4143             :     }
    4144             : 
    4145             : done:
    4146           0 :     talloc_free(tmp_ctx);
    4147             : }
    4148             : 
    4149             : /* FIXME: what about mpg, should we return the user's GID ? */
    4150             : /* FIXME: should we filter out GIDs ? */
    4151           5 : static int fill_initgr(struct sss_packet *packet,
    4152             :                        struct sss_domain_info *dom,
    4153             :                        struct ldb_result *res,
    4154             :                        struct nss_ctx *nctx,
    4155             :                        const char *mc_name,
    4156             :                        const char *name)
    4157             : {
    4158             :     uint8_t *body;
    4159             :     size_t blen;
    4160             :     gid_t gid;
    4161             :     int ret, i;
    4162             :     uint32_t num;
    4163             :     size_t bindex;
    4164           5 :     int skipped = 0;
    4165             :     const char *posix;
    4166             :     gid_t orig_primary_gid;
    4167             :     struct sized_string rawname;
    4168             :     uint8_t *gids;
    4169             : 
    4170           5 :     if (res->count == 0) {
    4171           0 :         return ENOENT;
    4172             :     }
    4173             : 
    4174             :     /* one less, the first one is the user entry */
    4175           5 :     num = res->count -1;
    4176             : 
    4177           5 :     ret = sss_packet_grow(packet, (2 + res->count) * sizeof(uint32_t));
    4178           5 :     if (ret != EOK) {
    4179           0 :         return ret;
    4180             :     }
    4181           5 :     sss_packet_get_body(packet, &body, &blen);
    4182             : 
    4183           5 :     orig_primary_gid = sss_view_ldb_msg_find_attr_as_uint64(dom, res->msgs[0],
    4184             :                                                      SYSDB_PRIMARY_GROUP_GIDNUM,
    4185             :                                                      0);
    4186             : 
    4187             :     /* If the GID of the original primary group is available but equal to the
    4188             :     * current primary GID it must not be added. */
    4189           5 :     if (orig_primary_gid != 0) {
    4190           0 :         gid = sss_view_ldb_msg_find_attr_as_uint64(dom, res->msgs[0],
    4191             :                                                    SYSDB_GIDNUM, 0);
    4192             : 
    4193           0 :         if (orig_primary_gid == gid) {
    4194           0 :             orig_primary_gid = 0;
    4195             :         }
    4196             :     }
    4197             : 
    4198             :     /* 0-3: 32bit unsigned number of results
    4199             :      * 4-7: 32bit unsigned (reserved/padding) */
    4200           5 :     bindex = 2 * sizeof(uint32_t);
    4201           5 :     gids = body + bindex;
    4202             : 
    4203             :     /* skip first entry, it's the user entry */
    4204          15 :     for (i = 0; i < num; i++) {
    4205          10 :         gid = sss_view_ldb_msg_find_attr_as_uint64(dom, res->msgs[i + 1],
    4206             :                                                    SYSDB_GIDNUM, 0);
    4207          10 :         posix = ldb_msg_find_attr_as_string(res->msgs[i + 1],
    4208             :                                             SYSDB_POSIX, NULL);
    4209          10 :         if (!gid) {
    4210           0 :             if (posix && strcmp(posix, "FALSE") == 0) {
    4211           0 :                 skipped++;
    4212           0 :                 continue;
    4213             :             } else {
    4214           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    4215             :                       "Incomplete group object for initgroups! Aborting\n");
    4216           0 :                 return EFAULT;
    4217             :             }
    4218             :         }
    4219          10 :         SAFEALIGN_COPY_UINT32(body + bindex, &gid, &bindex);
    4220             : 
    4221             :         /* do not add the GID of the original primary group is the user is
    4222             :          * already and explicit member of the group. */
    4223          10 :         if (orig_primary_gid == gid) {
    4224           0 :             orig_primary_gid = 0;
    4225             :         }
    4226             :     }
    4227             : 
    4228           5 :     if (orig_primary_gid != 0) {
    4229           0 :         SAFEALIGN_COPY_UINT32(body + bindex, &orig_primary_gid, &bindex);
    4230           0 :         num++;
    4231             :     }
    4232             : 
    4233           5 :     SAFEALIGN_SETMEM_UINT32(body, num - skipped, NULL); /* num results */
    4234           5 :     SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); /* reserved */
    4235           5 :     blen = bindex;
    4236           5 :     ret = sss_packet_set_size(packet, blen);
    4237           5 :     if (ret != EOK) {
    4238           0 :         DEBUG(SSSDBG_OP_FAILURE,
    4239             :               "Could not set packet size to value:%zu\n", blen);
    4240           0 :         return ret;
    4241             :     }
    4242             : 
    4243           5 :     if (nctx->initgr_mc_ctx) {
    4244             :         struct sized_string unique_name;
    4245           0 :         char *fq_name = sss_tc_fqname(packet, dom->names, dom, name);
    4246           0 :         if (!fq_name) {
    4247           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    4248             :                   "Could not create fq name\n");
    4249           0 :             return ENOMEM;
    4250             :         }
    4251             : 
    4252           0 :         to_sized_string(&rawname, mc_name);
    4253           0 :         to_sized_string(&unique_name, fq_name);
    4254           0 :         ret = sss_mmap_cache_initgr_store(&nctx->initgr_mc_ctx, &rawname,
    4255             :                                           &unique_name, num - skipped, gids);
    4256           0 :         if (ret != EOK && ret != ENOMEM) {
    4257           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    4258             :                   "Failed to store user %s(%s) in mmap cache!\n",
    4259             :                   rawname.str, dom->name);
    4260             :         }
    4261             :     }
    4262             : 
    4263           5 :     return EOK;
    4264             : }
    4265             : 
    4266           5 : static int nss_cmd_initgr_send_reply(struct nss_dom_ctx *dctx)
    4267             : {
    4268           5 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
    4269           5 :     struct cli_ctx *cctx = cmdctx->cctx;
    4270             :     struct nss_ctx *nctx;
    4271             :     int ret;
    4272             : 
    4273           5 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    4274             : 
    4275          10 :     ret = sss_packet_new(cctx->creq, 0,
    4276           5 :                          sss_packet_get_cmd(cctx->creq->in),
    4277           5 :                          &cctx->creq->out);
    4278           5 :     if (ret != EOK) {
    4279           0 :         return EFAULT;
    4280             :     }
    4281             : 
    4282           5 :     ret = fill_initgr(cctx->creq->out, dctx->domain, dctx->res, nctx,
    4283             :                       dctx->mc_name, cmdctx->normalized_name);
    4284           5 :     if (ret) {
    4285           0 :         return ret;
    4286             :     }
    4287           5 :     sss_packet_set_error(cctx->creq->out, EOK);
    4288           5 :     sss_cmd_done(cctx, cmdctx);
    4289           5 :     return EOK;
    4290             : }
    4291             : 
    4292          15 : static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx)
    4293             : {
    4294          15 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
    4295          15 :     struct sss_domain_info *dom = dctx->domain;
    4296          15 :     struct cli_ctx *cctx = cmdctx->cctx;
    4297          15 :     char *name = NULL;
    4298             :     struct nss_ctx *nctx;
    4299             :     int ret;
    4300             :     static const char *user_attrs[] = SYSDB_PW_ATTRS;
    4301             :     struct ldb_message *msg;
    4302             :     const char *sysdb_name;
    4303             :     size_t c;
    4304          15 :     const char *extra_flag = NULL;
    4305             : 
    4306          15 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    4307             : 
    4308          15 :     while (dom) {
    4309             :        /* if it is a domainless search, skip domains that require fully
    4310             :          * qualified names instead */
    4311          30 :         while (dom && cmdctx->check_next && dom->fqnames
    4312           0 :                 && !cmdctx->name_is_upn) {
    4313           0 :             dom = get_next_domain(dom, false);
    4314             :         }
    4315             : 
    4316          15 :         if (!dom) break;
    4317             : 
    4318          15 :         if (dom != dctx->domain) {
    4319             :             /* make sure we reset the check_provider flag when we check
    4320             :              * a new domain */
    4321           0 :             dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
    4322             :         }
    4323             : 
    4324             :         /* make sure to update the dctx if we changed domain */
    4325          15 :         dctx->domain = dom;
    4326             : 
    4327          15 :         talloc_zfree(cmdctx->normalized_name);
    4328          15 :         name = sss_get_cased_name(dctx, cmdctx->name, dom->case_sensitive);
    4329          15 :         if (!name) return ENOMEM;
    4330             : 
    4331          15 :         name = sss_reverse_replace_space(cmdctx, name,
    4332          15 :                                          nctx->rctx->override_space);
    4333             :         /* save name so it can be used in initgr reply */
    4334          15 :         cmdctx->normalized_name = name;
    4335          15 :         if (name == NULL) {
    4336           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    4337             :                   "sss_reverse_replace_space failed\n");
    4338           0 :             return ENOMEM;
    4339             :         }
    4340             : 
    4341             :         /* verify this user has not yet been negatively cached,
    4342             :         * or has been permanently filtered */
    4343          15 :         ret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
    4344             :                                     dom, name);
    4345             : 
    4346             :         /* if neg cached, return we didn't find it */
    4347          15 :         if (ret == EEXIST) {
    4348           3 :             DEBUG(SSSDBG_TRACE_FUNC,
    4349             :                   "User [%s] does not exist in [%s]! (negative cache)\n",
    4350             :                    name, dom->name);
    4351             :             /* if a multidomain search, try with next */
    4352           3 :             if (cmdctx->check_next) {
    4353           3 :                 dom = get_next_domain(dom, false);
    4354           3 :                 continue;
    4355             :             }
    4356             :             /* There are no further domains or this was a
    4357             :              * fully-qualified user request.
    4358             :              */
    4359           0 :             return ENOENT;
    4360             :         }
    4361             : 
    4362          12 :         DEBUG(SSSDBG_CONF_SETTINGS,
    4363             :               "Requesting info for [%s@%s]\n", name, dom->name);
    4364             : 
    4365          12 :         if (dom->sysdb == NULL) {
    4366           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    4367             :                   "Fatal: Sysdb CTX not found for this domain!\n");
    4368           0 :             return EIO;
    4369             :         }
    4370             : 
    4371          12 :         if (cmdctx->name_is_upn) {
    4372           3 :             ret = sysdb_search_user_by_upn(cmdctx, dom, name, user_attrs, &msg);
    4373           3 :             if (ret == ENOENT) {
    4374           2 :                 dctx->res = talloc_zero(cmdctx, struct ldb_result);
    4375           2 :                 if (dctx->res == NULL) {
    4376           0 :                     DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
    4377           0 :                     return ENOMEM;
    4378             :                 }
    4379             : 
    4380           2 :                 dctx->res->count = 0;
    4381           2 :                 dctx->res->msgs = NULL;
    4382           2 :                 ret = EOK;
    4383           1 :             } else if (ret != EOK) {
    4384           0 :                 DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_upn failed.\n");
    4385           0 :                 return ret;
    4386             :             } else {
    4387           1 :                 sysdb_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
    4388           1 :                 if (sysdb_name == NULL) {
    4389           0 :                     DEBUG(SSSDBG_OP_FAILURE,
    4390             :                         "Sysdb entry does not have a name.\n");
    4391           0 :                     return EINVAL;
    4392             :                 }
    4393             : 
    4394           1 :                 ret = sysdb_initgroups(cmdctx, dom, sysdb_name, &dctx->res);
    4395           1 :                 if (ret == EOK && DOM_HAS_VIEWS(dom)) {
    4396           0 :                     for (c = 0; c < dctx->res->count; c++) {
    4397           0 :                         ret = sysdb_add_overrides_to_object(dom, dctx->res->msgs[c],
    4398             :                                                             NULL, NULL);
    4399           0 :                         if (ret != EOK) {
    4400           0 :                             DEBUG(SSSDBG_OP_FAILURE,
    4401             :                                 "sysdb_add_overrides_to_object failed.\n");
    4402           0 :                             return ret;
    4403             :                         }
    4404             :                     }
    4405             :                 }
    4406             :             }
    4407             :         } else {
    4408           9 :             ret = sysdb_initgroups_with_views(cmdctx, dom, name, &dctx->res);
    4409             :         }
    4410          12 :         if (ret != EOK) {
    4411           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    4412             :                   "Failed to make request to our cache! [%d][%s]\n",
    4413             :                       ret, strerror(ret));
    4414           0 :             return EIO;
    4415             :         }
    4416             : 
    4417          12 :         if (dctx->res->count == 0 && !dctx->check_provider) {
    4418             :             /* set negative cache only if not result of cache check */
    4419           2 :             ret = sss_ncache_set_user(nctx->ncache, false, dom, name);
    4420           2 :             if (ret != EOK) {
    4421           0 :                 DEBUG(SSSDBG_MINOR_FAILURE, "Cannot set negcache for %s@%s\n",
    4422             :                       name, dom->name);
    4423             :             }
    4424             : 
    4425             :             /* if a multidomain search, try with next */
    4426           2 :             if (cmdctx->check_next) {
    4427           2 :                 dom = get_next_domain(dom, false);
    4428           2 :                 if (dom) continue;
    4429             :             }
    4430             : 
    4431           2 :             DEBUG(SSSDBG_OP_FAILURE, "No results for initgroups call\n");
    4432             : 
    4433           2 :             return ENOENT;
    4434             :         }
    4435             : 
    4436             :         /* if this is a caching provider (or if we haven't checked the cache
    4437             :          * yet) then verify that the cache is uptodate */
    4438          10 :         if (dctx->check_provider) {
    4439             : 
    4440           7 :             if (cmdctx->name_is_upn) {
    4441           2 :                 extra_flag = EXTRA_NAME_IS_UPN;
    4442           5 :             } else if (DOM_HAS_VIEWS(dom) && (dctx->res->count == 0
    4443           0 :                     || ldb_msg_find_attr_as_string(dctx->res->msgs[0],
    4444             :                                                    OVERRIDE_PREFIX SYSDB_NAME,
    4445             :                                                    NULL) != NULL)) {
    4446           0 :                 extra_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
    4447             :             } else {
    4448           5 :                 extra_flag = NULL;
    4449             :             }
    4450             : 
    4451           7 :             ret = check_cache(dctx, nctx, dctx->res, SSS_DP_INITGROUPS, name, 0,
    4452             :                               extra_flag, nss_cmd_getby_dp_callback, dctx);
    4453           7 :             if (ret != EOK) {
    4454             :                 /* Anything but EOK means we should reenter the mainloop
    4455             :                  * because we may be refreshing the cache
    4456             :                  */
    4457           5 :                 return ret;
    4458             :             }
    4459             :         }
    4460             : 
    4461           5 :         DEBUG(SSSDBG_TRACE_FUNC,
    4462             :               "Initgroups for [%s@%s] completed\n", name, dom->name);
    4463           5 :         return EOK;
    4464             :     }
    4465             : 
    4466           3 :     DEBUG(SSSDBG_MINOR_FAILURE,
    4467             :           "No matching domain found for [%s], fail!\n", cmdctx->name);
    4468           3 :     return ENOENT;
    4469             : }
    4470             : 
    4471             : /* for now, if we are online, try to always query the backend */
    4472           9 : static int nss_cmd_initgroups(struct cli_ctx *cctx)
    4473             : {
    4474           9 :     return nss_cmd_getbynam(SSS_NSS_INITGR, cctx);
    4475             : }
    4476             : 
    4477           3 : static errno_t nss_cmd_getsidby_search(struct nss_dom_ctx *dctx)
    4478             : {
    4479           3 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
    4480           3 :     struct sss_domain_info *dom = dctx->domain;
    4481           3 :     struct cli_ctx *cctx = cmdctx->cctx;
    4482             :     struct sysdb_ctx *sysdb;
    4483             :     struct nss_ctx *nctx;
    4484             :     int ret;
    4485             :     int err;
    4486           3 :     const char *default_attrs[] = {SYSDB_NAME, SYSDB_OBJECTCLASS, SYSDB_SID_STR,
    4487             :                                    ORIGINALAD_PREFIX SYSDB_NAME,
    4488             :                                    ORIGINALAD_PREFIX SYSDB_UIDNUM,
    4489             :                                    ORIGINALAD_PREFIX SYSDB_GIDNUM,
    4490             :                                    ORIGINALAD_PREFIX SYSDB_GECOS,
    4491             :                                    ORIGINALAD_PREFIX SYSDB_HOMEDIR,
    4492             :                                    ORIGINALAD_PREFIX SYSDB_SHELL,
    4493             :                                    SYSDB_UPN,
    4494             :                                    SYSDB_DEFAULT_OVERRIDE_NAME,
    4495             :                                    SYSDB_AD_ACCOUNT_EXPIRES,
    4496             :                                    SYSDB_AD_USER_ACCOUNT_CONTROL,
    4497             :                                    SYSDB_SSH_PUBKEY,
    4498             :                                    SYSDB_ORIG_DN,
    4499             :                                    SYSDB_ORIG_MEMBEROF,
    4500             :                                    SYSDB_DEFAULT_ATTRS, NULL};
    4501             :     const char **attrs;
    4502           3 :     bool user_found = false;
    4503           3 :     bool group_found = false;
    4504           3 :     struct ldb_message *msg = NULL;
    4505           3 :     char *sysdb_name = NULL;
    4506           3 :     char *name = NULL;
    4507             :     char *req_name;
    4508             :     uint32_t req_id;
    4509             :     enum sss_dp_acct_type req_type;
    4510             : 
    4511           3 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    4512             : 
    4513           3 :     while (dom) {
    4514             : 
    4515           3 :         if (cmdctx->cmd == SSS_NSS_GETSIDBYID) {
    4516             :             /* check that the uid is valid for this domain */
    4517           0 :             if ((dom->id_min && (cmdctx->id < dom->id_min)) ||
    4518           0 :                 (dom->id_max && (cmdctx->id > dom->id_max))) {
    4519           0 :                 DEBUG(SSSDBG_TRACE_FUNC,
    4520             :                       "Uid [%"PRIu32"] does not exist in domain [%s]! "
    4521             :                        "(id out of range)\n",
    4522             :                        cmdctx->id, dom->name);
    4523           0 :                 if (cmdctx->check_next) {
    4524           0 :                     dom = get_next_domain(dom, true);
    4525           0 :                     continue;
    4526             :                 }
    4527           0 :                 ret = ENOENT;
    4528           0 :                 goto done;
    4529             :             }
    4530             :         } else {
    4531             :            /* if it is a domainless search, skip domains that require fully
    4532             :             * qualified names instead */
    4533           6 :             while (dom && cmdctx->check_next && dom->fqnames) {
    4534           0 :                 dom = get_next_domain(dom, false);
    4535             :             }
    4536             : 
    4537           3 :             if (!dom) break;
    4538             :         }
    4539             : 
    4540           3 :         if (dom != dctx->domain) {
    4541             :             /* make sure we reset the check_provider flag when we check
    4542             :              * a new domain */
    4543           0 :             dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
    4544             :         }
    4545             : 
    4546             :         /* make sure to update the dctx if we changed domain */
    4547           3 :         dctx->domain = dom;
    4548             : 
    4549           3 :         if (cmdctx->cmd == SSS_NSS_GETSIDBYID) {
    4550           0 :             DEBUG(SSSDBG_TRACE_FUNC, "Requesting info for [%"PRIu32"@%s]\n",
    4551             :                                       cmdctx->id, dom->name);
    4552             : 
    4553           0 :             ret = sss_ncache_check_uid(nctx->ncache, nctx->neg_timeout, dom,
    4554             :                                        cmdctx->id);
    4555           0 :             if (ret == EEXIST) {
    4556           0 :                 ret = sss_ncache_check_gid(nctx->ncache, nctx->neg_timeout, dom,
    4557             :                                            cmdctx->id);
    4558           0 :                 if (ret == EEXIST) {
    4559           0 :                     DEBUG(SSSDBG_TRACE_FUNC,
    4560             :                           "ID [%"PRIu32"] does not exist in [%s]! (negative cache)\n",
    4561             :                            cmdctx->id, dom->name);
    4562             :                     /* if a multidomain search, try with next, including
    4563             :                      * sub-domains */
    4564           0 :                     if (cmdctx->check_next) {
    4565           0 :                         dom = get_next_domain(dom, true);
    4566           0 :                         continue;
    4567             :                     }
    4568             :                     /* There are no further domains. */
    4569           0 :                     ret = ENOENT;
    4570           0 :                     goto done;
    4571             :                 }
    4572             :             }
    4573             : 
    4574             :         } else {
    4575           3 :             talloc_free(name);
    4576           3 :             talloc_zfree(sysdb_name);
    4577             : 
    4578           3 :             name = sss_get_cased_name(cmdctx, cmdctx->name, dom->case_sensitive);
    4579           3 :             if (name == NULL) {
    4580           0 :                 DEBUG(SSSDBG_OP_FAILURE, "sss_get_cased_name failed.\n");
    4581           0 :                 ret = ENOMEM;
    4582           0 :                 goto done;
    4583             :             }
    4584             : 
    4585           3 :             name = sss_reverse_replace_space(dctx, name,
    4586           3 :                                              nctx->rctx->override_space);
    4587           3 :             if (name == NULL) {
    4588           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    4589             :                       "sss_reverse_replace_space failed\n");
    4590           0 :                 ret = ENOMEM;
    4591           0 :                 goto done;
    4592             :             }
    4593             : 
    4594             :             /* For subdomains a fully qualified name is needed for
    4595             :              * sysdb_search_user_by_name and sysdb_search_group_by_name. */
    4596           3 :             if (IS_SUBDOMAIN(dom)) {
    4597           0 :                 sysdb_name = sss_tc_fqname(cmdctx, dom->names, dom, name);
    4598           0 :                 if (sysdb_name == NULL) {
    4599           0 :                     DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
    4600           0 :                     ret = ENOMEM;
    4601           0 :                     goto done;
    4602             :                 }
    4603             :             }
    4604             : 
    4605             : 
    4606             :             /* verify this name has not yet been negatively cached, as user
    4607             :              * and groupm, or has been permanently filtered */
    4608           3 :             ret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
    4609             :                                         dom, name);
    4610             : 
    4611           3 :             if (ret == EEXIST) {
    4612           0 :                 ret = sss_ncache_check_group(nctx->ncache, nctx->neg_timeout,
    4613             :                                              dom, name);
    4614           0 :                 if (ret == EEXIST) {
    4615             :                     /* if neg cached, return we didn't find it */
    4616           0 :                     DEBUG(SSSDBG_TRACE_FUNC,
    4617             :                           "SID [%s] does not exist in [%s]! (negative cache)\n",
    4618             :                            name, dom->name);
    4619             :                     /* if a multidomain search, try with next */
    4620           0 :                     if (cmdctx->check_next) {
    4621           0 :                         dom = get_next_domain(dom, false);
    4622           0 :                         continue;
    4623             :                     }
    4624             :                     /* There are no further domains or this was a
    4625             :                      * fully-qualified user request.
    4626             :                      */
    4627           0 :                     ret = ENOENT;
    4628           0 :                     goto done;
    4629             :                 }
    4630             :             }
    4631             : 
    4632           3 :             DEBUG(SSSDBG_TRACE_FUNC, "Requesting info for [%s@%s]\n",
    4633             :                                       name, dom->name);
    4634             :         }
    4635             : 
    4636             : 
    4637           3 :         sysdb = dom->sysdb;
    4638           3 :         if (sysdb == NULL) {
    4639           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    4640             :                   "Fatal: Sysdb CTX not found for this domain!\n");
    4641           0 :             ret = EIO;
    4642           0 :             goto done;
    4643             :         }
    4644             : 
    4645           3 :         attrs = default_attrs;
    4646           3 :         if (cmdctx->cmd == SSS_NSS_GETORIGBYNAME
    4647           3 :                 && nctx->extra_attributes != NULL) {
    4648           2 :             ret = add_strings_lists(cmdctx, default_attrs,
    4649             :                                     nctx->extra_attributes, false,
    4650             :                                     discard_const(&attrs));
    4651           2 :             if (ret != EOK) {
    4652           0 :                 DEBUG(SSSDBG_OP_FAILURE, "add_strings_lists failed.\n");
    4653           0 :                 goto done;
    4654             :             }
    4655             :         }
    4656             : 
    4657           3 :         if (cmdctx->cmd == SSS_NSS_GETSIDBYID) {
    4658           0 :             ret = sysdb_search_user_by_uid(cmdctx, dom, cmdctx->id, attrs,
    4659             :                                            &msg);
    4660           0 :             if (ret != EOK && ret != ENOENT) {
    4661           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    4662             :                       "Failed to make request to our cache!\n");
    4663           0 :                 ret = EIO;
    4664           0 :                 goto done;
    4665             :             }
    4666             : 
    4667           0 :             if (ret == EOK) {
    4668           0 :                 user_found = true;
    4669             :             } else {
    4670           0 :                 talloc_free(msg);
    4671           0 :                 ret = sysdb_search_group_by_gid(cmdctx, dom, cmdctx->id, attrs,
    4672             :                                                 &msg);
    4673           0 :                 if (ret != EOK && ret != ENOENT) {
    4674           0 :                     DEBUG(SSSDBG_CRIT_FAILURE,
    4675             :                           "Failed to make request to our cache!\n");
    4676           0 :                     ret = EIO;
    4677           0 :                     goto done;
    4678             :                 }
    4679             : 
    4680           0 :                 if (ret == EOK) {
    4681           0 :                     group_found = true;
    4682             :                 }
    4683             :             }
    4684             :         } else {
    4685           3 :             ret = sysdb_search_user_by_name(cmdctx, dom,
    4686             :                                             sysdb_name ? sysdb_name : name,
    4687             :                                             attrs, &msg);
    4688           3 :             if (ret != EOK && ret != ENOENT) {
    4689           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    4690             :                       "Failed to make request to our cache!\n");
    4691           0 :                 ret = EIO;
    4692           0 :                 goto done;
    4693             :             }
    4694             : 
    4695           3 :             if (ret == EOK) {
    4696           3 :                 user_found = true;
    4697             :             } else {
    4698           0 :                 talloc_free(msg);
    4699           0 :                 ret = sysdb_search_group_by_name(cmdctx, dom,
    4700             :                                                  sysdb_name ? sysdb_name : name,
    4701             :                                                  attrs, &msg);
    4702           0 :                 if (ret != EOK && ret != ENOENT) {
    4703           0 :                     DEBUG(SSSDBG_CRIT_FAILURE,
    4704             :                           "Failed to make request to our cache!\n");
    4705           0 :                     ret = EIO;
    4706           0 :                     goto done;
    4707             :                 }
    4708             : 
    4709           0 :                 if (ret == EOK) {
    4710           0 :                     group_found = true;
    4711             :                 }
    4712             :             }
    4713             :         }
    4714             : 
    4715           3 :         dctx->res = talloc_zero(cmdctx, struct ldb_result);
    4716           3 :         if (dctx->res == NULL) {
    4717           0 :             DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
    4718           0 :             ret = ENOMEM;
    4719           0 :             goto done;
    4720             :         }
    4721             : 
    4722           3 :         if (user_found || group_found) {
    4723           3 :             dctx->res->count = 1;
    4724           3 :             dctx->res->msgs = talloc_array(dctx->res, struct ldb_message *, 1);
    4725           3 :             if (dctx->res->msgs == NULL) {
    4726           0 :                 DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
    4727           0 :                 ret = ENOMEM;
    4728           0 :                 goto done;
    4729             :             }
    4730           3 :             dctx->res->msgs[0] = talloc_steal(dctx->res, msg);
    4731             :         }
    4732             : 
    4733           3 :         if (dctx->res->count == 0 && !dctx->check_provider) {
    4734           0 :             if (cmdctx->cmd == SSS_NSS_GETSIDBYNAME
    4735           0 :                     || cmdctx->cmd == SSS_NSS_GETORIGBYNAME) {
    4736           0 :                 ret = sss_ncache_set_user(nctx->ncache, false, dom, name);
    4737           0 :                 if (ret != EOK) {
    4738           0 :                     DEBUG(SSSDBG_MINOR_FAILURE,
    4739             :                           "Cannot set negcache for %s@%s\n", name, dom->name);
    4740             :                 }
    4741             : 
    4742           0 :                 ret = sss_ncache_set_group(nctx->ncache, false, dom, name);
    4743           0 :                 if (ret != EOK) {
    4744           0 :                     DEBUG(SSSDBG_MINOR_FAILURE,
    4745             :                           "Cannot set negcache for %s@%s\n", name, dom->name);
    4746             :                 }
    4747             :             }
    4748             :             /* if a multidomain search, try with next */
    4749           0 :             if (cmdctx->check_next) {
    4750           0 :                 dom = get_next_domain(dom, true);
    4751           0 :                 continue;
    4752             :             }
    4753             : 
    4754           0 :             DEBUG(SSSDBG_OP_FAILURE, "No matching user or group found.\n");
    4755           0 :             ret = ENOENT;
    4756           0 :             goto done;
    4757             :         }
    4758             : 
    4759             :         /* if this is a caching provider (or if we haven't checked the cache
    4760             :          * yet) then verify that the cache is uptodate */
    4761           3 :         if (dctx->check_provider) {
    4762           3 :             if (cmdctx->cmd == SSS_NSS_GETSIDBYID) {
    4763           0 :                 req_name = NULL;
    4764           0 :                 req_id = cmdctx->id;
    4765             :             } else {
    4766           3 :                 req_name = name;
    4767           3 :                 req_id = 0;
    4768             :             }
    4769           3 :             if (user_found) {
    4770           3 :                 req_type = SSS_DP_USER;
    4771           0 :             } else if (group_found) {
    4772           0 :                 req_type = SSS_DP_GROUP;
    4773             :             } else {
    4774           0 :                 req_type = SSS_DP_USER_AND_GROUP;
    4775             :             }
    4776             : 
    4777           3 :             ret = check_cache(dctx, nctx, dctx->res,
    4778             :                               req_type, req_name, req_id, NULL,
    4779             :                               nss_cmd_getby_dp_callback,
    4780             :                               dctx);
    4781           3 :             if (ret != EOK) {
    4782             :                 /* Anything but EOK means we should reenter the mainloop
    4783             :                  * because we may be refreshing the cache
    4784             :                  */
    4785           0 :                 goto done;
    4786             :             }
    4787             :         }
    4788             : 
    4789             :         /* One result found */
    4790           3 :         if (cmdctx->cmd == SSS_NSS_GETSIDBYID) {
    4791           0 :             DEBUG(SSSDBG_TRACE_FUNC, "Returning info for id [%"PRIu32"@%s]\n",
    4792             :                                      cmdctx->id, dom->name);
    4793             :         } else {
    4794           3 :             DEBUG(SSSDBG_TRACE_FUNC, "Returning info for user/group [%s@%s]\n",
    4795             :                                       name, dom->name);
    4796             :         }
    4797             : 
    4798             :         /* Success. Break from the loop and return EOK */
    4799           3 :         ret = EOK;
    4800           3 :         goto done;
    4801             :     }
    4802             : 
    4803             :     /* All domains were tried and none had the entry. */
    4804           0 :     ret = ENOENT;
    4805             : done:
    4806           3 :     if (ret == ENOENT) {
    4807             :         /* The entry was not found, need to set result in negative cache */
    4808           0 :         if (cmdctx->cmd == SSS_NSS_GETSIDBYID) {
    4809           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
    4810             :                 "No matching domain found for [%"PRIu32"], fail!\n", cmdctx->id);
    4811           0 :             err = sss_ncache_set_uid(nctx->ncache, false, NULL, cmdctx->id);
    4812           0 :             if (err != EOK) {
    4813           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
    4814             :                     "Cannot set negative cache for UID %"PRIu32"\n", cmdctx->id);
    4815             :             }
    4816             : 
    4817           0 :             err = sss_ncache_set_gid(nctx->ncache, false, NULL, cmdctx->id);
    4818           0 :             if (err != EOK) {
    4819           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
    4820             :                     "Cannot set negative cache for GID %"PRIu32"\n", cmdctx->id);
    4821             :             }
    4822             :         } else {
    4823           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
    4824             :                   "No matching domain found for [%s], fail!\n", cmdctx->name);
    4825             :         }
    4826             :     }
    4827           3 :     return ret;
    4828             : }
    4829             : 
    4830           6 : static errno_t nss_cmd_getbysid_search(struct nss_dom_ctx *dctx)
    4831             : {
    4832           6 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
    4833           6 :     struct sss_domain_info *dom = dctx->domain;
    4834           6 :     struct cli_ctx *cctx = cmdctx->cctx;
    4835             :     struct sysdb_ctx *sysdb;
    4836             :     struct nss_ctx *nctx;
    4837             :     int ret;
    4838             : 
    4839           6 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    4840             : 
    4841           6 :     DEBUG(SSSDBG_TRACE_FUNC, "Requesting info for [%s@%s]\n", cmdctx->secid,
    4842             :                                                                dom->name);
    4843             : 
    4844           6 :     sysdb = dom->sysdb;
    4845           6 :     if (sysdb == NULL) {
    4846           0 :         DEBUG(SSSDBG_FATAL_FAILURE, "Fatal: Sysdb CTX not found for this " \
    4847             :                                      "domain!\n");
    4848           0 :         return EIO;
    4849             :     }
    4850             : 
    4851             :     /* verify this user has not yet been negatively cached,
    4852             :         * or has been permanently filtered */
    4853           6 :     ret = sss_ncache_check_sid(nctx->ncache, nctx->neg_timeout, cmdctx->secid);
    4854           6 :     if (ret == EEXIST) {
    4855           1 :         DEBUG(SSSDBG_TRACE_FUNC,
    4856             :               "SID [%s] does not exist! (negative cache)\n", cmdctx->secid);
    4857           1 :         return ENOENT;
    4858             :     }
    4859             : 
    4860           5 :     ret = sysdb_search_object_by_sid(cmdctx, dom, cmdctx->secid, NULL,
    4861             :                                      &dctx->res);
    4862           5 :     if (ret == ENOENT) {
    4863           2 :         if (!dctx->check_provider) {
    4864           1 :             DEBUG(SSSDBG_OP_FAILURE, "No results for getbysid call.\n");
    4865             : 
    4866             :             /* set negative cache only if not result of cache check */
    4867           1 :             ret = sss_ncache_set_sid(nctx->ncache, false, cmdctx->secid);
    4868           1 :             if (ret != EOK) {
    4869           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
    4870             :                       "Cannot set negative cache for %s\n", cmdctx->secid);
    4871             :             }
    4872             : 
    4873           1 :             return ENOENT;
    4874             :         }
    4875             : 
    4876           1 :         dctx->res = talloc_zero(cmdctx, struct ldb_result);
    4877           1 :         if (dctx->res == NULL) {
    4878           0 :             DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
    4879           0 :             return ENOMEM;
    4880             :         }
    4881             :         /* Fall through and call the backend */
    4882           3 :     } else if (ret != EOK) {
    4883           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to make request to our cache!\n");
    4884           0 :         return EIO;
    4885             :     }
    4886             : 
    4887           4 :     if (dctx->res->count > 1) {
    4888           0 :         DEBUG(SSSDBG_FATAL_FAILURE, "getbysid call returned more than one " \
    4889             :                                      "result !?!\n");
    4890           0 :         return ENOENT;
    4891             :     }
    4892             : 
    4893             :     /* if this is a caching provider (or if we haven't checked the cache
    4894             :      * yet) then verify that the cache is uptodate */
    4895           4 :     if (dctx->check_provider) {
    4896           3 :         ret = check_cache(dctx, nctx, dctx->res,
    4897           3 :                           SSS_DP_SECID, cmdctx->secid, 0, NULL,
    4898             :                           nss_cmd_getby_dp_callback,
    4899             :                           dctx);
    4900           3 :         if (ret != EOK) {
    4901             :             /* Anything but EOK means we should reenter the mainloop
    4902             :              * because we may be refreshing the cache
    4903             :              */
    4904           2 :             return ret;
    4905             :         }
    4906             :     }
    4907             : 
    4908             :     /* One result found */
    4909           2 :     DEBUG(SSSDBG_TRACE_FUNC, "Returning info for sid [%s@%s]\n", cmdctx->secid,
    4910             :                                                                   dom->name);
    4911             : 
    4912           2 :     return EOK;
    4913             : }
    4914             : 
    4915           5 : static errno_t find_sss_id_type(struct ldb_message *msg,
    4916             :                                 bool mpg,
    4917             :                                 enum sss_id_type *id_type)
    4918             : {
    4919             :     size_t c;
    4920             :     struct ldb_message_element *el;
    4921           5 :     struct ldb_val *val = NULL;
    4922             : 
    4923           5 :     el = ldb_msg_find_element(msg, SYSDB_OBJECTCLASS);
    4924           5 :     if (el == NULL) {
    4925           0 :         DEBUG(SSSDBG_OP_FAILURE, "Objectclass attribute not found.\n");
    4926           0 :         return EINVAL;
    4927             :     }
    4928             : 
    4929           5 :     for (c = 0; c < el->num_values; c++) {
    4930           5 :         val = &(el->values[c]);
    4931          10 :         if (strncasecmp(SYSDB_USER_CLASS,
    4932           5 :                         (char *)val->data, val->length) == 0) {
    4933           5 :             break;
    4934             :         }
    4935             :     }
    4936             : 
    4937           5 :     if (c == el->num_values) {
    4938           0 :         *id_type = SSS_ID_TYPE_GID;
    4939             :     } else {
    4940           5 :         if (mpg) {
    4941           0 :             *id_type = SSS_ID_TYPE_BOTH;
    4942             :         } else {
    4943           5 :             *id_type = SSS_ID_TYPE_UID;
    4944             :         }
    4945             :     }
    4946             : 
    4947           5 :     return EOK;
    4948             : }
    4949             : 
    4950           0 : static errno_t fill_sid(struct sss_packet *packet,
    4951             :                         enum sss_id_type id_type,
    4952             :                         struct ldb_message *msg)
    4953             : {
    4954             :     int ret;
    4955             :     const char *sid_str;
    4956             :     struct sized_string sid;
    4957             :     uint8_t *body;
    4958             :     size_t blen;
    4959           0 :     size_t pctr = 0;
    4960             : 
    4961           0 :     sid_str = ldb_msg_find_attr_as_string(msg, SYSDB_SID_STR, NULL);
    4962           0 :     if (sid_str == NULL) {
    4963           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Missing SID.\n");
    4964           0 :         return EINVAL;
    4965             :     }
    4966             : 
    4967           0 :     to_sized_string(&sid, sid_str);
    4968             : 
    4969           0 :     ret = sss_packet_grow(packet, sid.len +  3* sizeof(uint32_t));
    4970           0 :     if (ret != EOK) {
    4971           0 :         DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
    4972           0 :         return ret;
    4973             :     }
    4974             : 
    4975           0 :     sss_packet_get_body(packet, &body, &blen);
    4976           0 :     SAFEALIGN_SETMEM_UINT32(body, 1, &pctr); /* Num results */
    4977           0 :     SAFEALIGN_SETMEM_UINT32(body + pctr, 0, &pctr); /* reserved */
    4978           0 :     SAFEALIGN_COPY_UINT32(body + pctr, &id_type, &pctr);
    4979           0 :     memcpy(&body[pctr], sid.str, sid.len);
    4980             : 
    4981           0 :     return EOK;
    4982             : }
    4983             : 
    4984           5 : static errno_t process_attr_list(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
    4985             :                                  const char **attr_list,
    4986             :                                  struct sized_string **_keys,
    4987             :                                  struct sized_string **_vals,
    4988             :                                  size_t *array_size, size_t *sum, size_t *found)
    4989             : {
    4990             :     size_t c;
    4991             :     size_t d;
    4992             :     struct sized_string *keys;
    4993             :     struct sized_string *vals;
    4994             :     struct ldb_val *val;
    4995             :     struct ldb_message_element *el;
    4996             : 
    4997           5 :     keys = *_keys;
    4998           5 :     vals = *_vals;
    4999             : 
    5000          51 :     for (c = 0; attr_list[c] != NULL; c++) {
    5001          46 :         el = ldb_msg_find_element(msg, attr_list[c]);
    5002          46 :         if (el != NULL &&  el->num_values > 0) {
    5003          12 :             if (el->num_values > 1) {
    5004           1 :                 *array_size += el->num_values;
    5005           1 :                 keys = talloc_realloc(mem_ctx, keys, struct sized_string,
    5006             :                                       *array_size);
    5007           1 :                 vals = talloc_realloc(mem_ctx, vals, struct sized_string,
    5008             :                                       *array_size);
    5009           1 :                 if (keys == NULL || vals == NULL) {
    5010           0 :                     DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
    5011           0 :                     return ENOMEM;
    5012             :                 }
    5013             :             }
    5014          26 :             for (d = 0; d < el->num_values; d++) {
    5015          14 :                 to_sized_string(&keys[*found], attr_list[c]);
    5016          14 :                 *sum += keys[*found].len;
    5017          14 :                 val = &(el->values[d]);
    5018          14 :                 if (val == NULL || val->data == NULL
    5019          14 :                         || val->data[val->length] != '\0') {
    5020           0 :                     DEBUG(SSSDBG_CRIT_FAILURE,
    5021             :                           "Unexpected attribute value found for [%s].\n",
    5022             :                           attr_list[c]);
    5023           0 :                     return EINVAL;
    5024             :                 }
    5025          14 :                 to_sized_string(&vals[*found], (const char *)val->data);
    5026          14 :                 *sum += vals[*found].len;
    5027             : 
    5028          14 :                 (*found)++;
    5029             :             }
    5030             :         }
    5031             :     }
    5032             : 
    5033           5 :     *_keys = keys;
    5034           5 :     *_vals = vals;
    5035             : 
    5036           5 :     return EOK;
    5037             : }
    5038             : 
    5039           3 : static errno_t fill_orig(struct sss_packet *packet,
    5040             :                          struct resp_ctx *rctx,
    5041             :                          enum sss_id_type id_type,
    5042             :                          struct ldb_message *msg)
    5043             : {
    5044             :     int ret;
    5045             :     TALLOC_CTX *tmp_ctx;
    5046             :     uint8_t *body;
    5047             :     size_t blen;
    5048           3 :     size_t pctr = 0;
    5049             :     size_t c;
    5050             :     size_t sum;
    5051             :     size_t found;
    5052             :     size_t array_size;
    5053           3 :     size_t extra_attrs_count = 0;
    5054           3 :     const char **extra_attrs_list = NULL;
    5055           3 :     const char *orig_attr_list[] = {SYSDB_SID_STR,
    5056             :                                     ORIGINALAD_PREFIX SYSDB_NAME,
    5057             :                                     ORIGINALAD_PREFIX SYSDB_UIDNUM,
    5058             :                                     ORIGINALAD_PREFIX SYSDB_GIDNUM,
    5059             :                                     ORIGINALAD_PREFIX SYSDB_HOMEDIR,
    5060             :                                     ORIGINALAD_PREFIX SYSDB_GECOS,
    5061             :                                     ORIGINALAD_PREFIX SYSDB_SHELL,
    5062             :                                     SYSDB_UPN,
    5063             :                                     SYSDB_DEFAULT_OVERRIDE_NAME,
    5064             :                                     SYSDB_AD_ACCOUNT_EXPIRES,
    5065             :                                     SYSDB_AD_USER_ACCOUNT_CONTROL,
    5066             :                                     SYSDB_SSH_PUBKEY,
    5067             :                                     SYSDB_ORIG_DN,
    5068             :                                     SYSDB_ORIG_MEMBEROF,
    5069             :                                     NULL};
    5070             :     struct sized_string *keys;
    5071             :     struct sized_string *vals;
    5072             :     struct nss_ctx *nctx;
    5073             : 
    5074           3 :     tmp_ctx = talloc_new(NULL);
    5075           3 :     if (tmp_ctx == NULL) {
    5076           0 :         DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
    5077           0 :         return ENOMEM;
    5078             :     }
    5079             : 
    5080           3 :     nctx = talloc_get_type(rctx->pvt_ctx, struct nss_ctx);
    5081           3 :     if (nctx->extra_attributes != NULL) {
    5082           2 :         extra_attrs_list = nctx->extra_attributes;
    5083           8 :             for(extra_attrs_count = 0;
    5084           6 :                 extra_attrs_list[extra_attrs_count] != NULL;
    5085           4 :                 extra_attrs_count++);
    5086             :     }
    5087             : 
    5088           3 :     array_size = sizeof(orig_attr_list) + extra_attrs_count;
    5089           3 :     keys = talloc_array(tmp_ctx, struct sized_string, array_size);
    5090           3 :     vals = talloc_array(tmp_ctx, struct sized_string, array_size);
    5091           3 :     if (keys == NULL || vals == NULL) {
    5092           0 :         DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
    5093           0 :         ret = ENOMEM;
    5094           0 :         goto done;
    5095             :     }
    5096             : 
    5097           3 :     sum = 0;
    5098           3 :     found = 0;
    5099             : 
    5100           3 :     ret = process_attr_list(tmp_ctx, msg, orig_attr_list, &keys, &vals,
    5101             :                             &array_size, &sum, &found);
    5102           3 :     if (ret != EOK) {
    5103           0 :         DEBUG(SSSDBG_OP_FAILURE, "process_attr_list failed.\n");
    5104           0 :         goto done;
    5105             :     }
    5106             : 
    5107           3 :     if (extra_attrs_count != 0) {
    5108           2 :         ret = process_attr_list(tmp_ctx, msg, extra_attrs_list, &keys, &vals,
    5109             :                                 &array_size, &sum, &found);
    5110           2 :         if (ret != EOK) {
    5111           0 :             DEBUG(SSSDBG_OP_FAILURE, "process_attr_list failed.\n");
    5112           0 :             goto done;
    5113             :         }
    5114             :     }
    5115             : 
    5116           3 :     ret = sss_packet_grow(packet, sum +  3 * sizeof(uint32_t));
    5117           3 :     if (ret != EOK) {
    5118           0 :         DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
    5119           0 :         goto done;
    5120             :     }
    5121             : 
    5122           3 :     sss_packet_get_body(packet, &body, &blen);
    5123           3 :     SAFEALIGN_SETMEM_UINT32(body, 1, &pctr); /* Num results */
    5124           3 :     SAFEALIGN_SETMEM_UINT32(body + pctr, 0, &pctr); /* reserved */
    5125           3 :     SAFEALIGN_COPY_UINT32(body + pctr, &id_type, &pctr);
    5126          17 :     for (c = 0; c < found; c++) {
    5127          14 :         memcpy(&body[pctr], keys[c].str, keys[c].len);
    5128          14 :         pctr+= keys[c].len;
    5129          14 :         memcpy(&body[pctr], vals[c].str, vals[c].len);
    5130          14 :         pctr+= vals[c].len;
    5131             :     }
    5132             : 
    5133           3 :     ret = EOK;
    5134             : 
    5135             : done:
    5136           3 :     talloc_free(tmp_ctx);
    5137             : 
    5138           3 :     return ret;
    5139             : }
    5140             : 
    5141           2 : static errno_t fill_name(struct sss_packet *packet,
    5142             :                          struct sss_domain_info *dom,
    5143             :                          enum sss_id_type id_type,
    5144             :                          bool apply_no_view,
    5145             :                          struct ldb_message *msg)
    5146             : {
    5147             :     int ret;
    5148           2 :     TALLOC_CTX *tmp_ctx = NULL;
    5149           2 :     const char *orig_name = NULL;
    5150             :     const char *cased_name;
    5151             :     const char *fq_name;
    5152             :     struct sized_string name;
    5153           2 :     bool add_domain = (!IS_SUBDOMAIN(dom) && dom->fqnames);
    5154             :     uint8_t *body;
    5155             :     size_t blen;
    5156           2 :     size_t pctr = 0;
    5157             : 
    5158           2 :     if (apply_no_view) {
    5159           2 :         orig_name = ldb_msg_find_attr_as_string(msg,
    5160             :                                                 ORIGINALAD_PREFIX SYSDB_NAME,
    5161             :                                                 NULL);
    5162             :     } else {
    5163           0 :         if (DOM_HAS_VIEWS(dom)) {
    5164           0 :             orig_name = ldb_msg_find_attr_as_string(msg,
    5165             :                                                     OVERRIDE_PREFIX SYSDB_NAME,
    5166             :                                                     NULL);
    5167           0 :             if (orig_name != NULL && IS_SUBDOMAIN(dom)) {
    5168             :                 /* Override names are un-qualified */
    5169           0 :                 add_domain = true;
    5170             :             }
    5171             :         }
    5172             :     }
    5173             : 
    5174           2 :     if (orig_name == NULL) {
    5175           2 :         orig_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
    5176             :     }
    5177           2 :     if (orig_name == NULL) {
    5178           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Missing name.\n");
    5179           0 :         return EINVAL;
    5180             :     }
    5181             : 
    5182           2 :     tmp_ctx = talloc_new(NULL);
    5183           2 :     if (tmp_ctx == NULL) {
    5184           0 :         DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
    5185           0 :         return ENOMEM;
    5186             :     }
    5187             : 
    5188           2 :     cased_name= sss_get_cased_name(tmp_ctx, orig_name, dom->case_sensitive);
    5189           2 :     if (cased_name == NULL) {
    5190           0 :         DEBUG(SSSDBG_OP_FAILURE, "sss_get_cased_name failed.\n");
    5191           0 :         ret = ENOMEM;
    5192           0 :         goto done;
    5193             :     }
    5194             : 
    5195           2 :     if (add_domain) {
    5196           0 :         fq_name = sss_tc_fqname(tmp_ctx, dom->names, dom, cased_name);
    5197           0 :         if (fq_name == NULL) {
    5198           0 :             DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
    5199           0 :             ret = ENOMEM;
    5200           0 :             goto done;
    5201             :         }
    5202           0 :         to_sized_string(&name, fq_name);
    5203             :     } else {
    5204           2 :         to_sized_string(&name, cased_name);
    5205             :     }
    5206             : 
    5207           2 :     ret = sss_packet_grow(packet, name.len + 3 * sizeof(uint32_t));
    5208           2 :     if (ret != EOK) {
    5209           0 :         DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
    5210           0 :         goto done;
    5211             :     }
    5212             : 
    5213           2 :     sss_packet_get_body(packet, &body, &blen);
    5214           2 :     SAFEALIGN_SETMEM_UINT32(body, 1, &pctr); /* Num results */
    5215           2 :     SAFEALIGN_SETMEM_UINT32(body + pctr, 0, &pctr); /* reserved */
    5216           2 :     SAFEALIGN_COPY_UINT32(body + pctr, &id_type, &pctr);
    5217           2 :     memcpy(&body[pctr], name.str, name.len);
    5218             : 
    5219             : 
    5220           2 :     ret = EOK;
    5221             : 
    5222             : done:
    5223           2 :     talloc_free(tmp_ctx);
    5224             : 
    5225           2 :     return ret;
    5226             : }
    5227             : 
    5228           0 : static errno_t fill_id(struct sss_packet *packet,
    5229             :                        enum sss_id_type id_type,
    5230             :                        struct ldb_message *msg)
    5231             : {
    5232             :     int ret;
    5233             :     uint8_t *body;
    5234             :     size_t blen;
    5235           0 :     size_t pctr = 0;
    5236             :     uint64_t tmp_id;
    5237             :     uint32_t id;
    5238             : 
    5239           0 :     if (id_type == SSS_ID_TYPE_GID) {
    5240           0 :         tmp_id = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
    5241             :     } else {
    5242           0 :         tmp_id = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0);
    5243             :     }
    5244             : 
    5245           0 :     if (tmp_id == 0 || tmp_id >= UINT32_MAX) {
    5246           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid POSIX ID.\n");
    5247           0 :         return EINVAL;
    5248             :     }
    5249           0 :     id = (uint32_t) tmp_id;
    5250             : 
    5251           0 :     ret = sss_packet_grow(packet, 4 * sizeof(uint32_t));
    5252           0 :     if (ret != EOK) {
    5253           0 :         DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
    5254           0 :         return ret;
    5255             :     }
    5256             : 
    5257           0 :     sss_packet_get_body(packet, &body, &blen);
    5258           0 :     SAFEALIGN_SETMEM_UINT32(body, 1, &pctr); /* Num results */
    5259           0 :     SAFEALIGN_SETMEM_UINT32(body + pctr, 0, &pctr); /* reserved */
    5260           0 :     SAFEALIGN_COPY_UINT32(body + pctr, &id_type, &pctr);
    5261           0 :     SAFEALIGN_COPY_UINT32(body + pctr, &id, &pctr);
    5262             : 
    5263           0 :     return EOK;
    5264             : }
    5265             : 
    5266           5 : static errno_t nss_cmd_getbysid_send_reply(struct nss_dom_ctx *dctx)
    5267             : {
    5268           5 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
    5269           5 :     struct cli_ctx *cctx = cmdctx->cctx;
    5270             :     int ret;
    5271             :     enum sss_id_type id_type;
    5272             : 
    5273           5 :     if (dctx->res->count > 1) {
    5274           0 :         return EINVAL;
    5275           5 :     } else if (dctx->res->count == 0) {
    5276           0 :         return ENOENT;
    5277             :     }
    5278             : 
    5279          10 :     ret = sss_packet_new(cctx->creq, 0,
    5280           5 :                          sss_packet_get_cmd(cctx->creq->in),
    5281           5 :                          &cctx->creq->out);
    5282           5 :     if (ret != EOK) {
    5283           0 :         return EFAULT;
    5284             :     }
    5285             : 
    5286           5 :     ret = find_sss_id_type(dctx->res->msgs[0], dctx->domain->mpg, &id_type);
    5287           5 :     if (ret != 0) {
    5288           0 :         DEBUG(SSSDBG_OP_FAILURE, "find_sss_id_type failed.\n");
    5289           0 :         return ret;
    5290             :     }
    5291             : 
    5292           5 :     switch(cmdctx->cmd) {
    5293             :     case SSS_NSS_GETNAMEBYSID:
    5294           2 :         ret = fill_name(cctx->creq->out,
    5295             :                         dctx->domain,
    5296             :                         id_type,
    5297             :                         true,
    5298           2 :                         dctx->res->msgs[0]);
    5299           2 :         break;
    5300             :     case SSS_NSS_GETIDBYSID:
    5301           0 :         ret = fill_id(cctx->creq->out, id_type, dctx->res->msgs[0]);
    5302           0 :         break;
    5303             :     case SSS_NSS_GETSIDBYNAME:
    5304             :     case SSS_NSS_GETSIDBYID:
    5305           0 :         ret = fill_sid(cctx->creq->out, id_type, dctx->res->msgs[0]);
    5306           0 :         break;
    5307             :     case SSS_NSS_GETORIGBYNAME:
    5308           3 :         ret = fill_orig(cctx->creq->out, cctx->rctx, id_type,
    5309           3 :                         dctx->res->msgs[0]);
    5310           3 :         break;
    5311             :     default:
    5312           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported request type.\n");
    5313           0 :         return EINVAL;
    5314             :     }
    5315           5 :     if (ret != EOK) {
    5316           0 :         return ret;
    5317             :     }
    5318             : 
    5319           5 :     sss_packet_set_error(cctx->creq->out, EOK);
    5320           5 :     sss_cmd_done(cctx, cmdctx);
    5321           5 :     return EOK;
    5322             : }
    5323             : 
    5324           8 : static int nss_check_well_known_sid(struct nss_cmd_ctx *cmdctx)
    5325             : {
    5326             :     const char *wk_name;
    5327             :     const char *wk_dom_name;
    5328             :     int ret;
    5329           8 :     char *fq_name = NULL;
    5330             :     struct sized_string name;
    5331             :     uint8_t *body;
    5332             :     size_t blen;
    5333             :     struct cli_ctx *cctx;
    5334             :     struct nss_ctx *nss_ctx;
    5335           8 :     size_t pctr = 0;
    5336             : 
    5337           8 :     ret = well_known_sid_to_name(cmdctx->secid, &wk_dom_name, &wk_name);
    5338           8 :     if (ret != EOK) {
    5339           5 :         DEBUG(SSSDBG_TRACE_ALL, "SID [%s] is not a Well-Known SID.\n",
    5340             :                                  cmdctx->secid);
    5341           5 :         return ret;
    5342             :     }
    5343             : 
    5344           3 :     if (cmdctx->cmd != SSS_NSS_GETNAMEBYSID) {
    5345           1 :         DEBUG(SSSDBG_TRACE_ALL,
    5346             :               "Well-Known SIDs can only be translated to names.\n");
    5347           1 :         return EINVAL;
    5348             :     }
    5349             : 
    5350           2 :     if (wk_dom_name != NULL) {
    5351           2 :         nss_ctx = talloc_get_type(cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx);
    5352           2 :         fq_name = sss_tc_fqname2(cmdctx, nss_ctx->global_names,
    5353             :                                  wk_dom_name, wk_dom_name, wk_name);
    5354           2 :         if (fq_name == NULL) {
    5355           0 :             DEBUG(SSSDBG_OP_FAILURE, "sss_tc_fqname2 failed.\n");
    5356           0 :             return ENOMEM;
    5357             :         }
    5358           2 :         to_sized_string(&name, fq_name);
    5359             :     } else {
    5360           0 :         to_sized_string(&name, wk_name);
    5361             :     }
    5362             : 
    5363           2 :     cctx = cmdctx->cctx;
    5364           4 :     ret = sss_packet_new(cctx->creq, name.len + 3 * sizeof(uint32_t),
    5365           2 :                          sss_packet_get_cmd(cctx->creq->in),
    5366           2 :                          &cctx->creq->out);
    5367           2 :     if (ret != EOK) {
    5368           0 :         talloc_free(fq_name);
    5369           0 :         return ENOMEM;
    5370             :     }
    5371             : 
    5372           2 :     sss_packet_get_body(cctx->creq->out, &body, &blen);
    5373           2 :     SAFEALIGN_SETMEM_UINT32(body, 1, &pctr); /* num results */
    5374           2 :     SAFEALIGN_SETMEM_UINT32(body + pctr, 0, &pctr); /* reserved */
    5375           2 :     SAFEALIGN_SETMEM_UINT32(body + pctr, SSS_ID_TYPE_GID, &pctr);
    5376           2 :     memcpy(&body[pctr], name.str, name.len);
    5377             : 
    5378           2 :     sss_packet_set_error(cctx->creq->out, EOK);
    5379           2 :     sss_cmd_done(cctx, cmdctx);
    5380           2 :     return EOK;
    5381             : }
    5382             : 
    5383           8 : static int nss_cmd_getbysid(enum sss_cli_command cmd, struct cli_ctx *cctx)
    5384             : {
    5385             : 
    5386             :     struct tevent_req *req;
    5387             :     struct nss_cmd_ctx *cmdctx;
    5388             :     struct nss_dom_ctx *dctx;
    5389             :     const char *sid_str;
    5390             :     uint8_t *body;
    5391             :     size_t blen;
    5392             :     int ret;
    5393             :     struct nss_ctx *nctx;
    5394             :     enum idmap_error_code err;
    5395           8 :     uint8_t *bin_sid = NULL;
    5396             :     size_t bin_sid_length;
    5397             : 
    5398           8 :     if (cmd != SSS_NSS_GETNAMEBYSID && cmd != SSS_NSS_GETIDBYSID) {
    5399           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command type [%d][%s].\n",
    5400             :               cmd, sss_cmd2str(cmd));
    5401           0 :         return EINVAL;
    5402             :     }
    5403             : 
    5404           8 :     cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
    5405           8 :     if (!cmdctx) {
    5406           0 :         return ENOMEM;
    5407             :     }
    5408           8 :     cmdctx->cctx = cctx;
    5409           8 :     cmdctx->cmd = cmd;
    5410             : 
    5411           8 :     dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
    5412           8 :     if (!dctx) {
    5413           0 :         ret = ENOMEM;
    5414           0 :         goto done;
    5415             :     }
    5416           8 :     dctx->cmdctx = cmdctx;
    5417             : 
    5418             :     /* get SID to query */
    5419           8 :     sss_packet_get_body(cctx->creq->in, &body, &blen);
    5420             : 
    5421             :     /* if not terminated fail */
    5422           8 :     if (body[blen -1] != '\0') {
    5423           0 :         ret = EINVAL;
    5424           0 :         goto done;
    5425             :     }
    5426             : 
    5427           8 :     sid_str = (const char *) body;
    5428             : 
    5429           8 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    5430             : 
    5431             :     /* If the body isn't a SID, fail */
    5432           8 :     err = sss_idmap_sid_to_bin_sid(nctx->idmap_ctx, sid_str,
    5433             :                                    &bin_sid, &bin_sid_length);
    5434           8 :     sss_idmap_free_bin_sid(nctx->idmap_ctx, bin_sid);
    5435           8 :     if (err != IDMAP_SUCCESS) {
    5436           0 :         DEBUG(SSSDBG_OP_FAILURE, "sss_idmap_sid_to_bin_sid failed for [%s].\n",
    5437             :                                   body);
    5438           0 :         ret = EINVAL;
    5439           0 :         goto done;
    5440             :     }
    5441             : 
    5442           8 :     DEBUG(SSSDBG_TRACE_FUNC, "Running command [%d][%s] with SID [%s].\n",
    5443             :           dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd), sid_str);
    5444             : 
    5445           8 :     cmdctx->secid = talloc_strdup(cmdctx, sid_str);
    5446           8 :     if (cmdctx->secid == NULL) {
    5447           0 :         DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
    5448           0 :         ret = ENOMEM;
    5449           0 :         goto done;
    5450             :     }
    5451             : 
    5452           8 :     ret = nss_check_well_known_sid(cmdctx);
    5453           8 :     if (ret != ENOENT) {
    5454           4 :         if (ret == EOK) {
    5455           2 :             DEBUG(SSSDBG_TRACE_ALL, "SID [%s] is a Well-Known SID.\n",
    5456             :                                          cmdctx->secid);
    5457             :         } else {
    5458           2 :             DEBUG(SSSDBG_OP_FAILURE, "nss_check_well_known_sid failed.\n");
    5459             :         }
    5460           4 :         goto done;
    5461             :     }
    5462             : 
    5463           4 :     ret = responder_get_domain_by_id(cctx->rctx, cmdctx->secid, &dctx->domain);
    5464           4 :     if (ret == EAGAIN || ret == ENOENT) {
    5465           0 :         req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, NULL);
    5466           0 :         if (req == NULL) {
    5467           0 :             ret = ENOMEM;
    5468             :         } else {
    5469           0 :             dctx->rawname = sid_str;
    5470           0 :             tevent_req_set_callback(req, nss_cmd_getbyid_done, dctx);
    5471           0 :             ret = EAGAIN;
    5472             :         }
    5473           0 :         goto done;
    5474           4 :     } else if (ret != EOK) {
    5475           0 :         DEBUG(SSSDBG_OP_FAILURE, "responder_get_domain_by_id failed.\n");
    5476           0 :         goto done;
    5477             :     }
    5478             : 
    5479           4 :     DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for [%s] from [%s]\n",
    5480             :               cmdctx->secid, dctx->domain->name);
    5481             : 
    5482           4 :     dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
    5483             : 
    5484             :     /* ok, find it ! */
    5485           4 :     ret = nss_cmd_getbysid_search(dctx);
    5486           4 :     if (ret == EOK) {
    5487           1 :         ret = nss_cmd_getbysid_send_reply(dctx);
    5488             :     }
    5489             : 
    5490             : done:
    5491           8 :     return nss_cmd_done(cmdctx, ret);
    5492             : }
    5493             : 
    5494           6 : static int nss_cmd_getsidbyname(struct cli_ctx *cctx)
    5495             : {
    5496           6 :     return nss_cmd_getbynam(SSS_NSS_GETSIDBYNAME, cctx);
    5497             : }
    5498             : 
    5499           0 : static int nss_cmd_getsidbyid(struct cli_ctx *cctx)
    5500             : {
    5501           0 :     return nss_cmd_getbyid(SSS_NSS_GETSIDBYID, cctx);
    5502             : }
    5503             : 
    5504           7 : static int nss_cmd_getnamebysid(struct cli_ctx *cctx)
    5505             : {
    5506           7 :     return nss_cmd_getbysid(SSS_NSS_GETNAMEBYSID, cctx);
    5507             : }
    5508             : 
    5509           1 : static int nss_cmd_getidbysid(struct cli_ctx *cctx)
    5510             : {
    5511           1 :     return nss_cmd_getbysid(SSS_NSS_GETIDBYSID, cctx);
    5512             : }
    5513             : 
    5514           3 : static int nss_cmd_getorigbyname(struct cli_ctx *cctx)
    5515             : {
    5516           3 :     return nss_cmd_getbynam(SSS_NSS_GETORIGBYNAME, cctx);
    5517             : }
    5518             : 
    5519           0 : struct cli_protocol_version *register_cli_protocol_version(void)
    5520             : {
    5521             :     static struct cli_protocol_version nss_cli_protocol_version[] = {
    5522             :         {1, "2008-09-05", "initial version, \\0 terminated strings"},
    5523             :         {0, NULL, NULL}
    5524             :     };
    5525             : 
    5526           0 :     return nss_cli_protocol_version;
    5527             : }
    5528             : 
    5529             : static struct sss_cmd_table nss_cmds[] = {
    5530             :     {SSS_GET_VERSION, sss_cmd_get_version},
    5531             :     {SSS_NSS_GETPWNAM, nss_cmd_getpwnam},
    5532             :     {SSS_NSS_GETPWUID, nss_cmd_getpwuid},
    5533             :     {SSS_NSS_SETPWENT, nss_cmd_setpwent},
    5534             :     {SSS_NSS_GETPWENT, nss_cmd_getpwent},
    5535             :     {SSS_NSS_ENDPWENT, nss_cmd_endpwent},
    5536             :     {SSS_NSS_GETGRNAM, nss_cmd_getgrnam},
    5537             :     {SSS_NSS_GETGRGID, nss_cmd_getgrgid},
    5538             :     {SSS_NSS_SETGRENT, nss_cmd_setgrent},
    5539             :     {SSS_NSS_GETGRENT, nss_cmd_getgrent},
    5540             :     {SSS_NSS_ENDGRENT, nss_cmd_endgrent},
    5541             :     {SSS_NSS_INITGR, nss_cmd_initgroups},
    5542             :     {SSS_NSS_SETNETGRENT, nss_cmd_setnetgrent},
    5543             :     {SSS_NSS_GETNETGRENT, nss_cmd_getnetgrent},
    5544             :     {SSS_NSS_ENDNETGRENT, nss_cmd_endnetgrent},
    5545             :     {SSS_NSS_GETSERVBYNAME, nss_cmd_getservbyname},
    5546             :     {SSS_NSS_GETSERVBYPORT, nss_cmd_getservbyport},
    5547             :     {SSS_NSS_SETSERVENT, nss_cmd_setservent},
    5548             :     {SSS_NSS_GETSERVENT, nss_cmd_getservent},
    5549             :     {SSS_NSS_ENDSERVENT, nss_cmd_endservent},
    5550             :     {SSS_NSS_GETSIDBYNAME, nss_cmd_getsidbyname},
    5551             :     {SSS_NSS_GETSIDBYID, nss_cmd_getsidbyid},
    5552             :     {SSS_NSS_GETNAMEBYSID, nss_cmd_getnamebysid},
    5553             :     {SSS_NSS_GETIDBYSID, nss_cmd_getidbysid},
    5554             :     {SSS_NSS_GETORIGBYNAME, nss_cmd_getorigbyname},
    5555             :     {SSS_CLI_NULL, NULL}
    5556             : };
    5557             : 
    5558          44 : struct sss_cmd_table *get_nss_cmds(void) {
    5559          44 :     return nss_cmds;
    5560             : }

Generated by: LCOV version 1.10