s3: include smbd/smbd.h where needed.
[ira/wip.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 #include "smbd/smbd.h"
22 #include "smbd/globals.h"
23 #include "libcli/security/security_token.h"
24
25 extern struct current_user current_user;
26
27 /****************************************************************************
28  Are two UNIX tokens equal ?
29 ****************************************************************************/
30
31 bool unix_token_equal(const struct security_unix_token *t1, const struct security_unix_token *t2)
32 {
33         if (t1->uid != t2->uid || t1->gid != t2->gid ||
34                         t1->ngroups != t2->ngroups) {
35                 return false;
36         }
37         if (memcmp(t1->groups, t2->groups,
38                         t1->ngroups*sizeof(gid_t)) != 0) {
39                 return false;
40         }
41         return true;
42 }
43
44 /****************************************************************************
45  Become the specified uid.
46 ****************************************************************************/
47
48 static bool become_uid(uid_t uid)
49 {
50         /* Check for dodgy uid values */
51
52         if (uid == (uid_t)-1 || 
53             ((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) {
54                 if (!become_uid_done) {
55                         DEBUG(1,("WARNING: using uid %d is a security risk\n",
56                                  (int)uid));
57                         become_uid_done = true;
58                 }
59         }
60
61         /* Set effective user id */
62
63         set_effective_uid(uid);
64
65         DO_PROFILE_INC(uid_changes);
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                 if (!become_gid_done) {
80                         DEBUG(1,("WARNING: using gid %d is a security risk\n",
81                                  (int)gid));  
82                         become_gid_done = true;
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 static int get_current_groups(gid_t gid, uint32_t *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 = SMB_MALLOC_ARRAY(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( 4, ( "get_current_groups: user is in %u groups: ", ngroups));
173         for (i = 0; i < ngroups; i++ ) {
174                 DEBUG( 4, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
175         }
176         DEBUG( 4, ( "\n" ) );
177
178         return ngroups;
179
180 fail:
181         SAFE_FREE(groups);
182         restore_re_gid();
183         return -1;
184 }
185
186 /****************************************************************************
187  Create a new security context on the stack.  It is the same as the old
188  one.  User changes are done using the set_sec_ctx() function.
189 ****************************************************************************/
190
191 bool push_sec_ctx(void)
192 {
193         struct sec_ctx *ctx_p;
194
195         /* Check we don't overflow our stack */
196
197         if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
198                 DEBUG(0, ("Security context stack overflow!\n"));
199                 smb_panic("Security context stack overflow!");
200         }
201
202         /* Store previous user context */
203
204         sec_ctx_stack_ndx++;
205
206         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
207
208         ctx_p->ut.uid = geteuid();
209         ctx_p->ut.gid = getegid();
210
211         DEBUG(4, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n", 
212                   (unsigned int)ctx_p->ut.uid, (unsigned int)ctx_p->ut.gid, sec_ctx_stack_ndx ));
213
214         ctx_p->token = dup_nt_token(NULL,
215                                     sec_ctx_stack[sec_ctx_stack_ndx-1].token);
216
217         ctx_p->ut.ngroups = sys_getgroups(0, NULL);
218
219         if (ctx_p->ut.ngroups != 0) {
220                 if (!(ctx_p->ut.groups = SMB_MALLOC_ARRAY(gid_t, ctx_p->ut.ngroups))) {
221                         DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
222                         TALLOC_FREE(ctx_p->token);
223                         return False;
224                 }
225
226                 sys_getgroups(ctx_p->ut.ngroups, ctx_p->ut.groups);
227         } else {
228                 ctx_p->ut.groups = NULL;
229         }
230
231         return True;
232 }
233
234 /****************************************************************************
235  Change UNIX security context. Calls panic if not successful so no return value.
236 ****************************************************************************/
237
238 #ifndef HAVE_DARWIN_INITGROUPS
239
240 /* Normal credential switch path. */
241
242 static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
243 {
244         /* Start context switch */
245         gain_root();
246 #ifdef HAVE_SETGROUPS
247         if (sys_setgroups(gid, ngroups, groups) != 0 && !non_root_mode()) {
248                 smb_panic("sys_setgroups failed");
249         }
250 #endif
251         become_id(uid, gid);
252         /* end context switch */
253 }
254
255 #else /* HAVE_DARWIN_INITGROUPS */
256
257 /* The Darwin groups implementation is a little unusual. The list of
258 * groups in the kernel credential is not exhaustive, but more like
259 * a cache. The full group list is held in userspace and checked
260 * dynamically.
261 *
262 * This is an optional mechanism, and setgroups(2) opts out
263 * of it. That is, if you call setgroups, then the list of groups you
264 * set are the only groups that are ever checked. This is not what we
265 * want. We want to opt in to the dynamic resolution mechanism, so we
266 * need to specify the uid of the user whose group list (cache) we are
267 * setting.
268 *
269 * The Darwin rules are:
270 *  1. Thou shalt setegid, initgroups and seteuid IN THAT ORDER
271 *  2. Thou shalt not pass more that NGROUPS_MAX to initgroups
272 *  3. Thou shalt leave the first entry in the groups list well alone
273 */
274
275 #include <sys/syscall.h>
276
277 static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
278 {
279         int max = groups_max();
280
281         /* Start context switch */
282         gain_root();
283
284         become_gid(gid);
285
286
287         if (syscall(SYS_initgroups, (ngroups > max) ? max : ngroups,
288                         groups, uid) == -1 && !non_root_mode()) {
289                 DEBUG(0, ("WARNING: failed to set group list "
290                         "(%d groups) for UID %ld: %s\n",
291                         ngroups, uid, strerror(errno)));
292                 smb_panic("sys_setgroups failed");
293         }
294
295         become_uid(uid);
296         /* end context switch */
297 }
298
299 #endif /* HAVE_DARWIN_INITGROUPS */
300
301 /****************************************************************************
302  Set the current security context to a given user.
303 ****************************************************************************/
304
305 void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, struct security_token *token)
306 {
307         struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
308
309         /* Set the security context */
310
311         DEBUG(4, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
312                 (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
313
314         security_token_debug(DBGC_CLASS, 5, token);
315         debug_unix_user_token(DBGC_CLASS, 5, uid, gid, ngroups, groups);
316
317         /* Change uid, gid and supplementary group list. */
318         set_unix_security_ctx(uid, gid, ngroups, groups);
319
320         ctx_p->ut.ngroups = ngroups;
321
322         SAFE_FREE(ctx_p->ut.groups);
323         if (token && (token == ctx_p->token)) {
324                 smb_panic("DUPLICATE_TOKEN");
325         }
326
327         TALLOC_FREE(ctx_p->token);
328
329         if (ngroups) {
330                 ctx_p->ut.groups = (gid_t *)memdup(groups,
331                                                    sizeof(gid_t) * ngroups);
332                 if (!ctx_p->ut.groups) {
333                         smb_panic("memdup failed");
334                 }
335         } else {
336                 ctx_p->ut.groups = NULL;
337         }
338
339         if (token) {
340                 ctx_p->token = dup_nt_token(NULL, token);
341                 if (!ctx_p->token) {
342                         smb_panic("dup_nt_token failed");
343                 }
344         } else {
345                 ctx_p->token = NULL;
346         }
347
348         ctx_p->ut.uid = uid;
349         ctx_p->ut.gid = gid;
350
351         /* Update current_user stuff */
352
353         current_user.ut.uid = uid;
354         current_user.ut.gid = gid;
355         current_user.ut.ngroups = ngroups;
356         current_user.ut.groups = groups;
357         current_user.nt_user_token = ctx_p->token;
358 }
359
360 /****************************************************************************
361  Become root context.
362 ****************************************************************************/
363
364 void set_root_sec_ctx(void)
365 {
366         /* May need to worry about supplementary groups at some stage */
367
368         set_sec_ctx(0, 0, 0, NULL, NULL);
369 }
370
371 /****************************************************************************
372  Pop a security context from the stack.
373 ****************************************************************************/
374
375 bool pop_sec_ctx(void)
376 {
377         struct sec_ctx *ctx_p;
378         struct sec_ctx *prev_ctx_p;
379
380         /* Check for stack underflow */
381
382         if (sec_ctx_stack_ndx == 0) {
383                 DEBUG(0, ("Security context stack underflow!\n"));
384                 smb_panic("Security context stack underflow!");
385         }
386
387         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
388
389         /* Clear previous user info */
390
391         ctx_p->ut.uid = (uid_t)-1;
392         ctx_p->ut.gid = (gid_t)-1;
393
394         SAFE_FREE(ctx_p->ut.groups);
395         ctx_p->ut.ngroups = 0;
396
397         TALLOC_FREE(ctx_p->token);
398
399         /* Pop back previous user */
400
401         sec_ctx_stack_ndx--;
402
403         prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
404
405         /* Change uid, gid and supplementary group list. */
406         set_unix_security_ctx(prev_ctx_p->ut.uid,
407                         prev_ctx_p->ut.gid,
408                         prev_ctx_p->ut.ngroups,
409                         prev_ctx_p->ut.groups);
410
411         /* Update current_user stuff */
412
413         current_user.ut.uid = prev_ctx_p->ut.uid;
414         current_user.ut.gid = prev_ctx_p->ut.gid;
415         current_user.ut.ngroups = prev_ctx_p->ut.ngroups;
416         current_user.ut.groups = prev_ctx_p->ut.groups;
417         current_user.nt_user_token = prev_ctx_p->token;
418
419         DEBUG(4, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
420                 (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
421
422         return True;
423 }
424
425 /* Initialise the security context system */
426
427 void init_sec_ctx(void)
428 {
429         int i;
430         struct sec_ctx *ctx_p;
431
432         /* Initialise security context stack */
433
434         memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH);
435
436         for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
437                 sec_ctx_stack[i].ut.uid = (uid_t)-1;
438                 sec_ctx_stack[i].ut.gid = (gid_t)-1;
439         }
440
441         /* Initialise first level of stack.  It is the current context */
442         ctx_p = &sec_ctx_stack[0];
443
444         ctx_p->ut.uid = geteuid();
445         ctx_p->ut.gid = getegid();
446
447         get_current_groups(ctx_p->ut.gid, &ctx_p->ut.ngroups, &ctx_p->ut.groups);
448
449         ctx_p->token = NULL; /* Maps to guest user. */
450
451         /* Initialise current_user global */
452
453         current_user.ut.uid = ctx_p->ut.uid;
454         current_user.ut.gid = ctx_p->ut.gid;
455         current_user.ut.ngroups = ctx_p->ut.ngroups;
456         current_user.ut.groups = ctx_p->ut.groups;
457
458         /* The conn and vuid are usually taken care of by other modules.
459            We initialise them here. */
460
461         current_user.conn = NULL;
462         current_user.vuid = UID_FIELD_INVALID;
463         current_user.nt_user_token = NULL;
464 }