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