To stop people complaining about the mktemp call, move it into lib/util.c. Thanks
[samba.git] / source3 / lib / smbrun.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    run a command as a specified user
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 /* need to move this from here!! need some sleep ... */
25 struct current_user current_user;
26
27 extern int DEBUGLEVEL;
28
29 /****************************************************************************
30 This is a utility function of smbrun(). It must be called only from
31 the child as it may leave the caller in a privileged state.
32 ****************************************************************************/
33 static BOOL setup_stdout_file(char *outfile,BOOL shared)
34 {  
35   int fd;
36   mode_t mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH;
37   int flags = O_RDWR|O_CREAT|O_EXCL;
38
39   close(1);
40
41   if (shared) {
42         /* become root - unprivileged users can't delete these files */
43         gain_root_privilege();
44         gain_root_group_privilege();
45   }
46
47   unlink(outfile);
48
49   /* now create the file */
50   fd = sys_open(outfile,flags,mode);
51
52   if (fd == -1) return False;
53
54   if (fd != 1) {
55     if (dup2(fd,1) != 1) {
56       DEBUG(2,("Failed to create stdout file descriptor\n"));
57       close(fd);
58       return False;
59     }
60     close(fd);
61   }
62   return True;
63 }
64
65
66 /****************************************************************************
67 run a command being careful about uid/gid handling and putting the output in
68 outfile (or discard it if outfile is NULL).
69
70 if shared is True then ensure the file will be writeable by all users
71 but created such that its owned by root. This overcomes a security hole.
72
73 if shared is not set then open the file with O_EXCL set
74 ****************************************************************************/
75 int smbrun(char *cmd,char *outfile,BOOL shared)
76 {
77         int fd;
78         pid_t pid;
79         uid_t uid = current_user.uid;
80         gid_t gid = current_user.gid;
81         
82         /*
83          * Lose any kernel oplock capabilities we may have.
84          */
85         oplock_set_capability(False, False);
86
87 #ifndef HAVE_EXECL
88         {
89         int ret;
90         pstring syscmd;  
91         char *path = lp_smbrun();
92         
93         /* in the old method we use system() to execute smbrun which then
94            executes the command (using system() again!). This involves lots
95            of shell launches and is very slow. It also suffers from a
96            potential security hole */
97         if (!file_exist(path,NULL)) {
98                 DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",path));
99                 return(1);
100         }
101
102         slprintf(syscmd,sizeof(syscmd)-1,"%s %d %d \"(%s 2>&1) > %s\"",
103                  path,(int)uid,(int)gid,cmd,
104                  outfile?outfile:"/dev/null");
105         
106         DEBUG(5,("smbrun - running %s ",syscmd));
107         ret = system(syscmd);
108         DEBUG(5,("gave %d\n",ret));
109         return(ret);
110         }
111 #else
112         /* in this newer method we will exec /bin/sh with the correct
113            arguments, after first setting stdout to point at the file */
114
115         /*
116          * We need to temporarily stop CatchChild from eating
117          * SIGCLD signals as it also eats the exit status code. JRA.
118          */
119
120         CatchChildLeaveStatus();
121                                         
122         if ((pid=sys_fork()) < 0) {
123                 DEBUG(0,("smbrun: fork failed with error %s\n", strerror(errno) ));
124                 CatchChild(); 
125                 return errno;
126     }
127
128         if (pid) {
129                 /*
130                  * Parent.
131                  */
132                 int status=0;
133                 pid_t wpid;
134
135                 
136                 /* the parent just waits for the child to exit */
137                 while((wpid = sys_waitpid(pid,&status,0)) < 0) {
138                         if(errno == EINTR) {
139                                 errno = 0;
140                                 continue;
141                         }
142                         break;
143                 }
144
145                 CatchChild(); 
146
147                 if (wpid != pid) {
148                         DEBUG(2,("waitpid(%d) : %s\n",(int)pid,strerror(errno)));
149                         return -1;
150                 }
151 #if defined(WIFEXITED) && defined(WEXITSTATUS)
152                 if (WIFEXITED(status)) {
153                         return WEXITSTATUS(status);
154                 }
155 #endif
156                 return status;
157         }
158         
159         CatchChild(); 
160         
161         /* we are in the child. we exec /bin/sh to do the work for us. we
162            don't directly exec the command we want because it may be a
163            pipeline or anything else the config file specifies */
164         
165         /* point our stdout at the file we want output to go into */
166         if (outfile && !setup_stdout_file(outfile,shared)) {
167                 exit(80);
168         }
169         
170         /* now completely lose our privileges. This is a fairly paranoid
171            way of doing it, but it does work on all systems that I know of */
172
173         become_user_permanently(uid, gid);
174
175         if (getuid() != uid || geteuid() != uid ||
176             getgid() != gid || getegid() != gid) {
177                 /* we failed to lose our privileges - do not execute
178                    the command */
179                 exit(81); /* we can't print stuff at this stage,
180                              instead use exit codes for debugging */
181         }
182         
183 #ifndef __INSURE__
184         /* close all other file descriptors, leaving only 0, 1 and 2. 0 and
185            2 point to /dev/null from the startup code */
186         for (fd=3;fd<256;fd++) close(fd);
187 #endif
188
189         execl("/bin/sh","sh","-c",cmd,NULL);  
190         
191         /* not reached */
192         exit(82);
193 #endif
194         return 1;
195 }