root/source3/lib/smbrun.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. setup_out_fd
  2. smbrun_internal
  3. smbrun_no_sanitize
  4. smbrun
  5. smbrunsecret

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

/* [<][>][^][v][top][bottom][index][help] */