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