Makefile :
[samba.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-1997
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 static int initial_uid;
27 static int initial_gid;
28
29 /* what user is current? */
30 struct current_user current_user;
31
32 pstring OriginalDir;
33
34 /****************************************************************************
35 initialise the uid routines
36 ****************************************************************************/
37 void init_uid(void)
38 {
39   initial_uid = current_user.uid = geteuid();
40   initial_gid = current_user.gid = getegid();
41
42   if (initial_gid != 0 && initial_uid == 0)
43     {
44 #ifdef HPUX
45       setresgid(0,0,0);
46 #else
47       setgid(0);
48       setegid(0);
49 #endif
50     }
51
52   initial_uid = geteuid();
53   initial_gid = getegid();
54
55   current_user.cnum = -1;
56
57   ChDir(OriginalDir);
58 }
59
60
61 /****************************************************************************
62   become the specified uid 
63 ****************************************************************************/
64 static BOOL become_uid(int uid)
65 {
66   if (initial_uid != 0)
67     return(True);
68
69   if (uid == -1 || uid == 65535) {
70     DEBUG(1,("WARNING: using uid %d is a security risk\n",uid));    
71   }
72
73 #ifdef AIX
74   {
75     /* AIX 3 stuff - inspired by a code fragment in wu-ftpd */
76     priv_t priv;
77
78     priv.pv_priv[0] = 0;
79     priv.pv_priv[1] = 0;
80     if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
81                 &priv, sizeof(priv_t)) < 0 ||
82         setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)uid) < 0 ||
83         seteuid((uid_t)uid) < 0) 
84       DEBUG(1,("Can't set uid (AIX3)\n"));
85   }
86 #endif
87
88 #ifdef USE_SETRES
89   if (setresuid(-1,uid,-1) != 0)
90 #elif defined(USE_SETFS)
91     if (setfsuid(uid) != 0)
92 #else
93     if ((seteuid(uid) != 0) && 
94         (setuid(uid) != 0))
95 #endif
96       {
97         DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n",
98                  uid,getuid(), geteuid()));
99         if (uid > 32000)
100           DEBUG(0,("Looks like your OS doesn't like high uid values - try using a different account\n"));
101         return(False);
102       }
103
104   if (((uid == -1) || (uid == 65535)) && geteuid() != uid) {
105     DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n"));
106     return(False);
107   }
108
109   current_user.uid = uid;
110
111   return(True);
112 }
113
114
115 /****************************************************************************
116   become the specified gid
117 ****************************************************************************/
118 static BOOL become_gid(int gid)
119 {
120   if (initial_uid != 0)
121     return(True);
122
123   if (gid == -1 || gid == 65535) {
124     DEBUG(1,("WARNING: using gid %d is a security risk\n",gid));    
125   }
126   
127 #ifdef USE_SETRES 
128   if (setresgid(-1,gid,-1) != 0)
129 #elif defined(USE_SETFS)
130   if (setfsgid(gid) != 0)
131 #else
132   if (setgid(gid) != 0)
133 #endif
134       {
135         DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n",
136                  gid,getgid(),getegid()));
137         if (gid > 32000)
138           DEBUG(0,("Looks like your OS doesn't like high gid values - try using a different account\n"));
139         return(False);
140       }
141
142   current_user.gid = gid;
143
144   return(True);
145 }
146
147
148 /****************************************************************************
149   become the specified uid and gid
150 ****************************************************************************/
151 static BOOL become_id(int uid,int gid)
152 {
153   return(become_gid(gid) && become_uid(uid));
154 }
155
156 /****************************************************************************
157 become the guest user
158 ****************************************************************************/
159 BOOL become_guest(void)
160 {
161   BOOL ret;
162   static struct passwd *pass=NULL;
163
164   if (initial_uid != 0) 
165     return(True);
166
167   if (!pass)
168     pass = Get_Pwnam(lp_guestaccount(-1),True);
169   if (!pass) return(False);
170
171   ret = become_id(pass->pw_uid,pass->pw_gid);
172
173   if (!ret)
174     DEBUG(1,("Failed to become guest. Invalid guest account?\n"));
175
176   current_user.cnum = -2;
177
178   return(ret);
179 }
180
181 /*******************************************************************
182 check if a username is OK
183 ********************************************************************/
184 static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
185 {
186   int i;
187   for (i=0;i<conn->uid_cache.entries;i++)
188     if (conn->uid_cache.list[i] == vuser->uid) return(True);
189
190   if (!user_ok(vuser->name,snum)) return(False);
191
192   i = conn->uid_cache.entries % UID_CACHE_SIZE;
193   conn->uid_cache.list[i] = vuser->uid;
194
195   if (conn->uid_cache.entries < UID_CACHE_SIZE)
196     conn->uid_cache.entries++;
197
198   return(True);
199 }
200
201
202 /****************************************************************************
203   become the user of a connection number
204 ****************************************************************************/
205 BOOL become_user(connection_struct *conn, int cnum, uint16 vuid)
206 {
207   user_struct *vuser = get_valid_user_struct(vuid);
208   int snum,gid;
209   int uid;
210
211   if (current_user.cnum == cnum && vuser != 0 && current_user.id == vuser->uid) {
212     DEBUG(4,("Skipping become_user - already user\n"));
213     return(True);
214   }
215
216   unbecome_user();
217
218   if (!(VALID_CNUM(cnum) && conn->open)) {
219     DEBUG(2,("Connection %d not open\n",cnum));
220     return(False);
221   }
222
223   snum = conn->service;
224
225   if (conn->force_user || 
226       lp_security() == SEC_SHARE ||
227       !(vuser) || (vuser->guest) ||
228       !check_user_ok(conn, vuser, snum))
229   {
230     uid = conn->uid;
231     gid = conn->gid;
232     current_user.groups = conn->groups;
233     current_user.igroups = conn->igroups;
234     current_user.ngroups = conn->ngroups;
235     current_user.attrs   = vuser->attrs;
236   }
237   else
238   {
239     if (!vuser) {
240       DEBUG(2,("Invalid vuid used %d\n",vuid));
241       return(False);
242     }
243     uid = vuser->uid;
244     if(!*lp_force_group(snum))
245       gid = vuser->gid;
246     else
247       gid = conn->gid;
248     current_user.ngroups = vuser->n_groups;
249     current_user.groups  = vuser->groups;
250     current_user.igroups = vuser->igroups;
251     current_user.attrs   = vuser->attrs;
252   }
253
254   if (initial_uid == 0)
255     {
256       if (!become_gid(gid)) return(False);
257
258 #ifndef NO_SETGROUPS      
259       if (!(VALID_CNUM(cnum) && conn->ipc)) {
260         /* groups stuff added by ih/wreu */
261         if (current_user.ngroups > 0)
262           if (setgroups(current_user.ngroups,current_user.groups)<0)
263             DEBUG(0,("setgroups call failed!\n"));
264       }
265 #endif
266
267       if (!conn->admin_user && !become_uid(uid))
268         return(False);
269     }
270
271   current_user.cnum = cnum;
272   current_user.id = uid;
273   
274   DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d)\n",
275            getuid(),geteuid(),getgid(),getegid()));
276   
277   return(True);
278 }
279
280 /****************************************************************************
281   unbecome the user of a connection number
282 ****************************************************************************/
283 BOOL unbecome_user(void )
284 {
285   if (current_user.cnum == -1)
286     return(False);
287
288   ChDir(OriginalDir);
289
290   if (initial_uid == 0)
291     {
292 #ifdef USE_SETRES
293       setresuid(-1,getuid(),-1);
294       setresgid(-1,getgid(),-1);
295 #elif defined(USE_SETFS)
296       setfsuid(initial_uid);
297       setfsgid(initial_gid);
298 #else
299       if (seteuid(initial_uid) != 0) 
300         setuid(initial_uid);
301       setgid(initial_gid);
302 #endif
303     }
304 #ifdef NO_EID
305   if (initial_uid == 0)
306     DEBUG(2,("Running with no EID\n"));
307   initial_uid = getuid();
308   initial_gid = getgid();
309 #else
310   if (geteuid() != initial_uid)
311     {
312       DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
313       initial_uid = geteuid();
314     }
315   if (getegid() != initial_gid)
316     {
317       DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
318       initial_gid = getegid();
319     }
320 #endif
321
322   current_user.uid = initial_uid;
323   current_user.gid = initial_gid;
324   
325   if (ChDir(OriginalDir) != 0)
326     DEBUG(0,("%s chdir(%s) failed in unbecome_user\n",
327              timestring(),OriginalDir));
328
329   DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n",
330         getuid(),geteuid(),getgid(),getegid()));
331
332   current_user.cnum = -1;
333
334   return(True);
335 }
336
337
338 /****************************************************************************
339 This is a utility function of smbrun(). It must be called only from
340 the child as it may leave the caller in a privilaged state.
341 ****************************************************************************/
342 static BOOL setup_stdout_file(char *outfile,BOOL shared)
343 {  
344   int fd;
345   struct stat st;
346   mode_t mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH;
347   int flags = O_RDWR|O_CREAT|O_TRUNC|O_EXCL;
348
349   close(1);
350
351   if (shared) {
352     /* become root - unprivilaged users can't delete these files */
353 #ifdef USE_SETRES
354     setresgid(0,0,0);
355     setresuid(0,0,0);
356 #else      
357     setuid(0);
358     seteuid(0);
359 #endif
360   }
361
362   if(stat(outfile, &st) == 0) {
363     /* Check we're not deleting a device file. */ 
364     if(st.st_mode & S_IFREG)
365       unlink(outfile);
366     else
367       flags = O_RDWR;
368   }
369   /* now create the file */
370   fd = open(outfile,flags,mode);
371
372   if (fd == -1) return False;
373
374   if (fd != 1) {
375     if (dup2(fd,1) != 0) {
376       DEBUG(2,("Failed to create stdout file descriptor\n"));
377       close(fd);
378       return False;
379     }
380     close(fd);
381   }
382   return True;
383 }
384
385
386 /****************************************************************************
387 run a command being careful about uid/gid handling and putting the output in
388 outfile (or discard it if outfile is NULL).
389
390 if shared is True then ensure the file will be writeable by all users
391 but created such that its owned by root. This overcomes a security hole.
392
393 if shared is not set then open the file with O_EXCL set
394 ****************************************************************************/
395 int smbrun(char *cmd,char *outfile,BOOL shared)
396 {
397   int fd,pid;
398   int uid = current_user.uid;
399   int gid = current_user.gid;
400
401 #if USE_SYSTEM
402   int ret;
403   pstring syscmd;  
404   char *path = lp_smbrun();
405
406   /* in the old method we use system() to execute smbrun which then
407      executes the command (using system() again!). This involves lots
408      of shell launches and is very slow. It also suffers from a
409      potential security hole */
410   if (!file_exist(path,NULL))
411     {
412       DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",path));
413       return(1);
414     }
415
416   sprintf(syscmd,"%s %d %d \"(%s 2>&1) > %s\"",
417           path,uid,gid,cmd,
418           outfile?outfile:"/dev/null");
419
420   DEBUG(5,("smbrun - running %s ",syscmd));
421   ret = system(syscmd);
422   DEBUG(5,("gave %d\n",ret));
423   return(ret);
424 #else
425   /* in this newer method we will exec /bin/sh with the correct
426      arguments, after first setting stdout to point at the file */
427
428   if ((pid=fork())) {
429     int status=0;
430     /* the parent just waits for the child to exit */
431     if (sys_waitpid(pid,&status,0) != pid) {
432       DEBUG(2,("waitpid(%d) : %s\n",pid,strerror(errno)));
433       return -1;
434     }
435     return status;
436   }
437
438
439   /* we are in the child. we exec /bin/sh to do the work for us. we
440      don't directly exec the command we want because it may be a
441      pipeline or anything else the config file specifies */
442
443   /* point our stdout at the file we want output to go into */
444   if (outfile && !setup_stdout_file(outfile,shared)) {
445     exit(80);
446   }
447
448   /* now completely lose our privilages. This is a fairly paranoid
449      way of doing it, but it does work on all systems that I know of */
450 #ifdef USE_SETRES
451   setresgid(0,0,0);
452   setresuid(0,0,0);
453   setresgid(gid,gid,gid);
454   setresuid(uid,uid,uid);      
455 #else      
456   setuid(0);
457   seteuid(0);
458   setgid(gid);
459   setegid(gid);
460   setuid(uid);
461   seteuid(uid);
462 #endif
463
464   if (getuid() != uid || geteuid() != uid ||
465       getgid() != gid || getegid() != gid) {
466     /* we failed to lose our privilages - do not execute the command */
467     exit(81); /* we can't print stuff at this stage, instead use exit codes
468                  for debugging */
469   }
470
471   /* close all other file descriptors, leaving only 0, 1 and 2. 0 and
472      2 point to /dev/null from the startup code */
473   for (fd=3;fd<256;fd++) close(fd);
474
475   execl("/bin/sh","sh","-c",cmd,NULL);  
476
477   /* not reached */
478   exit(82);
479 #endif
480   return 1;
481 }
482
483 static struct current_user current_user_saved;
484 static int become_root_depth;
485 static pstring become_root_dir;
486
487 /****************************************************************************
488 This is used when we need to do a privilaged operation (such as mucking
489 with share mode files) and temporarily need root access to do it. This
490 call should always be paired with an unbecome_root() call immediately
491 after the operation
492
493 Set save_dir if you also need to save/restore the CWD 
494 ****************************************************************************/
495 void become_root(BOOL save_dir) 
496 {
497         if (become_root_depth) {
498                 DEBUG(0,("ERROR: become root depth is non zero\n"));
499         }
500         if (save_dir)
501                 GetWd(become_root_dir);
502
503         current_user_saved = current_user;
504         become_root_depth = 1;
505
506         become_uid(0);
507         become_gid(0);
508 }
509
510 /****************************************************************************
511 When the privilaged operation is over call this
512
513 Set save_dir if you also need to save/restore the CWD 
514 ****************************************************************************/
515 void unbecome_root(BOOL restore_dir)
516 {
517         if (become_root_depth != 1) {
518                 DEBUG(0,("ERROR: unbecome root depth is %d\n",
519                          become_root_depth));
520         }
521
522         /* we might have done a become_user() while running as root,
523            if we have then become root again in order to become 
524            non root! */
525         if (current_user.uid != 0) {
526                 become_uid(0);
527         }
528
529         /* restore our gid first */
530         if (!become_gid(current_user_saved.gid)) {
531                 DEBUG(0,("ERROR: Failed to restore gid\n"));
532                 exit_server("Failed to restore gid");
533         }
534
535 #ifndef NO_SETGROUPS      
536         if (current_user_saved.ngroups > 0) {
537                 if (setgroups(current_user_saved.ngroups,
538                               current_user_saved.groups)<0)
539                         DEBUG(0,("ERROR: setgroups call failed!\n"));
540         }
541 #endif
542
543         /* now restore our uid */
544         if (!become_uid(current_user_saved.uid)) {
545                 DEBUG(0,("ERROR: Failed to restore uid\n"));
546                 exit_server("Failed to restore uid");
547         }
548
549         if (restore_dir)
550                 ChDir(become_root_dir);
551
552         current_user = current_user_saved;
553
554         become_root_depth = 0;
555 }