r12312: Reformatting and a trivial change: is_share_read_only_for_user only uses
[samba.git] / source3 / 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.gid;
34 }
35
36 gid_t get_current_user_gid_next(int *piterator)
37 {
38         gid_t ret;
39
40         if (!current_user.groups || *piterator >= current_user.ngroups) {
41                 return (gid_t)-1;
42         }
43
44         ret = current_user.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(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         passwd_free(&pass);
76
77         return True;
78 }
79
80 /****************************************************************************
81  Readonly share for this user ?
82 ****************************************************************************/
83
84 static BOOL is_share_read_only_for_user(int snum, user_struct *vuser)
85 {
86         char **list;
87         const char *service = lp_servicename(snum);
88         BOOL read_only_ret = lp_readonly(snum);
89
90         if (!service)
91                 return read_only_ret;
92
93         str_list_copy(&list, lp_readlist(snum));
94         if (list) {
95                 if (!str_list_sub_basic(list, vuser->user.smb_name) ) {
96                         DEBUG(0, ("is_share_read_only_for_user: ERROR: read "
97                                   "list substitution failed\n"));
98                 }
99                 if (!str_list_substitute(list, "%S", service)) {
100                         DEBUG(0, ("is_share_read_only_for_user: ERROR: read "
101                                   "list service substitution failed\n"));
102                 }
103                 if (user_in_list(vuser->user.unix_name, (const char **)list,
104                                  vuser->groups, vuser->n_groups)) {
105                         read_only_ret = True;
106                 }
107                 str_list_free(&list);
108         }
109
110         str_list_copy(&list, lp_writelist(snum));
111         if (list) {
112                 if (!str_list_sub_basic(list, vuser->user.smb_name) ) {
113                         DEBUG(0, ("is_share_read_only_for_user: ERROR: write "
114                                   "list substitution failed\n"));
115                 }
116                 if (!str_list_substitute(list, "%S", service)) {
117                         DEBUG(0, ("is_share_read_only_for_user: ERROR: write "
118                                   "list service substitution failed\n"));
119                 }
120                 if (user_in_list(vuser->user.unix_name, (const char **)list,
121                                  vuser->groups, vuser->n_groups)) {
122                         read_only_ret = False;
123                 }
124                 str_list_free(&list);
125         }
126
127         DEBUG(10,("is_share_read_only_for_user: share %s is %s for unix user "
128                   "%s\n", service,
129                   read_only_ret ? "read-only" : "read-write",
130                   vuser->user.unix_name ));
131
132         return read_only_ret;
133 }
134
135 /*******************************************************************
136  Check if a username is OK.
137 ********************************************************************/
138
139 static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
140 {
141         unsigned int i;
142         struct vuid_cache_entry *ent = NULL;
143         BOOL readonly_share;
144
145         for (i=0;i<conn->vuid_cache.entries && i< VUID_CACHE_SIZE;i++) {
146                 if (conn->vuid_cache.array[i].vuid == vuser->vuid) {
147                         ent = &conn->vuid_cache.array[i];
148                         conn->read_only = ent->read_only;
149                         conn->admin_user = ent->admin_user;
150                         return(True);
151                 }
152         }
153
154         if (!user_ok(vuser->user.unix_name,snum, vuser->groups, vuser->n_groups))
155                 return(False);
156
157         readonly_share = is_share_read_only_for_user(conn->service, vuser);
158
159         if (!readonly_share &&
160             !share_access_check(conn, snum, vuser, FILE_WRITE_DATA)) {
161                 /* smb.conf allows r/w, but the security descriptor denies
162                  * write. Fall back to looking at readonly. */
163                 readonly_share = True;
164                 DEBUG(5,("falling back to read-only access-evaluation due to security descriptor\n"));
165         }
166
167         if (!share_access_check(conn, snum, vuser, readonly_share ? FILE_READ_DATA : FILE_WRITE_DATA)) {
168                 return False;
169         }
170
171         i = conn->vuid_cache.entries % VUID_CACHE_SIZE;
172         if (conn->vuid_cache.entries < VUID_CACHE_SIZE)
173                 conn->vuid_cache.entries++;
174
175         ent = &conn->vuid_cache.array[i];
176         ent->vuid = vuser->vuid;
177         ent->read_only = readonly_share;
178
179         if (user_in_list(vuser->user.unix_name ,lp_admin_users(conn->service), vuser->groups, vuser->n_groups)) {
180                 ent->admin_user = True;
181         } else {
182                 ent->admin_user = False;
183         }
184
185         conn->read_only = ent->read_only;
186         conn->admin_user = ent->admin_user;
187
188         return(True);
189 }
190
191 /****************************************************************************
192  Become the user of a connection number without changing the security context
193  stack, but modify the currnet_user entries.
194 ****************************************************************************/
195
196 BOOL change_to_user(connection_struct *conn, uint16 vuid)
197 {
198         user_struct *vuser = get_valid_user_struct(vuid);
199         int snum;
200         gid_t gid;
201         uid_t uid;
202         char group_c;
203         BOOL must_free_token = False;
204         NT_USER_TOKEN *token = NULL;
205
206         if (!conn) {
207                 DEBUG(2,("change_to_user: Connection not open\n"));
208                 return(False);
209         }
210
211         /*
212          * We need a separate check in security=share mode due to vuid
213          * always being UID_FIELD_INVALID. If we don't do this then
214          * in share mode security we are *always* changing uid's between
215          * SMB's - this hurts performance - Badly.
216          */
217
218         if((lp_security() == SEC_SHARE) && (current_user.conn == conn) &&
219            (current_user.uid == conn->uid)) {
220                 DEBUG(4,("change_to_user: Skipping user change - already user\n"));
221                 return(True);
222         } else if ((current_user.conn == conn) && 
223                    (vuser != 0) && (current_user.vuid == vuid) && 
224                    (current_user.uid == vuser->uid)) {
225                 DEBUG(4,("change_to_user: Skipping user change - already user\n"));
226                 return(True);
227         }
228
229         snum = SNUM(conn);
230
231         if ((vuser) && !check_user_ok(conn, vuser, snum)) {
232                 DEBUG(2,("change_to_user: SMB user %s (unix user %s, vuid %d) not permitted access to share %s.\n",
233                         vuser->user.smb_name, vuser->user.unix_name, vuid, lp_servicename(snum)));
234                 return False;
235         }
236
237         if (conn->force_user) /* security = share sets this too */ {
238                 uid = conn->uid;
239                 gid = conn->gid;
240                 current_user.groups = conn->groups;
241                 current_user.ngroups = conn->ngroups;
242                 token = conn->nt_user_token;
243         } else if (vuser) {
244                 uid = conn->admin_user ? 0 : vuser->uid;
245                 gid = vuser->gid;
246                 current_user.ngroups = vuser->n_groups;
247                 current_user.groups  = vuser->groups;
248                 token = vuser->nt_user_token;
249         } else {
250                 DEBUG(2,("change_to_user: Invalid vuid used %d in accessing share %s.\n",vuid, lp_servicename(snum) ));
251                 return False;
252         }
253
254         /*
255          * See if we should force group for this service.
256          * If so this overrides any group set in the force
257          * user code.
258          */
259
260         if((group_c = *lp_force_group(snum))) {
261                 BOOL is_guest = False;
262
263                 if(group_c == '+') {
264
265                         /*
266                          * Only force group if the user is a member of
267                          * the service group. Check the group memberships for
268                          * this user (we already have this) to
269                          * see if we should force the group.
270                          */
271
272                         int i;
273                         for (i = 0; i < current_user.ngroups; i++) {
274                                 if (current_user.groups[i] == conn->gid) {
275                                         gid = conn->gid;
276                                         break;
277                                 }
278                         }
279                 } else {
280                         gid = conn->gid;
281                 }
282
283                 /*
284                  * We've changed the group list in the token - we must
285                  * re-create it.
286                  */
287
288                 if (vuser && vuser->guest)
289                         is_guest = True;
290
291                 token = create_nt_token(uid, gid, current_user.ngroups, current_user.groups, is_guest);
292                 if (!token) {
293                         DEBUG(1, ("change_to_user: create_nt_token failed!\n"));
294                         return False;
295                 }
296                 must_free_token = True;
297         }
298         
299         set_sec_ctx(uid, gid, current_user.ngroups, current_user.groups, token);
300
301         /*
302          * Free the new token (as set_sec_ctx copies it).
303          */
304
305         if (must_free_token)
306                 delete_nt_token(&token);
307
308         current_user.conn = conn;
309         current_user.vuid = vuid;
310
311         DEBUG(5,("change_to_user uid=(%d,%d) gid=(%d,%d)\n",
312                  (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
313   
314         return(True);
315 }
316
317 /****************************************************************************
318  Go back to being root without changing the security context stack,
319  but modify the current_user entries.
320 ****************************************************************************/
321
322 BOOL change_to_root_user(void)
323 {
324         set_root_sec_ctx();
325
326         DEBUG(5,("change_to_root_user: now uid=(%d,%d) gid=(%d,%d)\n",
327                 (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
328
329         current_user.conn = NULL;
330         current_user.vuid = UID_FIELD_INVALID;
331
332         return(True);
333 }
334
335 /****************************************************************************
336  Become the user of an authenticated connected named pipe.
337  When this is called we are currently running as the connection
338  user. Doesn't modify current_user.
339 ****************************************************************************/
340
341 BOOL become_authenticated_pipe_user(pipes_struct *p)
342 {
343         if (!push_sec_ctx())
344                 return False;
345
346         set_sec_ctx(p->pipe_user.uid, p->pipe_user.gid, 
347                     p->pipe_user.ngroups, p->pipe_user.groups, p->pipe_user.nt_user_token);
348
349         return True;
350 }
351
352 /****************************************************************************
353  Unbecome the user of an authenticated connected named pipe.
354  When this is called we are running as the authenticated pipe
355  user and need to go back to being the connection user. Doesn't modify
356  current_user.
357 ****************************************************************************/
358
359 BOOL unbecome_authenticated_pipe_user(void)
360 {
361         return pop_sec_ctx();
362 }
363
364 /****************************************************************************
365  Utility functions used by become_xxx/unbecome_xxx.
366 ****************************************************************************/
367
368 struct conn_ctx {
369         connection_struct *conn;
370         uint16 vuid;
371 };
372  
373 /* A stack of current_user connection contexts. */
374  
375 static struct conn_ctx conn_ctx_stack[MAX_SEC_CTX_DEPTH];
376 static int conn_ctx_stack_ndx;
377
378 static void push_conn_ctx(void)
379 {
380         struct conn_ctx *ctx_p;
381  
382         /* Check we don't overflow our stack */
383  
384         if (conn_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
385                 DEBUG(0, ("Connection context stack overflow!\n"));
386                 smb_panic("Connection context stack overflow!\n");
387         }
388  
389         /* Store previous user context */
390         ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
391  
392         ctx_p->conn = current_user.conn;
393         ctx_p->vuid = current_user.vuid;
394  
395         DEBUG(3, ("push_conn_ctx(%u) : conn_ctx_stack_ndx = %d\n",
396                 (unsigned int)ctx_p->vuid, conn_ctx_stack_ndx ));
397
398         conn_ctx_stack_ndx++;
399 }
400
401 static void pop_conn_ctx(void)
402 {
403         struct conn_ctx *ctx_p;
404  
405         /* Check for stack underflow. */
406
407         if (conn_ctx_stack_ndx == 0) {
408                 DEBUG(0, ("Connection context stack underflow!\n"));
409                 smb_panic("Connection context stack underflow!\n");
410         }
411
412         conn_ctx_stack_ndx--;
413         ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
414
415         current_user.conn = ctx_p->conn;
416         current_user.vuid = ctx_p->vuid;
417
418         ctx_p->conn = NULL;
419         ctx_p->vuid = UID_FIELD_INVALID;
420 }
421
422 /****************************************************************************
423  Temporarily become a root user.  Must match with unbecome_root(). Saves and
424  restores the connection context.
425 ****************************************************************************/
426
427 void become_root(void)
428 {
429         push_sec_ctx();
430         push_conn_ctx();
431         set_root_sec_ctx();
432 }
433
434 /* Unbecome the root user */
435
436 void unbecome_root(void)
437 {
438         pop_sec_ctx();
439         pop_conn_ctx();
440 }
441
442 /****************************************************************************
443  Push the current security context then force a change via change_to_user().
444  Saves and restores the connection context.
445 ****************************************************************************/
446
447 BOOL become_user(connection_struct *conn, uint16 vuid)
448 {
449         if (!push_sec_ctx())
450                 return False;
451
452         push_conn_ctx();
453
454         if (!change_to_user(conn, vuid)) {
455                 pop_sec_ctx();
456                 pop_conn_ctx();
457                 return False;
458         }
459
460         return True;
461 }
462
463 BOOL unbecome_user(void)
464 {
465         pop_sec_ctx();
466         pop_conn_ctx();
467         return True;
468 }
469