mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-22 10:07:12 +00:00
Bug: https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1207424 This patch makes the default value for AADefaultHatName be the server/vhost name, which can be specified in apache via the ServerName configuration declaration. It can be overridden by setting AADefaultHatName directly. Thus, with this patch applied, the order of attempted hats will be: 1. try to aa_change_hat(2) into a matching AAHatName hat if it exists and applies, otherwise 2. try to aa_change_hat(2) into the URI itself, otherwise 3. try to aa_change_hat(2) into the value of ServerName, unless AADefaultHatName has been explicitly set for this server/vhost, in which case that value will be used, otherwise 4. try to aa_change_hat(2) into the DEFAULT_URI hat, if it exists, otherwise 5. fall back to the global Apache policy This should eliminate the need for most admins to define both ServerName and AADefaultHatName, unless there's a specific need for the values to deviate. Man page documentation is updated as well, though probably more wordsmithing is needed there for clarity. Signed-off-by: Steve Beattie <steve@nxnw.org> Acked-by: John Johansen <john.johansen@canonical.com>
382 lines
11 KiB
C
382 lines
11 KiB
C
/*
|
|
* 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 long 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 with '%s'", DEFAULT_HAT);
|
|
ret = aa_change_hat(DEFAULT_HAT, magic_token);
|
|
if (ret < 0) {
|
|
aa_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) {
|
|
aa_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 = aa_change_hat(dcfg->hat_name, magic_token);
|
|
if (sd_ret < 0) {
|
|
aa_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 = aa_change_hat(r->uri, magic_token);
|
|
if (sd_ret < 0) {
|
|
aa_change_hat(NULL, magic_token);
|
|
} else {
|
|
return OK;
|
|
}
|
|
|
|
if (scfg) {
|
|
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "Dumping scfg info: "
|
|
"scfg='0x%lx' scfg->hat_name='%s'",
|
|
(unsigned long) scfg, scfg->hat_name);
|
|
} else {
|
|
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "scfg is null");
|
|
}
|
|
if (scfg != NULL) {
|
|
if (scfg->hat_name != NULL) {
|
|
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "calling change_hat [scfg] %s", scfg->hat_name);
|
|
sd_ret = aa_change_hat(scfg->hat_name, magic_token);
|
|
if (sd_ret < 0) {
|
|
aa_change_hat(NULL, magic_token);
|
|
} else {
|
|
return OK;
|
|
}
|
|
} else {
|
|
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "calling change_hat w/server_name %s", r->server->server_hostname);
|
|
sd_ret = aa_change_hat(r->server->server_hostname, magic_token);
|
|
if (sd_ret < 0) {
|
|
aa_change_hat(NULL, magic_token);
|
|
} else {
|
|
return OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "calling change_hat DEFAULT_URI");
|
|
sd_ret = aa_change_hat(DEFAULT_URI_HAT, magic_token);
|
|
if (sd_ret < 0) aa_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 dir path %s",
|
|
dcfg->hat_name, dcfg->path);
|
|
aa_change_hat(NULL, magic_token);
|
|
|
|
sd_ret = aa_change_hat(DEFAULT_HAT, magic_token);
|
|
if (sd_ret < 0) {
|
|
aa_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 = (immunix_srv_cfg *)
|
|
ap_get_module_config(cmd->server->module_config, &apparmor_module);
|
|
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 */
|
|
};
|