~ubuntu-branches/ubuntu/oneiric/likewise-open/oneiric

« back to all changes in this revision

Viewing changes to winbindd/source/smbd/sec_ctx.c

  • Committer: Bazaar Package Importer
  • Author(s): Rick Clark
  • Date: 2008-02-14 13:53:05 UTC
  • Revision ID: james.westby@ubuntu.com-20080214135305-cf45nar4lz1wgpz3
Tags: upstream-4.0.4
ImportĀ upstreamĀ versionĀ 4.0.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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, int *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
}