chgpasswd.c: Fixed up debug calls to stop crashes if ptsname failed.
[bbaumbach/samba-autobuild/.git] / source / 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 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   current_user.vuid = UID_FIELD_INVALID;
57
58   ChDir(OriginalDir);
59 }
60
61
62 /****************************************************************************
63   become the specified uid 
64 ****************************************************************************/
65 static BOOL become_uid(int uid)
66 {
67   if (initial_uid != 0)
68     return(True);
69
70   if (uid == -1 || uid == 65535) {
71     DEBUG(1,("WARNING: using uid %d is a security risk\n",uid));    
72   }
73
74 #ifdef AIX
75   {
76     /* AIX 3 stuff - inspired by a code fragment in wu-ftpd */
77     /* MWW: This is all undocumented, of course.  There's a patch to WU-ftpd
78        in the AIX FAQ which does the setpriv, then sets the gid stuff, then
79        sets uid.  Since Samba separates setting the gid and setting the uid,
80        I've duplicated the setpriv code in become_gid.  I've also made the
81        requisite changes to become_gid to match the WU-ftpd patch.
82
83        I believe we'll still get errors in the Samba logs.  This setpriv
84        call is supposed to disable "trapdooring" on AIX - ie. normally
85        once a seteuid / setegid is done, the effective ID can't be set back
86        to what it was before.  See the comments in become_root / unbecome_root.
87        I *think* that we may have to do something additional here to prevent
88        the "Can't set uid (AIX3)" messages, though - possibly change the
89        values of priv.pv_priv to keep the SET_PROC_DAC privilege, and
90        possibly SET_OBJ_DAC and SET_OBJ_STAT as well.
91
92        The pv_priv array is two longwords, and the constants in sys/priv.h
93        have values between 1 and 64, according to the comments in priv.h.
94        This strongly suggests a bit vector - but does BYPASS_DAC_WRITE
95        (#define'd to 1) mean 1<<0 or 1<<1?  Unfortunately, nothing's
96        defined to 0 or 64, which would be a dead giveaway.  Also, what's the
97        fullword-boundary endianness?  That is, is pv_priv[0] the high or
98        the low 32 bits?  Fortunately, the values used by "su" (see below)
99        don't make much sense if pv_priv[0] is the high bits.  Also, based
100        on analysis of the values used by su, I concluded that, for example,
101        BYPASS_DAC_READ (defined to 2) is bit "2" counting from 1 - ie.
102        if (pv_priv[0] & (1 << (BYPASS_DAC_READ - 1))) then BYPASS_DAC_READ
103        is on.  That's a bit odd, but it makes more sense than if the
104        privilege constants are meant to be taken as exponents of 2.
105
106        For analysis, I ran "su" as root under dbx, and stopped in setpriv.
107        The first argument to setpriv can be examined using
108
109           print $r3   (eg. "0x30009" = PRIV_SET|PRIV_MAXIMUM|PRIV_EFFECTIV)
110
111        the contents of the pv_priv array can be examined using
112
113           ($r4)/2X
114
115        Here's what su does:
116
117           setpriv(PRIV_SET | PRIV_INHERITED | PRIV_BEQUEATH, {0,0})
118           setpriv(PRIV_SET | PRIV_EFFECTIVE | PRIV_MAXIMUM,
119                   {0x02800006, 0x00040000})
120              0x02800006 = SET_PROC_AUDIT | SET_PROC_ENV |
121                           BYPASS_DAC_EXEC | BYPASS_DAC_READ
122              0x00040000 = TPATH_CONFIG
123           setpriv(PRIV_SET | PRIV_EFFECTIVE, {0, 0})
124
125        Analysis:
126
127           Reduce inherited privileges to none, so the child doesn't inherit
128              anything special.
129           Change su's privileges so it can execute the shell even if someone
130              has goofed up the permissions to it or to directories on the
131              search path; so it can set the process auditing characteristics
132              and privileged environment (stuff in /etc/security/environ with
133              the sysenv attribute); and so that it can set the trusted path
134              characteristics for this login.
135           Zap those privileges back off when we don't need them any more.
136
137        I'm guessing we want to keep SET_PROC_DAC in the current priv set,
138        but not in the inherited one.  That is, set PRIV_INHERITED and
139        PRIV_BEQUEATH to 0.  We also probably want to set PRIV_MAXIMUM and
140        PRIV_EFFECTIVE to only the privs we need, which at this point would
141        appear to be just SET_PROC_DAC.  *Note*: setting PRIV_MAXIMUM
142        with any of the privilege sets higher than what you're trying to
143        set the maximum to will produce an EINVAL.  For example, if we
144        try to set PRIV_MAXIMUM to SET_PROC_DAC *before* we reduce
145        PRIV_INHERITED and PRIV_BEQUEATH, it won't work.  Zero out the
146        inherited privileges first.
147
148        Some experimentation with simple programs confirms that if we're
149        running with an EUID of 0 we can switch our UID/EUID back and
150        forth with setuidx - *unless* we call setpriv({0,0}, ...) first.
151        In other words, by default root has SET_PROC_DAT set, but we can
152        remove it from our privilege set.  This is what we want to do for
153        child processes, I believe.
154
155        Also, calling setpriv(PRIV_SUB|PRIV_EFFECTIVE,...) with pv_priv[0]
156        set to SET_PROC_DAC (1 << (SET_PROC_DAC - 1)) will prevent an
157        EUID-root process from switching its EUID back with setuidx.
158
159        In other words, setuidx on AIX is *not* trapdoor.  setuid is
160        trapdoor.  We need a non-trapdoor setuid function, but we don't
161        want processes we fork to have access to it.  Thus we use setuidx
162        but first we disable it for our children.
163
164        Note, however, that we can only increase our privileges (as we
165        do in the first call to setpriv) if we're EUID-root.  If we
166        started out as root, and then switched to a non-root user ID,
167        that's OK; we've already set them.  Just don't try to set them
168        again.
169
170        Also, I suspect that after using setpriv / setuidx / etc. here in
171        the AIX-specific code we DON'T want to fall through to the code that
172        calls setuid, etc.  However, I haven't noticed any more problems with
173        the code the way it is here.
174        */
175
176     priv_t priv;
177
178     priv.pv_priv[0] = 0;
179     priv.pv_priv[1] = 0;
180     if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_BEQUEATH,
181                 &priv, sizeof(priv_t)) < 0) {
182        DEBUG(1, ("Can't set child privileges (AIX3): %s\n", strerror(errno)));
183     }
184
185     priv.pv_priv[0] = (1 << (SET_PROC_DAC - 1));
186     if (setpriv(PRIV_SET|PRIV_EFFECTIVE|PRIV_MAXIMUM,
187                 &priv, sizeof(priv_t)) < 0) {
188        DEBUG(1, ("Can't set own privileges (AIX3): %s\n", strerror(errno)));
189     }
190
191     if (setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)uid) < 0 ||
192         seteuid((uid_t)uid) < 0) {
193       DEBUG(1,("Can't set uid (AIX3)\n"));
194     }
195   }
196 #endif
197
198 #ifdef USE_SETRES
199   if (setresuid(-1,uid,-1) != 0)
200 #elif defined(USE_SETFS)
201     if (setfsuid(uid) != 0)
202 #else
203     if ((seteuid(uid) != 0) && 
204         (setuid(uid) != 0))
205 #endif
206       {
207         DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n",
208                  uid,getuid(), geteuid()));
209         if (uid > 32000)
210           DEBUG(0,("Looks like your OS doesn't like high uid values - try using a different account\n"));
211         return(False);
212       }
213
214   if (((uid == -1) || (uid == 65535)) && geteuid() != uid) {
215     DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n"));
216     return(False);
217   }
218
219   current_user.uid = uid;
220
221   return(True);
222 }
223
224
225 /****************************************************************************
226   become the specified gid
227 ****************************************************************************/
228 static BOOL become_gid(int gid)
229 {
230   if (initial_uid != 0)
231     return(True);
232
233   if (gid == -1 || gid == 65535) {
234     DEBUG(1,("WARNING: using gid %d is a security risk\n",gid));    
235   }
236   
237 #ifdef AIX
238   {
239     /* MWW: See comment above in become_uid. */
240     priv_t priv;
241
242     priv.pv_priv[0] = 0;
243     priv.pv_priv[1] = 0;
244     if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
245                 &priv, sizeof(priv_t)) < 0) {
246        DEBUG(1, ("Can't set privilege (AIX3)\n"));
247        }
248     if (setgidx(ID_REAL|ID_EFFECTIVE, (gid_t)gid) < 0 ||
249         setegid((gid_t)gid) < 0) {
250       DEBUG(1,("Can't set gid (AIX3)\n"));
251     }
252   }
253 #endif
254
255 #ifdef USE_SETRES 
256   if (setresgid(-1,gid,-1) != 0)
257 #elif defined(USE_SETFS)
258   if (setfsgid(gid) != 0)
259 #else
260   if (setgid(gid) != 0)
261 #endif
262       {
263         DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n",
264                  gid,getgid(),getegid()));
265         if (gid > 32000)
266           DEBUG(0,("Looks like your OS doesn't like high gid values - try using a different account\n"));
267         return(False);
268       }
269
270   current_user.gid = gid;
271
272   return(True);
273 }
274
275
276 /****************************************************************************
277   become the specified uid and gid
278 ****************************************************************************/
279 static BOOL become_id(int uid,int gid)
280 {
281   return(become_gid(gid) && become_uid(uid));
282 }
283
284 /****************************************************************************
285 become the guest user
286 ****************************************************************************/
287 BOOL become_guest(void)
288 {
289   BOOL ret;
290   static struct passwd *pass=NULL;
291
292   if (initial_uid != 0) 
293     return(True);
294
295   if (!pass)
296     pass = Get_Pwnam(lp_guestaccount(-1),True);
297   if (!pass) return(False);
298
299 #ifdef AIX
300   /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before setting IDs */
301   initgroups(pass->pw_name, (gid_t)pass->pw_gid);
302 #endif
303   ret = become_id(pass->pw_uid,pass->pw_gid);
304
305   if (!ret)
306     DEBUG(1,("Failed to become guest. Invalid guest account?\n"));
307
308   current_user.cnum = -2;
309   current_user.vuid = UID_FIELD_INVALID;
310
311   return(ret);
312 }
313
314 /*******************************************************************
315 check if a username is OK
316 ********************************************************************/
317 static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
318 {
319   int i;
320   for (i=0;i<conn->uid_cache.entries;i++)
321     if (conn->uid_cache.list[i] == vuser->uid) return(True);
322
323   if (!user_ok(vuser->name,snum)) return(False);
324
325   i = conn->uid_cache.entries % UID_CACHE_SIZE;
326   conn->uid_cache.list[i] = vuser->uid;
327
328   if (conn->uid_cache.entries < UID_CACHE_SIZE)
329     conn->uid_cache.entries++;
330
331   return(True);
332 }
333
334
335 /****************************************************************************
336   become the user of a connection number
337 ****************************************************************************/
338 BOOL become_user(connection_struct *conn, int cnum, uint16 vuid)
339 {
340   user_struct *vuser = get_valid_user_struct(vuid);
341   int snum,gid;
342   int uid;
343
344   if ((current_user.cnum == cnum) && (vuser != 0) && (current_user.vuid == vuid) && 
345       (current_user.uid == vuser->uid)) {
346     DEBUG(4,("Skipping become_user - already user\n"));
347     return(True);
348   }
349
350   unbecome_user();
351
352   if (!(VALID_CNUM(cnum) && conn->open)) {
353     DEBUG(2,("Connection %d not open\n",cnum));
354     return(False);
355   }
356
357   snum = conn->service;
358
359   if((vuser != NULL) && !check_user_ok(conn, vuser, snum))
360     return False;
361
362   if (conn->force_user || 
363       lp_security() == SEC_SHARE ||
364       !(vuser) || (vuser->guest)
365      )
366   {
367     uid = conn->uid;
368     gid = conn->gid;
369     current_user.groups = conn->groups;
370     current_user.igroups = conn->igroups;
371     current_user.ngroups = conn->ngroups;
372   }
373   else
374   {
375     if (!vuser) {
376       DEBUG(2,("Invalid vuid used %d\n",vuid));
377       return(False);
378     }
379     uid = vuser->uid;
380     if(!*lp_force_group(snum))
381       gid = vuser->gid;
382     else
383       gid = conn->gid;
384     current_user.ngroups = vuser->n_groups;
385     current_user.groups  = vuser->groups;
386     current_user.igroups = vuser->igroups;
387   }
388
389   if (initial_uid == 0)
390     {
391       if (!become_gid(gid)) return(False);
392
393 #ifndef NO_SETGROUPS      
394       if (!(VALID_CNUM(cnum) && conn->ipc)) {
395         /* groups stuff added by ih/wreu */
396         if (current_user.ngroups > 0)
397           if (setgroups(current_user.ngroups,current_user.groups)<0)
398             DEBUG(0,("setgroups call failed!\n"));
399       }
400 #endif
401
402       if (!conn->admin_user && !become_uid(uid))
403         return(False);
404     }
405
406   current_user.cnum = cnum;
407   current_user.vuid = vuid;
408
409   DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d)\n",
410            getuid(),geteuid(),getgid(),getegid()));
411   
412   return(True);
413 }
414
415 /****************************************************************************
416   unbecome the user of a connection number
417 ****************************************************************************/
418 BOOL unbecome_user(void )
419 {
420   if (current_user.cnum == -1)
421     return(False);
422
423   ChDir(OriginalDir);
424
425   if (initial_uid == 0)
426     {
427 #ifdef USE_SETRES
428       setresuid(-1,getuid(),-1);
429       setresgid(-1,getgid(),-1);
430 #elif defined(USE_SETFS)
431       setfsuid(initial_uid);
432       setfsgid(initial_gid);
433 #else
434       if (seteuid(initial_uid) != 0) 
435         setuid(initial_uid);
436       setgid(initial_gid);
437 #endif
438     }
439 #ifdef NO_EID
440   if (initial_uid == 0)
441     DEBUG(2,("Running with no EID\n"));
442   initial_uid = getuid();
443   initial_gid = getgid();
444 #else
445   if (geteuid() != initial_uid)
446     {
447       DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
448       initial_uid = geteuid();
449     }
450   if (getegid() != initial_gid)
451     {
452       DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
453       initial_gid = getegid();
454     }
455 #endif
456
457   current_user.uid = initial_uid;
458   current_user.gid = initial_gid;
459   
460   if (ChDir(OriginalDir) != 0)
461     DEBUG(0,("%s chdir(%s) failed in unbecome_user\n",
462              timestring(),OriginalDir));
463
464   DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n",
465         getuid(),geteuid(),getgid(),getegid()));
466
467   current_user.cnum = -1;
468   current_user.vuid = UID_FIELD_INVALID;
469
470   return(True);
471 }
472
473
474 /****************************************************************************
475 This is a utility function of smbrun(). It must be called only from
476 the child as it may leave the caller in a privilaged state.
477 ****************************************************************************/
478 static BOOL setup_stdout_file(char *outfile,BOOL shared)
479 {  
480   int fd;
481   struct stat st;
482   mode_t mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH;
483   int flags = O_RDWR|O_CREAT|O_TRUNC|O_EXCL;
484
485   close(1);
486
487   if (shared) {
488     /* become root - unprivilaged users can't delete these files */
489 #ifdef USE_SETRES
490     setresgid(0,0,0);
491     setresuid(0,0,0);
492 #else      
493     setuid(0);
494     seteuid(0);
495 #endif
496   }
497
498   if(stat(outfile, &st) == 0) {
499     /* Check we're not deleting a device file. */ 
500     if(st.st_mode & S_IFREG)
501       unlink(outfile);
502     else
503       flags = O_RDWR;
504   }
505   /* now create the file */
506   fd = open(outfile,flags,mode);
507
508   if (fd == -1) return False;
509
510   if (fd != 1) {
511     if (dup2(fd,1) != 0) {
512       DEBUG(2,("Failed to create stdout file descriptor\n"));
513       close(fd);
514       return False;
515     }
516     close(fd);
517   }
518   return True;
519 }
520
521
522 /****************************************************************************
523 run a command being careful about uid/gid handling and putting the output in
524 outfile (or discard it if outfile is NULL).
525
526 if shared is True then ensure the file will be writeable by all users
527 but created such that its owned by root. This overcomes a security hole.
528
529 if shared is not set then open the file with O_EXCL set
530 ****************************************************************************/
531 int smbrun(char *cmd,char *outfile,BOOL shared)
532 {
533   int fd,pid;
534   int uid = current_user.uid;
535   int gid = current_user.gid;
536
537 #if USE_SYSTEM
538   int ret;
539   pstring syscmd;  
540   char *path = lp_smbrun();
541
542   /* in the old method we use system() to execute smbrun which then
543      executes the command (using system() again!). This involves lots
544      of shell launches and is very slow. It also suffers from a
545      potential security hole */
546   if (!file_exist(path,NULL))
547     {
548       DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",path));
549       return(1);
550     }
551
552   slprintf(syscmd,sizeof(syscmd)-1,"%s %d %d \"(%s 2>&1) > %s\"",
553           path,uid,gid,cmd,
554           outfile?outfile:"/dev/null");
555
556   DEBUG(5,("smbrun - running %s ",syscmd));
557   ret = system(syscmd);
558   DEBUG(5,("gave %d\n",ret));
559   return(ret);
560 #else
561   /* in this newer method we will exec /bin/sh with the correct
562      arguments, after first setting stdout to point at the file */
563
564   if ((pid=fork())) {
565     int status=0;
566     /* the parent just waits for the child to exit */
567     if (sys_waitpid(pid,&status,0) != pid) {
568       DEBUG(2,("waitpid(%d) : %s\n",pid,strerror(errno)));
569       return -1;
570     }
571     return status;
572   }
573
574
575   /* we are in the child. we exec /bin/sh to do the work for us. we
576      don't directly exec the command we want because it may be a
577      pipeline or anything else the config file specifies */
578
579   /* point our stdout at the file we want output to go into */
580   if (outfile && !setup_stdout_file(outfile,shared)) {
581     exit(80);
582   }
583
584   /* now completely lose our privilages. This is a fairly paranoid
585      way of doing it, but it does work on all systems that I know of */
586 #ifdef USE_SETRES
587   setresgid(0,0,0);
588   setresuid(0,0,0);
589   setresgid(gid,gid,gid);
590   setresuid(uid,uid,uid);      
591 #else      
592   setuid(0);
593   seteuid(0);
594   setgid(gid);
595   setegid(gid);
596   setuid(uid);
597   seteuid(uid);
598 #endif
599
600   if (getuid() != uid || geteuid() != uid ||
601       getgid() != gid || getegid() != gid) {
602     /* we failed to lose our privilages - do not execute the command */
603     exit(81); /* we can't print stuff at this stage, instead use exit codes
604                  for debugging */
605   }
606
607   /* close all other file descriptors, leaving only 0, 1 and 2. 0 and
608      2 point to /dev/null from the startup code */
609   for (fd=3;fd<256;fd++) close(fd);
610
611   execl("/bin/sh","sh","-c",cmd,NULL);  
612
613   /* not reached */
614   exit(82);
615 #endif
616   return 1;
617 }
618
619 static struct current_user current_user_saved;
620 static int become_root_depth;
621 static pstring become_root_dir;
622
623 /****************************************************************************
624 This is used when we need to do a privilaged operation (such as mucking
625 with share mode files) and temporarily need root access to do it. This
626 call should always be paired with an unbecome_root() call immediately
627 after the operation
628
629 Set save_dir if you also need to save/restore the CWD 
630 ****************************************************************************/
631 void become_root(BOOL save_dir) 
632 {
633         if (become_root_depth) {
634                 DEBUG(0,("ERROR: become root depth is non zero\n"));
635         }
636         if (save_dir)
637                 GetWd(become_root_dir);
638
639         current_user_saved = current_user;
640         become_root_depth = 1;
641
642         become_uid(0);
643         become_gid(0);
644 }
645
646 /****************************************************************************
647 When the privilaged operation is over call this
648
649 Set save_dir if you also need to save/restore the CWD 
650 ****************************************************************************/
651 void unbecome_root(BOOL restore_dir)
652 {
653         if (become_root_depth != 1) {
654                 DEBUG(0,("ERROR: unbecome root depth is %d\n",
655                          become_root_depth));
656         }
657
658         /* we might have done a become_user() while running as root,
659            if we have then become root again in order to become 
660            non root! */
661         if (current_user.uid != 0) {
662                 become_uid(0);
663         }
664
665         /* restore our gid first */
666         if (!become_gid(current_user_saved.gid)) {
667                 DEBUG(0,("ERROR: Failed to restore gid\n"));
668                 exit_server("Failed to restore gid");
669         }
670
671 #ifndef NO_SETGROUPS      
672         if (current_user_saved.ngroups > 0) {
673                 if (setgroups(current_user_saved.ngroups,
674                               current_user_saved.groups)<0)
675                         DEBUG(0,("ERROR: setgroups call failed!\n"));
676         }
677 #endif
678
679         /* now restore our uid */
680         if (!become_uid(current_user_saved.uid)) {
681                 DEBUG(0,("ERROR: Failed to restore uid\n"));
682                 exit_server("Failed to restore uid");
683         }
684
685         if (restore_dir)
686                 ChDir(become_root_dir);
687
688         current_user = current_user_saved;
689
690         become_root_depth = 0;
691 }