Coverage Report

Created: 2026-07-25 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/lib/system_smbd.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   system call wrapper interface.
4
   Copyright (C) Andrew Tridgell 2002
5
   Copyright (C) Andrew Barteltt 2002
6
7
   This program is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3 of the License, or
10
   (at your option) any later version.
11
12
   This program is distributed in the hope that it will be useful,
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
   GNU General Public License for more details.
16
17
   You should have received a copy of the GNU General Public License
18
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
*/
20
21
/*
22
   This file may assume linkage with smbd - for things like become_root()
23
   etc.
24
*/
25
26
#include "includes.h"
27
#include "system/passwd.h"
28
#include "nsswitch/winbind_client.h"
29
#include "../lib/util/setid.h"
30
31
#ifndef HAVE_GETGROUPLIST
32
33
#ifdef HAVE_GETGRSET
34
static int getgrouplist_getgrset(const char *user, gid_t gid, gid_t *groups,
35
         int *grpcnt)
36
{
37
  char *grplist;
38
  char *grp;
39
  gid_t temp_gid;
40
  int num_gids = 1;
41
  int ret = 0;
42
  long l;
43
44
  grplist = getgrset(user);
45
46
  DEBUG(10, ("getgrset returned %s\n", grplist));
47
48
  if (grplist == NULL) {
49
    return -1;
50
  }
51
52
  if (*grpcnt > 0) {
53
    groups[0] = gid;
54
  }
55
56
  while ((grp = strsep(&grplist, ",")) != NULL) {
57
    l = strtol(grp, NULL, 10);
58
    temp_gid = (gid_t) l;
59
    if (temp_gid == gid) {
60
      continue;
61
    }
62
63
    if (num_gids + 1 > *grpcnt) {
64
      num_gids++;
65
      continue;
66
    }
67
    groups[num_gids++] = temp_gid;
68
  }
69
  free(grplist);
70
71
  if (num_gids > *grpcnt) {
72
    ret = -1;
73
  }
74
  *grpcnt = num_gids;
75
76
  DEBUG(10, ("Found %d groups for user %s\n", *grpcnt, user));
77
78
  return ret;
79
}
80
81
#else /* HAVE_GETGRSET */
82
83
/*
84
  This is a *much* faster way of getting the list of groups for a user
85
  without changing the current supplementary group list. The old
86
  method used getgrent() which could take 20 minutes on a really big
87
  network with hundeds of thousands of groups and users. The new method
88
  takes a couple of seconds.
89
90
  NOTE!! this function only works if it is called as root!
91
  */
92
93
static int getgrouplist_internals(const char *user, gid_t gid, gid_t *groups,
94
          int *grpcnt)
