Removed 'extern int DEBUGLEVEL' as it is now in the smb.h header.
[samba.git] / source3 / smbd / sec_ctx.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    uid/user handling
5    Copyright (C) Tim Potter 2000
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 2 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, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 extern struct current_user current_user;
25
26 struct sec_ctx {
27         uid_t uid;
28         uid_t gid;
29         int ngroups;
30         gid_t *groups;
31         NT_USER_TOKEN *token;
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         current_user.uid = uid;
63
64         DO_PROFILE_INC(uid_changes);
65
66         return True;
67 }
68
69 /****************************************************************************
70  Become the specified gid.
71 ****************************************************************************/
72
73 static BOOL become_gid(gid_t gid)
74 {
75         /* Check for dodgy gid values */
76
77         if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) && 
78                                  (gid == (gid_t)65535))) {
79                 static int done;
80                 
81                 if (!done) {
82                         DEBUG(1,("WARNING: using gid %d is a security risk\n",
83                                  (int)gid));  
84                         done = 1;
85                 }
86         }
87   
88         /* Set effective group id */
89
90         set_effective_gid(gid);
91         current_user.gid = gid;
92         
93         return True;
94 }
95
96 /****************************************************************************
97  Become the specified uid and gid.
98 ****************************************************************************/
99
100 static BOOL become_id(uid_t uid, gid_t gid)
101 {
102         return become_gid(gid) && become_uid(uid);
103 }
104
105 /****************************************************************************
106  Drop back to root privileges in order to change to another user.
107 ****************************************************************************/
108
109 static void gain_root(void)
110 {
111         if (non_root_mode()) {
112                 return;
113         }
114
115         if (geteuid() != 0) {
116                 set_effective_uid(0);
117
118                 if (geteuid() != 0) {
119                         DEBUG(0,
120                               ("Warning: You appear to have a trapdoor "
121                                "uid system\n"));
122                 }
123         }
124
125         if (getegid() != 0) {
126                 set_effective_gid(0);
127
128                 if (getegid() != 0) {
129                         DEBUG(0,
130                               ("Warning: You appear to have a trapdoor "
131                                "gid system\n"));
132                 }
133         }
134 }
135
136 /****************************************************************************
137  Get the list of current groups.
138 ****************************************************************************/
139
140 int get_current_groups(int *p_ngroups, gid_t **p_groups)
141 {
142         int i;
143         gid_t grp;
144         int ngroups = sys_getgroups(0,&grp);
145         gid_t *groups;
146
147         (*p_ngroups) = 0;
148         (*p_groups) = NULL;
149
150         if (ngroups <= 0)
151                 return -1;
152
153         if((groups = (gid_t *)malloc(sizeof(gid_t)*ngroups)) == NULL) {
154                 DEBUG(0,("setup_groups malloc fail !\n"));
155                 return -1;
156         }
157
158         if ((ngroups = sys_getgroups(ngroups,groups)) == -1) {
159                 SAFE_FREE(groups);
160                 return -1;
161         }
162
163         (*p_ngroups) = ngroups;
164         (*p_groups) = groups;
165
166         DEBUG( 3, ( "get_current_groups: uid %u is in %u groups: ", (unsigned int)getuid() , ngroups ) );
167         for (i = 0; i < ngroups; i++ ) {
168                 DEBUG( 3, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
169         }
170         DEBUG( 3, ( "\n" ) );
171
172     return ngroups;
173 }
174
175 /****************************************************************************
176  Delete a SID token.
177 ****************************************************************************/
178
179 void delete_nt_token(NT_USER_TOKEN **pptoken)
180 {
181     if (*pptoken) {
182                 NT_USER_TOKEN *ptoken = *pptoken;
183         SAFE_FREE( ptoken->user_sids );
184         ZERO_STRUCTP(ptoken);
185     }
186     SAFE_FREE(*pptoken);
187 }
188
189 /****************************************************************************
190  Duplicate a SID token.
191 ****************************************************************************/
192
193 NT_USER_TOKEN *dup_nt_token(NT_USER_TOKEN *ptoken)
194 {
195         NT_USER_TOKEN *token;
196
197         if (!ptoken)
198                 return NULL;
199
200     if ((token = (NT_USER_TOKEN *)malloc( sizeof(NT_USER_TOKEN) ) ) == NULL)
201         return NULL;
202
203     ZERO_STRUCTP(token);
204
205     if ((token->user_sids = (DOM_SID *)memdup( ptoken->user_sids, sizeof(DOM_SID) * ptoken->num_sids )) == NULL) {
206         SAFE_FREE(token);
207         return NULL;
208     }
209
210     token->num_sids = ptoken->num_sids;
211
212         return token;
213 }
214
215 /****************************************************************************
216  Initialize the groups a user belongs to.
217 ****************************************************************************/
218
219 BOOL initialise_groups(char *user, uid_t uid, gid_t gid)
220 {
221         struct sec_ctx *prev_ctx_p;
222         BOOL result = True;
223
224         if (non_root_mode()) {
225                 return True;
226         }
227
228         become_root();
229
230         /* Call initgroups() to get user groups */
231
232         if (winbind_initgroups(user,gid) == -1) {
233                 DEBUG(0,("Unable to initgroups. Error was %s\n", strerror(errno) ));
234                 if (getuid() == 0) {
235                         if (gid < 0 || gid > 32767 || uid < 0 || uid > 32767) {
236                                 DEBUG(0,("This is probably a problem with the account %s\n", user));
237                         }
238                 }
239                 result = False;
240                 goto done;
241         }
242
243         /* Store groups in previous user's security context.  This will
244            always work as the become_root() call increments the stack
245            pointer. */
246
247         prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx - 1];
248
249         SAFE_FREE(prev_ctx_p->groups);
250         prev_ctx_p->ngroups = 0;
251
252         get_current_groups(&prev_ctx_p->ngroups, &prev_ctx_p->groups);
253
254  done:
255         unbecome_root();
256
257         return result;
258 }
259
260 /****************************************************************************
261  Create a new security context on the stack.  It is the same as the old
262  one.  User changes are done using the set_sec_ctx() function.
263 ****************************************************************************/
264
265 BOOL push_sec_ctx(void)
266 {
267         struct sec_ctx *ctx_p;
268
269         /* Check we don't overflow our stack */
270
271         if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
272                 DEBUG(0, ("Security context stack overflow!\n"));
273                 smb_panic("Security context stack overflow!\n");
274         }
275
276         /* Store previous user context */
277
278         sec_ctx_stack_ndx++;
279
280         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
281
282         ctx_p->uid = geteuid();
283         ctx_p->gid = getegid();
284
285         DEBUG(3, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n", 
286                   (unsigned int)ctx_p->uid, (unsigned int)ctx_p->gid, sec_ctx_stack_ndx ));
287
288         ctx_p->token = dup_nt_token(sec_ctx_stack[sec_ctx_stack_ndx-1].token);
289
290         ctx_p->ngroups = sys_getgroups(0, NULL);
291
292         if (ctx_p->ngroups != 0) {
293                 if (!(ctx_p->groups = malloc(ctx_p->ngroups * sizeof(gid_t)))) {
294                         DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
295                         delete_nt_token(&ctx_p->token);
296                         return False;
297                 }
298
299                 sys_getgroups(ctx_p->ngroups, ctx_p->groups);
300         } else {
301                 ctx_p->groups = NULL;
302         }
303
304         return True;
305 }
306
307 /****************************************************************************
308  Set the current security context to a given user.
309 ****************************************************************************/
310
311 void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token)
312 {
313         struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
314         
315         /* Set the security context */
316
317         DEBUG(3, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
318                 (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
319
320         if (ngroups) {
321                 int i;
322
323                 DEBUG(3, ("%d user groups: \n", ngroups));
324                 for (i = 0; i < ngroups; i++) {
325                         DEBUGADD(3, ("%u ", (unsigned int)groups[i]));
326                 }
327
328                 DEBUG(3, ("\n"));
329         }
330         
331
332         gain_root();
333
334 #ifdef HAVE_SETGROUPS
335         sys_setgroups(ngroups, groups);
336 #endif
337
338         ctx_p->ngroups = ngroups;
339
340         SAFE_FREE(ctx_p->groups);
341         if (token && (token == ctx_p->token))
342                 smb_panic("DUPLICATE_TOKEN");
343
344         delete_nt_token(&ctx_p->token);
345         
346         ctx_p->groups = memdup(groups, sizeof(gid_t) * ngroups);
347         ctx_p->token = dup_nt_token(token);
348
349         become_id(uid, gid);
350
351         ctx_p->uid = uid;
352         ctx_p->gid = gid;
353
354         /* Update current_user stuff */
355
356         current_user.uid = uid;
357         current_user.gid = gid;
358         current_user.ngroups = ngroups;
359         current_user.groups = groups;
360         current_user.nt_user_token = ctx_p->token;
361 }
362
363 /****************************************************************************
364  Become root context.
365 ****************************************************************************/
366
367 void set_root_sec_ctx(void)
368 {
369         /* May need to worry about supplementary groups at some stage */
370
371         set_sec_ctx(0, 0, 0, NULL, NULL);
372 }
373
374 /****************************************************************************
375  Pop a security context from the stack.
376 ****************************************************************************/
377
378 BOOL pop_sec_ctx(void)
379 {
380         struct sec_ctx *ctx_p;
381         struct sec_ctx *prev_ctx_p;
382
383         /* Check for stack underflow */
384
385         if (sec_ctx_stack_ndx == 0) {
386                 DEBUG(0, ("Security context stack underflow!\n"));
387                 smb_panic("Security context stack underflow!\n");
388         }
389
390         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
391
392         /* Clear previous user info */
393
394         ctx_p->uid = (uid_t)-1;
395         ctx_p->gid = (gid_t)-1;
396
397         SAFE_FREE(ctx_p->groups);
398         ctx_p->ngroups = 0;
399
400         delete_nt_token(&ctx_p->token);
401
402         /* Pop back previous user */
403
404         sec_ctx_stack_ndx--;
405
406         gain_root();
407
408         prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
409
410 #ifdef HAVE_SETGROUPS
411         sys_setgroups(prev_ctx_p->ngroups, prev_ctx_p->groups);
412 #endif
413
414         become_id(prev_ctx_p->uid, prev_ctx_p->gid);
415
416         /* Update current_user stuff */
417
418         current_user.uid = prev_ctx_p->uid;
419         current_user.gid = prev_ctx_p->gid;
420         current_user.ngroups = prev_ctx_p->ngroups;
421         current_user.groups = prev_ctx_p->groups;
422         current_user.nt_user_token = prev_ctx_p->token;
423
424         DEBUG(3, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
425                 (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
426
427         return True;
428 }
429
430 /* Initialise the security context system */
431
432 void init_sec_ctx(void)
433 {
434         int i;
435         struct sec_ctx *ctx_p;
436
437         /* Initialise security context stack */
438
439         memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH);
440
441         for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
442                 sec_ctx_stack[i].uid = (uid_t)-1;
443                 sec_ctx_stack[i].gid = (gid_t)-1;
444         }
445
446         /* Initialise first level of stack.  It is the current context */
447         ctx_p = &sec_ctx_stack[0];
448
449         ctx_p->uid = geteuid();
450         ctx_p->gid = getegid();
451
452         get_current_groups(&ctx_p->ngroups, &ctx_p->groups);
453
454         ctx_p->token = NULL; /* Maps to guest user. */
455
456         /* Initialise current_user global */
457
458         current_user.uid = ctx_p->uid;
459         current_user.gid = ctx_p->gid;
460         current_user.ngroups = ctx_p->ngroups;
461         current_user.groups = ctx_p->groups;
462
463         /* The conn and vuid are usually taken care of by other modules.
464            We initialise them here. */
465
466         current_user.conn = NULL;
467         current_user.vuid = UID_FIELD_INVALID;
468         current_user.nt_user_token = NULL;
469 }