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