2 fixes to enhance readability of source code and debug messages
[amitay/samba.git] / source3 / 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         if (! ctx_p->token) {
261                 DEBUG(0, ("Out of memory on dup_nt_token() in push_sec_ctx()\n"));
262                 return False;
263         }
264
265         ctx_p->ngroups = sys_getgroups(0, NULL);
266
267         if (ctx_p->ngroups != 0) {
268                 if (!(ctx_p->groups = malloc(ctx_p->ngroups * sizeof(gid_t)))) {
269                         DEBUG(0, ("Out of memory on malloc() in push_sec_ctx()\n"));
270                         delete_nt_token(&ctx_p->token);
271                         return False;
272                 }
273
274                 sys_getgroups(ctx_p->ngroups, ctx_p->groups);
275         } else {
276                 ctx_p->groups = NULL;
277         }
278
279         init_privilege(&ctx_p->privs);
280         if (! NT_STATUS_IS_OK(dup_priv_set(ctx_p->privs, sec_ctx_stack[sec_ctx_stack_ndx-1].privs))) {
281                 DEBUG(0, ("Out of memory on dup_priv_set() in push_sec_ctx()\n"));
282                 delete_nt_token(&ctx_p->token);
283                 destroy_privilege(&ctx_p->privs);
284                 return False;
285         }
286
287         return True;
288 }
289
290 /****************************************************************************
291  Set the current security context to a given user.
292 ****************************************************************************/
293
294 void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token, PRIVILEGE_SET *privs)
295 {
296         struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
297         
298         /* Set the security context */
299
300         DEBUG(3, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
301                 (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
302
303         debug_nt_user_token(DBGC_CLASS, 5, token);
304         debug_unix_user_token(DBGC_CLASS, 5, uid, gid, ngroups, groups);
305
306         gain_root();
307
308 #ifdef HAVE_SETGROUPS
309         sys_setgroups(ngroups, groups);
310 #endif
311
312         ctx_p->ngroups = ngroups;
313
314         SAFE_FREE(ctx_p->groups);
315         if (token && (token == ctx_p->token))
316                 smb_panic("DUPLICATE_TOKEN");
317
318         delete_nt_token(&ctx_p->token);
319         if (ctx_p->privs)
320                 reset_privilege(ctx_p->privs);
321         else
322                 init_privilege(&ctx_p->privs);
323         
324         ctx_p->groups = memdup(groups, sizeof(gid_t) * ngroups);
325         ctx_p->token = dup_nt_token(token);
326         dup_priv_set(ctx_p->privs, privs);
327
328         become_id(uid, gid);
329
330         ctx_p->uid = uid;
331         ctx_p->gid = gid;
332
333         /* Update current_user stuff */
334
335         current_user.uid = uid;
336         current_user.gid = gid;
337         current_user.ngroups = ngroups;
338         current_user.groups = groups;
339         current_user.nt_user_token = ctx_p->token;
340         current_user.privs = ctx_p->privs;
341 }
342
343 /****************************************************************************
344  Become root context.
345 ****************************************************************************/
346
347 void set_root_sec_ctx(void)
348 {
349         /* May need to worry about supplementary groups at some stage */
350
351         set_sec_ctx(0, 0, 0, NULL, NULL, NULL);
352 }
353
354 /****************************************************************************
355  Pop a security context from the stack.
356 ****************************************************************************/
357
358 BOOL pop_sec_ctx(void)
359 {
360         struct sec_ctx *ctx_p;
361         struct sec_ctx *prev_ctx_p;
362
363         /* Check for stack underflow */
364
365         if (sec_ctx_stack_ndx == 0) {
366                 DEBUG(0, ("Security context stack underflow!\n"));
367                 smb_panic("Security context stack underflow!\n");
368         }
369
370         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
371
372         /* Clear previous user info */
373
374         ctx_p->uid = (uid_t)-1;
375         ctx_p->gid = (gid_t)-1;
376
377         SAFE_FREE(ctx_p->groups);
378         ctx_p->ngroups = 0;
379
380         delete_nt_token(&ctx_p->token);
381         destroy_privilege(&ctx_p->privs);
382
383         /* Pop back previous user */
384
385         sec_ctx_stack_ndx--;
386
387         gain_root();
388
389         prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
390
391 #ifdef HAVE_SETGROUPS
392         sys_setgroups(prev_ctx_p->ngroups, prev_ctx_p->groups);
393 #endif
394
395         become_id(prev_ctx_p->uid, prev_ctx_p->gid);
396
397         /* Update current_user stuff */
398
399         current_user.uid = prev_ctx_p->uid;
400         current_user.gid = prev_ctx_p->gid;
401         current_user.ngroups = prev_ctx_p->ngroups;
402         current_user.groups = prev_ctx_p->groups;
403         current_user.nt_user_token = prev_ctx_p->token;
404         current_user.privs = prev_ctx_p->privs;
405
406         DEBUG(3, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
407                 (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
408
409         return True;
410 }
411
412 /* Initialise the security context system */
413
414 void init_sec_ctx(void)
415 {
416         int i;
417         struct sec_ctx *ctx_p;
418
419         /* Initialise security context stack */
420
421         memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH);
422
423         for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
424                 sec_ctx_stack[i].uid = (uid_t)-1;
425                 sec_ctx_stack[i].gid = (gid_t)-1;
426         }
427
428         /* Initialise first level of stack.  It is the current context */
429         ctx_p = &sec_ctx_stack[0];
430
431         ctx_p->uid = geteuid();
432         ctx_p->gid = getegid();
433
434         get_current_groups(ctx_p->gid, &ctx_p->ngroups, &ctx_p->groups);
435
436         ctx_p->token = NULL; /* Maps to guest user. */
437         ctx_p->privs = NULL;
438
439         /* Initialise current_user global */
440
441         current_user.uid = ctx_p->uid;
442         current_user.gid = ctx_p->gid;
443         current_user.ngroups = ctx_p->ngroups;
444         current_user.groups = ctx_p->groups;
445
446         /* The conn and vuid are usually taken care of by other modules.
447            We initialise them here. */
448
449         current_user.conn = NULL;
450         current_user.vuid = UID_FIELD_INVALID;
451         current_user.nt_user_token = NULL;
452         current_user.privs = NULL;
453 }