Line data Source code
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 46971 : static int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt)
172 : {
173 3 : int retval;
174 3 : bool winbind_env;
175 :
176 46971 : 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 46971 : winbind_env = winbind_env_set();
182 46971 : (void)winbind_off();
183 :
184 : #ifdef HAVE_GETGROUPLIST
185 46971 : 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 46971 : if (!winbind_env) {
198 43833 : (void)winbind_on();
199 : }
200 :
201 46971 : return retval;
202 : }
203 :
204 46971 : 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 46971 : {
208 46971 : int max_grp = MIN(128, getgroups_max());
209 46971 : gid_t stack_groups[max_grp];
210 3 : uint32_t ngrp;
211 46971 : gid_t *temp_groups = stack_groups;
212 46971 : gid_t *to_free = NULL;
213 3 : gid_t *groups;
214 3 : int i;
215 :
216 46971 : 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 : }
221 0 : temp_groups = to_free;
222 :
223 0 : if (sys_getgrouplist(user, primary_gid,
224 : temp_groups, &max_grp) == -1) {
225 0 : DEBUG(0, ("get_user_groups: failed to get the unix "
226 : "group list\n"));
227 0 : TALLOC_FREE(to_free);
228 0 : return False;
229 : }
230 : }
231 :
232 46971 : ngrp = 0;
233 46971 : groups = NULL;
234 :
235 : /* Add in primary group first */
236 46971 : if (!add_gid_to_array_unique(mem_ctx, primary_gid, &groups, &ngrp)) {
237 0 : TALLOC_FREE(to_free);
238 0 : return False;
239 : }
240 :
241 135788 : for (i=0; i<max_grp; i++) {
242 88817 : if (!add_gid_to_array_unique(mem_ctx, temp_groups[i],
243 : &groups, &ngrp)) {
244 0 : TALLOC_FREE(to_free);
245 0 : return False;
246 : }
247 : }
248 :
249 46971 : *p_ngroups = ngrp;
250 46971 : *ret_groups = groups;
251 46971 : TALLOC_FREE(to_free);
252 46968 : return True;
253 : }
|