Wrap the unix token info in a unix_user_token in auth_serversupplied_info
[kai/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 3 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, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21
22 extern struct current_user current_user;
23
24 struct sec_ctx {
25         UNIX_USER_TOKEN ut;
26         NT_USER_TOKEN *token;
27 };
28
29 /* A stack of security contexts.  We include the current context as being
30    the first one, so there is room for another MAX_SEC_CTX_DEPTH more. */
31
32 static struct sec_ctx sec_ctx_stack[MAX_SEC_CTX_DEPTH + 1];
33 static int sec_ctx_stack_ndx;
34
35 /****************************************************************************
36  Are two UNIX tokens equal ?
37 ****************************************************************************/
38
39 bool unix_token_equal(const UNIX_USER_TOKEN *t1, const UNIX_USER_TOKEN *t2)
40 {
41         if (t1->uid != t2->uid || t1->gid != t2->gid ||
42                         t1->ngroups != t2->ngroups) {
43                 return false;
44         }
45         if (memcmp(t1->groups, t2->groups,
46                         t1->ngroups*sizeof(gid_t)) != 0) {
47                 return false;
48         }
49         return true;
50 }
51
52 /****************************************************************************
53  Become the specified uid.
54 ****************************************************************************/
55
56 static bool become_uid(uid_t uid)
57 {
58         /* Check for dodgy uid values */
59
60         if (uid == (uid_t)-1 || 
61             ((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) {
62                 static int done;
63  
64                 if (!done) {
65                         DEBUG(1,("WARNING: using uid %d is a security risk\n",
66                                  (int)uid));
67                         done = 1;
68                 }
69         }
70
71         /* Set effective user id */
72
73         set_effective_uid(uid);
74
75         DO_PROFILE_INC(uid_changes);
76         return True;
77 }
78
79 /****************************************************************************
80  Become the specified gid.
81 ****************************************************************************/
82
83 static bool become_gid(gid_t gid)
84 {
85         /* Check for dodgy gid values */
86
87         if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) && 
88                                  (gid == (gid_t)65535))) {
89                 static int done;
90                 
91                 if (!done) {
92                         DEBUG(1,("WARNING: using gid %d is a security risk\n",
93                                  (int)gid));  
94                         done = 1;
95                 }
96         }
97   
98         /* Set effective group id */
99
100         set_effective_gid(gid);
101         return True;
102 }
103
104 /****************************************************************************
105  Become the specified uid and gid.
106 ****************************************************************************/
107
108 static bool become_id(uid_t uid, gid_t gid)
109 {
110         return become_gid(gid) && become_uid(uid);
111 }
112
113 /****************************************************************************
114  Drop back to root privileges in order to change to another user.
115 ****************************************************************************/
116
117 static void gain_root(void)
118 {
119         if (non_root_mode()) {
120                 return;
121         }
122
123         if (geteuid() != 0) {
124                 set_effective_uid(0);
125
126                 if (geteuid() != 0) {
127                         DEBUG(0,
128                               ("Warning: You appear to have a trapdoor "
129                                "uid system\n"));
130                 }
131         }
132
133         if (getegid() != 0) {
134                 set_effective_gid(0);
135
136                 if (getegid() != 0) {
137                         DEBUG(0,
138                               ("Warning: You appear to have a trapdoor "
139                                "gid system\n"));
140                 }
141         }
142 }
143
144 /****************************************************************************
145  Get the list of current groups.
146 ****************************************************************************/
147
148 static int get_current_groups(gid_t gid, size_t *p_ngroups, gid_t **p_groups)
149 {
150         int i;
151         gid_t grp;
152         int ngroups;
153         gid_t *groups = NULL;
154
155         (*p_ngroups) = 0;
156         (*p_groups) = NULL;
157
158         /* this looks a little strange, but is needed to cope with
159            systems that put the current egid in the group list
160            returned from getgroups() (tridge) */
161         save_re_gid();
162         set_effective_gid(gid);
163         setgid(gid);
164
165         ngroups = sys_getgroups(0,&grp);
166         if (ngroups <= 0) {
167                 goto fail;
168         }
169
170         if((groups = SMB_MALLOC_ARRAY(gid_t, ngroups+1)) == NULL) {
171                 DEBUG(0,("setup_groups malloc fail !\n"));
172                 goto fail;
173         }
174
175         if ((ngroups = sys_getgroups(ngroups,groups)) == -1) {
176                 goto fail;
177         }
178
179         restore_re_gid();
180
181         (*p_ngroups) = ngroups;
182         (*p_groups) = groups;
183
184         DEBUG( 3, ( "get_current_groups: user is in %u groups: ", ngroups));
185         for (i = 0; i < ngroups; i++ ) {
186                 DEBUG( 3, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
187         }
188         DEBUG( 3, ( "\n" ) );
189
190         return ngroups;
191
192 fail:
193         SAFE_FREE(groups);
194         restore_re_gid();
195         return -1;
196 }
197
198 /****************************************************************************
199  Create a new security context on the stack.  It is the same as the old
200  one.  User changes are done using the set_sec_ctx() function.
201 ****************************************************************************/
202
203 bool push_sec_ctx(void)
204 {
205         struct sec_ctx *ctx_p;
206
207         /* Check we don't overflow our stack */
208
209         if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
210                 DEBUG(0, ("Security context stack overflow!\n"));
211                 smb_panic("Security context stack overflow!");
212         }
213
214         /* Store previous user context */
215
216         sec_ctx_stack_ndx++;
217
218         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
219
220         ctx_p->ut.uid = geteuid();
221         ctx_p->ut.gid = getegid();
222
223         DEBUG(3, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n", 
224                   (unsigned int)ctx_p->ut.uid, (unsigned int)ctx_p->ut.gid, sec_ctx_stack_ndx ));
225
226         ctx_p->token = dup_nt_token(NULL,
227                                     sec_ctx_stack[sec_ctx_stack_ndx-1].token);
228
229         ctx_p->ut.ngroups = sys_getgroups(0, NULL);
230
231         if (ctx_p->ut.ngroups != 0) {
232                 if (!(ctx_p->ut.groups = SMB_MALLOC_ARRAY(gid_t, ctx_p->ut.ngroups))) {
233                         DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
234                         TALLOC_FREE(ctx_p->token);
235                         return False;
236                 }
237
238                 sys_getgroups(ctx_p->ut.ngroups, ctx_p->ut.groups);
239         } else {
240                 ctx_p->ut.groups = NULL;
241         }
242
243         return True;
244 }
245
246 /****************************************************************************
247  Change UNIX security context. Calls panic if not successful so no return value.
248 ****************************************************************************/
249
250 #ifndef HAVE_DARWIN_INITGROUPS
251
252 /* Normal credential switch path. */
253
254 static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
255 {
256         /* Start context switch */
257         gain_root();
258 #ifdef HAVE_SETGROUPS
259         if (sys_setgroups(gid, ngroups, groups) != 0 && !non_root_mode()) {
260                 smb_panic("sys_setgroups failed");
261         }
262 #endif
263         become_id(uid, gid);
264         /* end context switch */
265 }
266
267 #else /* HAVE_DARWIN_INITGROUPS */
268
269 /* The Darwin groups implementation is a little unusual. The list of
270 * groups in the kernel credential is not exhaustive, but more like
271 * a cache. The full group list is held in userspace and checked
272 * dynamically.
273 *
274 * This is an optional mechanism, and setgroups(2) opts out
275 * of it. That is, if you call setgroups, then the list of groups you
276 * set are the only groups that are ever checked. This is not what we
277 * want. We want to opt in to the dynamic resolution mechanism, so we
278 * need to specify the uid of the user whose group list (cache) we are
279 * setting.
280 *
281 * The Darwin rules are:
282 *  1. Thou shalt setegid, initgroups and seteuid IN THAT ORDER
283 *  2. Thou shalt not pass more that NGROUPS_MAX to initgroups
284 *  3. Thou shalt leave the first entry in the groups list well alone
285 */
286
287 #include <sys/syscall.h>
288
289 static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
290 {
291         int max = groups_max();
292
293         /* Start context switch */
294         gain_root();
295
296         become_gid(gid);
297
298
299         if (syscall(SYS_initgroups, (ngroups > max) ? max : ngroups,
300                         groups, uid) == -1 && !non_root_mode()) {
301                 DEBUG(0, ("WARNING: failed to set group list "
302                         "(%d groups) for UID %ld: %s\n",
303                         ngroups, uid, strerror(errno)));
304                 smb_panic("sys_setgroups failed");
305         }
306
307         become_uid(uid);
308         /* end context switch */
309 }
310
311 #endif /* HAVE_DARWIN_INITGROUPS */
312
313 /****************************************************************************
314  Set the current security context to a given user.
315 ****************************************************************************/
316
317 void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token)
318 {
319         struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
320         
321         /* Set the security context */
322
323         DEBUG(3, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
324                 (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
325
326         debug_nt_user_token(DBGC_CLASS, 5, token);
327         debug_unix_user_token(DBGC_CLASS, 5, uid, gid, ngroups, groups);
328
329         /* Change uid, gid and supplementary group list. */
330         set_unix_security_ctx(uid, gid, ngroups, groups);
331
332         ctx_p->ut.ngroups = ngroups;
333
334         SAFE_FREE(ctx_p->ut.groups);
335         if (token && (token == ctx_p->token)) {
336                 smb_panic("DUPLICATE_TOKEN");
337         }
338
339         TALLOC_FREE(ctx_p->token);
340         
341         if (ngroups) {
342                 ctx_p->ut.groups = (gid_t *)memdup(groups,
343                                                    sizeof(gid_t) * ngroups);
344                 if (!ctx_p->ut.groups) {
345                         smb_panic("memdup failed");
346                 }
347         } else {
348                 ctx_p->ut.groups = NULL;
349         }
350
351         if (token) {
352                 ctx_p->token = dup_nt_token(NULL, token);
353                 if (!ctx_p->token) {
354                         smb_panic("dup_nt_token failed");
355                 }
356         } else {
357                 ctx_p->token = NULL;
358         }
359
360         ctx_p->ut.uid = uid;
361         ctx_p->ut.gid = gid;
362
363         /* Update current_user stuff */
364
365         current_user.ut.uid = uid;
366         current_user.ut.gid = gid;
367         current_user.ut.ngroups = ngroups;
368         current_user.ut.groups = groups;
369         current_user.nt_user_token = ctx_p->token;
370 }
371
372 /****************************************************************************
373  Become root context.
374 ****************************************************************************/
375
376 void set_root_sec_ctx(void)
377 {
378         /* May need to worry about supplementary groups at some stage */
379
380         set_sec_ctx(0, 0, 0, NULL, NULL);
381 }
382
383 /****************************************************************************
384  Pop a security context from the stack.
385 ****************************************************************************/
386
387 bool pop_sec_ctx(void)
388 {
389         struct sec_ctx *ctx_p;
390         struct sec_ctx *prev_ctx_p;
391
392         /* Check for stack underflow */
393
394         if (sec_ctx_stack_ndx == 0) {
395                 DEBUG(0, ("Security context stack underflow!\n"));
396                 smb_panic("Security context stack underflow!");
397         }
398
399         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
400
401         /* Clear previous user info */
402
403         ctx_p->ut.uid = (uid_t)-1;
404         ctx_p->ut.gid = (gid_t)-1;
405
406         SAFE_FREE(ctx_p->ut.groups);
407         ctx_p->ut.ngroups = 0;
408
409         TALLOC_FREE(ctx_p->token);
410
411         /* Pop back previous user */
412
413         sec_ctx_stack_ndx--;
414
415         prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
416
417         /* Change uid, gid and supplementary group list. */
418         set_unix_security_ctx(prev_ctx_p->ut.uid,
419                         prev_ctx_p->ut.gid,
420                         prev_ctx_p->ut.ngroups,
421                         prev_ctx_p->ut.groups);
422
423         /* Update current_user stuff */
424
425         current_user.ut.uid = prev_ctx_p->ut.uid;
426         current_user.ut.gid = prev_ctx_p->ut.gid;
427         current_user.ut.ngroups = prev_ctx_p->ut.ngroups;
428         current_user.ut.groups = prev_ctx_p->ut.groups;
429         current_user.nt_user_token = prev_ctx_p->token;
430
431         DEBUG(3, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
432                 (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
433
434         return True;
435 }
436
437 /* Initialise the security context system */
438
439 void init_sec_ctx(void)
440 {
441         int i;
442         struct sec_ctx *ctx_p;
443
444         /* Initialise security context stack */
445
446         memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH);
447
448         for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
449                 sec_ctx_stack[i].ut.uid = (uid_t)-1;
450                 sec_ctx_stack[i].ut.gid = (gid_t)-1;
451         }
452
453         /* Initialise first level of stack.  It is the current context */
454         ctx_p = &sec_ctx_stack[0];
455
456         ctx_p->ut.uid = geteuid();
457         ctx_p->ut.gid = getegid();
458
459         get_current_groups(ctx_p->ut.gid, &ctx_p->ut.ngroups, &ctx_p->ut.groups);
460
461         ctx_p->token = NULL; /* Maps to guest user. */
462
463         /* Initialise current_user global */
464
465         current_user.ut.uid = ctx_p->ut.uid;
466         current_user.ut.gid = ctx_p->ut.gid;
467         current_user.ut.ngroups = ctx_p->ut.ngroups;
468         current_user.ut.groups = ctx_p->ut.groups;
469
470         /* The conn and vuid are usually taken care of by other modules.
471            We initialise them here. */
472
473         current_user.conn = NULL;
474         current_user.vuid = UID_FIELD_INVALID;
475         current_user.nt_user_token = NULL;
476 }