1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00
samba-mirror/source3/lib/smbrun.c

302 lines
7.2 KiB
C
Raw Normal View History

/*
Unix SMB/CIFS implementation.
run a command as a specified user
Copyright (C) Andrew Tridgell 1992-1998
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
/* need to move this from here!! need some sleep ... */
struct current_user current_user;
/****************************************************************************
This is a utility function of smbrun().
****************************************************************************/
static int setup_out_fd(void)
{
int fd;
pstring path;
slprintf(path, sizeof(path)-1, "%s/smb.XXXXXX", tmpdir());
/* now create the file */
fd = smb_mkstemp(path);
if (fd == -1) {
DEBUG(0,("setup_out_fd: Failed to create file %s. (%s)\n",
path, strerror(errno) ));
return -1;
}
DEBUG(10,("setup_out_fd: Created tmp file %s\n", path ));
/* Ensure file only kept around by open fd. */
unlink(path);
return fd;
}
/****************************************************************************
run a command being careful about uid/gid handling and putting the output in
outfd (or discard it if outfd is NULL).
****************************************************************************/
int smbrun(const char *cmd, int *outfd)
{
pid_t pid;
uid_t uid = current_user.uid;
gid_t gid = current_user.gid;
/*
* Lose any kernel oplock capabilities we may have.
*/
oplock_set_capability(False, False);
Makefile.in: Fixed bug with continuation line causing proto to fail. Added $(PROGS) $(SPROGS) as targets for make clean. acconfig.h: Added HAVE_IRIX_SPECIFIC_CAPABILITIES. configure.in: Added sys/capability.h header check. Added function checks for srandom random srand rand. Added HAVE_IRIX_SPECIFIC_CAPABILITIES test. includes.h: Added #include <sys/capability.h>. ntdomain.h: Moved struct acct_info into here from smb.h smb.h: Added KERNEL_OPLOCK_CAPABILITY define. Moved enum action_type into rpcclient.h Moved struct cli_state into client.h Moved struct nt_client_info, struct tar_client_info, struct client_info into rpcclient.h lib/genrand.c: Changed to use sys_random() & friends. lib/smbrun.c: Lose capabilities after fork. lib/system.c: Added set_process_capability(), set_inherited_process_capability() sys_random(), sys_srandom(). lib/util.c: Added Ander's EFBIG lock check to fcntl_lock for 64 bit access to an 32 bit mounted NFS filesystem. nmbd/nmbd.c: Changed to use sys_random() & friends. nmbd/nmbd_browsesync.c: Changed to use sys_random() & friends. passdb/ldap.c: Missed one pdb_encode_acct_ctrl call. passdb/passdb.c: Changed to Ander's code for ' ' characters. passdb/smbpass.c: Added Ander's code to reset ACB_PWNOTREQ. script/mkproto.awk: Added 'long' to prototypes. smbd/chgpasswd.c: Lose capabilities after fork. smbd/open.c: Do the mmap *after* the kernel oplock. smbd/oplock.c: Removed stub code from kernel oplock path. Added set_process_capability(), set_inherited_process_capability() calls. smbd/reply.c: Initialize count = 0, offset = 0. smbd/server.c: Added set_process_capability(), set_inherited_process_capability() calls. tests/summary.c: Ensure we have RANDOM or RAND. utils/smbpasswd.c: Added Ander's code to reset ACB_PWNOTREQ. utils/torture.c: Changed to use sys_random() & friends. Jeremy. (This used to be commit e8be306f23963ac00b1a383ebe0cc1421529fb02)
1998-09-26 03:40:49 +04:00
/* point our stdout at the file we want output to go into */
if (outfd && ((*outfd = setup_out_fd()) == -1)) {
return -1;
}
/* in this method we will exec /bin/sh with the correct
arguments, after first setting stdout to point at the file */
/*
* We need to temporarily stop CatchChild from eating
* SIGCLD signals as it also eats the exit status code. JRA.
*/
CatchChildLeaveStatus();
if ((pid=sys_fork()) < 0) {
DEBUG(0,("smbrun: fork failed with error %s\n", strerror(errno) ));
CatchChild();
if (outfd) {
close(*outfd);
*outfd = -1;
}
return errno;
}
if (pid) {
/*
* Parent.
*/
int status=0;
pid_t wpid;
/* the parent just waits for the child to exit */
while((wpid = sys_waitpid(pid,&status,0)) < 0) {
if(errno == EINTR) {
errno = 0;
continue;
}
break;
}
CatchChild();
if (wpid != pid) {
DEBUG(2,("waitpid(%d) : %s\n",(int)pid,strerror(errno)));
if (outfd) {
close(*outfd);
*outfd = -1;
}
return -1;
}
/* Reset the seek pointer. */
if (outfd) {
sys_lseek(*outfd, 0, SEEK_SET);
}
#if defined(WIFEXITED) && defined(WEXITSTATUS)
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
}
#endif
return status;
}
CatchChild();
/* we are in the child. we exec /bin/sh to do the work for us. we
don't directly exec the command we want because it may be a
pipeline or anything else the config file specifies */
/* point our stdout at the file we want output to go into */
if (outfd) {
close(1);
if (sys_dup2(*outfd,1) != 1) {
DEBUG(2,("Failed to create stdout file descriptor\n"));
close(*outfd);
exit(80);
}
}
/* now completely lose our privileges. This is a fairly paranoid
way of doing it, but it does work on all systems that I know of */
become_user_permanently(uid, gid);
if (getuid() != uid || geteuid() != uid ||
getgid() != gid || getegid() != gid) {
/* we failed to lose our privileges - do not execute
the command */
exit(81); /* we can't print stuff at this stage,
instead use exit codes for debugging */
}
#ifndef __INSURE__
/* close all other file descriptors, leaving only 0, 1 and 2. 0 and
2 point to /dev/null from the startup code */
{
int fd;
for (fd=3;fd<256;fd++) close(fd);
}
#endif
execl("/bin/sh","sh","-c",cmd,NULL);
/* not reached */
exit(82);
return 1;
}
/****************************************************************************
run a command being careful about uid/gid handling and putting the output in
outfd (or discard it if outfd is NULL).
sends the provided secret to the child stdin.
****************************************************************************/
int smbrunsecret(const char *cmd, const char *secret)
{
pid_t pid;
uid_t uid = current_user.uid;
gid_t gid = current_user.gid;
int ifd[2];
/*
* Lose any kernel oplock capabilities we may have.
*/
oplock_set_capability(False, False);
/* build up an input pipe */
if(pipe(ifd)) {
return -1;
}
/* in this method we will exec /bin/sh with the correct
arguments, after first setting stdout to point at the file */
/*
* We need to temporarily stop CatchChild from eating
* SIGCLD signals as it also eats the exit status code. JRA.
*/
CatchChildLeaveStatus();
if ((pid=sys_fork()) < 0) {
DEBUG(0, ("smbrunsecret: fork failed with error %s\n", strerror(errno)));
CatchChild();
return errno;
}
if (pid) {
/*
* Parent.
*/
int status = 0;
pid_t wpid;
close(ifd[0]);
/* send the secret */
write(ifd[1], secret, strlen(secret));
fsync(ifd[1]);
close(ifd[1]);
/* the parent just waits for the child to exit */
while((wpid = sys_waitpid(pid, &status, 0)) < 0) {
if(errno == EINTR) {
errno = 0;
continue;
}
break;
}
CatchChild();
if (wpid != pid) {
DEBUG(2, ("waitpid(%d) : %s\n", (int)pid, strerror(errno)));
return -1;
}
#if defined(WIFEXITED) && defined(WEXITSTATUS)
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
}
#endif
return status;
}
CatchChild();
/* we are in the child. we exec /bin/sh to do the work for us. we
don't directly exec the command we want because it may be a
pipeline or anything else the config file specifies */
close(ifd[1]);
close(0);
if (sys_dup2(ifd[0], 0) != 0) {
DEBUG(2,("Failed to create stdin file descriptor\n"));
close(ifd[0]);
exit(80);
}
/* now completely lose our privileges. This is a fairly paranoid
way of doing it, but it does work on all systems that I know of */
become_user_permanently(uid, gid);
if (getuid() != uid || geteuid() != uid ||
getgid() != gid || getegid() != gid) {
/* we failed to lose our privileges - do not execute
the command */
exit(81); /* we can't print stuff at this stage,
instead use exit codes for debugging */
}
#ifndef __INSURE__
/* close all other file descriptors, leaving only 0, 1 and 2. 0 and
2 point to /dev/null from the startup code */
{
int fd;
for (fd = 3; fd < 256; fd++) close(fd);
}
#endif
execl("/bin/sh", "sh", "-c", cmd, NULL);
/* not reached */
exit(82);
return 1;
}