]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afprun.c
Merge remote-tracking branch 'remotes/origin/branch-netatalk-2-1'
[netatalk.git] / etc / afpd / afprun.c
1 /* 
2    Unix SMB/CIFS implementation.
3    run a command as a specified user
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    modified for netatalk dgautheron@magic.fr
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif /* HAVE_CONFIG_H */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 #include <sys/types.h>
31 #define __USE_GNU 1
32 #include <unistd.h>
33
34 #include <errno.h>
35
36 #ifdef HAVE_SYS_WAIT_H 
37 #include <sys/wait.h>
38 #endif
39
40 #include <sys/param.h>  
41 #include <string.h>
42
43 /* FIXME */
44 #ifdef linux
45 #ifndef USE_SETRESUID
46 #define USE_SETRESUID 1
47 #endif
48 #else
49 #ifndef USE_SETEUID
50 #define USE_SETEUID 1
51 #endif
52 #endif
53
54 #include <atalk/logger.h>
55
56 /**************************************************************************n
57  Find a suitable temporary directory. The result should be copied immediately
58   as it may be overwritten by a subsequent call.
59   ****************************************************************************/
60    
61 static const char *tmpdir(void)
62 {
63     char *p;
64
65     if ((p = getenv("TMPDIR")))
66         return p;
67     return "/tmp";
68 }
69
70 /****************************************************************************
71 This is a utility function of afprun().
72 ****************************************************************************/
73
74 static int setup_out_fd(void)
75 {  
76         int fd;
77         char path[MAXPATHLEN +1];
78
79         snprintf(path, sizeof(path)-1, "%s/afp.XXXXXX", tmpdir());
80
81         /* now create the file */
82         fd = mkstemp(path);
83
84         if (fd == -1) {
85                 LOG(log_error, logtype_afpd, "setup_out_fd: Failed to create file %s. (%s)",path, strerror(errno) );
86                 return -1;
87         }
88
89         /* Ensure file only kept around by open fd. */
90         unlink(path);
91         return fd;
92 }
93
94 /****************************************************************************
95  Gain root privilege before doing something.
96  We want to end up with ruid==euid==0
97 ****************************************************************************/
98 static void gain_root_privilege(void)
99 {
100         seteuid(0);
101 }
102  
103 /****************************************************************************
104  Ensure our real and effective groups are zero.
105  we want to end up with rgid==egid==0
106 ****************************************************************************/
107 static void gain_root_group_privilege(void)
108 {
109         setegid(0);
110 }
111
112 /****************************************************************************
113  Become the specified uid and gid - permanently !
114  there should be no way back if possible
115 ****************************************************************************/
116 static void become_user_permanently(uid_t uid, gid_t gid)
117 {
118     /*
119      * First - gain root privilege. We do this to ensure
120      * we can lose it again.
121      */
122  
123     gain_root_privilege();
124     gain_root_group_privilege();
125  
126 #if USE_SETRESUID
127     setresgid(gid,gid,gid);
128     setgid(gid);
129     setresuid(uid,uid,uid);
130     setuid(uid);
131 #endif
132  
133 #if USE_SETREUID
134     setregid(gid,gid);
135     setgid(gid);
136     setreuid(uid,uid);
137     setuid(uid);
138 #endif
139  
140 #if USE_SETEUID
141     setegid(gid);
142     setgid(gid);
143     setuid(uid);
144     seteuid(uid);
145     setuid(uid);
146 #endif
147  
148 #if USE_SETUIDX
149     setgidx(ID_REAL, gid);
150     setgidx(ID_EFFECTIVE, gid);
151     setgid(gid);
152     setuidx(ID_REAL, uid);
153     setuidx(ID_EFFECTIVE, uid);
154     setuid(uid);
155 #endif
156 }
157
158 /****************************************************************************
159 run a command being careful about uid/gid handling and putting the output in
160 outfd (or discard it if outfd is NULL).
161 ****************************************************************************/
162
163 int afprun(int root, char *cmd, int *outfd)
164 {
165     pid_t pid;
166     uid_t uid = geteuid();
167     gid_t gid = getegid();
168         
169     /* point our stdout at the file we want output to go into */
170     if (outfd && ((*outfd = setup_out_fd()) == -1)) {
171         return -1;
172     }
173     LOG(log_debug, logtype_afpd, "running %s as user %d", cmd, root?0:uid);
174     /* in this method we will exec /bin/sh with the correct
175        arguments, after first setting stdout to point at the file */
176
177     if ((pid=fork()) < 0) {
178         LOG(log_error, logtype_afpd, "afprun: fork failed with error %s", strerror(errno) );
179         if (outfd) {
180             close(*outfd);
181             *outfd = -1;
182         }
183         return errno;
184     }
185
186     if (pid) {
187         /*
188          * Parent.
189          */
190         int status=0;
191         pid_t wpid;
192
193         /* the parent just waits for the child to exit */
194         while((wpid = waitpid(pid,&status,0)) < 0) {
195             if (errno == EINTR) {
196                 errno = 0;
197                 continue;
198             }
199             break;
200         }
201         if (wpid != pid) {
202             LOG(log_error, logtype_afpd, "waitpid(%d) : %s",(int)pid, strerror(errno) );
203             if (outfd) {
204                 close(*outfd);
205                 *outfd = -1;
206             }
207             return -1;
208         }
209         /* Reset the seek pointer. */
210         if (outfd) {
211             lseek(*outfd, 0, SEEK_SET);
212         }
213
214 #if defined(WIFEXITED) && defined(WEXITSTATUS)
215         if (WIFEXITED(status)) {
216             return WEXITSTATUS(status);
217         }
218 #endif
219         return status;
220     }
221     
222     /* we are in the child. we exec /bin/sh to do the work for us. we
223        don't directly exec the command we want because it may be a
224        pipeline or anything else the config file specifies */
225
226     /* point our stdout at the file we want output to go into */
227     if (outfd) {
228         close(1);
229         if (dup2(*outfd,1) != 1) {
230             LOG(log_error, logtype_afpd, "Failed to create stdout file descriptor");
231             close(*outfd);
232             exit(80);
233         }
234     }
235     
236     if (chdir("/") < 0) {
237         LOG(log_error, logtype_afpd, "afprun: can't change directory to \"/\" %s", strerror(errno) );
238         exit(83);
239     }
240
241     /* now completely lose our privileges. This is a fairly paranoid
242        way of doing it, but it does work on all systems that I know of */
243     if (root) {
244         become_user_permanently(0, 0);
245         uid = gid = 0;
246     }
247     else {
248         become_user_permanently(uid, gid);
249     }
250     if (getuid() != uid || geteuid() != uid || getgid() != gid || getegid() != gid) {
251         /* we failed to lose our privileges - do not execute the command */
252         exit(81); /* we can't print stuff at this stage, instead use exit codes for debugging */
253     }
254     
255     /* close all other file descriptors, leaving only 0, 1 and 2. 0 and
256        2 point to /dev/null from the startup code */
257     {
258         int fd;
259         for (fd=3;fd<256;fd++) close(fd);
260     }
261
262     execl("/bin/sh","sh","-c",cmd,NULL);  
263     /* not reached */
264     exit(82);
265     return 1;
266 }