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