apparmor/changehat/mod_apparmor/mod_apparmor.c

362 lines
10 KiB
C
Raw Normal View History

/*
* Copyright (c) 2004, 2005, 2006 NOVELL (All rights reserved)
*
* The mod_apparmor module is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
* COPYING.LGPL.
*
* mod_apparmor - (apache 2.0.x)
* Author: Steve Beattie <sbeattie@suse.de>
*
* This currently only implements change_hat functionality, but could be
* extended for other stuff we decide to do.
*/
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_log.h"
#include "http_main.h"
#include "http_protocol.h"
#include "util_filter.h"
#include "apr.h"
#include "apr_strings.h"
#include "apr_lib.h"
#include <sys/apparmor.h>
#include <unistd.h>
/* #define DEBUG */
#ifndef __unused
#define __unused __attribute__((unused))
#endif
/* should the following be configurable? */
#define DEFAULT_HAT "HANDLING_UNTRUSTED_INPUT"
#define DEFAULT_URI_HAT "DEFAULT_URI"
/* Compatibility with apache 2.2 */
#if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER < 3
#define APLOG_TRACE1 APLOG_DEBUG
server_rec *ap_server_conf = NULL;
#endif
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(apparmor);
#endif
module AP_MODULE_DECLARE_DATA apparmor_module;
static unsigned int magic_token = 0;
static int inside_default_hat = 0;
typedef struct {
const char * hat_name;
char * path;
} immunix_dir_cfg;
typedef struct {
const char * hat_name;
int is_initialized;
} immunix_srv_cfg;
/* immunix_init() gets invoked in the post_config stage of apache.
* Unfortunately, apache reads its config once when it starts up, then
* it re-reads it when goes into its restart loop, where it starts it's
* children. This means we cannot call change_hat here, as the modules
* memory will be wiped out, and the magic_token will be lost, so apache
* wouldn't be able to change_hat back out. */
static int
immunix_init (apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
{
apr_file_t * file;
apr_size_t size = sizeof (magic_token);
int ret;
ret = apr_file_open (&file, "/dev/urandom", APR_READ, APR_OS_DEFAULT, p);
if (!ret) {
apr_file_read (file, (void *) &magic_token, &size);
apr_file_close (file);
} else {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "Failed to open /dev/urandom");
}
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "Opened /dev/urandom successfully");
return OK;
}
/* As each child starts up, we'll change_hat into a default hat, mostly
* to protect ourselves from bugs in parsing network input, but before
* we change_hat to the uri specific hat. */
static void
immunix_child_init (apr_pool_t *p, server_rec *s)
{
int ret;
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "init: calling change_hat");
ret = change_hat (DEFAULT_HAT, magic_token);
if (ret < 0) {
change_hat (NULL, magic_token);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "Failed to change_hat to '%s'",
DEFAULT_HAT);
} else {
inside_default_hat = 1;
}
}
static void
debug_dump_uri(request_rec *r)
{
apr_uri_t *uri = &r->parsed_uri;
if (uri)
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "Dumping uri info "
"scheme='%s' host='%s' path='%s' query='%s' fragment='%s'",
uri->scheme, uri->hostname, uri->path, uri->query,
uri->fragment);
else
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "Asked to dump NULL uri");
}
/*
immunix_enter_hat will attempt to change_hat in the following order:
(1) to a hatname in a location directive
(2) to the uri
(3) to a per-server default
(4) to DEFAULT_URI
(5) back to the parent profile
*/
static int
immunix_enter_hat (request_rec *r)
{
int sd_ret = -1;
immunix_dir_cfg * dcfg = (immunix_dir_cfg *)
ap_get_module_config (r->per_dir_config, &apparmor_module);
immunix_srv_cfg * scfg = (immunix_srv_cfg *)
ap_get_module_config (r->server->module_config, &apparmor_module);
debug_dump_uri(r);
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "in immunix_enter_hat (%s) n:0x%lx p:0x%lx main:0x%lx",
dcfg->path, (unsigned long) r->next, (unsigned long) r->prev,
(unsigned long) r->main);
/* We only call change_hat for the main request, not subrequests */
if (r->main)
return OK;
if (inside_default_hat) {
change_hat (NULL, magic_token);
inside_default_hat = 0;
}
if (dcfg != NULL && dcfg->hat_name != NULL) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "calling change_hat [dcfg] %s", dcfg->hat_name);
sd_ret = change_hat (dcfg->hat_name, magic_token);
if (sd_ret < 0) {
change_hat (NULL, magic_token);
} else {
return OK;
}
}
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "calling change_hat [uri] %s", r->uri);
sd_ret = change_hat (r->uri, magic_token);
if (sd_ret < 0) {
change_hat (NULL, magic_token);
} else {
return OK;
}
if (scfg != NULL && scfg->hat_name != NULL) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "calling change_hat [scfg] %s", scfg->hat_name);
sd_ret = change_hat (scfg->hat_name, magic_token);
if (sd_ret < 0) {
change_hat (NULL, magic_token);
} else {
return OK;
}
}
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "calling change_hat DEFAULT_URI");
sd_ret = change_hat (DEFAULT_URI_HAT, magic_token);
if (sd_ret < 0) change_hat (NULL, magic_token);
return OK;
}
static int
immunix_exit_hat (request_rec *r)
{
int sd_ret;
immunix_dir_cfg * dcfg = (immunix_dir_cfg *)
ap_get_module_config (r->per_dir_config, &apparmor_module);
/* immunix_srv_cfg * scfg = (immunix_srv_cfg *)
ap_get_module_config (r->server->module_config, &apparmor_module); */
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "exiting change_hat - dir hat %s path %s", dcfg->hat_name, dcfg->path);
change_hat (NULL, magic_token);
sd_ret = change_hat (DEFAULT_HAT, magic_token);
if (sd_ret < 0) {
change_hat (NULL, magic_token);
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to change_hat to '%s'",
DEFAULT_HAT);
} else {
inside_default_hat = 1;
}
return OK;
}
static const char *
aa_cmd_ch_path (cmd_parms * cmd, void * mconfig, const char * parm1)
{
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "directory config change hat %s",
parm1 ? parm1 : "DEFAULT");
immunix_dir_cfg * dcfg = mconfig;
if (parm1 != NULL) {
dcfg->hat_name = parm1;
} else {
dcfg->hat_name = "DEFAULT";
}
return NULL;
}
static int path_warn_once;
static const char *
immunix_cmd_ch_path (cmd_parms * cmd, void * mconfig, const char * parm1)
{
if (path_warn_once == 0) {
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "ImmHatName is "
"deprecated, please use AAHatName instead");
path_warn_once = 1;
}
return aa_cmd_ch_path(cmd, mconfig, parm1);
}
static const char *
aa_cmd_ch_srv (cmd_parms * cmd, void * mconfig, const char * parm1)
{
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "server config change hat %s",
parm1 ? parm1 : "DEFAULT");
immunix_srv_cfg * scfg = mconfig;
if (parm1 != NULL) {
scfg->hat_name = parm1;
} else {
scfg->hat_name = "DEFAULT";
}
return NULL;
}
static int srv_warn_once;
static const char *
immunix_cmd_ch_srv (cmd_parms * cmd, void * mconfig, const char * parm1)
{
if (srv_warn_once == 0) {
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "ImmDefaultHatName is "
"deprecated, please use AADefaultHatName instead");
srv_warn_once = 1;
}
return aa_cmd_ch_srv(cmd, mconfig, parm1);
}
static void *
immunix_create_dir_config (apr_pool_t * p, char * path)
{
immunix_dir_cfg * newcfg = (immunix_dir_cfg *) apr_pcalloc(p, sizeof(* newcfg));
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "in immunix_create_dir (%s)", path ? path : ":no path:");
if (newcfg == NULL) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "immunix_create_dir: couldn't alloc dir config");
return NULL;
}
newcfg->path = apr_pstrdup (p, path ? path : ":no path:");
return newcfg;
}
/* XXX: Should figure out an appropriate action to take here, if any
static void *
immunix_merge_dir_config (apr_pool_t * p, void * parent, void * child)
{
immunix_dir_cfg * newcfg = (immunix_dir_cfg *) apr_pcalloc(p, sizeof(* newcfg));
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "in immunix_merge_dir ()");
if (newcfg == NULL)
return NULL;
return newcfg;
}
*/
static void *
immunix_create_srv_config (apr_pool_t * p, server_rec * srv)
{
immunix_srv_cfg * newcfg = (immunix_srv_cfg *) apr_pcalloc(p, sizeof(* newcfg));
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "in immunix_create_srv");
if (newcfg == NULL) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "immunix_create_srv: couldn't alloc srv config");
return NULL;
}
return newcfg;
}
static const command_rec immunix_cmds[] = {
AP_INIT_TAKE1 (
"ImmHatName",
immunix_cmd_ch_path,
NULL,
ACCESS_CONF,
""
),
AP_INIT_TAKE1 (
"ImmDefaultHatName",
immunix_cmd_ch_srv,
NULL,
RSRC_CONF,
""
),
AP_INIT_TAKE1 (
"AAHatName",
aa_cmd_ch_path,
NULL,
ACCESS_CONF,
""
),
AP_INIT_TAKE1 (
"AADefaultHatName",
aa_cmd_ch_srv,
NULL,
RSRC_CONF,
""
),
{ NULL }
};
static void
register_hooks (apr_pool_t *p)
{
ap_hook_post_config (immunix_init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_child_init (immunix_child_init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_access_checker(immunix_enter_hat, NULL, NULL, APR_HOOK_FIRST);
/* ap_hook_post_read_request(immunix_enter_hat, NULL, NULL, APR_HOOK_FIRST); */
ap_hook_log_transaction(immunix_exit_hat, NULL, NULL, APR_HOOK_LAST);
}
module AP_MODULE_DECLARE_DATA apparmor_module = {
STANDARD20_MODULE_STUFF,
immunix_create_dir_config, /* dir config creater */
NULL, /* dir merger --- default is to override */
/* immunix_merge_dir_config, */ /* dir merger --- default is to override */
immunix_create_srv_config, /* server config */
NULL, /* merge server config */
immunix_cmds, /* command table */
register_hooks /* register hooks */
};