r23398: Support membership of >16 groups on Darwin by making sure we opt in to the
[tprouty/samba.git] / source / smbd / uid.c
1 /* 
2    Unix SMB/CIFS implementation.
3    uid/user handling
4    Copyright (C) Andrew Tridgell 1992-1998
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 /* what user is current? */
24 extern struct current_user current_user;
25
26 /****************************************************************************
27  Iterator functions for getting all gid's from current_user.
28 ****************************************************************************/
29
30 gid_t get_current_user_gid_first(int *piterator)
31 {
32         *piterator = 0;
33         return current_user.ut.gid;
34 }
35
36 gid_t get_current_user_gid_next(int *piterator)
37 {
38         gid_t ret;
39
40         if (!current_user.ut.groups || *piterator >= current_user.ut.ngroups) {
41                 return (gid_t)-1;
42         }
43
44         ret = current_user.ut.groups[*piterator];
45         (*piterator) += 1;
46         return ret;
47 }
48
49 /****************************************************************************
50  Become the guest user without changing the security context stack.
51 ****************************************************************************/
52
53 BOOL change_to_guest(void)
54 {
55         static struct passwd *pass=NULL;
56
57         if (!pass) {
58                 /* Don't need to free() this as its stored in a static */
59                 pass = getpwnam_alloc(NULL, lp_guestaccount());
60                 if (!pass)
61                         return(False);
62         }
63         
64 #ifdef AIX
65         /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before 
66            setting IDs */
67         initgroups(pass->pw_name, pass->pw_gid);
68 #endif
69         
70         set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL);
71         
72         current_user.conn = NULL;
73         current_user.vuid = UID_FIELD_INVALID;
74
75         TALLOC_FREE(pass);
76         pass = NULL;
77         
78         return True;
79 }
80
81 /*******************************************************************
82  Check if a username is OK.
83 ********************************************************************/
84
85 static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
86 {
87         unsigned int i;
88         struct vuid_cache_entry *ent = NULL;
89         BOOL readonly_share;
90         NT_USER_TOKEN *token;
91
92         for (i=0;i<conn->vuid_cache.entries && i< VUID_CACHE_SIZE;i++) {
93                 if (conn->vuid_cache.array[i].vuid == vuser->vuid) {
94                         ent = &conn->vuid_cache.array[i];
95                         conn->read_only = ent->read_only;
96                         conn->admin_user = ent->admin_user;
97                         return(True);
98                 }
99         }
100
101         if (!user_ok_token(vuser->user.unix_name, vuser->nt_user_token, snum))
102                 return(False);
103
104         readonly_share = is_share_read_only_for_token(vuser->user.unix_name,
105                                                       vuser->nt_user_token,
106                                                       SNUM(conn));
107
108         token = conn->nt_user_token ?
109                 conn->nt_user_token : vuser->nt_user_token;
110
111         if (!readonly_share &&
112             !share_access_check(token, lp_servicename(snum),
113                                 FILE_WRITE_DATA)) {
114                 /* smb.conf allows r/w, but the security descriptor denies
115                  * write. Fall back to looking at readonly. */
116                 readonly_share = True;
117                 DEBUG(5,("falling back to read-only access-evaluation due to "
118                          "security descriptor\n"));
119         }
120
121         if (!share_access_check(token, lp_servicename(snum),
122                                 readonly_share ?
123                                 FILE_READ_DATA : FILE_WRITE_DATA)) {
124                 return False;
125         }
126
127         i = conn->vuid_cache.entries % VUID_CACHE_SIZE;
128         if (conn->vuid_cache.entries < VUID_CACHE_SIZE)
129                 conn->vuid_cache.entries++;
130
131         ent = &conn->vuid_cache.array[i];
132         ent->vuid = vuser->vuid;
133         ent->read_only = readonly_share;
134
135         ent->admin_user = token_contains_name_in_list(
136                 vuser->user.unix_name, NULL, vuser->nt_user_token,
137                 lp_admin_users(SNUM(conn)));
138
139         conn->read_only = ent->read_only;
140         conn->admin_user = ent->admin_user;
141
142         return(True);
143 }
144
145 /****************************************************************************
146  Become the user of a connection number without changing the security context
147  stack, but modify the current_user entries.
148 ****************************************************************************/
149
150 BOOL change_to_user(connection_struct *conn, uint16 vuid)
151 {
152         user_struct *vuser = get_valid_user_struct(vuid);
153         int snum;
154         gid_t gid;
155         uid_t uid;
156         char group_c;
157         BOOL must_free_token = False;
158         NT_USER_TOKEN *token = NULL;
159         int num_groups = 0;
160         gid_t *group_list = NULL;
161         
162         if (!conn) {
163                 DEBUG(2,("change_to_user: Connection not open\n"));
164                 return(False);
165         }
166
167         /*
168          * We need a separate check in security=share mode due to vuid
169          * always being UID_FIELD_INVALID. If we don't do this then
170          * in share mode security we are *always* changing uid's between
171          * SMB's - this hurts performance - Badly.
172          */
173
174         if((lp_security() == SEC_SHARE) && (current_user.conn == conn) &&
175            (current_user.ut.uid == conn->uid)) {
176                 DEBUG(4,("change_to_user: Skipping user change - already "
177                          "user\n"));
178                 return(True);
179         } else if ((current_user.conn == conn) && 
180                    (vuser != 0) && (current_user.vuid == vuid) && 
181                    (current_user.ut.uid == vuser->uid)) {
182                 DEBUG(4,("change_to_user: Skipping user change - already "
183                          "user\n"));
184                 return(True);
185         }
186
187         snum = SNUM(conn);
188
189         if ((vuser) && !check_user_ok(conn, vuser, snum)) {
190                 DEBUG(2,("change_to_user: SMB user %s (unix user %s, vuid %d) "
191                          "not permitted access to share %s.\n",
192                          vuser->user.smb_name, vuser->user.unix_name, vuid,
193                          lp_servicename(snum)));
194                 return False;
195         }
196
197         if (conn->force_user) /* security = share sets this too */ {
198                 uid = conn->uid;
199                 gid = conn->gid;
200                 group_list = conn->groups;
201                 num_groups = conn->ngroups;
202                 token = conn->nt_user_token;
203         } else if (vuser) {
204                 uid = conn->admin_user ? 0 : vuser->uid;
205                 gid = vuser->gid;
206                 num_groups = vuser->n_groups;
207                 group_list  = vuser->groups;
208                 token = vuser->nt_user_token;
209         } else {
210                 DEBUG(2,("change_to_user: Invalid vuid used %d in accessing "
211                          "share %s.\n",vuid, lp_servicename(snum) ));
212                 return False;
213         }
214
215         /*
216          * See if we should force group for this service.
217          * If so this overrides any group set in the force
218          * user code.
219          */
220
221         if((group_c = *lp_force_group(snum))) {
222
223                 token = dup_nt_token(NULL, token);
224                 if (token == NULL) {
225                         DEBUG(0, ("dup_nt_token failed\n"));
226                         return False;
227                 }
228                 must_free_token = True;
229
230                 if(group_c == '+') {
231
232                         /*
233                          * Only force group if the user is a member of
234                          * the service group. Check the group memberships for
235                          * this user (we already have this) to
236                          * see if we should force the group.
237                          */
238
239                         int i;
240                         for (i = 0; i < num_groups; i++) {
241                                 if (group_list[i] == conn->gid) {
242                                         gid = conn->gid;
243                                         gid_to_sid(&token->user_sids[1], gid);
244                                         break;
245                                 }
246                         }
247                 } else {
248                         gid = conn->gid;
249                         gid_to_sid(&token->user_sids[1], gid);
250                 }
251         }
252         
253         /* Now set current_user since we will immediately also call
254            set_sec_ctx() */
255
256         current_user.ut.ngroups = num_groups;
257         current_user.ut.groups  = group_list;   
258
259         set_sec_ctx(uid, gid, current_user.ut.ngroups, current_user.ut.groups,
260                     token);
261
262         /*
263          * Free the new token (as set_sec_ctx copies it).
264          */
265
266         if (must_free_token)
267                 TALLOC_FREE(token);
268
269         current_user.conn = conn;
270         current_user.vuid = vuid;
271
272         DEBUG(5,("change_to_user uid=(%d,%d) gid=(%d,%d)\n",
273                  (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
274   
275         return(True);
276 }
277
278 /****************************************************************************
279  Go back to being root without changing the security context stack,
280  but modify the current_user entries.
281 ****************************************************************************/
282
283 BOOL change_to_root_user(void)
284 {
285         set_root_sec_ctx();
286
287         DEBUG(5,("change_to_root_user: now uid=(%d,%d) gid=(%d,%d)\n",
288                 (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
289
290         current_user.conn = NULL;
291         current_user.vuid = UID_FIELD_INVALID;
292
293         return(True);
294 }
295
296 /****************************************************************************
297  Become the user of an authenticated connected named pipe.
298  When this is called we are currently running as the connection
299  user. Doesn't modify current_user.
300 ****************************************************************************/
301
302 BOOL become_authenticated_pipe_user(pipes_struct *p)
303 {
304         if (!push_sec_ctx())
305                 return False;
306
307         set_sec_ctx(p->pipe_user.ut.uid, p->pipe_user.ut.gid, 
308                     p->pipe_user.ut.ngroups, p->pipe_user.ut.groups,
309                     p->pipe_user.nt_user_token);
310
311         return True;
312 }
313
314 /****************************************************************************
315  Unbecome the user of an authenticated connected named pipe.
316  When this is called we are running as the authenticated pipe
317  user and need to go back to being the connection user. Doesn't modify
318  current_user.
319 ****************************************************************************/
320
321 BOOL unbecome_authenticated_pipe_user(void)
322 {
323         return pop_sec_ctx();
324 }
325
326 /****************************************************************************
327  Utility functions used by become_xxx/unbecome_xxx.
328 ****************************************************************************/
329
330 struct conn_ctx {
331         connection_struct *conn;
332         uint16 vuid;
333 };
334  
335 /* A stack of current_user connection contexts. */
336  
337 static struct conn_ctx conn_ctx_stack[MAX_SEC_CTX_DEPTH];
338 static int conn_ctx_stack_ndx;
339
340 static void push_conn_ctx(void)
341 {
342         struct conn_ctx *ctx_p;
343  
344         /* Check we don't overflow our stack */
345  
346         if (conn_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
347                 DEBUG(0, ("Connection context stack overflow!\n"));
348                 smb_panic("Connection context stack overflow!\n");
349         }
350  
351         /* Store previous user context */
352         ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
353  
354         ctx_p->conn = current_user.conn;
355         ctx_p->vuid = current_user.vuid;
356  
357         DEBUG(3, ("push_conn_ctx(%u) : conn_ctx_stack_ndx = %d\n",
358                 (unsigned int)ctx_p->vuid, conn_ctx_stack_ndx ));
359
360         conn_ctx_stack_ndx++;
361 }
362
363 static void pop_conn_ctx(void)
364 {
365         struct conn_ctx *ctx_p;
366  
367         /* Check for stack underflow. */
368
369         if (conn_ctx_stack_ndx == 0) {
370                 DEBUG(0, ("Connection context stack underflow!\n"));
371                 smb_panic("Connection context stack underflow!\n");
372         }
373
374         conn_ctx_stack_ndx--;
375         ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
376
377         current_user.conn = ctx_p->conn;
378         current_user.vuid = ctx_p->vuid;
379
380         ctx_p->conn = NULL;
381         ctx_p->vuid = UID_FIELD_INVALID;
382 }
383
384 /****************************************************************************
385  Temporarily become a root user.  Must match with unbecome_root(). Saves and
386  restores the connection context.
387 ****************************************************************************/
388
389 void become_root(void)
390 {
391         push_sec_ctx();
392         push_conn_ctx();
393         set_root_sec_ctx();
394 }
395
396 /* Unbecome the root user */
397
398 void unbecome_root(void)
399 {
400         pop_sec_ctx();
401         pop_conn_ctx();
402 }
403
404 /****************************************************************************
405  Push the current security context then force a change via change_to_user().
406  Saves and restores the connection context.
407 ****************************************************************************/
408
409 BOOL become_user(connection_struct *conn, uint16 vuid)
410 {
411         if (!push_sec_ctx())
412                 return False;
413
414         push_conn_ctx();
415
416         if (!change_to_user(conn, vuid)) {
417                 pop_sec_ctx();
418                 pop_conn_ctx();
419                 return False;
420         }
421
422         return True;
423 }
424
425 BOOL unbecome_user(void)
426 {
427         pop_sec_ctx();
428         pop_conn_ctx();
429         return True;
430 }