diff options
author | Joe Orton <jorton@apache.org> | 2007-06-29 10:33:14 +0000 |
---|---|---|
committer | Joe Orton <jorton@apache.org> | 2007-06-29 10:33:14 +0000 |
commit | 0a0d324e07439178928268989607d3219f7e8b34 (patch) | |
tree | ede12ebc2873490c3c3234e07f8ab2e01fa7b00b | |
parent | ada9c28dcf715bd6f61c1f2761a0a04f68f2a019 (diff) | |
download | httpd-0a0d324e07439178928268989607d3219f7e8b34.tar.gz |
Add alternative fixes for CVE-2007-3304:
* configure.in: Check for getpgid.
* include/mpm_common.h (ap_mpm_safe_kill): New prototype.
* server/mpm_common.c (reclaim_one_pid): Ensure pid validity before
calling apr_proc_wait().
(ap_mpm_safe_kill): New function.
* server/mpm/prefork/prefork.c, server/mpm/worker/worker.c,
server/mpm/experimental/event/event.c: Use ap_mpm_safe_kill() on pids
from the scoreboard, throughout.
* include/ap_mmn.h: Minor bump.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@551843 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | configure.in | 1 | ||||
-rw-r--r-- | include/ap_mmn.h | 3 | ||||
-rw-r--r-- | include/mpm_common.h | 13 | ||||
-rw-r--r-- | server/mpm/experimental/event/event.c | 10 | ||||
-rw-r--r-- | server/mpm/prefork/prefork.c | 14 | ||||
-rw-r--r-- | server/mpm/worker/worker.c | 10 | ||||
-rw-r--r-- | server/mpm_common.c | 65 |
8 files changed, 99 insertions, 21 deletions
@@ -2,6 +2,10 @@ Changes with Apache 2.3.0 [Remove entries to the current 2.0 and 2.2 section below, when backported] + *) SECURITY: CVE-2007-3304 (cve.mitre.org) + prefork, worker, event MPMs: Ensure that the parent process cannot + be forced to kill processes outside its process group. [Joe Orton] + *) SECURITY: CVE-2006-5752 (cve.mitre.org) mod_status: Fix a possible XSS attack against a site with a public server-status page and ExtendedStatus enabled, for browsers which diff --git a/configure.in b/configure.in index e906f68103..dd560e1ecf 100644 --- a/configure.in +++ b/configure.in @@ -399,6 +399,7 @@ initgroups \ bindprocessor \ prctl \ timegm \ +getpgid ) dnl confirm that a void pointer is large enough to store a long integer diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 206a833929..888147a23c 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -129,6 +129,7 @@ * ap_parse_mutex() * 20060905.3 (2.3.0-dev) Added conn_rec::clogging_input_filters. * 20060905.4 (2.3.0-dev) Added proxy_balancer::sticky_path. + * 20060905.5 (2.3.0-dev) Added ap_mpm_safe_kill() * */ @@ -137,7 +138,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20060905 #endif -#define MODULE_MAGIC_NUMBER_MINOR 4 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 5 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/mpm_common.h b/include/mpm_common.h index 20a74ac642..30786751c9 100644 --- a/include/mpm_common.h +++ b/include/mpm_common.h @@ -145,6 +145,19 @@ int ap_unregister_extra_mpm_process(pid_t pid); #endif /** + * Safely signal an MPM child process, if the process is in the + * current process group. Otherwise fail. + * @param pid the process id of a child process to signal + * @param sig the signal number to send + * @return APR_SUCCESS if signal is sent, otherwise an error as per kill(3); + * APR_EINVAL is returned if passed either an invalid (< 1) pid, or if + * the pid is not in the current process group + */ +#ifdef AP_MPM_WANT_RECLAIM_CHILD_PROCESSES +apr_status_t ap_mpm_safe_kill(pid_t pid, int sig); +#endif + +/** * Determine if any child process has died. If no child process died, then * this process sleeps for the amount of time specified by the MPM defined * macro SCOREBOARD_MAINTENANCE_INTERVAL. diff --git a/server/mpm/experimental/event/event.c b/server/mpm/experimental/event/event.c index 9d242b3867..d0128e5101 100644 --- a/server/mpm/experimental/event/event.c +++ b/server/mpm/experimental/event/event.c @@ -2070,12 +2070,10 @@ int ap_mpm_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s) active_children = 0; for (index = 0; index < ap_daemons_limit; ++index) { - if (MPM_CHILD_PID(index) != 0) { - if (kill(MPM_CHILD_PID(index), 0) == 0) { - active_children = 1; - /* Having just one child is enough to stay around */ - break; - } + if (ap_mpm_safe_kill(MPM_CHILD_PID(index), 0) == APR_SUCCESS) { + active_children = 1; + /* Having just one child is enough to stay around */ + break; } } } while (!shutdown_pending && active_children && diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c index 5f0b1fbd0d..98b07e6e76 100644 --- a/server/mpm/prefork/prefork.c +++ b/server/mpm/prefork/prefork.c @@ -1096,7 +1096,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) for (index = 0; index < ap_daemons_limit; ++index) { if (ap_scoreboard_image->servers[index][0].status != SERVER_DEAD) { /* Ask each child to close its listeners. */ - kill(MPM_CHILD_PID(index), AP_SIG_GRACEFUL); + ap_mpm_safe_kill(MPM_CHILD_PID(index), AP_SIG_GRACEFUL); active_children++; } } @@ -1134,12 +1134,10 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) active_children = 0; for (index = 0; index < ap_daemons_limit; ++index) { - if (MPM_CHILD_PID(index) != 0) { - if (kill(MPM_CHILD_PID(index), 0) == 0) { - active_children = 1; - /* Having just one child is enough to stay around */ - break; - } + if (ap_mpm_safe_kill(MPM_CHILD_PID(index), 0) == APR_SUCCESS) { + active_children = 1; + /* Having just one child is enough to stay around */ + break; } } } while (!shutdown_pending && active_children && @@ -1191,7 +1189,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) * piped loggers, etc. They almost certainly won't handle * it gracefully. */ - kill(ap_scoreboard_image->parent[index].pid, AP_SIG_GRACEFUL); + ap_mpm_safe_kill(ap_scoreboard_image->parent[index].pid, AP_SIG_GRACEFUL); } } } diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index e8eca8c197..5b531c6ede 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -1815,12 +1815,10 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) active_children = 0; for (index = 0; index < ap_daemons_limit; ++index) { - if (MPM_CHILD_PID(index) != 0) { - if (kill(MPM_CHILD_PID(index), 0) == 0) { - active_children = 1; - /* Having just one child is enough to stay around */ - break; - } + if (ap_mpm_safe_kill(MPM_CHILD_PID(index), 0) == APR_SUCCESS) { + active_children = 1; + /* Having just one child is enough to stay around */ + break; } } } while (!shutdown_pending && active_children && diff --git a/server/mpm_common.c b/server/mpm_common.c index 6e485c70c2..8ead7e39dd 100644 --- a/server/mpm_common.c +++ b/server/mpm_common.c @@ -127,6 +127,11 @@ static int reclaim_one_pid(pid_t pid, action_t action) apr_proc_t proc; apr_status_t waitret; + /* Ensure pid sanity. */ + if (pid < 1) { + return 1; + } + proc.pid = pid; waitret = apr_proc_wait(&proc, NULL, NULL, APR_NOWAIT); if (waitret != APR_CHILD_NOTDONE) { @@ -306,6 +311,66 @@ void ap_relieve_child_processes(void) cur_extra = next; } } + +/* Before sending the signal to the pid this function verifies that + * the pid is a member of the current process group; either using + * apr_proc_wait(), where waitpid() guarantees to fail for non-child + * processes; or by using getpgid() directly, if available. */ +apr_status_t ap_mpm_safe_kill(pid_t pid, int sig) +{ +#ifndef HAVE_GETPGID + apr_proc_t proc; + apr_status_t rv; + apr_exit_why_e why; + int status; + + /* Ensure pid sanity */ + if (pid < 1) { + return APR_EINVAL; + } + + proc.pid = pid; + rv = apr_proc_wait(&proc, &status, &why, APR_NOWAIT); + if (rv == APR_CHILD_DONE) { +#ifdef AP_MPM_WANT_PROCESS_CHILD_STATUS + /* The child already died - log the termination status if + * necessary: */ + ap_process_child_status(&proc, why, status); +#endif + return APR_EINVAL; + } + else if (rv != APR_CHILD_NOTDONE) { + /* The child is already dead and reaped, or was a bogus pid - + * log this either way. */ + ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, ap_server_conf, + "cannot send signal %d to pid %ld (non-child or " + "already dead)", sig, (long)pid); + return APR_EINVAL; + } +#else + int pg; + + /* Ensure pid sanity. */ + if (pid < 1) { + return APR_EINVAL; + } + + pg = getpgid(pid); + if (pg == -1) { + /* Process already dead... */ + return errno; + } + + if (pg != getpgrp()) { + ap_log_error(APLOG_MARK, APLOG_ALERT, 0, ap_server_conf, + "refusing to send signal %d to pid %ld outside " + "process group", sig, (long)pid); + return APR_EINVAL; + } +#endif + + return kill(pid, sig) ? errno : APR_SUCCESS; +} #endif /* AP_MPM_WANT_RECLAIM_CHILD_PROCESSES */ #ifdef AP_MPM_WANT_WAIT_OR_TIMEOUT |