(merge from 3.0)
[nivanova/samba-autobuild/.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 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  Become the guest user without changing the security context stack.
28 ****************************************************************************/
29
30 BOOL change_to_guest(void)
31 {
32         static struct passwd *pass=NULL;
33
34         if (!pass) {
35                 /* Don't need to free() this as its stored in a static */
36                 pass = getpwnam_alloc(lp_guestaccount());
37                 if (!pass)
38                         return(False);
39         }
40         
41 #ifdef AIX
42         /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before 
43            setting IDs */
44         initgroups(pass->pw_name, pass->pw_gid);
45 #endif
46         
47         set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL);
48         
49         current_user.conn = NULL;
50         current_user.vuid = UID_FIELD_INVALID;
51         
52         passwd_free(&pass);
53
54         return True;
55 }
56
57 /*******************************************************************
58  Check if a username is OK.
59 ********************************************************************/
60
61 static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
62 {
63         unsigned i;
64         for (i=0;i<conn->vuid_cache.entries && i< VUID_CACHE_SIZE;i++)
65                 if (conn->vuid_cache.list[i] == vuser->vuid)
66                         return(True);
67
68         if ((conn->force_user || conn->force_group) 
69             && (conn->vuid != vuser->vuid)) {
70                 return False;
71         }
72         
73         if (!user_ok(vuser->user.unix_name,snum, vuser->groups, vuser->n_groups))
74                 return(False);
75
76         if (!share_access_check(conn, snum, vuser, conn->read_only ? FILE_READ_DATA : FILE_WRITE_DATA)) {
77                 return False;
78         }
79
80         i = conn->vuid_cache.entries % VUID_CACHE_SIZE;
81         conn->vuid_cache.list[i] = vuser->vuid;
82
83         conn->vuid_cache.entries++;
84
85         return(True);
86 }
87
88 /****************************************************************************
89  Become the user of a connection number without changing the security context
90  stack, but modify the currnet_user entries.
91 ****************************************************************************/
92
93 BOOL change_to_user(connection_struct *conn, uint16 vuid)
94 {
95         user_struct *vuser = get_valid_user_struct(vuid);
96         int snum;
97         gid_t gid;
98         uid_t uid;
99         char group_c;
100         BOOL must_free_token = False;
101         NT_USER_TOKEN *token = NULL;
102
103         if (!conn) {
104                 DEBUG(2,("change_to_user: Connection not open\n"));
105                 return(False);
106         }
107
108         /*
109          * We need a separate check in security=share mode due to vuid
110          * always being UID_FIELD_INVALID. If we don't do this then
111          * in share mode security we are *always* changing uid's between
112          * SMB's - this hurts performance - Badly.
113          */
114
115         if((lp_security() == SEC_SHARE) && (current_user.conn == conn) &&
116            (current_user.uid == conn->uid)) {
117                 DEBUG(4,("change_to_user: Skipping user change - already user\n"));
118                 return(True);
119         } else if ((current_user.conn == conn) && 
120                    (vuser != 0) && (current_user.vuid == vuid) && 
121                    (current_user.uid == vuser->uid)) {
122                 DEBUG(4,("change_to_user: Skipping user change - already user\n"));
123                 return(True);
124         }
125
126         snum = SNUM(conn);
127
128         if (conn->force_user) /* security = share sets this too */ {
129                 uid = conn->uid;
130                 gid = conn->gid;
131                 current_user.groups = conn->groups;
132                 current_user.ngroups = conn->ngroups;
133                 token = conn->nt_user_token;
134         } else if ((vuser) && check_user_ok(conn, vuser, snum)) {
135                 uid = vuser->uid;
136                 gid = vuser->gid;
137                 current_user.ngroups = vuser->n_groups;
138                 current_user.groups  = vuser->groups;
139                 token = vuser->nt_user_token;
140         } else {
141                 DEBUG(2,("change_to_user: Invalid vuid used %d or vuid not permitted access to share.\n",vuid));
142                 return False;
143         }
144
145         /*
146          * See if we should force group for this service.
147          * If so this overrides any group set in the force
148          * user code.
149          */
150
151         if((group_c = *lp_force_group(snum))) {
152                 BOOL is_guest = False;
153
154                 if(group_c == '+') {
155
156                         /*
157                          * Only force group if the user is a member of
158                          * the service group. Check the group memberships for
159                          * this user (we already have this) to
160                          * see if we should force the group.
161                          */
162
163                         int i;
164                         for (i = 0; i < current_user.ngroups; i++) {
165                                 if (current_user.groups[i] == conn->gid) {
166                                         gid = conn->gid;
167                                         break;
168                                 }
169                         }
170                 } else {
171                         gid = conn->gid;
172                 }
173
174                 /*
175                  * We've changed the group list in the token - we must
176                  * re-create it.
177                  */
178
179                 if (vuser && vuser->guest)
180                         is_guest = True;
181
182                 token = create_nt_token(uid, gid, current_user.ngroups, current_user.groups, is_guest);
183                 if (!token) {
184                         DEBUG(1, ("change_to_user: create_nt_token failed!\n"));
185                         return False;
186                 }
187                 must_free_token = True;
188         }
189         
190         set_sec_ctx(uid, gid, current_user.ngroups, current_user.groups, token);
191
192         /*
193          * Free the new token (as set_sec_ctx copies it).
194          */
195
196         if (must_free_token)
197                 delete_nt_token(&token);
198
199         current_user.conn = conn;
200         current_user.vuid = vuid;
201
202         DEBUG(5,("change_to_user uid=(%d,%d) gid=(%d,%d)\n",
203                  (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
204   
205         return(True);
206 }
207
208 /****************************************************************************
209  Go back to being root without changing the security context stack,
210  but modify the current_user entries.
211 ****************************************************************************/
212
213 BOOL change_to_root_user(void)
214 {
215         set_root_sec_ctx();
216
217         DEBUG(5,("change_to_root_user: now uid=(%d,%d) gid=(%d,%d)\n",
218                 (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
219
220         current_user.conn = NULL;
221         current_user.vuid = UID_FIELD_INVALID;
222
223         return(True);
224 }
225
226 /****************************************************************************
227  Become the user of an authenticated connected named pipe.
228  When this is called we are currently running as the connection
229  user. Doesn't modify current_user.
230 ****************************************************************************/
231
232 BOOL become_authenticated_pipe_user(pipes_struct *p)
233 {
234         if (!push_sec_ctx())
235                 return False;
236
237         set_sec_ctx(p->pipe_user.uid, p->pipe_user.gid, 
238                     p->pipe_user.ngroups, p->pipe_user.groups, p->pipe_user.nt_user_token);
239
240         return True;
241 }
242
243 /****************************************************************************
244  Unbecome the user of an authenticated connected named pipe.
245  When this is called we are running as the authenticated pipe
246  user and need to go back to being the connection user. Doesn't modify
247  current_user.
248 ****************************************************************************/
249
250 BOOL unbecome_authenticated_pipe_user(void)
251 {
252         return pop_sec_ctx();
253 }
254
255 /****************************************************************************
256  Utility functions used by become_xxx/unbecome_xxx.
257 ****************************************************************************/
258
259 struct conn_ctx {
260         connection_struct *conn;
261         uint16 vuid;
262 };
263  
264 /* A stack of current_user connection contexts. */
265  
266 static struct conn_ctx conn_ctx_stack[MAX_SEC_CTX_DEPTH];
267 static int conn_ctx_stack_ndx;
268
269 static void push_conn_ctx(void)
270 {
271         struct conn_ctx *ctx_p;
272  
273         /* Check we don't overflow our stack */
274  
275         if (conn_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
276                 DEBUG(0, ("Connection context stack overflow!\n"));
277                 smb_panic("Connection context stack overflow!\n");
278         }
279  
280         /* Store previous user context */
281         ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
282  
283         ctx_p->conn = current_user.conn;
284         ctx_p->vuid = current_user.vuid;
285  
286         DEBUG(3, ("push_conn_ctx(%u) : conn_ctx_stack_ndx = %d\n",
287                 (unsigned int)ctx_p->vuid, conn_ctx_stack_ndx ));
288
289         conn_ctx_stack_ndx++;
290 }
291
292 static void pop_conn_ctx(void)
293 {
294         struct conn_ctx *ctx_p;
295  
296         /* Check for stack underflow. */
297
298         if (conn_ctx_stack_ndx == 0) {
299                 DEBUG(0, ("Connection context stack underflow!\n"));
300                 smb_panic("Connection context stack underflow!\n");
301         }
302
303         conn_ctx_stack_ndx--;
304         ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
305
306         current_user.conn = ctx_p->conn;
307         current_user.vuid = ctx_p->vuid;
308
309         ctx_p->conn = NULL;
310         ctx_p->vuid = UID_FIELD_INVALID;
311 }
312
313 void init_conn_ctx(void)
314 {
315     int i;
316  
317     /* Initialise connection context stack */
318         for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
319                 conn_ctx_stack[i].conn = NULL;
320                 conn_ctx_stack[i].vuid = UID_FIELD_INVALID;
321     }
322 }
323
324 /****************************************************************************
325  Temporarily become a root user.  Must match with unbecome_root(). Saves and
326  restores the connection context.
327 ****************************************************************************/
328
329 void become_root(void)
330 {
331         push_sec_ctx();
332         push_conn_ctx();
333         set_root_sec_ctx();
334 }
335
336 /* Unbecome the root user */
337
338 void unbecome_root(void)
339 {
340         pop_sec_ctx();
341         pop_conn_ctx();
342 }
343
344 /****************************************************************************
345  Push the current security context then force a change via change_to_user().
346  Saves and restores the connection context.
347 ****************************************************************************/
348
349 BOOL become_user(connection_struct *conn, uint16 vuid)
350 {
351         if (!push_sec_ctx())
352                 return False;
353
354         push_conn_ctx();
355
356         if (!change_to_user(conn, vuid)) {
357                 pop_sec_ctx();
358                 pop_conn_ctx();
359                 return False;
360         }
361
362         return True;
363 }
364
365 BOOL unbecome_user(void)
366 {
367         pop_sec_ctx();
368         pop_conn_ctx();
369         return True;
370 }
371