mod_privileges.c revision 3a30e21be501f59d364e8ab72298aaa1ec03892d
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <priv.h>
#include <unistd.h>
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_log.h"
#include "mpm_common.h"
#include "ap_mpm.h"
#include "apr_strings.h"
/* TODO - get rid of unixd dependency */
#include "unixd.h"
#define CFG_CHECK(x) if ((x) == -1) { \
char msgbuf[128]; \
}
#define CR_CHECK(x) if (x == -1) \
"Failed to initialise privileges")
/* #define BIG_SECURITY_HOLE 1 */
typedef struct {
} priv_cfg;
typedef struct {
} priv_dir_cfg;
static priv_set_t *priv_setid;
static int dtrace_enabled = 0;
{
return APR_SUCCESS;
}
{
/* inherit the mode if it's not set; the rest won't be inherited */
return ret;
}
{
/* Start at basic privileges all round. */
/* By default, run in secure vhost mode.
* That means dropping basic privileges we don't usually need.
*/
/* Hmmm, should CGI default to secure too ? */
/*
CR_CHECK(priv_delset(cfg->child_priv, PRIV_FILE_LINK_ANY));
CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_INFO));
CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_SESSION));
CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_FORK));
CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_EXEC));
*/
/* we´ll use 0 for unset */
/* top-level default_priv wants the top-level cfg */
if (priv_default == NULL) {
}
return cfg;
}
{
return cfg;
}
{
return ret;
}
{
request_rec *r = data;
/* ugly hack: grab default uid and gid from unixd */
extern unixd_config_rec ap_unixd_config;
/* If we forked a child, we dropped privilege to revert, so
* all we can do now is exit
*/
exit(0);
}
/* if either user or group are not the default, restore them */
}
"Error restoring default userid");
}
"Error restoring default group");
}
}
/* restore default privileges */
"Error restoring default privileges");
}
return APR_SUCCESS;
}
static int privileges_req(request_rec *r)
{
/* secure mode: fork a process to handle the request */
int exitcode;
int fork_req;
if (!breadcrumb) {
/* first call: this is the vhost */
/* set breadcrumb */
/* If we have per-dir config, defer doing anything */
/* Defer dropping privileges 'til we have a directory
* context that'll tell us whether to fork.
*/
return DECLINED;
}
}
else {
/* second call is for per-directory. */
/* Our fate was already determined for the vhost -
* nothing to do per-directory
*/
return DECLINED;
}
}
if (fork_req) {
switch (rv) {
case APR_INPARENT:
"parent waiting for child");
/* FIXME - does the child need to run synchronously?
* esp. if we enable mod_privileges with threaded MPMs?
* We do need at least to ensure r outlives the child.
*/
/* The child has taken responsibility for reading all input
* and sending all output. So we need to bow right out,
* and even abandon "normal" housekeeping.
*/
r->eos_sent = 1;
/* Testing with ab and 100k requests reveals no nasties
* so I infer we're not leaking anything like memory
* or file descriptors. That's nice!
*/
return DONE;
case APR_INCHILD:
break; /* now we'll drop privileges in the child */
default:
"Failed to fork secure child process!");
return HTTP_INTERNAL_SERVER_ERROR;
}
}
/* OK, now drop privileges. */
/* cleanup should happen even if something fails part-way through here */
/* set user and group if configured */
}
/* if we should be able to set these but can't, it could be
* a serious security issue. Bail out rather than risk it!
*/
"Error setting userid");
return HTTP_INTERNAL_SERVER_ERROR;
}
"Error setting group");
return HTTP_INTERNAL_SERVER_ERROR;
}
}
/* set vhost's privileges */
"Error setting effective privileges");
return HTTP_INTERNAL_SERVER_ERROR;
}
/* ... including those of any subprocesses */
"Error setting inheritable privileges");
return HTTP_INTERNAL_SERVER_ERROR;
}
"Error setting limit privileges");
return HTTP_INTERNAL_SERVER_ERROR;
}
/* If we're in a child process, drop down PPERM too */
if (fork_req) {
"Error setting permitted privileges");
return HTTP_INTERNAL_SERVER_ERROR;
}
}
return OK;
}
#define PDROP_CHECK(x) if (x == -1) { \
"Error dropping privileges"); \
return !OK; \
}
{
/* We need to set privileges before mod_unixd,
* 'cos otherwise setuid will wipe our privilege to do so
*/
server_rec *sp;
/* compute ppriv from the union of all the vhosts plus setid */
}
return OK;
}
{
/* Our config stuff has set the privileges we need, so now
* we just set them to those of the parent server_rec
*
* This has to happen after mod_unixd, 'cos mod_unixd needs
* privileges we drop here.
*/
/* defaults - the default vhost */
return OK;
}
{
return APR_SUCCESS;
}
{
server_rec *sp;
/* if we have dtrace enabled, merge it into everything */
if (dtrace_enabled) {
}
}
/* set up priv_setid for per-request use */
priv_setid = priv_allocset();
"priv_addset");
return !OK;
}
return OK;
}
{
/* refuse to work if the MPM is threaded */
int threaded;
if (rv != APR_SUCCESS) {
"mod_privileges: unable to determine MPM characteristics."
" Please ensure you are using a non-threaded MPM "
"with this module.");
}
if (threaded) {
"mod_privileges is not compatible with a threaded MPM.");
return !OK;
}
return OK;
}
{
}
{
}
return NULL;
}
{
}
return NULL;
}
{
if (!arg) {
/* add basic privileges, excluding those covered by cgimode */
}
return NULL;
}
{
/* default - nothing to do */
}
/* drop fork+exec privs */
}
/* deny privileges to CGI procs */
}
else {
return "VHostCGIMode must be On, Off or Secure";
}
return NULL;
}
{
return err;
}
return NULL;
}
{
}
mode = PRIV_SECURE;
}
}
/* In a directory context, set the per_dir_config */
return "PrivilegesMode in a Directory context must be FAST or SECURE";
}
}
else {
/* In a global or vhost context, set the server config */
if (mode == PRIV_UNSET) {
return "PrivilegesMode must be FAST, SECURE or SELECTIVE";
}
}
return NULL;
}
#ifdef BIG_SECURITY_HOLE
{
if (*priv == '-') {
}
else if (*priv == '+') {
}
else {
}
return NULL;
}
{
if (*priv == '-') {
}
else if (*priv == '+') {
}
else {
}
return NULL;
}
#endif
static const command_rec privileges_cmds[] = {
"Userid under which the virtualhost will run"),
"Group under which the virtualhost will run"),
"Run in enhanced security mode (default ON)"),
"Enable fork+exec for this virtualhost (Off|Secure|On)"),
"Enable DTrace"),
"tradeoff performance vs security (fast or secure)"),
#ifdef BIG_SECURITY_HOLE
"Privileges available in the (virtual) server"),
"Privileges available to external programs"),
#endif
{NULL}
};
};