s3: piddir creation fix part 2.
[ira/wip.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
30 #ifndef HAVE_GETGROUPLIST
31
32 #ifdef HAVE_GETGRSET
33 static int getgrouplist_getgrset(const char *user, gid_t gid, gid_t *groups,
34                                  int *grpcnt)
35 {
36         char *grplist;
37         char *grp;
38         gid_t temp_gid;
39         int num_gids = 1;
40         int ret = 0;
41         long l;
42
43         grplist = getgrset(user);
44
45         DEBUG(10, ("getgrset returned %s\n", grplist));
46
47         if (grplist == NULL) {
48                 return -1;
49         }
50
51         if (*grpcnt > 0) {
52                 groups[0] = gid;
53         }
54
55         while ((grp = strsep(&grplist, ",")) != NULL) {
56                 l = strtol(grp, NULL, 10);
57                 temp_gid = (gid_t) l;
58                 if (temp_gid == gid) {
59                         continue;
60                 }
61
62                 if (num_gids + 1 > *grpcnt) {
63                         num_gids++;
64                         continue;
65                 }
66                 groups[num_gids++] = temp_gid;
67         }
68         free(grplist);
69
70         if (num_gids > *grpcnt) {
71                 ret = -1;
72         }
73         *grpcnt = num_gids;
74
75         DEBUG(10, ("Found %d groups for user %s\n", *grpcnt, user));
76
77         return ret;
78 }
79
80 #else /* HAVE_GETGRSET */
81
82 /*
83   This is a *much* faster way of getting the list of groups for a user
84   without changing the current supplementary group list. The old
85   method used getgrent() which could take 20 minutes on a really big
86   network with hundeds of thousands of groups and users. The new method
87   takes a couple of seconds.
88
89   NOTE!! this function only works if it is called as root!
90   */
91
92 static int getgrouplist_internals(const char *user, gid_t gid, gid_t *groups,
93                                   int *grpcnt)
94 {
95         gid_t *gids_saved;
96         int ret, ngrp_saved, num_gids;
97
98         if (non_root_mode()) {
99                 *grpcnt = 0;
100                 return 0;
101         }
102
103         /* work out how many groups we need to save */
104         ngrp_saved = getgroups(0, NULL);
105         if (ngrp_saved == -1) {
106                 /* this shouldn't happen */
107                 return -1;
108         }
109
110         gids_saved = SMB_MALLOC_ARRAY(gid_t, ngrp_saved+1);
111         if (!gids_saved) {
112                 errno = ENOMEM;
113                 return -1;
114         }
115
116         ngrp_saved = getgroups(ngrp_saved, gids_saved);
117         if (ngrp_saved == -1) {
118                 SAFE_FREE(gids_saved);
119                 /* very strange! */
120                 return -1;
121         }
122
123         if (initgroups(user, gid) == -1) {
124                 DEBUG(0, ("getgrouplist_internals: initgroups() failed!\n"));
125                 SAFE_FREE(gids_saved);
126                 return -1;
127         }
128
129         /* this must be done to cope with systems that put the current egid in the
130            return from getgroups() */
131         save_re_gid();
132         set_effective_gid(gid);
133         setgid(gid);
134
135         num_gids = getgroups(0, NULL);
136         if (num_gids == -1) {
137                 SAFE_FREE(gids_saved);
138                 /* very strange! */
139                 return -1;
140         }
141
142         if (num_gids + 1 > *grpcnt) {
143                 *grpcnt = num_gids + 1;
144                 ret = -1;
145         } else {
146                 ret = getgroups(*grpcnt - 1, &groups[1]);
147                 if (ret < 0) {
148                         SAFE_FREE(gids_saved);
149                         /* very strange! */
150                         return -1;
151                 }
152                 groups[0] = gid;
153                 *grpcnt = ret + 1;
154         }
155
156         restore_re_gid();
157
158         if (sys_setgroups(gid, ngrp_saved, gids_saved) != 0) {
159                 /* yikes! */
160                 DEBUG(0,("ERROR: getgrouplist: failed to reset group list!\n"));
161                 smb_panic("getgrouplist: failed to reset group list!");
162         }
163
164         free(gids_saved);
165         return ret;
166 }
167 #endif /* HAVE_GETGRSET */
168 #endif /* HAVE_GETGROUPLIST */
169
170 static int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt)
171 {
172         int retval;
173         bool winbind_env;
174
175         DEBUG(10,("sys_getgrouplist: user [%s]\n", user));
176
177         /* This is only ever called for Unix users, remote memberships are
178          * always determined by the info3 coming back from auth3 or the
179          * PAC. */
180         winbind_env = winbind_env_set();
181         (void)winbind_off();
182
183 #ifdef HAVE_GETGROUPLIST
184         retval = getgrouplist(user, gid, groups, grpcnt);
185 #else
186 #ifdef HAVE_GETGRSET
187         retval = getgrouplist_getgrset(user, gid, groups, grpcnt);
188 #else
189         become_root();
190         retval = getgrouplist_internals(user, gid, groups, grpcnt);
191         unbecome_root();
192 #endif /* HAVE_GETGRSET */
193 #endif /* HAVE_GETGROUPLIST */
194
195         /* allow winbindd lookups, but only if they were not already disabled */
196         if (!winbind_env) {
197                 (void)winbind_on();
198         }
199
200         return retval;
201 }
202
203 bool getgroups_unix_user(TALLOC_CTX *mem_ctx, const char *user,
204                          gid_t primary_gid,
205                          gid_t **ret_groups, uint32_t *p_ngroups)
206 {
207         uint32_t ngrp;
208         int max_grp;
209         gid_t *temp_groups;
210         gid_t *groups;
211         int i;
212
213         max_grp = MIN(128, groups_max());
214         temp_groups = SMB_MALLOC_ARRAY(gid_t, max_grp);
215         if (! temp_groups) {
216                 return False;
217         }
218
219         if (sys_getgrouplist(user, primary_gid, temp_groups, &max_grp) == -1) {
220                 temp_groups = SMB_REALLOC_ARRAY(temp_groups, gid_t, max_grp);
221                 if (!temp_groups) {
222                         return False;
223                 }
224                 
225                 if (sys_getgrouplist(user, primary_gid,
226                                      temp_groups, &max_grp) == -1) {
227                         DEBUG(0, ("get_user_groups: failed to get the unix "
228                                   "group list\n"));
229                         SAFE_FREE(temp_groups);
230                         return False;
231                 }
232         }
233         
234         ngrp = 0;
235         groups = NULL;
236
237         /* Add in primary group first */
238         if (!add_gid_to_array_unique(mem_ctx, primary_gid, &groups, &ngrp)) {
239                 SAFE_FREE(temp_groups);
240                 return False;
241         }
242
243         for (i=0; i<max_grp; i++) {
244                 if (!add_gid_to_array_unique(mem_ctx, temp_groups[i],
245                                         &groups, &ngrp)) {
246                         SAFE_FREE(temp_groups);
247                         return False;
248                 }
249         }
250
251         *p_ngroups = ngrp;
252         *ret_groups = groups;
253         SAFE_FREE(temp_groups);
254         return True;
255 }