~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source3/smbd/sec_ctx.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

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