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