Initial import
[samba] / source / 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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 extern struct current_user current_user;
24
25 struct sec_ctx {
26         uid_t uid;
27         uid_t gid;
28         int ngroups;
29         gid_t *groups;
30         NT_USER_TOKEN *token;
31 };
32
33 /* A stack of security contexts.  We include the current context as being
34    the first one, so there is room for another MAX_SEC_CTX_DEPTH more. */
35
36 static struct sec_ctx sec_ctx_stack[MAX_SEC_CTX_DEPTH + 1];
37 static int sec_ctx_stack_ndx;
38
39 /****************************************************************************
40  Become the specified uid.
41 ****************************************************************************/
42
43 static BOOL become_uid(uid_t uid)
44 {
45         /* Check for dodgy uid values */
46
47         if (uid == (uid_t)-1 || 
48             ((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) {
49                 static int done;
50  
51                 if (!done) {
52                         DEBUG(1,("WARNING: using uid %d is a security risk\n",
53                                  (int)uid));
54                         done = 1;
55                 }
56         }
57
58         /* Set effective user id */
59
60         set_effective_uid(uid);
61
62         DO_PROFILE_INC(uid_changes);
63         return True;
64 }
65
66 /****************************************************************************
67  Become the specified gid.
68 ****************************************************************************/
69
70 static BOOL become_gid(gid_t gid)
71 {
72         /* Check for dodgy gid values */
73
74         if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) && 
75                                  (gid == (gid_t)65535))) {
76                 static int done;
77                 
78                 if (!done) {
79                         DEBUG(1,("WARNING: using gid %d is a security risk\n",
80                                  (int)gid));  
81                         done = 1;
82                 }
83         }
84   
85         /* Set effective group id */
86
87         set_effective_gid(gid);
88         return True;
89 }
90
91 /****************************************************************************
92  Become the specified uid and gid.
93 ****************************************************************************/
94
95 static BOOL become_id(uid_t uid, gid_t gid)
96 {
97         return become_gid(gid) && become_uid(uid);
98 }
99
100 /****************************************************************************
101  Drop back to root privileges in order to change to another user.
102 ****************************************************************************/
103
104 static void gain_root(void)
105 {
106         if (non_root_mode()) {
107                 return;
108         }
109
110         if (geteuid() != 0) {
111                 set_effective_uid(0);
112
113                 if (geteuid() != 0) {
114                         DEBUG(0,
115                               ("Warning: You appear to have a trapdoor "
116                                "uid system\n"));
117                 }
118         }
119
120         if (getegid() != 0) {
121                 set_effective_gid(0);
122
123                 if (getegid() != 0) {
124                         DEBUG(0,
125                               ("Warning: You appear to have a trapdoor "
126                                "gid system\n"));
127                 }
128         }
129 }
130
131 /****************************************************************************
132  Get the list of current groups.
133 ****************************************************************************/
134
135 int get_current_groups(gid_t gid, int *p_ngroups, gid_t **p_groups)
136 {
137         int i;
138         gid_t grp;
139         int ngroups;
140         gid_t *groups = NULL;
141
142         (*p_ngroups) = 0;
143         (*p_groups) = NULL;
144
145         /* this looks a little strange, but is needed to cope with
146            systems that put the current egid in the group list
147            returned from getgroups() (tridge) */
148         save_re_gid();
149         set_effective_gid(gid);
150         setgid(gid);
151
152         ngroups = sys_getgroups(0,&grp);
153         if (ngroups <= 0) {
154                 goto fail;
155         }
156
157         if((groups = SMB_MALLOC_ARRAY(gid_t, ngroups+1)) == NULL) {
158                 DEBUG(0,("setup_groups malloc fail !\n"));
159                 goto fail;
160         }
161
162         if ((ngroups = sys_getgroups(ngroups,groups)) == -1) {
163                 goto fail;
164         }
165
166         restore_re_gid();
167
168         (*p_ngroups) = ngroups;
169         (*p_groups) = groups;
170
171         DEBUG( 3, ( "get_current_groups: user is in %u groups: ", ngroups));
172         for (i = 0; i < ngroups; i++ ) {
173                 DEBUG( 3, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
174         }
175         DEBUG( 3, ( "\n" ) );
176
177         return ngroups;
178
179 fail:
180         SAFE_FREE(groups);
181         restore_re_gid();
182         return -1;
183 }
184
185 /****************************************************************************
186  Initialize the groups a user belongs to.
187 ****************************************************************************/
188
189 BOOL initialise_groups(char *user, uid_t uid, gid_t gid)
190 {
191         struct sec_ctx *prev_ctx_p;
192         BOOL result = True;
193
194         if (non_root_mode()) {
195                 return True;
196         }
197
198         become_root();
199
200         /* Call initgroups() to get user groups */
201
202         if (winbind_initgroups(user,gid) == -1) {
203                 DEBUG(0,("Unable to initgroups. Error was %s\n", strerror(errno) ));
204                 if (getuid() == 0) {
205                         if (gid < 0 || gid > 32767 || uid < 0 || uid > 32767) {
206                                 DEBUG(0,("This is probably a problem with the account %s\n", user));
207                         }
208                 }
209                 result = False;
210                 goto done;
211         }
212
213         /* Store groups in previous user's security context.  This will
214            always work as the become_root() call increments the stack
215            pointer. */
216
217         prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx - 1];
218
219         SAFE_FREE(prev_ctx_p->groups);
220         prev_ctx_p->ngroups = 0;
221
222         get_current_groups(gid, &prev_ctx_p->ngroups, &prev_ctx_p->groups);
223
224  done:
225         unbecome_root();
226
227         return result;
228 }
229
230 /****************************************************************************
231  Create a new security context on the stack.  It is the same as the old
232  one.  User changes are done using the set_sec_ctx() function.
233 ****************************************************************************/
234
235 BOOL push_sec_ctx(void)
236 {
237         struct sec_ctx *ctx_p;
238
239         /* Check we don't overflow our stack */
240
241         if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
242                 DEBUG(0, ("Security context stack overflow!\n"));
243                 smb_panic("Security context stack overflow!\n");
244         }
245
246         /* Store previous user context */
247
248         sec_ctx_stack_ndx++;
249
250         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
251
252         ctx_p->uid = geteuid();
253         ctx_p->gid = getegid();
254
255         DEBUG(3, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n", 
256                   (unsigned int)ctx_p->uid, (unsigned int)ctx_p->gid, sec_ctx_stack_ndx ));
257
258         ctx_p->token = dup_nt_token(sec_ctx_stack[sec_ctx_stack_ndx-1].token);
259
260         ctx_p->ngroups = sys_getgroups(0, NULL);
261
262         if (ctx_p->ngroups != 0) {
263                 if (!(ctx_p->groups = SMB_MALLOC_ARRAY(gid_t, ctx_p->ngroups))) {
264                         DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
265                         delete_nt_token(&ctx_p->token);
266                         return False;
267                 }
268
269                 sys_getgroups(ctx_p->ngroups, ctx_p->groups);
270         } else {
271                 ctx_p->groups = NULL;
272         }
273
274         return True;
275 }
276
277 /****************************************************************************
278  Set the current security context to a given user.
279 ****************************************************************************/
280
281 void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token)
282 {
283         struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
284         
285         /* Set the security context */
286
287         DEBUG(3, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
288                 (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
289
290         debug_nt_user_token(DBGC_CLASS, 5, token);
291         debug_unix_user_token(DBGC_CLASS, 5, uid, gid, ngroups, groups);
292
293         gain_root();
294
295 #ifdef HAVE_SETGROUPS
296         sys_setgroups(ngroups, groups);
297 #endif
298
299         ctx_p->ngroups = ngroups;
300
301         SAFE_FREE(ctx_p->groups);
302         if (token && (token == ctx_p->token))
303                 smb_panic("DUPLICATE_TOKEN");
304
305         delete_nt_token(&ctx_p->token);
306         
307         ctx_p->groups = memdup(groups, sizeof(gid_t) * ngroups);
308         ctx_p->token = dup_nt_token(token);
309
310         become_id(uid, gid);
311
312         ctx_p->uid = uid;
313         ctx_p->gid = gid;
314
315         /* Update current_user stuff */
316
317         current_user.uid = uid;
318         current_user.gid = gid;
319         current_user.ngroups = ngroups;
320         current_user.groups = groups;
321         current_user.nt_user_token = ctx_p->token;
322 }
323
324 /****************************************************************************
325  Become root context.
326 ****************************************************************************/
327
328 void set_root_sec_ctx(void)
329 {
330         /* May need to worry about supplementary groups at some stage */
331
332         set_sec_ctx(0, 0, 0, NULL, NULL);
333 }
334
335 /****************************************************************************
336  Pop a security context from the stack.
337 ****************************************************************************/
338
339 BOOL pop_sec_ctx(void)
340 {
341         struct sec_ctx *ctx_p;
342         struct sec_ctx *prev_ctx_p;
343
344         /* Check for stack underflow */
345
346         if (sec_ctx_stack_ndx == 0) {
347                 DEBUG(0, ("Security context stack underflow!\n"));
348                 smb_panic("Security context stack underflow!\n");
349         }
350
351         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
352
353         /* Clear previous user info */
354
355         ctx_p->uid = (uid_t)-1;
356         ctx_p->gid = (gid_t)-1;
357
358         SAFE_FREE(ctx_p->groups);
359         ctx_p->ngroups = 0;
360
361         delete_nt_token(&ctx_p->token);
362
363         /* Pop back previous user */
364
365         sec_ctx_stack_ndx--;
366
367         gain_root();
368
369         prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
370
371 #ifdef HAVE_SETGROUPS
372         sys_setgroups(prev_ctx_p->ngroups, prev_ctx_p->groups);
373 #endif
374
375         become_id(prev_ctx_p->uid, prev_ctx_p->gid);
376
377         /* Update current_user stuff */
378
379         current_user.uid = prev_ctx_p->uid;
380         current_user.gid = prev_ctx_p->gid;
381         current_user.ngroups = prev_ctx_p->ngroups;
382         current_user.groups = prev_ctx_p->groups;
383         current_user.nt_user_token = prev_ctx_p->token;
384
385         DEBUG(3, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
386                 (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
387
388         return True;
389 }
390
391 /* Initialise the security context system */
392
393 void init_sec_ctx(void)
394 {
395         int i;
396         struct sec_ctx *ctx_p;
397
398         /* Initialise security context stack */
399
400         memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH);
401
402         for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
403                 sec_ctx_stack[i].uid = (uid_t)-1;
404                 sec_ctx_stack[i].gid = (gid_t)-1;
405         }
406
407         /* Initialise first level of stack.  It is the current context */
408         ctx_p = &sec_ctx_stack[0];
409
410         ctx_p->uid = geteuid();
411         ctx_p->gid = getegid();
412
413         get_current_groups(ctx_p->gid, &ctx_p->ngroups, &ctx_p->groups);
414
415         ctx_p->token = NULL; /* Maps to guest user. */
416
417         /* Initialise current_user global */
418
419         current_user.uid = ctx_p->uid;
420         current_user.gid = ctx_p->gid;
421         current_user.ngroups = ctx_p->ngroups;
422         current_user.groups = ctx_p->groups;
423
424         /* The conn and vuid are usually taken care of by other modules.
425            We initialise them here. */
426
427         current_user.conn = NULL;
428         current_user.vuid = UID_FIELD_INVALID;
429         current_user.nt_user_token = NULL;
430 }