LCOV - code coverage report
Current view: top level - source3/winbindd - wb_queryuser.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-lts cc996e7c Lines: 162 226 71.7 %
Date: 2025-10-17 03:45:34 Functions: 8 9 88.9 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    async queryuser
       4             :    Copyright (C) Volker Lendecke 2009
       5             : 
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : #include "includes.h"
      21             : #include "util/debug.h"
      22             : #include "winbindd.h"
      23             : #include "librpc/gen_ndr/ndr_winbind_c.h"
      24             : #include "../libcli/security/security.h"
      25             : #include "libsmb/samlogon_cache.h"
      26             : #include "librpc/gen_ndr/ndr_winbind.h"
      27             : 
      28             : struct wb_queryuser_state {
      29             :         struct tevent_context *ev;
      30             :         struct wbint_userinfo *info;
      31             :         const struct wb_parent_idmap_config *idmap_cfg;
      32             :         bool tried_dclookup;
      33             : };
      34             : 
      35             : static void wb_queryuser_idmap_setup_done(struct tevent_req *subreq);
      36             : static void wb_queryuser_got_uid(struct tevent_req *subreq);
      37             : static void wb_queryuser_got_domain(struct tevent_req *subreq);
      38             : static void wb_queryuser_got_dc(struct tevent_req *subreq);
      39             : static void wb_queryuser_got_gid(struct tevent_req *subreq);
      40             : static void wb_queryuser_got_group_name(struct tevent_req *subreq);
      41             : static void wb_queryuser_done(struct tevent_req *subreq);
      42             : 
      43        4772 : struct tevent_req *wb_queryuser_send(TALLOC_CTX *mem_ctx,
      44             :                                      struct tevent_context *ev,
      45             :                                      const struct dom_sid *user_sid)
      46             : {
      47             :         struct tevent_req *req, *subreq;
      48             :         struct wb_queryuser_state *state;
      49             :         struct wbint_userinfo *info;
      50             :         struct dom_sid_buf buf;
      51             : 
      52        4772 :         req = tevent_req_create(mem_ctx, &state, struct wb_queryuser_state);
      53        4772 :         if (req == NULL) {
      54           0 :                 return NULL;
      55             :         }
      56        4772 :         D_INFO("WB command queryuser start.\nQuery user sid %s\n",
      57             :                dom_sid_str_buf(user_sid, &buf));
      58        4772 :         state->ev = ev;
      59             : 
      60        4772 :         state->info = talloc_zero(state, struct wbint_userinfo);
      61        4772 :         if (tevent_req_nomem(state->info, req)) {
      62           0 :                 return tevent_req_post(req, ev);
      63             :         }
      64        4772 :         info = state->info;
      65             : 
      66        4772 :         info->primary_gid = (gid_t)-1;
      67             : 
      68        4772 :         sid_copy(&info->user_sid, user_sid);
      69             : 
      70        4772 :         subreq = wb_parent_idmap_setup_send(state, state->ev);
      71        4772 :         if (tevent_req_nomem(subreq, req)) {
      72           0 :                 return tevent_req_post(req, ev);
      73             :         }
      74        4772 :         tevent_req_set_callback(subreq, wb_queryuser_idmap_setup_done, req);
      75        4772 :         return req;
      76             : }
      77             : 
      78        4772 : static void wb_queryuser_idmap_setup_done(struct tevent_req *subreq)
      79             : {
      80        4772 :         struct tevent_req *req = tevent_req_callback_data(
      81             :                 subreq, struct tevent_req);
      82        4772 :         struct wb_queryuser_state *state = tevent_req_data(
      83             :                 req, struct wb_queryuser_state);
      84             :         NTSTATUS status;
      85             :         struct dom_sid_buf buf;
      86             : 
      87        4772 :         status = wb_parent_idmap_setup_recv(subreq, &state->idmap_cfg);
      88        4772 :         TALLOC_FREE(subreq);
      89        4772 :         if (tevent_req_nterror(req, status)) {
      90           0 :                 D_WARNING("wb_parent_idmap_setup_recv() failed with %s.\n",
      91             :                           nt_errstr(status));
      92           0 :                 return;
      93             :         }
      94             : 
      95        4772 :         D_DEBUG("Convert the user SID %s to XID.\n",
      96             :                 dom_sid_str_buf(&state->info->user_sid, &buf));
      97        4772 :         subreq = wb_sids2xids_send(
      98        4772 :                 state, state->ev, &state->info->user_sid, 1);
      99        4772 :         if (tevent_req_nomem(subreq, req)) {
     100           0 :                 return;
     101             :         }
     102        4772 :         tevent_req_set_callback(subreq, wb_queryuser_got_uid, req);
     103        4772 :         return;
     104             : }
     105             : 
     106        4772 : static void wb_queryuser_got_uid(struct tevent_req *subreq)
     107             : {
     108        4772 :         struct tevent_req *req = tevent_req_callback_data(
     109             :                 subreq, struct tevent_req);
     110        4772 :         struct wb_queryuser_state *state = tevent_req_data(
     111             :                 req, struct wb_queryuser_state);
     112        4772 :         struct wbint_userinfo *info = state->info;
     113             :         struct netr_SamInfo3 *info3;
     114        4772 :         struct dcerpc_binding_handle *child_binding_handle = NULL;
     115             :         struct unixid xid;
     116             :         NTSTATUS status;
     117             :         struct dom_sid_buf buf, buf1;
     118             : 
     119        4772 :         status = wb_sids2xids_recv(subreq, &xid, 1);
     120        4772 :         TALLOC_FREE(subreq);
     121        4772 :         if (tevent_req_nterror(req, status)) {
     122           0 :                 D_WARNING("wb_sids2xids_recv() failed with %s.\n",
     123             :                           nt_errstr(status));
     124        2789 :                 return;
     125             :         }
     126             : 
     127        4772 :         if ((xid.type != ID_TYPE_UID) && (xid.type != ID_TYPE_BOTH)) {
     128          48 :                 D_WARNING("XID type is %d, should be ID_TYPE_UID or ID_TYPE_BOTH.\n",
     129             :                           xid.type);
     130          48 :                 tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
     131          48 :                 return;
     132             :         }
     133             : 
     134        4724 :         D_DEBUG("Received XID %"PRIu32" for SID %s.\n",
     135             :                 xid.id,
     136             :                 dom_sid_str_buf(&info->user_sid, &buf1));
     137        4724 :         info->uid = xid.id;
     138             : 
     139             :         /*
     140             :          * Default the group sid to "Domain Users" in the user's
     141             :          * domain. The samlogon cache or the query_user call later on
     142             :          * can override this.
     143             :          */
     144        4724 :         sid_copy(&info->group_sid, &info->user_sid);
     145        4724 :         sid_split_rid(&info->group_sid, NULL);
     146        4724 :         sid_append_rid(&info->group_sid, DOMAIN_RID_USERS);
     147             : 
     148        4724 :         D_DEBUG("Preconfigured 'Domain Users' RID %u was used to create group SID %s from user SID %s.\n",
     149             :                 DOMAIN_RID_USERS,
     150             :                 dom_sid_str_buf(&info->group_sid, &buf),
     151             :                 dom_sid_str_buf(&info->user_sid, &buf1));
     152             : 
     153        4724 :         info->homedir = talloc_strdup(info, lp_template_homedir());
     154        4724 :         D_DEBUG("Setting 'homedir' to the template '%s'.\n", info->homedir);
     155        4724 :         if (tevent_req_nomem(info->homedir, req)) {
     156           0 :                 return;
     157             :         }
     158             : 
     159        4724 :         info->shell = talloc_strdup(info, lp_template_shell());
     160        4724 :         D_DEBUG("Setting 'shell' to the template '%s'.\n", info->shell);
     161        4724 :         if (tevent_req_nomem(info->shell, req)) {
     162           0 :                 return;
     163             :         }
     164             : 
     165        4724 :         info3 = netsamlogon_cache_get(state, &info->user_sid);
     166        4724 :         if (info3 != NULL) {
     167        1983 :                 D_DEBUG("Filling data received from netsamlogon_cache\n");
     168        1983 :                 sid_compose(&info->group_sid, info3->base.domain_sid,
     169             :                             info3->base.primary_gid);
     170        1983 :                 info->acct_name = talloc_move(
     171             :                         info, &info3->base.account_name.string);
     172        1983 :                 info->full_name = talloc_move(
     173             :                         info, &info3->base.full_name.string);
     174             : 
     175        1983 :                 info->domain_name = talloc_move(
     176             :                         state, &info3->base.logon_domain.string);
     177             : 
     178        1983 :                 TALLOC_FREE(info3);
     179             :         }
     180             : 
     181        4724 :         NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, wbint_userinfo, state->info);
     182        4724 :         if (info->domain_name == NULL) {
     183        2741 :                 D_DEBUG("Domain name is empty, calling wb_lookupsid_send() to get it.\n");
     184        2741 :                 subreq = wb_lookupsid_send(state, state->ev, &info->user_sid);
     185        2741 :                 if (tevent_req_nomem(subreq, req)) {
     186           0 :                         return;
     187             :                 }
     188        2741 :                 tevent_req_set_callback(subreq, wb_queryuser_got_domain, req);
     189        2741 :                 return;
     190             :         }
     191             : 
     192             :         /*
     193             :          * Note wb_sids2xids_send/recv was called before,
     194             :          * so we're sure that wb_parent_idmap_setup_send/recv
     195             :          * was already called.
     196             :          */
     197        1983 :         child_binding_handle = idmap_child_handle();
     198        1983 :         D_DEBUG("Domain name is set, calling dcerpc_wbint_GetNssInfo_send()\n");
     199        1983 :         subreq = dcerpc_wbint_GetNssInfo_send(
     200             :                 state, state->ev, child_binding_handle, info);
     201        1983 :         if (tevent_req_nomem(subreq, req)) {
     202           0 :                 return;
     203             :         }
     204        1983 :         tevent_req_set_callback(subreq, wb_queryuser_done, req);
     205             : }
     206             : 
     207        2741 : static void wb_queryuser_got_domain(struct tevent_req *subreq)
     208             : {
     209        2741 :         struct tevent_req *req = tevent_req_callback_data(
     210             :                 subreq, struct tevent_req);
     211        2741 :         struct wb_queryuser_state *state = tevent_req_data(
     212             :                 req, struct wb_queryuser_state);
     213        2741 :         struct wbint_userinfo *info = state->info;
     214             :         enum lsa_SidType type;
     215        2741 :         struct dcerpc_binding_handle *child_binding_handle = NULL;
     216             :         NTSTATUS status;
     217             : 
     218        2741 :         status = wb_lookupsid_recv(subreq, state, &type,
     219             :                                    &info->domain_name, &info->acct_name);
     220        2741 :         TALLOC_FREE(subreq);
     221        2741 :         if (tevent_req_nterror(req, status)) {
     222           0 :                 D_WARNING("wb_lookupsid_recv failed with %s.\n",
     223             :                           nt_errstr(status));
     224           0 :                 return;
     225             :         }
     226             : 
     227        2741 :         NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, wbint_userinfo, state->info);
     228        2741 :         switch (type) {
     229        2733 :         case SID_NAME_USER:
     230             :         case SID_NAME_COMPUTER:
     231             :                 /*
     232             :                  * user case: we only need the account name from lookup_sids
     233             :                  */
     234        2733 :                 break;
     235           8 :         case SID_NAME_DOM_GRP:
     236             :         case SID_NAME_ALIAS:
     237             :         case SID_NAME_WKN_GRP:
     238             :                 /*
     239             :                  * also treat group-type SIDs (they might map to ID_TYPE_BOTH)
     240             :                  */
     241           8 :                 sid_copy(&info->group_sid, &info->user_sid);
     242           8 :                 break;
     243           0 :         default:
     244           0 :                 D_WARNING("Unknown type:%d, return NT_STATUS_NO_SUCH_USER.\n",
     245             :                           type);
     246           0 :                 tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
     247           0 :                 return;
     248             :         }
     249             : 
     250             :         /*
     251             :          * Note wb_sids2xids_send/recv was called before,
     252             :          * so we're sure that wb_parent_idmap_setup_send/recv
     253             :          * was already called.
     254             :          */
     255        2741 :         child_binding_handle = idmap_child_handle();
     256        2741 :         D_DEBUG("About to call dcerpc_wbint_GetNssInfo_send()\n");
     257        2741 :         subreq = dcerpc_wbint_GetNssInfo_send(
     258             :                 state, state->ev, child_binding_handle, info);
     259        2741 :         if (tevent_req_nomem(subreq, req)) {
     260           0 :                 return;
     261             :         }
     262        2741 :         tevent_req_set_callback(subreq, wb_queryuser_done, req);
     263             : }
     264             : 
     265        4724 : static void wb_queryuser_done(struct tevent_req *subreq)
     266             : {
     267        4724 :         struct tevent_req *req = tevent_req_callback_data(
     268             :                 subreq, struct tevent_req);
     269        4724 :         struct wb_queryuser_state *state = tevent_req_data(
     270             :                 req, struct wb_queryuser_state);
     271        4724 :         struct wbint_userinfo *info = state->info;
     272             :         NTSTATUS status, result;
     273        4724 :         bool need_group_name = false;
     274        4724 :         const char *tmpl = NULL;
     275             : 
     276        4724 :         status = dcerpc_wbint_GetNssInfo_recv(subreq, info, &result);
     277        4724 :         TALLOC_FREE(subreq);
     278        4724 :         if (tevent_req_nterror(req, status)) {
     279           0 :                 D_WARNING("GetNssInfo failed with %s.\n", nt_errstr(status));
     280        4722 :                 return;
     281             :         }
     282             : 
     283        4724 :         if (NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) &&
     284           0 :             !state->tried_dclookup) {
     285           0 :                 const char *domain_name = find_dns_domain_name(
     286           0 :                         state->info->domain_name);
     287             : 
     288           0 :                 D_DEBUG("GetNssInfo got DOMAIN_CONTROLLER_NOT_FOUND, calling "
     289             :                         "wb_dsgetdcname_send(%s)\n",
     290             :                         domain_name);
     291             : 
     292           0 :                 subreq = wb_dsgetdcname_send(state,
     293             :                                              state->ev,
     294             :                                              domain_name,
     295             :                                              NULL,
     296             :                                              NULL,
     297             :                                              DS_RETURN_DNS_NAME);
     298           0 :                 if (tevent_req_nomem(subreq, req)) {
     299           0 :                         return;
     300             :                 }
     301           0 :                 tevent_req_set_callback(subreq, wb_queryuser_got_dc, req);
     302           0 :                 return;
     303             :         }
     304             : 
     305             :         /*
     306             :          * Ignore failure in "result" here. We'll try to fill in stuff
     307             :          * that misses further down.
     308             :          */
     309             : 
     310        4724 :         if (state->info->primary_gid == (gid_t)-1) {
     311        4722 :                 D_DEBUG("Calling wb_sids2xids_send() to resolve primary gid.\n");
     312        4722 :                 subreq = wb_sids2xids_send(
     313        4722 :                         state, state->ev, &info->group_sid, 1);
     314        4722 :                 if (tevent_req_nomem(subreq, req)) {
     315           0 :                         return;
     316             :                 }
     317        4722 :                 tevent_req_set_callback(subreq, wb_queryuser_got_gid, req);
     318        4722 :                 return;
     319             :         }
     320             : 
     321           2 :         tmpl = lp_template_homedir();
     322           2 :         if(strstr_m(tmpl, "%g") || strstr_m(tmpl, "%G")) {
     323           0 :                 need_group_name = true;
     324             :         }
     325           2 :         tmpl = lp_template_shell();
     326           2 :         if(strstr_m(tmpl, "%g") || strstr_m(tmpl, "%G")) {
     327           0 :                 need_group_name = true;
     328             :         }
     329             : 
     330           2 :         if (need_group_name && state->info->primary_group_name == NULL) {
     331           0 :                 D_DEBUG("Calling wb_lookupsid_send() to resolve primary group name.\n");
     332           0 :                 subreq = wb_lookupsid_send(state, state->ev, &info->group_sid);
     333           0 :                 if (tevent_req_nomem(subreq, req)) {
     334           0 :                         return;
     335             :                 }
     336           0 :                 tevent_req_set_callback(subreq, wb_queryuser_got_group_name,
     337             :                                         req);
     338           0 :                 return;
     339             :         }
     340             : 
     341           2 :         NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, wbint_userinfo, state->info);
     342           2 :         tevent_req_done(req);
     343             : }
     344             : 
     345           0 : static void wb_queryuser_got_dc(struct tevent_req *subreq)
     346             : {
     347           0 :         struct tevent_req *req = tevent_req_callback_data(
     348             :                 subreq, struct tevent_req);
     349           0 :         struct wb_queryuser_state *state = tevent_req_data(
     350             :                 req, struct wb_queryuser_state);
     351           0 :         struct wbint_userinfo *info = state->info;
     352             :         struct netr_DsRGetDCNameInfo *dcinfo;
     353           0 :         struct dcerpc_binding_handle *child_binding_handle = NULL;
     354             :         NTSTATUS status;
     355             : 
     356           0 :         status = wb_dsgetdcname_recv(subreq, state, &dcinfo);
     357           0 :         TALLOC_FREE(subreq);
     358           0 :         if (tevent_req_nterror(req, status)) {
     359           0 :                 D_WARNING("wb_dsgetdcname_recv() failed with %s.\n",
     360             :                           nt_errstr(status));
     361           0 :                 return;
     362             :         }
     363             : 
     364           0 :         state->tried_dclookup = true;
     365             : 
     366           0 :         D_DEBUG("Got DC name, calling wb_dsgetdcname_gencache_set().\n");
     367           0 :         status = wb_dsgetdcname_gencache_set(info->domain_name, dcinfo);
     368           0 :         if (tevent_req_nterror(req, status)) {
     369           0 :                 D_WARNING("wb_dsgetdcname_gencache_set() failed with %s.\n",
     370             :                           nt_errstr(status));
     371           0 :                 return;
     372             :         }
     373           0 :         NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, wbint_userinfo, state->info);
     374             : 
     375             :         /*
     376             :          * Note wb_sids2xids_send/recv was called before,
     377             :          * so we're sure that wb_parent_idmap_setup_send/recv
     378             :          * was already called.
     379             :          */
     380           0 :         child_binding_handle = idmap_child_handle();
     381           0 :         subreq = dcerpc_wbint_GetNssInfo_send(
     382             :                 state, state->ev, child_binding_handle, info);
     383           0 :         if (tevent_req_nomem(subreq, req)) {
     384           0 :                 return;
     385             :         }
     386           0 :         tevent_req_set_callback(subreq, wb_queryuser_done, req);
     387             : }
     388             : 
     389        4722 : static void wb_queryuser_got_gid(struct tevent_req *subreq)
     390             : {
     391        4722 :         struct tevent_req *req = tevent_req_callback_data(
     392             :                 subreq, struct tevent_req);
     393        4722 :         struct wb_queryuser_state *state = tevent_req_data(
     394             :                 req, struct wb_queryuser_state);
     395             :         struct unixid xid;
     396             :         NTSTATUS status;
     397        4722 :         bool need_group_name = false;
     398        4722 :         const char *tmpl = NULL;
     399             :         struct dom_sid_buf buf;
     400             : 
     401        4722 :         status = wb_sids2xids_recv(subreq, &xid, 1);
     402        4722 :         TALLOC_FREE(subreq);
     403        4722 :         if (tevent_req_nterror(req, status)) {
     404           0 :                 D_WARNING("wb_sids2xids_recv() failed with %s.\n",
     405             :                           nt_errstr(status));
     406        1594 :                 return;
     407             :         }
     408             : 
     409        4722 :         D_DEBUG("Got XID %"PRIu32" with type %d.\n", xid.id, xid.type);
     410        4722 :         if ((xid.type != ID_TYPE_GID) && (xid.type != ID_TYPE_BOTH)) {
     411          30 :                 D_WARNING("Returning NT_STATUS_NO_SUCH_USER\n"
     412             :                           "xid.type must be ID_TYPE_UID or ID_TYPE_BOTH.\n");
     413          30 :                 tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
     414          30 :                 return;
     415             :         }
     416             : 
     417        4692 :         state->info->primary_gid = xid.id;
     418             : 
     419        4692 :         tmpl = lp_template_homedir();
     420        4692 :         if(strstr_m(tmpl, "%g") || strstr_m(tmpl, "%G")) {
     421        1564 :                 need_group_name = true;
     422             :         }
     423        4692 :         tmpl = lp_template_shell();
     424        4692 :         if(strstr_m(tmpl, "%g") || strstr_m(tmpl, "%G")) {
     425           0 :                 need_group_name = true;
     426             :         }
     427             : 
     428        4692 :         NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, wbint_userinfo, state->info);
     429             : 
     430        4692 :         if (need_group_name && state->info->primary_group_name == NULL) {
     431        1564 :                 D_DEBUG("Calling wb_lookupsid_send for group SID %s.\n",
     432             :                           dom_sid_str_buf(&state->info->group_sid, &buf));
     433        1564 :                 subreq = wb_lookupsid_send(state, state->ev,
     434        1564 :                                            &state->info->group_sid);
     435        1564 :                 if (tevent_req_nomem(subreq, req)) {
     436           0 :                         return;
     437             :                 }
     438        1564 :                 tevent_req_set_callback(subreq, wb_queryuser_got_group_name,
     439             :                                         req);
     440        1564 :                 return;
     441             :         }
     442             : 
     443        3128 :         D_DEBUG("No need to lookup primary group name. Request is done!\n");
     444        3128 :         tevent_req_done(req);
     445             : }
     446             : 
     447        1564 : static void wb_queryuser_got_group_name(struct tevent_req *subreq)
     448             : {
     449        1564 :         struct tevent_req *req = tevent_req_callback_data(
     450             :                 subreq, struct tevent_req);
     451        1564 :         struct wb_queryuser_state *state = tevent_req_data(
     452             :                 req, struct wb_queryuser_state);
     453             :         enum lsa_SidType type;
     454             :         NTSTATUS status;
     455             :         const char *domain_name;
     456             : 
     457        1564 :         status = wb_lookupsid_recv(subreq, state->info, &type, &domain_name,
     458        1564 :                                    &state->info->primary_group_name);
     459        1564 :         TALLOC_FREE(subreq);
     460        1564 :         if (tevent_req_nterror(req, status)) {
     461           0 :                 D_WARNING("wb_lookupsid_recv() failed with %s.\n",
     462             :                           nt_errstr(status));
     463           0 :                 return;
     464             :         }
     465        1564 :         tevent_req_done(req);
     466             : }
     467             : 
     468        4772 : NTSTATUS wb_queryuser_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
     469             :                            struct wbint_userinfo **pinfo)
     470             : {
     471        4772 :         struct wb_queryuser_state *state = tevent_req_data(
     472             :                 req, struct wb_queryuser_state);
     473             :         NTSTATUS status;
     474             : 
     475        4772 :         D_INFO("WB command queryuser end.\n");
     476        4772 :         NDR_PRINT_DEBUG_LEVEL(DBGLVL_INFO, wbint_userinfo, state->info);
     477        4772 :         if (tevent_req_is_nterror(req, &status)) {
     478          78 :                 return status;
     479             :         }
     480        4694 :         *pinfo = talloc_move(mem_ctx, &state->info);
     481        4694 :         return NT_STATUS_OK;
     482             : }

Generated by: LCOV version 1.13