summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPhilip Hazel <ph10@hermes.cam.ac.uk>2006-02-07 14:05:17 +0000
committerPhilip Hazel <ph10@hermes.cam.ac.uk>2006-02-07 14:05:17 +0000
commit59e82a2a0e97f55f0e27323112116e01a06cb198 (patch)
tree945e9bc068b5c4dded91c9404f6ac1df785f1a78 /src
parent312fd9e929e73d6c0d5ca7414431d2b87fa0ea21 (diff)
Improve queryprogram behaviour when not running as root.
Diffstat (limited to 'src')
-rw-r--r--src/src/child.c59
-rw-r--r--src/src/routers/queryprogram.c25
2 files changed, 63 insertions, 21 deletions
diff --git a/src/src/child.c b/src/src/child.c
index 8f2f9d78c..4a1a89887 100644
--- a/src/src/child.c
+++ b/src/src/child.c
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/child.c,v 1.7 2006/02/07 11:19:00 ph10 Exp $ */
+/* $Cambridge: exim/src/src/child.c,v 1.8 2006/02/07 14:05:17 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -292,14 +292,48 @@ otherwise. Save the old state for resetting on the wait. */
oldsignal = signal(SIGCHLD, SIG_DFL);
pid = fork();
-/* The child process becomes a process group leader if requested, and then
-organizes the pipes. Any unexpected failure is signalled with EX_EXECFAILED;
-these are all "should never occur" failures, except perhaps for exec failing
-because the command doesn't exist. */
+/* Handle the child process. First, set the required environment. We must do
+this before messing with the pipes, in order to be able to write debugging
+output when things go wrong. */
if (pid == 0)
{
- if (make_leader && setpgid(0,0) < 0) goto CHILD_FAILED;
+ signal(SIGUSR1, SIG_IGN);
+
+ if (newgid != NULL && setgid(*newgid) < 0)
+ {
+ DEBUG(D_any) debug_printf("failed to set gid=%ld in subprocess: %s\n",
+ (long int)(*newgid), strerror(errno));
+ goto CHILD_FAILED;
+ }
+
+ if (newuid != NULL && setuid(*newuid) < 0)
+ {
+ DEBUG(D_any) debug_printf("failed to set uid=%ld in subprocess: %s\n",
+ (long int)(*newuid), strerror(errno));
+ goto CHILD_FAILED;
+ }
+
+ (void)umask(newumask);
+
+ if (wd != NULL && Uchdir(wd) < 0)
+ {
+ DEBUG(D_any) debug_printf("failed to chdir to %s: %s\n", wd,
+ strerror(errno));
+ goto CHILD_FAILED;
+ }
+
+ /* Becomes a process group leader if requested, and then organize the pipes.
+ Any unexpected failure is signalled with EX_EXECFAILED; these are all "should
+ never occur" failures, except for exec failing because the command doesn't
+ exist. */
+
+ if (make_leader && setpgid(0,0) < 0)
+ {
+ DEBUG(D_any) debug_printf("failed to set group leader in subprocess: %s\n",
+ strerror(errno));
+ goto CHILD_FAILED;
+ }
(void)close(inpfd[pipe_write]);
force_fd(inpfd[pipe_read], 0);
@@ -310,17 +344,6 @@ if (pid == 0)
(void)close(2);
(void)dup2(1, 2);
- /* Set the required environment. */
-
- signal(SIGUSR1, SIG_IGN);
- if (newgid != NULL && setgid(*newgid) < 0) goto CHILD_FAILED;
- if (newuid != NULL && setuid(*newuid) < 0) goto CHILD_FAILED;
- (void)umask(newumask);
-
- /* Set the working directory if required */
-
- if (wd != NULL && Uchdir(wd) < 0) goto CHILD_FAILED;
-
/* Now do the exec */
if (envp == NULL) execv(CS argv[0], (char *const *)argv);
@@ -328,7 +351,7 @@ if (pid == 0)
/* Failed to execv. Signal this failure using EX_EXECFAILED. We are
losing the actual errno we got back, because there is no way to return
- this. */
+ this information. */
CHILD_FAILED:
_exit(EX_EXECFAILED); /* Note: must be _exit(), NOT exit() */
diff --git a/src/src/routers/queryprogram.c b/src/src/routers/queryprogram.c
index c9b409e78..1b974fc4e 100644
--- a/src/src/routers/queryprogram.c
+++ b/src/src/routers/queryprogram.c
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/routers/queryprogram.c,v 1.7 2006/02/07 11:19:02 ph10 Exp $ */
+/* $Cambridge: exim/src/src/routers/queryprogram.c,v 1.8 2006/02/07 14:05:17 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -198,8 +198,12 @@ queryprogram_router_options_block *ob =
(queryprogram_router_options_block *)(rblock->options_block);
uschar *current_directory = ob->current_directory;
ugid_block ugid;
+uid_t curr_uid = getuid();
+gid_t curr_gid = getgid();
uid_t uid = ob->cmd_uid;
gid_t gid = ob->cmd_gid;
+uid_t *puid = &uid;
+gid_t *pgid = &gid;
DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n",
rblock->name, addr->address, addr->domain);
@@ -250,9 +254,24 @@ if (!ob->cmd_gid_set)
}
}
-DEBUG(D_route) debug_printf("uid=%ld gid=%ld current_directory=%s\n",
+DEBUG(D_route) debug_printf("requires uid=%ld gid=%ld current_directory=%s\n",
(long int)uid, (long int)gid, current_directory);
+/* If we are not running as root, we will not be able to change uid/gid. */
+
+if (curr_uid != root_uid && (uid != curr_uid || gid != curr_gid))
+ {
+ DEBUG(D_route)
+ {
+ debug_printf("not running as root: cannot change uid/gid\n");
+ debug_printf("subprocess will run with uid=%ld gid=%ld\n",
+ (long int)curr_uid, (long int)curr_gid);
+ }
+ puid = pgid = NULL;
+ }
+
+/* Set up the command to run */
+
if (!transport_set_up_command(&argvptr, /* anchor for arg list */
ob->command, /* raw command */
TRUE, /* expand the arguments */
@@ -266,7 +285,7 @@ if (!transport_set_up_command(&argvptr, /* anchor for arg list */
/* Create the child process, making it a group leader. */
-pid = child_open_uid(argvptr, NULL, 0077, &uid, &gid, &fd_in, &fd_out,
+pid = child_open_uid(argvptr, NULL, 0077, puid, pgid, &fd_in, &fd_out,
current_directory, TRUE);
if (pid < 0)