95
{
96
  gid_t *gids_saved;
97
  int ret, ngrp_saved, num_gids;
98
99
  if (non_root_mode()) {
100
    *grpcnt = 0;
101
    return 0;
102
  }
103
104
  /* work out how many groups we need to save */
105
  ngrp_saved = getgroups(0, NULL);
106
  if (ngrp_saved == -1) {
107
    /* this shouldn't happen */
108
    return -1;
109
  }
110
111
  gids_saved = SMB_MALLOC_ARRAY(gid_t, ngrp_saved+1);
112
  if (!gids_saved) {
113
    errno = ENOMEM;
114
    return -1;
115
  }
116
117
  ngrp_saved = getgroups(ngrp_saved, gids_saved);
118
  if (ngrp_saved == -1) {
119
    SAFE_FREE(gids_saved);
120
    /* very strange! */
121
    return -1;
122
  }
123
124
  if (initgroups(user, gid) == -1) {
125
    DEBUG(0, ("getgrouplist_internals: initgroups() failed!\n"));
126
    SAFE_FREE(gids_saved);
127
    return -1;
128
  }
129
130
  /* this must be done to cope with systems that put the current egid in the
131
     return from getgroups() */
132
  save_re_gid();
133
  set_effective_gid(gid);
134
  samba_setgid(gid);
135
136
  num_gids = getgroups(0, NULL);
137
  if (num_gids == -1) {
138
    SAFE_FREE(gids_saved);
139
    /* very strange! */
140
    return -1;
141
  }
142
143
  if (num_gids + 1 > *grpcnt) {
144
    *grpcnt = num_gids + 1;
145
    ret = -1;
146
  } else {
147
    ret = getgroups(*grpcnt - 1, &groups[1]);
148
    if (ret < 0) {
149
      SAFE_FREE(gids_saved);
150
      /* very strange! */
151
      return -1;
152
    }
153
    groups[0] = gid;
154
    *grpcnt = ret + 1;
155
  }
156
157
  restore_re_gid();
158
159
  if (sys_setgroups(gid, ngrp_saved, gids_saved) != 0) {
160
    /* yikes! */
161
    DEBUG(0,("ERROR: getgrouplist: failed to reset group list!\n"));
162
    smb_panic("getgrouplist: failed to reset group list!");
163
  }
164
165
  free(gids_saved);
166
  return ret;
167
}
168
#endif /* HAVE_GETGRSET */
169
#endif /* HAVE_GETGROUPLIST */
170
171
static int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt)
172
0
{
173
0
  int retval;
174
0
  bool winbind_env;
175
176
0
  DEBUG(10,("sys_getgrouplist: user [%s]\n", user));
177
178
  /* This is only ever called for Unix users, remote memberships are
179
   * always determined by the info3 coming back from auth3 or the
180
   * PAC. */
181
0
  winbind_env = winbind_env_set();
182
0
  (void)winbind_off();
183
184
0
#ifdef HAVE_GETGROUPLIST
185
0
  retval = getgrouplist(user, gid, groups, grpcnt);
186
#else
187
#ifdef HAVE_GETGRSET
188
  retval = getgrouplist_getgrset(user, gid, groups, grpcnt);
189
#else
190
  become_root();
191
  retval = getgrouplist_internals(user, gid, groups, grpcnt);
192
  unbecome_root();
193
#endif /* HAVE_GETGRSET */
194
#endif /* HAVE_GETGROUPLIST */
195
196
  /* allow winbindd lookups, but only if they were not already disabled */
197
0
  if (!winbind_env) {
198
0
    (void)winbind_on();
199
0
  }
200
201
0
  return retval;
202
0
}
203
204
bool getgroups_unix_user(TALLOC_CTX *mem_ctx, const char *user,
205
       gid_t primary_gid,
206
       gid_t **ret_groups, uint32_t *p_ngroups)
207
0
{
208
0
  int max_grp = MIN(128, getgroups_max());
209
0
  gid_t stack_groups[max_grp];
210
0
  uint32_t ngrp;
211
0
  gid_t *temp_groups = stack_groups;
212
0
  gid_t *to_free = NULL;
213
0
  gid_t *groups;
214
0
  int i;
215
216
0
  if (sys_getgrouplist(user, primary_gid, temp_groups, &max_grp) == -1) {
217
0
    to_free = talloc_array(mem_ctx, gid_t, max_grp);
218
0
    if (!to_free) {
219
0
      return False;
220
0
    }
221
0
    temp_groups = to_free;
222
223
0
    if (sys_getgrouplist(user, primary_gid,
224
0
             temp_groups, &max_grp) == -1) {
225
0
      DEBUG(0, ("get_user_groups: failed to get the unix "
226
0
          "group list\n"));
227
0
      TALLOC_FREE(to_free);
228
0
      return False;
229
0
    }
230
0
  }
231
232
0
  ngrp = 0;
233
0
  groups = NULL;
234
235
  /* Add in primary group first */
236
0
  if (!add_gid_to_array_unique(mem_ctx, primary_gid, &groups, &ngrp)) {
237
0
    TALLOC_FREE(to_free);
238
0
    return False;
239
0
  }
240
241
0
  for (i=0; i<max_grp; i++) {
242
0
    if (!add_gid_to_array_unique(mem_ctx, temp_groups[i],
243
0
          &groups, &ngrp)) {
244
0
      TALLOC_FREE(to_free);
245
0
      return False;
246
0
    }
247
0
  }
248
249
0
  *p_ngroups = ngrp;
250
0
  *ret_groups = groups;
251
0
  TALLOC_FREE(to_free);
252
  return True;
253
0
}