updated the 3.0 branch from the head branch - ready for alpha18
[tprouty/samba.git] / source / smbd / sec_ctx.c
1 /* 
2    Unix SMB/CIFS implementation.
3    uid/user handling
4    Copyright (C) Tim Potter 2000
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 extern struct current_user current_user;
24
25 struct sec_ctx {
26         uid_t uid;
27         uid_t gid;
28         int ngroups;
29         gid_t *groups;
30         NT_USER_TOKEN *token;
31 };
32
33 /* A stack of security contexts.  We include the current context as being
34    the first one, so there is room for another MAX_SEC_CTX_DEPTH more. */
35
36 static struct sec_ctx sec_ctx_stack[MAX_SEC_CTX_DEPTH + 1];
37 static int sec_ctx_stack_ndx;
38
39 /****************************************************************************
40  Become the specified uid.
41 ****************************************************************************/
42
43 static BOOL become_uid(uid_t uid)
44 {
45         /* Check for dodgy uid values */
46
47         if (uid == (uid_t)-1 || 
48             ((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) {
49                 static int done;
50  
51                 if (!done) {
52                         DEBUG(1,("WARNING: using uid %d is a security risk\n",
53                                  (int)uid));
54                         done = 1;
55                 }
56         }
57
58         /* Set effective user id */
59
60         set_effective_uid(uid);
61
62         DO_PROFILE_INC(uid_changes);
63         return True;
64 }
65
66 /****************************************************************************
67  Become the specified gid.
68 ****************************************************************************/
69
70 static BOOL become_gid(gid_t gid)
71 {
72         /* Check for dodgy gid values */
73
74         if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) && 
75                                  (gid == (gid_t)65535))) {
76                 static int done;
77                 
78                 if (!done) {
79                         DEBUG(1,("WARNING: using gid %d is a security risk\n",
80                                  (int)gid));  
81                         done = 1;
82                 }
83         }
84   
85         /* Set effective group id */
86
87         set_effective_gid(gid);
88         return True;
89 }
90
91 /****************************************************************************
92  Become the specified uid and gid.
93 ****************************************************************************/
94
95 static BOOL become_id(uid_t uid, gid_t gid)
96 {
97         return become_gid(gid) && become_uid(uid);
98 }
99
100 /****************************************************************************
101  Drop back to root privileges in order to change to another user.
102 ****************************************************************************/
103
104 static void gain_root(void)
105 {
106         if (non_root_mode()) {
107                 return;
108         }
109
110         if (geteuid() != 0) {
111                 set_effective_uid(0);
112
113                 if (geteuid() != 0) {
114                         DEBUG(0,
115                               ("Warning: You appear to have a trapdoor "
116                                "uid system\n"));
117                 }
118         }
119
120         if (getegid() != 0) {
121                 set_effective_gid(0);
122
123                 if (getegid() != 0) {
124                         DEBUG(0,
125                               ("Warning: You appear to have a trapdoor "
126                                "gid system\n"));
127                 }
128         }
129 }
130
131 /****************************************************************************
132  Get the list of current groups.
133 ****************************************************************************/
134
135 int get_current_groups(gid_t gid, int *p_ngroups, gid_t **p_groups)
136 {
137         int i;
138         gid_t grp;
139         int ngroups;
140         gid_t *groups = NULL;
141
142         (*p_ngroups) = 0;
143         (*p_groups) = NULL;
144
145         /* this looks a little strange, but is needed to cope with
146            systems that put the current egid in the group list
147            returned from getgroups() (tridge) */
148         save_re_gid();
149         set_effective_gid(gid);
150         setgid(gid);
151
152         ngroups = sys_getgroups(0,&grp);
153         if (ngroups <= 0) {
154                 goto fail;
155         }
156
157         if((groups = (gid_t *)malloc(sizeof(gid_t)*(ngroups+1))) == NULL) {
158                 DEBUG(0,("setup_groups malloc fail !\n"));
159                 goto fail;
160         }
161
162         if ((ngroups = sys_getgroups(ngroups,groups)) == -1) {
163                 goto fail;
164         }
165
166         restore_re_gid();
167
168         (*p_ngroups) = ngroups;
169         (*p_groups) = groups;
170
171         DEBUG( 3, ( "get_current_groups: user is in %u groups: ", ngroups));
172         for (i = 0; i < ngroups; i++ ) {
173                 DEBUG( 3, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
174         }
175         DEBUG( 3, ( "\n" ) );
176
177         return ngroups;
178
179 fail:
180         SAFE_FREE(groups);
181         restore_re_gid();
182         return -1;
183 }
184
185 /****************************************************************************
186  Initialize the groups a user belongs to.
187 ****************************************************************************/
188
189 BOOL initialise_groups(char *user, uid_t uid, gid_t gid)
190 {
191         struct sec_ctx *prev_ctx_p;
192         BOOL result = True;
193
194         if (non_root_mode()) {
195                 return True;
196         }
197
198         become_root();
199
200         /* Call initgroups() to get user groups */
201
202         if (initgroups(user,gid) == -1) {
203                 DEBUG(0,("Unable to initgroups. Error was %s\n", strerror(errno) ));
204                 if (getuid() == 0) {
205                         if (gid < 0 || gid > 32767 || uid < 0 || uid > 32767) {
206                                 DEBUG(0,("This is probably a problem with the account %s\n", user));
207                         }
208                 }
209                 result = False;
210                 goto done;
211         }
212
213         /* Store groups in previous user's security context.  This will
214            always work as the become_root() call increments the stack
215            pointer. */
216
217         prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx - 1];
218
219         SAFE_FREE(prev_ctx_p->groups);
220         prev_ctx_p->ngroups = 0;
221
222         get_current_groups(gid, &prev_ctx_p->ngroups, &prev_ctx_p->groups);
223
224  done:
225         unbecome_root();
226
227         return result;
228 }
229
230 /****************************************************************************
231  Create a new security context on the stack.  It is the same as the old
232  one.  User changes are done using the set_sec_ctx() function.
233 ****************************************************************************/
234
235 BOOL push_sec_ctx(void)
236 {
237         struct sec_ctx *ctx_p;
238
239         /* Check we don't overflow our stack */
240
241         if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
242                 DEBUG(0, ("Security context stack overflow!\n"));
243                 smb_panic("Security context stack overflow!\n");
244         }
245
246         /* Store previous user context */
247
248         sec_ctx_stack_ndx++;
249
250         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
251
252         ctx_p->uid = geteuid();
253         ctx_p->gid = getegid();
254
255         DEBUG(3, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n", 
256                   (unsigned int)ctx_p->uid, (unsigned int)ctx_p->gid, sec_ctx_stack_ndx ));
257
258         ctx_p->token = dup_nt_token(sec_ctx_stack[sec_ctx_stack_ndx-1].token);
259
260         ctx_p->ngroups = sys_getgroups(0, NULL);
261
262         if (ctx_p->ngroups != 0) {
263                 if (!(ctx_p->groups = malloc(ctx_p->ngroups * sizeof(gid_t)))) {
264                         DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
265                         delete_nt_token(&ctx_p->token);
266                         return False;
267                 }
268
269                 sys_getgroups(ctx_p->ngroups, ctx_p->groups);
270         } else {
271                 ctx_p->groups = NULL;
272         }
273
274         return True;
275 }
276
277 /****************************************************************************
278  Set the current security context to a given user.
279 ****************************************************************************/
280
281 void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token)
282 {
283         struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
284         
285         /* Set the security context */
286
287         DEBUG(3, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
288                 (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
289
290         if (ngroups) {
291                 int i;
292
293                 DEBUG(3, ("%d user groups: \n", ngroups));
294                 for (i = 0; i < ngroups; i++) {
295                         DEBUGADD(3, ("%u ", (unsigned int)groups[i]));
296                 }
297
298                 DEBUG(3, ("\n"));
299         }
300         
301
302         gain_root();
303
304 #ifdef HAVE_SETGROUPS
305         sys_setgroups(ngroups, groups);
306 #endif
307
308         ctx_p->ngroups = ngroups;
309
310         SAFE_FREE(ctx_p->groups);
311         if (token && (token == ctx_p->token))
312                 smb_panic("DUPLICATE_TOKEN");
313
314         delete_nt_token(&ctx_p->token);
315         
316         ctx_p->groups = memdup(groups, sizeof(gid_t) * ngroups);
317         ctx_p->token = dup_nt_token(token);
318
319         become_id(uid, gid);
320
321         ctx_p->uid = uid;
322         ctx_p->gid = gid;
323
324         /* Update current_user stuff */
325
326         current_user.uid = uid;
327         current_user.gid = gid;
328         current_user.ngroups = ngroups;
329         current_user.groups = groups;
330         current_user.nt_user_token = ctx_p->token;
331 }
332
333 /****************************************************************************
334  Become root context.
335 ****************************************************************************/
336
337 void set_root_sec_ctx(void)
338 {
339         /* May need to worry about supplementary groups at some stage */
340
341         set_sec_ctx(0, 0, 0, NULL, NULL);
342 }
343
344 /****************************************************************************
345  Pop a security context from the stack.
346 ****************************************************************************/
347
348 BOOL pop_sec_ctx(void)
349 {
350         struct sec_ctx *ctx_p;
351         struct sec_ctx *prev_ctx_p;
352
353         /* Check for stack underflow */
354
355         if (sec_ctx_stack_ndx == 0) {
356                 DEBUG(0, ("Security context stack underflow!\n"));
357                 smb_panic("Security context stack underflow!\n");
358         }
359
360         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
361
362         /* Clear previous user info */
363
364         ctx_p->uid = (uid_t)-1;
365         ctx_p->gid = (gid_t)-1;
366
367         SAFE_FREE(ctx_p->groups);
368         ctx_p->ngroups = 0;
369
370         delete_nt_token(&ctx_p->token);
371
372         /* Pop back previous user */
373
374         sec_ctx_stack_ndx--;
375
376         gain_root();
377
378         prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
379
380 #ifdef HAVE_SETGROUPS
381         sys_setgroups(prev_ctx_p->ngroups, prev_ctx_p->groups);
382 #endif
383
384         become_id(prev_ctx_p->uid, prev_ctx_p->gid);
385
386         /* Update current_user stuff */
387
388         current_user.uid = prev_ctx_p->uid;
389         current_user.gid = prev_ctx_p->gid;
390         current_user.ngroups = prev_ctx_p->ngroups;
391         current_user.groups = prev_ctx_p->groups;
392         current_user.nt_user_token = prev_ctx_p->token;
393
394         DEBUG(3, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
395                 (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
396
397         return True;
398 }
399
400 /* Initialise the security context system */
401
402 void init_sec_ctx(void)
403 {
404         int i;
405         struct sec_ctx *ctx_p;
406
407         /* Initialise security context stack */
408
409         memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH);
410
411         for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
412                 sec_ctx_stack[i].uid = (uid_t)-1;
413                 sec_ctx_stack[i].gid = (gid_t)-1;
414         }
415
416         /* Initialise first level of stack.  It is the current context */
417         ctx_p = &sec_ctx_stack[0];
418
419         ctx_p->uid = geteuid();
420         ctx_p->gid = getegid();
421
422         get_current_groups(ctx_p->gid, &ctx_p->ngroups, &ctx_p->groups);
423
424         ctx_p->token = NULL; /* Maps to guest user. */
425
426         /* Initialise current_user global */
427
428         current_user.uid = ctx_p->uid;
429         current_user.gid = ctx_p->gid;
430         current_user.ngroups = ctx_p->ngroups;
431         current_user.groups = ctx_p->groups;
432
433         /* The conn and vuid are usually taken care of by other modules.
434            We initialise them here. */
435
436         current_user.conn = NULL;
437         current_user.vuid = UID_FIELD_INVALID;
438         current_user.nt_user_token = NULL;
439 }