first pass at updating head branch to be to be the same as the SAMBA_2_0 branch
[nivanova/samba-autobuild/.git] / source3 / smbd / uid.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    uid/user handling
5    Copyright (C) Andrew Tridgell 1992-1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 extern int DEBUGLEVEL;
25
26 /* what user is current? */
27 extern struct current_user current_user;
28
29 pstring OriginalDir;
30
31 /****************************************************************************
32  Initialise the uid routines.
33 ****************************************************************************/
34
35 void init_uid(void)
36 {
37         current_user.uid = geteuid();
38         current_user.gid = getegid();
39
40         if (current_user.gid != 0 && current_user.uid == 0) {
41                 gain_root_group_privilege();
42         }
43
44         current_user.conn = NULL;
45         current_user.vuid = UID_FIELD_INVALID;
46         
47         dos_ChDir(OriginalDir);
48 }
49
50 /****************************************************************************
51  Become the specified uid.
52 ****************************************************************************/
53
54 static BOOL become_uid(uid_t uid)
55 {
56         if (uid == (uid_t)-1 || ((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) {
57                 static int done;
58                 if (!done) {
59                         DEBUG(1,("WARNING: using uid %d is a security risk\n",(int)uid));
60                         done=1;
61                 }
62         }
63
64         set_effective_uid(uid);
65
66         current_user.uid = uid;
67
68 #ifdef WITH_PROFILE
69         profile_p->uid_changes++;
70 #endif
71
72         return(True);
73 }
74
75
76 /****************************************************************************
77  Become the specified gid.
78 ****************************************************************************/
79
80 static BOOL become_gid(gid_t gid)
81 {
82         if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) && (gid == (gid_t)65535))) {
83                 DEBUG(1,("WARNING: using gid %d is a security risk\n",(int)gid));    
84         }
85   
86         set_effective_gid(gid);
87         
88         current_user.gid = gid;
89         
90         return(True);
91 }
92
93
94 /****************************************************************************
95  Become the specified uid and gid.
96 ****************************************************************************/
97
98 static BOOL become_id(uid_t uid,gid_t gid)
99 {
100         return(become_gid(gid) && become_uid(uid));
101 }
102
103 /****************************************************************************
104  Become the guest user.
105 ****************************************************************************/
106
107 BOOL become_guest(void)
108 {
109   BOOL ret;
110   static struct passwd *pass=NULL;
111
112   if (!pass)
113     pass = Get_Pwnam(lp_guestaccount(-1),True);
114   if (!pass) return(False);
115
116 #ifdef AIX
117   /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before setting IDs */
118   initgroups(pass->pw_name, (gid_t)pass->pw_gid);
119 #endif
120
121   ret = become_id(pass->pw_uid,pass->pw_gid);
122
123   if (!ret) {
124     DEBUG(1,("Failed to become guest. Invalid guest account?\n"));
125   }
126
127   current_user.conn = NULL;
128   current_user.vuid = UID_FIELD_INVALID;
129
130   return(ret);
131 }
132
133 /*******************************************************************
134  Check if a username is OK.
135 ********************************************************************/
136
137 static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
138 {
139   int i;
140   for (i=0;i<conn->uid_cache.entries;i++)
141     if (conn->uid_cache.list[i] == vuser->uid) return(True);
142
143   if (!user_ok(vuser->name,snum)) return(False);
144
145   i = conn->uid_cache.entries % UID_CACHE_SIZE;
146   conn->uid_cache.list[i] = vuser->uid;
147
148   if (conn->uid_cache.entries < UID_CACHE_SIZE)
149     conn->uid_cache.entries++;
150
151   return(True);
152 }
153
154
155 /****************************************************************************
156  Become the user of a connection number.
157 ****************************************************************************/
158
159 BOOL become_user(connection_struct *conn, uint16 vuid)
160 {
161         user_struct *vuser = get_valid_user_struct(vuid);
162         int snum;
163     gid_t gid;
164         uid_t uid;
165         char group_c;
166
167         if (!conn) {
168                 DEBUG(2,("Connection not open\n"));
169                 return(False);
170         }
171
172         /*
173          * We need a separate check in security=share mode due to vuid
174          * always being UID_FIELD_INVALID. If we don't do this then
175          * in share mode security we are *always* changing uid's between
176          * SMB's - this hurts performance - Badly.
177          */
178
179         if((lp_security() == SEC_SHARE) && (current_user.conn == conn) &&
180            (current_user.uid == conn->uid)) {
181                 DEBUG(4,("Skipping become_user - already user\n"));
182                 return(True);
183         } else if ((current_user.conn == conn) && 
184                    (vuser != 0) && (current_user.vuid == vuid) && 
185                    (current_user.uid == vuser->uid)) {
186                 DEBUG(4,("Skipping become_user - already user\n"));
187                 return(True);
188         }
189
190         unbecome_user();
191
192         snum = SNUM(conn);
193
194         if((vuser != NULL) && !check_user_ok(conn, vuser, snum))
195                 return False;
196
197         if (conn->force_user || 
198             lp_security() == SEC_SHARE ||
199             !(vuser) || (vuser->guest)) {
200                 uid = conn->uid;
201                 gid = conn->gid;
202                 current_user.groups = conn->groups;
203                 current_user.ngroups = conn->ngroups;
204         } else {
205                 if (!vuser) {
206                         DEBUG(2,("Invalid vuid used %d\n",vuid));
207                         return(False);
208                 }
209                 uid = vuser->uid;
210                 gid = vuser->gid;
211                 current_user.ngroups = vuser->n_groups;
212                 current_user.groups  = vuser->groups;
213         }
214
215         /*
216          * See if we should force group for this service.
217          * If so this overrides any group set in the force
218          * user code.
219          */
220
221         if((group_c = *lp_force_group(snum))) {
222                 if(group_c == '+') {
223
224                         /*
225                          * Only force group if the user is a member of
226                          * the service group. Check the group memberships for
227                          * this user (we already have this) to
228                          * see if we should force the group.
229                          */
230
231                         int i;
232                         for (i = 0; i < current_user.ngroups; i++) {
233                                 if (current_user.groups[i] == conn->gid) {
234                                         gid = conn->gid;
235                                         break;
236                                 }
237                         }
238                 } else {
239                         gid = conn->gid;
240                 }
241         }
242         
243         if (!become_gid(gid))
244                 return(False);
245
246 #ifdef HAVE_SETGROUPS      
247         if (!(conn && conn->ipc)) {
248                 /* groups stuff added by ih/wreu */
249                 if (current_user.ngroups > 0)
250                         if (sys_setgroups(current_user.ngroups,
251                                       current_user.groups)<0) {
252                                 DEBUG(0,("sys_setgroups call failed!\n"));
253                         }
254         }
255 #endif
256
257         if (!conn->admin_user && !become_uid(uid))
258                 return(False);
259         
260         current_user.conn = conn;
261         current_user.vuid = vuid;
262
263         DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d)\n",
264                  (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
265   
266         return(True);
267 }
268
269 /****************************************************************************
270  Unbecome the user of a connection number.
271 ****************************************************************************/
272
273 BOOL unbecome_user(void )
274 {
275         if (!current_user.conn)
276                 return(False);
277
278         dos_ChDir(OriginalDir);
279
280         set_effective_uid(0);
281         set_effective_gid(0);
282
283         if (geteuid() != 0) {
284                 DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
285         }
286         if (getegid() != 0) {
287                 DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
288         }
289
290         current_user.uid = 0;
291         current_user.gid = 0;
292   
293         if (dos_ChDir(OriginalDir) != 0)
294                 DEBUG( 0, ( "chdir(%s) failed in unbecome_user\n", OriginalDir ) );
295
296         DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n",
297                 (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
298
299         current_user.conn = NULL;
300         current_user.vuid = UID_FIELD_INVALID;
301
302         return(True);
303 }
304
305 /****************************************************************************
306  Become the user of an authenticated connected named pipe.
307  When this is called we are currently running as the connection
308  user.
309 ****************************************************************************/
310
311 BOOL become_authenticated_pipe_user(pipes_struct *p)
312 {
313         /*
314          * Go back to root.
315          */
316
317         if(!unbecome_user())
318                 return False;
319
320         /*
321          * Now become the authenticated user stored in the pipe struct.
322          */
323
324         if(!become_id(p->uid, p->gid)) {
325                 /* Go back to the connection user. */
326                 become_user(p->conn, p->vuid);
327                 return False;
328         }
329
330         return True;     
331 }
332
333 /****************************************************************************
334  Unbecome the user of an authenticated connected named pipe.
335  When this is called we are running as the authenticated pipe
336  user and need to go back to being the connection user.
337 ****************************************************************************/
338
339 BOOL unbecome_authenticated_pipe_user(pipes_struct *p)
340 {
341         if(!become_id(0,0)) {
342                 DEBUG(0,("unbecome_authenticated_pipe_user: Unable to go back to root.\n"));
343                 return False;
344         }
345
346         return become_user(p->conn, p->vuid);
347 }
348
349 static struct current_user current_user_saved;
350 static int become_root_depth;
351 static pstring become_root_dir;
352
353 /****************************************************************************
354 This is used when we need to do a privileged operation (such as mucking
355 with share mode files) and temporarily need root access to do it. This
356 call should always be paired with an unbecome_root() call immediately
357 after the operation
358
359 Set save_dir if you also need to save/restore the CWD 
360 ****************************************************************************/
361
362 void become_root(BOOL save_dir) 
363 {
364         if (become_root_depth) {
365                 DEBUG(0,("ERROR: become root depth is non zero\n"));
366         }
367         if (save_dir)
368                 dos_GetWd(become_root_dir);
369
370         current_user_saved = current_user;
371         become_root_depth = 1;
372
373         become_uid(0);
374         become_gid(0);
375 }
376
377 /****************************************************************************
378 When the privileged operation is over call this
379
380 Set save_dir if you also need to save/restore the CWD 
381 ****************************************************************************/
382
383 void unbecome_root(BOOL restore_dir)
384 {
385         if (become_root_depth != 1) {
386                 DEBUG(0,("ERROR: unbecome root depth is %d\n",
387                          become_root_depth));
388         }
389
390         /* we might have done a become_user() while running as root,
391            if we have then become root again in order to become 
392            non root! */
393         if (current_user.uid != 0) {
394                 become_uid(0);
395         }
396
397         /* restore our gid first */
398         if (!become_gid(current_user_saved.gid)) {
399                 DEBUG(0,("ERROR: Failed to restore gid\n"));
400                 exit_server("Failed to restore gid");
401         }
402
403 #ifdef HAVE_SETGROUPS      
404         if (current_user_saved.ngroups > 0) {
405                 if (sys_setgroups(current_user_saved.ngroups,
406                               current_user_saved.groups)<0)
407                         DEBUG(0,("ERROR: sys_setgroups call failed!\n"));
408         }
409 #endif
410
411         /* now restore our uid */
412         if (!become_uid(current_user_saved.uid)) {
413                 DEBUG(0,("ERROR: Failed to restore uid\n"));
414                 exit_server("Failed to restore uid");
415         }
416
417         if (restore_dir)
418                 dos_ChDir(become_root_dir);
419
420         current_user = current_user_saved;
421
422         become_root_depth = 0;
423 }