s3: add a debug message for failed execv in sys_popen()
[kai/samba.git] / source3 / lib / system_smbd.c
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 {
173         int retval;
174         bool winbind_env;
175
176         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         winbind_env = winbind_env_set();
182         (void)winbind_off();
183
184 #ifdef HAVE_GETGROUPLIST
185         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         if (!winbind_env) {
198                 (void)winbind_on();
199         }
200
201         return retval;
202 }
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 {
208         uint32_t ngrp;
209         int max_grp;
210         gid_t *temp_groups;
211         gid_t *groups;
212         int i;
213
214         max_grp = MIN(128, groups_max());
215         temp_groups = SMB_MALLOC_ARRAY(gid_t, max_grp);
216         if (! temp_groups) {
217                 return False;
218         }
219
220         if (sys_getgrouplist(user, primary_gid, temp_groups, &max_grp) == -1) {
221                 temp_groups = SMB_REALLOC_ARRAY(temp_groups, gid_t, max_grp);
222                 if (!temp_groups) {
223                         return False;
224                 }
225                 
226                 if (sys_getgrouplist(user, primary_gid,
227                                      temp_groups, &max_grp) == -1) {
228                         DEBUG(0, ("get_user_groups: failed to get the unix "
229                                   "group list\n"));
230                         SAFE_FREE(temp_groups);
231                         return False;
232                 }
233         }
234         
235         ngrp = 0;
236         groups = NULL;
237
238         /* Add in primary group first */
239         if (!add_gid_to_array_unique(mem_ctx, primary_gid, &groups, &ngrp)) {
240                 SAFE_FREE(temp_groups);
241                 return False;
242         }
243
244         for (i=0; i<max_grp; i++) {
245                 if (!add_gid_to_array_unique(mem_ctx, temp_groups[i],
246                                         &groups, &ngrp)) {
247                         SAFE_FREE(temp_groups);
248                         return False;
249                 }
250         }
251
252         *p_ngroups = ngrp;
253         *ret_groups = groups;
254         SAFE_FREE(temp_groups);
255         return True;
256 }