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

365 lines
8.8 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "system/filesys.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;
TALLOC_CTX *ctx = talloc_stackframe();
char *path = NULL;
mode_t mask;
path = talloc_asprintf(ctx,
"%s/smb.XXXXXX",
tmpdir());
if (!path) {
TALLOC_FREE(ctx);
errno = ENOMEM;
return -1;
}
/* now create the file */
mask = umask(S_IRWXO | S_IRWXG);
fd = mkstemp(path);
umask(mask);
if (fd == -1) {
DEBUG(0,("setup_out_fd: Failed to create file %s. (%s)\n",
path, strerror(errno) ));
TALLOC_FREE(ctx);
return -1;
}
DEBUG(10,("setup_out_fd: Created tmp file %s\n", path ));
/* Ensure file only kept around by open fd. */
unlink(path);
TALLOC_FREE(ctx);
return fd;
}
/****************************************************************************
run a command being careful about uid/gid handling and putting the output in
outfd (or discard it if outfd is NULL).
****************************************************************************/
static int smbrun_internal(const char *cmd, int *outfd, bool sanitize,
char * const *env)
{
pid_t pid;
uid_t uid = current_user.ut.uid;
gid_t gid = current_user.ut.gid;
void (*saved_handler)(int);
/*
* Lose any elevated privileges.
*/
drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
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.
*/
saved_handler = CatchChildLeaveStatus();
if ((pid=fork()) < 0) {
DEBUG(0,("smbrun: fork failed with error %s\n", strerror(errno) ));
(void)CatchSignal(SIGCLD, saved_handler);
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 = waitpid(pid,&status,0)) < 0) {
if(errno == EINTR) {
errno = 0;
continue;
}
break;
}
(void)CatchSignal(SIGCLD, saved_handler);
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) {
2012-03-28 05:37:04 +04:00
lseek(*outfd, 0, SEEK_SET);
}
#if defined(WIFEXITED) && defined(WEXITSTATUS)
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
}
#endif
return status;
}
(void)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 (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 (!non_root_mode()) {
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
{
2009-02-10 00:25:59 +03:00
char *newcmd = NULL;
if (sanitize) {
newcmd = escape_shell_string(cmd);
if (!newcmd)
exit(82);
}
2009-02-10 00:25:59 +03:00
if (env != NULL) {
execle("/bin/sh","sh","-c",
newcmd ? (const char *)newcmd : cmd, NULL,
env);
} else {
execl("/bin/sh","sh","-c",
newcmd ? (const char *)newcmd : cmd, NULL);
}
2009-02-10 00:25:59 +03:00
SAFE_FREE(newcmd);
}
/* not reached */
exit(83);
return 1;
}
/****************************************************************************
Use only in known safe shell calls (printing).
****************************************************************************/
int smbrun_no_sanitize(const char *cmd, int *outfd, char * const *env)
{
return smbrun_internal(cmd, outfd, false, env);
}
/****************************************************************************
By default this now sanitizes shell expansion.
****************************************************************************/
int smbrun(const char *cmd, int *outfd, char * const *env)
{
return smbrun_internal(cmd, outfd, true, env);
}
/****************************************************************************
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.ut.uid;
gid_t gid = current_user.ut.gid;
int ifd[2];
void (*saved_handler)(int);
/*
* Lose any elevated privileges.
*/
drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
/* 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.
*/
saved_handler = CatchChildLeaveStatus();
if ((pid=fork()) < 0) {
DEBUG(0, ("smbrunsecret: fork failed with error %s\n", strerror(errno)));
(void)CatchSignal(SIGCLD, saved_handler);
return errno;
}
if (pid) {
/*
* Parent.
*/
int status = 0;
pid_t wpid;
size_t towrite;
ssize_t wrote;
close(ifd[0]);
/* send the secret */
towrite = strlen(secret);
wrote = write(ifd[1], secret, towrite);
if ( wrote != towrite ) {
DEBUG(0,("smbrunsecret: wrote %ld of %lu bytes\n",(long)wrote,(unsigned long)towrite));
}
fsync(ifd[1]);
close(ifd[1]);
/* the parent just waits for the child to exit */
while((wpid = waitpid(pid, &status, 0)) < 0) {
if(errno == EINTR) {
errno = 0;
continue;
}
break;
}
(void)CatchSignal(SIGCLD, saved_handler);
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;
}
(void)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 (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 (!non_root_mode()) {
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;
}