/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 | } |