LCOV - code coverage report
Current view: top level - source3/lib - smbrun.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 51 139 36.7 %
Date: 2024-04-21 15:09:00 Functions: 4 5 80.0 %

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

Generated by: LCOV version 1.14