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 21#include "includes.h" 22 23/* need to move this from here!! need some sleep ... */ 24struct current_user current_user; 25 26/**************************************************************************** 27This is a utility function of smbrun(). 28****************************************************************************/ 29 30static int setup_out_fd(void) 31{ 32 int fd; 33 pstring path; 34 35 slprintf(path, sizeof(path)-1, "%s/smb.XXXXXX", tmpdir()); 36 37 /* now create the file */ 38 fd = smb_mkstemp(path); 39 40 if (fd == -1) { 41 DEBUG(0,("setup_out_fd: Failed to create file %s. (%s)\n", 42 path, strerror(errno) )); 43 return -1; 44 } 45 46 DEBUG(10,("setup_out_fd: Created tmp file %s\n", path )); 47 48 /* Ensure file only kept around by open fd. */ 49 unlink(path); 50 return fd; 51} 52 53/**************************************************************************** 54run a command being careful about uid/gid handling and putting the output in 55outfd (or discard it if outfd is NULL). 56****************************************************************************/ 57 58int smbrun(char *cmd, int *outfd) 59{ 60 pid_t pid; 61 uid_t uid = current_user.uid; 62 gid_t gid = current_user.gid; 63 64 /* 65 * Lose any kernel oplock capabilities we may have. 66 */ 67 oplock_set_capability(False, False); 68 69 /* point our stdout at the file we want output to go into */ 70 71 if (outfd && ((*outfd = setup_out_fd()) == -1)) { 72 return -1; 73 } 74 75 /* in this method we will exec /bin/sh with the correct 76 arguments, after first setting stdout to point at the file */ 77 78 /* 79 * We need to temporarily stop CatchChild from eating 80 * SIGCLD signals as it also eats the exit status code. JRA. 81 */ 82 83 CatchChildLeaveStatus(); 84 85 if ((pid=sys_fork()) < 0) { 86 DEBUG(0,("smbrun: fork failed with error %s\n", strerror(errno) )); 87 CatchChild(); 88 if (outfd) { 89 close(*outfd); 90 *outfd = -1; 91 } 92 return errno; 93 } 94 95 if (pid) { 96 /* 97 * Parent. 98 */ 99 int status=0; 100 pid_t wpid; 101 102 103 /* the parent just waits for the child to exit */ 104 while((wpid = sys_waitpid(pid,&status,0)) < 0) { 105 if(errno == EINTR) { 106 errno = 0; 107 continue; 108 } 109 break; 110 } 111 112 CatchChild(); 113 114 if (wpid != pid) { 115 DEBUG(2,("waitpid(%d) : %s\n",(int)pid,strerror(errno))); 116 if (outfd) { 117 close(*outfd); 118 *outfd = -1; 119 } 120 return -1; 121 } 122 123 /* Reset the seek pointer. */ 124 if (outfd) { 125 sys_lseek(*outfd, 0, SEEK_SET); 126 } 127 128#if defined(WIFEXITED) && defined(WEXITSTATUS) 129 if (WIFEXITED(status)) { 130 return WEXITSTATUS(status); 131 } 132#endif 133 134 return status; 135 } 136 137 CatchChild(); 138 139 /* we are in the child. we exec /bin/sh to do the work for us. we 140 don't directly exec the command we want because it may be a 141 pipeline or anything else the config file specifies */ 142 143 /* point our stdout at the file we want output to go into */ 144 if (outfd) { 145 close(1); 146 if (sys_dup2(*outfd,1) != 1) { 147 DEBUG(2,("Failed to create stdout file descriptor\n")); 148 close(*outfd); 149 exit(80); 150 } 151 } 152 153 /* now completely lose our privileges. This is a fairly paranoid 154 way of doing it, but it does work on all systems that I know of */ 155 156 become_user_permanently(uid, gid); 157 158 if (getuid() != uid || geteuid() != uid || 159 getgid() != gid || getegid() != gid) { 160 /* we failed to lose our privileges - do not execute 161 the command */ 162 exit(81); /* we can't print stuff at this stage, 163 instead use exit codes for debugging */ 164 } 165 166#ifndef __INSURE__ 167 /* close all other file descriptors, leaving only 0, 1 and 2. 0 and 168 2 point to /dev/null from the startup code */ 169 { 170 int fd; 171 for (fd=3;fd<256;fd++) close(fd); 172 } 173#endif 174 175 execl("/bin/sh","sh","-c",cmd,NULL); 176 177 /* not reached */ 178 exit(82); 179 return 1; 180} 181 182 183/**************************************************************************** 184run a command being careful about uid/gid handling and putting the output in 185outfd (or discard it if outfd is NULL). 186sends the provided secret to the child stdin. 187****************************************************************************/ 188 189int smbrunsecret(char *cmd, char *secret) 190{ 191 pid_t pid; 192 uid_t uid = current_user.uid; 193 gid_t gid = current_user.gid; 194 int ifd[2]; 195 196 /* 197 * Lose any kernel oplock capabilities we may have. 198 */ 199 oplock_set_capability(False, False); 200 201 /* build up an input pipe */ 202 if(pipe(ifd)) { 203 return -1; 204 } 205 206 /* in this method we will exec /bin/sh with the correct 207 arguments, after first setting stdout to point at the file */ 208 209 /* 210 * We need to temporarily stop CatchChild from eating 211 * SIGCLD signals as it also eats the exit status code. JRA. 212 */ 213 214 CatchChildLeaveStatus(); 215 216 if ((pid=sys_fork()) < 0) { 217 DEBUG(0, ("smbrunsecret: fork failed with error %s\n", strerror(errno))); 218 CatchChild(); 219 return errno; 220 } 221 222 if (pid) { 223 /* 224 * Parent. 225 */ 226 int status = 0; 227 pid_t wpid; 228 229 close(ifd[0]); 230 /* send the secret */ 231 write(ifd[1], secret, strlen(secret)); 232 fsync(ifd[1]); 233 close(ifd[1]); 234 235 /* the parent just waits for the child to exit */ 236 while((wpid = sys_waitpid(pid, &status, 0)) < 0) { 237 if(errno == EINTR) { 238 errno = 0; 239 continue; 240 } 241 break; 242 } 243 244 CatchChild(); 245 246 if (wpid != pid) { 247 DEBUG(2, ("waitpid(%d) : %s\n", (int)pid, strerror(errno))); 248 return -1; 249 } 250 251#if defined(WIFEXITED) && defined(WEXITSTATUS) 252 if (WIFEXITED(status)) { 253 return WEXITSTATUS(status); 254 } 255#endif 256 257 return status; 258 } 259 260 CatchChild(); 261 262 /* we are in the child. we exec /bin/sh to do the work for us. we 263 don't directly exec the command we want because it may be a 264 pipeline or anything else the config file specifies */ 265 266 close(ifd[1]); 267 close(0); 268 if (sys_dup2(ifd[0], 0) != 0) { 269 DEBUG(2,("Failed to create stdin file descriptor\n")); 270 close(ifd[0]); 271 exit(80); 272 } 273 274 /* now completely lose our privileges. This is a fairly paranoid 275 way of doing it, but it does work on all systems that I know of */ 276 277 become_user_permanently(uid, gid); 278 279 if (getuid() != uid || geteuid() != uid || 280 getgid() != gid || getegid() != gid) { 281 /* we failed to lose our privileges - do not execute 282 the command */ 283 exit(81); /* we can't print stuff at this stage, 284 instead use exit codes for debugging */ 285 } 286 287#ifndef __INSURE__ 288 /* close all other file descriptors, leaving only 0, 1 and 2. 0 and 289 2 point to /dev/null from the startup code */ 290 { 291 int fd; 292 for (fd = 3; fd < 256; fd++) close(fd); 293 } 294#endif 295 296 execl("/bin/sh", "sh", "-c", cmd, NULL); 297 298 /* not reached */ 299 exit(82); 300 return 1; 301} 302