Fix bug found by Tim Prouty, logging off and then re-using a vuid can cause smbd to
[kai/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 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 /* what user is current? */
24 extern struct current_user current_user;
25
26 /****************************************************************************
27  Become the guest user without changing the security context stack.
28 ****************************************************************************/
29
30 bool change_to_guest(void)
31 {
32         struct passwd *pass;
33
34         pass = getpwnam_alloc(talloc_autofree_context(), lp_guestaccount());
35         if (!pass) {
36                 return false;
37         }
38
39 #ifdef AIX
40         /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before 
41            setting IDs */
42         initgroups(pass->pw_name, pass->pw_gid);
43 #endif
44
45         set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL);
46
47         current_user.conn = NULL;
48         current_user.vuid = UID_FIELD_INVALID;
49
50         TALLOC_FREE(pass);
51
52         return true;
53 }
54
55 /*******************************************************************
56  Check if a username is OK.
57
58  This sets up conn->server_info with a copy related to this vuser that
59  later code can then mess with.
60 ********************************************************************/
61
62 static bool check_user_ok(connection_struct *conn,
63                         uint16_t vuid,
64                         const struct auth_serversupplied_info *server_info,
65                         int snum)
66 {
67         bool valid_vuid = (vuid != UID_FIELD_INVALID);
68         unsigned int i;
69         bool readonly_share;
70         bool admin_user;
71
72         if (valid_vuid) {
73                 struct vuid_cache_entry *ent;
74
75                 for (i=0; i<VUID_CACHE_SIZE; i++) {
76                         ent = &conn->vuid_cache.array[i];
77                         if (ent->vuid == vuid) {
78                                 conn->server_info = ent->server_info;
79                                 conn->read_only = ent->read_only;
80                                 conn->admin_user = ent->admin_user;
81                                 return(True);
82                         }
83                 }
84         }
85
86         if (!user_ok_token(server_info->unix_name,
87                            pdb_get_domain(server_info->sam_account),
88                            server_info->ptok, snum))
89                 return(False);
90
91         readonly_share = is_share_read_only_for_token(
92                 server_info->unix_name,
93                 pdb_get_domain(server_info->sam_account),
94                 server_info->ptok,
95                 conn);
96
97         if (!readonly_share &&
98             !share_access_check(server_info->ptok, lp_servicename(snum),
99                                 FILE_WRITE_DATA)) {
100                 /* smb.conf allows r/w, but the security descriptor denies
101                  * write. Fall back to looking at readonly. */
102                 readonly_share = True;
103                 DEBUG(5,("falling back to read-only access-evaluation due to "
104                          "security descriptor\n"));
105         }
106
107         if (!share_access_check(server_info->ptok, lp_servicename(snum),
108                                 readonly_share ?
109                                 FILE_READ_DATA : FILE_WRITE_DATA)) {
110                 return False;
111         }
112
113         admin_user = token_contains_name_in_list(
114                 server_info->unix_name,
115                 pdb_get_domain(server_info->sam_account),
116                 NULL, server_info->ptok, lp_admin_users(snum));
117
118         if (valid_vuid) {
119                 struct vuid_cache_entry *ent =
120                         &conn->vuid_cache.array[conn->vuid_cache.next_entry];
121
122                 conn->vuid_cache.next_entry =
123                         (conn->vuid_cache.next_entry + 1) % VUID_CACHE_SIZE;
124
125                 TALLOC_FREE(ent->server_info);
126
127                 /*
128                  * If force_user was set, all server_info's are based on the same
129                  * username-based faked one.
130                  */
131
132                 ent->server_info = copy_serverinfo(
133                         conn, conn->force_user ? conn->server_info : server_info);
134
135                 if (ent->server_info == NULL) {
136                         ent->vuid = UID_FIELD_INVALID;
137                         return false;
138                 }
139
140                 ent->vuid = vuid;
141                 ent->read_only = readonly_share;
142                 ent->admin_user = admin_user;
143                 conn->server_info = ent->server_info;
144         }
145
146         conn->read_only = readonly_share;
147         conn->admin_user = admin_user;
148
149         return(True);
150 }
151
152 /****************************************************************************
153  Clear a vuid out of the connection's vuid cache
154 ****************************************************************************/
155
156 void conn_clear_vuid_cache(connection_struct *conn, uint16_t vuid)
157 {
158         int i;
159
160         for (i=0; i<VUID_CACHE_SIZE; i++) {
161                 struct vuid_cache_entry *ent;
162
163                 ent = &conn->vuid_cache.array[i];
164
165                 if (ent->vuid == vuid) {
166                         ent->vuid = UID_FIELD_INVALID;
167                         /* Ensure we're not freeing an active pointer. */
168                         if (conn->server_info == ent->server_info) {
169                                 conn->server_info = NULL;
170                         }
171                         TALLOC_FREE(ent->server_info);
172                         ent->read_only = False;
173                         ent->admin_user = False;
174                 }
175         }
176 }
177
178 /****************************************************************************
179  Become the user of a connection number without changing the security context
180  stack, but modify the current_user entries.
181 ****************************************************************************/
182
183 bool change_to_user(connection_struct *conn, uint16 vuid)
184 {
185         const struct auth_serversupplied_info *server_info = NULL;
186         user_struct *vuser = get_valid_user_struct(vuid);
187         int snum;
188         gid_t gid;
189         uid_t uid;
190         char group_c;
191         int num_groups = 0;
192         gid_t *group_list = NULL;
193
194         if (!conn) {
195                 DEBUG(2,("change_to_user: Connection not open\n"));
196                 return(False);
197         }
198
199         /*
200          * We need a separate check in security=share mode due to vuid
201          * always being UID_FIELD_INVALID. If we don't do this then
202          * in share mode security we are *always* changing uid's between
203          * SMB's - this hurts performance - Badly.
204          */
205
206         if((lp_security() == SEC_SHARE) && (current_user.conn == conn) &&
207            (current_user.ut.uid == conn->server_info->utok.uid)) {
208                 DEBUG(4,("change_to_user: Skipping user change - already "
209                          "user\n"));
210                 return(True);
211         } else if ((current_user.conn == conn) && 
212                    (vuser != NULL) && (current_user.vuid == vuid) &&
213                    (current_user.ut.uid == vuser->server_info->utok.uid)) {
214                 DEBUG(4,("change_to_user: Skipping user change - already "
215                          "user\n"));
216                 return(True);
217         }
218
219         snum = SNUM(conn);
220
221         server_info = vuser ? vuser->server_info : conn->server_info;
222
223         if (!server_info) {
224                 /* Invalid vuid sent - even with security = share. */
225                 DEBUG(2,("change_to_user: Invalid vuid %d used on "
226                          "share %s.\n",vuid, lp_servicename(snum) ));
227                 return false;
228         }
229
230         if (!check_user_ok(conn, vuid, server_info, snum)) {
231                 DEBUG(2,("change_to_user: SMB user %s (unix user %s, vuid %d) "
232                          "not permitted access to share %s.\n",
233                          server_info->sanitized_username,
234                          server_info->unix_name, vuid,
235                          lp_servicename(snum)));
236                 return false;
237         }
238
239         /*
240          * conn->server_info is now correctly set up with a copy we can mess
241          * with for force_group etc.
242          */
243
244         if (conn->force_user) /* security = share sets this too */ {
245                 uid = conn->server_info->utok.uid;
246                 gid = conn->server_info->utok.gid;
247                 group_list = conn->server_info->utok.groups;
248                 num_groups = conn->server_info->utok.ngroups;
249         } else if (vuser) {
250                 uid = conn->admin_user ? 0 : vuser->server_info->utok.uid;
251                 gid = conn->server_info->utok.gid;
252                 num_groups = conn->server_info->utok.ngroups;
253                 group_list  = conn->server_info->utok.groups;
254         } else {
255                 DEBUG(2,("change_to_user: Invalid vuid used %d in accessing "
256                          "share %s.\n",vuid, lp_servicename(snum) ));
257                 return False;
258         }
259
260         /*
261          * See if we should force group for this service.
262          * If so this overrides any group set in the force
263          * user code.
264          */
265
266         if((group_c = *lp_force_group(snum))) {
267
268                 SMB_ASSERT(conn->force_group_gid != (gid_t)-1);
269
270                 if(group_c == '+') {
271
272                         /*
273                          * Only force group if the user is a member of
274                          * the service group. Check the group memberships for
275                          * this user (we already have this) to
276                          * see if we should force the group.
277                          */
278
279                         int i;
280                         for (i = 0; i < num_groups; i++) {
281                                 if (group_list[i]
282                                     == conn->force_group_gid) {
283                                         conn->server_info->utok.gid =
284                                                 conn->force_group_gid;
285                                         gid = conn->force_group_gid;
286                                         gid_to_sid(&conn->server_info->ptok
287                                                    ->user_sids[1], gid);
288                                         break;
289                                 }
290                         }
291                 } else {
292                         conn->server_info->utok.gid = conn->force_group_gid;
293                         gid = conn->force_group_gid;
294                         gid_to_sid(&conn->server_info->ptok->user_sids[1],
295                                    gid);
296                 }
297         }
298
299         /* Now set current_user since we will immediately also call
300            set_sec_ctx() */
301
302         current_user.ut.ngroups = num_groups;
303         current_user.ut.groups  = group_list;   
304
305         set_sec_ctx(uid, gid, current_user.ut.ngroups, current_user.ut.groups,
306                     conn->server_info->ptok);
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->server_info->utok.uid, p->server_info->utok.gid,
347                     p->server_info->utok.ngroups, p->server_info->utok.groups,
348                     p->server_info->ptok);
349
350         return True;
351 }
352
353 /****************************************************************************
354  Unbecome the user of an authenticated connected named pipe.
355  When this is called we are running as the authenticated pipe
356  user and need to go back to being the connection user. Doesn't modify
357  current_user.
358 ****************************************************************************/
359
360 bool unbecome_authenticated_pipe_user(void)
361 {
362         return pop_sec_ctx();
363 }
364
365 /****************************************************************************
366  Utility functions used by become_xxx/unbecome_xxx.
367 ****************************************************************************/
368
369 static void push_conn_ctx(void)
370 {
371         struct conn_ctx *ctx_p;
372
373         /* Check we don't overflow our stack */
374
375         if (conn_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
376                 DEBUG(0, ("Connection context stack overflow!\n"));
377                 smb_panic("Connection context stack overflow!\n");
378         }
379
380         /* Store previous user context */
381         ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
382
383         ctx_p->conn = current_user.conn;
384         ctx_p->vuid = current_user.vuid;
385
386         DEBUG(3, ("push_conn_ctx(%u) : conn_ctx_stack_ndx = %d\n",
387                 (unsigned int)ctx_p->vuid, conn_ctx_stack_ndx ));
388
389         conn_ctx_stack_ndx++;
390 }
391
392 static void pop_conn_ctx(void)
393 {
394         struct conn_ctx *ctx_p;
395
396         /* Check for stack underflow. */
397
398         if (conn_ctx_stack_ndx == 0) {
399                 DEBUG(0, ("Connection context stack underflow!\n"));
400                 smb_panic("Connection context stack underflow!\n");
401         }
402
403         conn_ctx_stack_ndx--;
404         ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
405
406         current_user.conn = ctx_p->conn;
407         current_user.vuid = ctx_p->vuid;
408
409         ctx_p->conn = NULL;
410         ctx_p->vuid = UID_FIELD_INVALID;
411 }
412
413 /****************************************************************************
414  Temporarily become a root user.  Must match with unbecome_root(). Saves and
415  restores the connection context.
416 ****************************************************************************/
417
418 void become_root(void)
419 {
420          /*
421           * no good way to handle push_sec_ctx() failing without changing
422           * the prototype of become_root()
423           */
424         if (!push_sec_ctx()) {
425                 smb_panic("become_root: push_sec_ctx failed");
426         }
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 }