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