apparmor/parser/parser_interface.c

961 lines
21 KiB
C
Raw Normal View History

/*
2007-04-11 08:12:51 +00:00
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
* NOVELL (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#define _(s) gettext(s)
#include "parser.h"
2007-02-27 02:29:16 +00:00
#include "libapparmor_re/apparmor_re.h"
#include <unistd.h>
#include <linux/unistd.h>
/* only for x86 at the moment */
#include <endian.h>
#include <byteswap.h>
#include <libintl.h>
#define _(s) gettext(s)
#define u8 unsigned char
2008-04-06 18:55:46 +00:00
#define u16 uint16_t
#define u32 uint32_t
#define u64 uint64_t
#define BUFFERINC 65536
//#define BUFFERINC 16
#if __BYTE_ORDER == __BIG_ENDIAN
# define cpu_to_le16(x) ((u16)(bswap_16 ((u16) x)))
# define cpu_to_le32(x) ((u32)(bswap_32 ((u32) x)))
2008-04-06 18:55:46 +00:00
# define cpu_to_le64(x) ((u64)(bswap_64 ((u64) x)))
#else
# define cpu_to_le16(x) ((u16)(x))
# define cpu_to_le32(x) ((u32)(x))
2008-04-06 18:55:46 +00:00
# define cpu_to_le64(x) ((u64)(x))
#endif
#define SD_CODE_SIZE (sizeof(u8))
#define SD_STR_LEN (sizeof(u16))
2006-08-04 21:30:34 +00:00
#define SUBDOMAIN_INTERFACE_VERSION 2
2008-04-08 20:30:06 +00:00
#define SUBDOMAIN_INTERFACE_DFA_VERSION 5
#define SUBDOMAIN_INTERFACE_POLICY_DB 16
int sd_serialize_codomain(int option, struct codomain *cod);
static void print_error(int error)
{
switch (error) {
case -ESPIPE:
PERROR(_("Bad write position\n"));
break;
case -EPERM:
PERROR(_("Permission denied\n"));
break;
case -ENOMEM:
PERROR(_("Out of memory\n"));
break;
case -EFAULT:
PERROR(_("Couldn't copy profile: Bad memory address\n"));
break;
case -EPROTO:
PERROR(_("Profile doesn't conform to protocol\n"));
break;
case -EBADMSG:
PERROR(_("Profile does not match signature\n"));
break;
case -EPROTONOSUPPORT:
PERROR(_("Profile version not supported by Apparmor module\n"));
break;
case -EEXIST:
PERROR(_("Profile already exists\n"));
break;
case -ENOENT:
PERROR(_("Profile doesn't exist\n"));
break;
case -EACCES:
PERROR(_("Permission denied; attempted to load a profile while confined?\n"));
break;
default:
PERROR(_("Unknown error (%d): %s\n"), -error, strerror(-error));
break;
}
}
int load_codomain(int option, struct codomain *cod)
{
int retval = 0;
int error = 0;
PDEBUG("Serializing policy for %s.\n", cod->name);
retval = sd_serialize_codomain(option, cod);
if (retval < 0) {
error = retval; /* yeah, we'll just report the last error */
switch (option) {
case OPTION_ADD:
PERROR(_("%s: Unable to add \"%s\". "),
progname, cod->name);
print_error(error);
break;
case OPTION_REPLACE:
PERROR(_("%s: Unable to replace \"%s\". "),
progname, cod->name);
print_error(error);
break;
case OPTION_REMOVE:
PERROR(_("%s: Unable to remove \"%s\". "),
progname, cod->name);
print_error(error);
break;
case OPTION_STDOUT:
PERROR(_("%s: Unable to write to stdout\n"),
progname);
break;
case OPTION_OFILE:
PERROR(_("%s: Unable to write to output file\n"),
progname);
default:
PERROR(_("%s: ASSERT: Invalid option: %d\n"),
progname, option);
exit(1);
break;
}
} else if (conf_verbose) {
switch (option) {
case OPTION_ADD:
printf(_("Addition succeeded for \"%s\".\n"),
cod->name);
break;
case OPTION_REPLACE:
printf(_("Replacement succeeded for \"%s\".\n"),
cod->name);
break;
case OPTION_REMOVE:
printf(_("Removal succeeded for \"%s\".\n"),
cod->name);
break;
case OPTION_STDOUT:
case OPTION_OFILE:
break;
default:
PERROR(_("%s: ASSERT: Invalid option: %d\n"),
progname, option);
exit(1);
break;
}
}
return error;
}
enum sd_code {
SD_U8,
SD_U16,
SD_U32,
SD_U64,
SD_NAME, /* same as string except it is items name */
SD_STRING,
SD_BLOB,
SD_STRUCT,
SD_STRUCTEND,
SD_LIST,
SD_LISTEND,
SD_ARRAY,
SD_ARRAYEND,
SD_OFFSET
};
const char *sd_code_names[] = {
"SD_U8",
"SD_U16",
"SD_U32",
"SD_U64",
"SD_NAME",
"SD_STRING",
"SD_BLOB",
"SD_STRUCT",
"SD_STRUCTEND",
"SD_LIST",
"SD_LISTEND",
2008-04-06 18:55:46 +00:00
"SD_ARRAY",
"SD_ARRAYEND",
"SD_OFFSET"
};
/* Currently we will just use a contiguous block of memory
be we are going to just hide this for the moment. */
struct __sdserialize {
void *buffer;
void *pos;
void *extent;
};
sd_serialize *alloc_sd_serial(void)
{
Add Audit control to AppArmor through, the use of audit and deny key words. Deny is also used to subtract permissions from the profiles permission set. the audit key word can be prepended to any file, network, or capability rule, to force a selective audit when that rule is matched. Audit permissions accumulate just like standard permissions. eg. audit /bin/foo rw, will force an audit message when the file /bin/foo is opened for read or write. audit /etc/shadow w, /etc/shadow r, will force an audit message when /etc/shadow is opened for writing. The audit message is per permission bit so only opening the file for read access will not, force an audit message. audit can also be used in block form instead of prepending audit to every rule. audit { /bin/foo rw, /etc/shadow w, } /etc/shadow r, # don't audit r access to /etc/shadow the deny key word can be prepended to file, network and capability rules, to result in a denial of permissions when matching that rule. The deny rule specifically does 3 things - it gives AppArmor the ability to remember what has been denied so that the tools don't prompt for what has been denied in previous profiling sessions. - it subtracts globally from the allowed permissions. Deny permissions accumulate in the the deny set just as allow permissions accumulate then, the deny set is subtracted from the allow set. - it quiets known rejects. The default audit behavior of deny rules is to quiet known rejects so that audit logs are not flooded with already known rejects. To have known rejects logged prepend the audit keyword to the deny rule. Deny rules do not have a block form. eg. deny /foo/bar rw, audit deny /etc/shadow w, audit { deny owner /blah w, deny other /foo w, deny /etc/shadow w, }
2008-03-13 17:39:03 +00:00
sd_serialize *p = calloc(1, sizeof(sd_serialize));
if (!p)
return NULL;
p->buffer = malloc(BUFFERINC);
if (!p->buffer) {
free(p);
return NULL;
}
p->pos = p->buffer;
p->extent = p->buffer + BUFFERINC;
return p;
}
void free_sd_serial(sd_serialize *p)
{
if (p) {
if (p->buffer)
free(p->buffer);
free(p);
}
}
/*check if something of size length is in sd_serial bounds */
static inline int sd_inbounds(sd_serialize *p, int size)
{
return (p->pos + size <= p->extent);
}
static inline void sd_inc(sd_serialize *p, int size)
{
if (sd_inbounds(p, size)) {
p->pos += size;
} else {
PERROR(_("PANIC bad increment buffer %p pos %p ext %p size %d res %p\n"),
p->buffer, p->pos, p->extent, size, p->pos + size);
exit(-1);
}
}
static inline long sd_serial_size(sd_serialize *p)
{
return (p->pos - p->buffer);
}
/* routines for writing data to the serialization buffer */
static inline int sd_prepare_write(sd_serialize *p, enum sd_code code, size_t size)
{
int num = (size / BUFFERINC) + 1;
if (p->pos + SD_CODE_SIZE + size > p->extent) {
long pos;
/* try and reallocate the buffer */
void *buffer = malloc(p->extent - p->buffer + (BUFFERINC * num));
memcpy(buffer, p->buffer, p->extent - p->buffer);
pos = p->pos - p->buffer;
if (buffer == NULL || errno == ENOMEM)
return 0;
p->extent = buffer + (p->extent - p->buffer) + (BUFFERINC * num);
free(p->buffer);
p->buffer = buffer;
p->pos = buffer + pos;
}
*(u8 *) (p->pos) = code;
sd_inc(p, SD_CODE_SIZE);
return 1;
}
static inline int sd_write8(sd_serialize *p, u8 b)
{
u8 *c;
if (!sd_prepare_write(p, SD_U8, sizeof(b)))
return 0;
c = (u8 *) p->pos;
*c = b;
sd_inc(p, 1);
return 1;
}
static inline int sd_write16(sd_serialize *p, u16 b)
{
u16 tmp;
if (!sd_prepare_write(p, SD_U16, sizeof(b)))
return 0;
tmp = cpu_to_le16(b);
memcpy(p->pos, &tmp, sizeof(tmp));
sd_inc(p, sizeof(tmp));
return 1;
}
static inline int sd_write32(sd_serialize *p, u32 b)
{
u32 tmp;
if (!sd_prepare_write(p, SD_U32, sizeof(b)))
return 0;
tmp = cpu_to_le32(b);
memcpy(p->pos, &tmp, sizeof(tmp));
sd_inc(p, sizeof(tmp));
return 1;
}
static inline int sd_write64(sd_serialize *p, u64 b)
2008-04-06 18:55:46 +00:00
{
u64 tmp;
if (!sd_prepare_write(p, SD_U64, sizeof(b)))
return 0;
tmp = cpu_to_le64(b);
memcpy(p->pos, &tmp, sizeof(tmp));
sd_inc(p, sizeof(tmp));
return 1;
}
static inline int sd_write_name(sd_serialize *p, char *name)
{
long size = 0;
PDEBUG("Writing name '%s'\n", name);
if (name) {
u16 tmp;
size = strlen(name) + 1;
if (!sd_prepare_write(p, SD_NAME, SD_STR_LEN + size))
return 0;
tmp = cpu_to_le16(size);
memcpy(p->pos, &tmp, sizeof(tmp));
sd_inc(p, sizeof(tmp));
memcpy(p->pos, name, size);
sd_inc(p, size);
}
return 1;
}
static inline int sd_write_blob(sd_serialize *p, void *b, int buf_size, char *name)
{
u32 tmp;
if (!sd_write_name(p, name))
return 0;
if (!sd_prepare_write(p, SD_BLOB, 4 + buf_size))
return 0;
tmp = cpu_to_le32(buf_size);
memcpy(p->pos, &tmp, sizeof(tmp));
sd_inc(p, sizeof(tmp));
memcpy(p->pos, b, buf_size);
sd_inc(p, buf_size);
return 1;
}
Fix policy generation for small dfas cherry-pick of r2303 from trunk So there are multiple bugs in policy generation for small dfas. - A bug where dfas reduced to only have a none accepting state drop the start state for accept tables in the chfa encoding eg. deny audit dbus, the accept and accept2 tables are resized to 1 but the cfha format requires at least 2. 1 for the none accepting state and 1 for the start state. the kernel check that the accept tables == other state table sizes caught this and rejected it. - the next/check table needs to be padded to the largest base position used + 256 so no input can ever overflow the next/check table (next/check[base+c]). This is normally handled by inserting a transition which resizes the table. However in this case there where no transitions being inserted into the dfa. Resulting in a next/check table size of 2, with a base pos of 0. Meaning the table needed to be padded to 256. - there is an alignment bug for dfas within the container (see below) what follows is a hexdump of the generated policy. With the different parts broken out. There are 2 dfas (policy and older file) and it is the second dfa that is out of alignment. The aadfa blob wrapper should be making sure that the start of the actual dfa is in alignment but this is not happening. In this example 00000000 04 08 00 76 65 72 73 69 6f 6e 00 02 05 00 00 00 |...version......| 00000010 04 08 00 70 72 6f 66 69 6c 65 00 07 05 40 00 2f |...profile...@./| 00000020 68 6f 6d 65 2f 75 62 75 6e 74 75 2f 62 7a 72 2f |home/ubuntu/bzr/| 00000030 61 70 70 61 72 6d 6f 72 2f 74 65 73 74 73 2f 72 |apparmor/tests/r| 00000040 65 67 72 65 73 73 69 6f 6e 2f 61 70 70 61 72 6d |egression/apparm| 00000050 6f 72 2f 71 75 65 72 79 5f 6c 61 62 65 6c 00 04 |or/query_label..| 00000060 06 00 66 6c 61 67 73 00 07 02 00 00 00 00 02 00 |..flags.........| 00000070 00 00 00 02 00 00 00 00 08 02 00 00 00 00 02 00 |................| 00000080 00 00 00 02 00 00 00 00 02 00 00 00 00 04 07 00 |................| 00000090 63 61 70 73 36 34 00 07 02 00 00 00 00 02 00 00 |caps64..........| 000000a0 00 00 02 00 00 00 00 02 00 00 00 00 08 04 09 00 |................| 000000b0 70 6f 6c 69 63 79 64 62 00 07 begin of policy dfa blob wrapper 000000b0 04 06 00 61 61 64 |policydb.....aad| 000000c0 66 61 00 06 size of the following blob (in little endian) so 0x80 000000c0 80 00 00 00 begin of actual policy dfa, notice alignment on 8 byte boundry 000000c0 1b 5e 78 3d 00 00 00 18 |fa.......^x=....| 000000d0 00 00 00 80 00 00 6e 6f 74 66 6c 65 78 00 00 00 |......notflex...| 000000e0 00 01 00 04 00 00 00 00 00 00 00 01 00 00 00 00 |................| 000000f0 00 07 00 04 00 00 00 00 00 00 00 01 00 00 00 00 |................| 00000100 00 02 00 04 00 00 00 00 00 00 00 02 00 00 00 00 |................| 00000110 00 00 00 00 00 00 00 00 00 04 00 02 00 00 00 00 |................| 00000120 00 00 00 02 00 00 00 00 00 08 00 02 00 00 00 00 |................| 00000130 00 00 00 02 00 00 00 00 00 03 00 02 00 00 00 00 |................| 00000140 00 00 00 02 00 00 00 00 08 dfa blob wrapper 00000140 04 06 00 61 61 64 66 |............aadf| 00000150 61 00 06 size of the following blob (in little endian) so 0x4c8 00000150 c8 04 00 00 begin of file dfa, notice alignment. NOT on 8 byte boundry 1b 5e 78 3d 00 00 00 18 00 |a.......^x=.....| 00000160 00 04 c8 00 00 6e 6f 74 66 6c 65 78 00 00 00 00 |.....notflex....| 00000170 01 00 04 00 00 00 00 00 00 00 06 00 00 00 00 00 |................| 00000180 00 00 00 00 9f c2 7f 00 00 00 00 00 00 00 00 00 |................| 00000190 04 00 30 00 00 00 00 00 07 00 04 00 00 00 00 00 |..0.............| 000001a0 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001c0 02 00 04 00 00 00 00 00 00 00 06 00 00 00 00 00 |................| 000001d0 00 00 00 00 00 00 01 00 00 00 01 00 00 00 02 00 |................| 000001e0 00 00 00 00 00 00 00 00 04 00 02 00 00 00 00 00 |................| 000001f0 00 00 06 00 00 00 00 00 02 00 00 00 05 00 05 00 |................| 00000200 08 00 02 00 00 00 00 00 00 01 02 00 00 00 03 00 |................| 00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000260 00 00 00 00 00 00 00 00 00 00 02 00 04 00 00 00 |................| 00000270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000410 03 00 02 00 00 00 00 00 00 01 02 00 00 00 02 00 |................| 00000420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000470 00 00 00 00 00 00 00 00 00 00 01 00 03 00 04 00 |................| 00000480 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000610 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 end of container 00000610 08 |................| 00000620 Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Steve Beattie <steve@nxnw.org>
2014-01-09 17:43:59 -08:00
#define align64(X) (((X) + (typeof(X)) 7) & ~((typeof(X)) 7))
static inline int sd_write_aligned_blob(sd_serialize *p, void *b, int buf_size,
2007-02-27 02:29:16 +00:00
char *name)
{
size_t pad;
u32 tmp;
if (!sd_write_name(p, name))
return 0;
Fix policy generation for small dfas cherry-pick of r2303 from trunk So there are multiple bugs in policy generation for small dfas. - A bug where dfas reduced to only have a none accepting state drop the start state for accept tables in the chfa encoding eg. deny audit dbus, the accept and accept2 tables are resized to 1 but the cfha format requires at least 2. 1 for the none accepting state and 1 for the start state. the kernel check that the accept tables == other state table sizes caught this and rejected it. - the next/check table needs to be padded to the largest base position used + 256 so no input can ever overflow the next/check table (next/check[base+c]). This is normally handled by inserting a transition which resizes the table. However in this case there where no transitions being inserted into the dfa. Resulting in a next/check table size of 2, with a base pos of 0. Meaning the table needed to be padded to 256. - there is an alignment bug for dfas within the container (see below) what follows is a hexdump of the generated policy. With the different parts broken out. There are 2 dfas (policy and older file) and it is the second dfa that is out of alignment. The aadfa blob wrapper should be making sure that the start of the actual dfa is in alignment but this is not happening. In this example 00000000 04 08 00 76 65 72 73 69 6f 6e 00 02 05 00 00 00 |...version......| 00000010 04 08 00 70 72 6f 66 69 6c 65 00 07 05 40 00 2f |...profile...@./| 00000020 68 6f 6d 65 2f 75 62 75 6e 74 75 2f 62 7a 72 2f |home/ubuntu/bzr/| 00000030 61 70 70 61 72 6d 6f 72 2f 74 65 73 74 73 2f 72 |apparmor/tests/r| 00000040 65 67 72 65 73 73 69 6f 6e 2f 61 70 70 61 72 6d |egression/apparm| 00000050 6f 72 2f 71 75 65 72 79 5f 6c 61 62 65 6c 00 04 |or/query_label..| 00000060 06 00 66 6c 61 67 73 00 07 02 00 00 00 00 02 00 |..flags.........| 00000070 00 00 00 02 00 00 00 00 08 02 00 00 00 00 02 00 |................| 00000080 00 00 00 02 00 00 00 00 02 00 00 00 00 04 07 00 |................| 00000090 63 61 70 73 36 34 00 07 02 00 00 00 00 02 00 00 |caps64..........| 000000a0 00 00 02 00 00 00 00 02 00 00 00 00 08 04 09 00 |................| 000000b0 70 6f 6c 69 63 79 64 62 00 07 begin of policy dfa blob wrapper 000000b0 04 06 00 61 61 64 |policydb.....aad| 000000c0 66 61 00 06 size of the following blob (in little endian) so 0x80 000000c0 80 00 00 00 begin of actual policy dfa, notice alignment on 8 byte boundry 000000c0 1b 5e 78 3d 00 00 00 18 |fa.......^x=....| 000000d0 00 00 00 80 00 00 6e 6f 74 66 6c 65 78 00 00 00 |......notflex...| 000000e0 00 01 00 04 00 00 00 00 00 00 00 01 00 00 00 00 |................| 000000f0 00 07 00 04 00 00 00 00 00 00 00 01 00 00 00 00 |................| 00000100 00 02 00 04 00 00 00 00 00 00 00 02 00 00 00 00 |................| 00000110 00 00 00 00 00 00 00 00 00 04 00 02 00 00 00 00 |................| 00000120 00 00 00 02 00 00 00 00 00 08 00 02 00 00 00 00 |................| 00000130 00 00 00 02 00 00 00 00 00 03 00 02 00 00 00 00 |................| 00000140 00 00 00 02 00 00 00 00 08 dfa blob wrapper 00000140 04 06 00 61 61 64 66 |............aadf| 00000150 61 00 06 size of the following blob (in little endian) so 0x4c8 00000150 c8 04 00 00 begin of file dfa, notice alignment. NOT on 8 byte boundry 1b 5e 78 3d 00 00 00 18 00 |a.......^x=.....| 00000160 00 04 c8 00 00 6e 6f 74 66 6c 65 78 00 00 00 00 |.....notflex....| 00000170 01 00 04 00 00 00 00 00 00 00 06 00 00 00 00 00 |................| 00000180 00 00 00 00 9f c2 7f 00 00 00 00 00 00 00 00 00 |................| 00000190 04 00 30 00 00 00 00 00 07 00 04 00 00 00 00 00 |..0.............| 000001a0 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001c0 02 00 04 00 00 00 00 00 00 00 06 00 00 00 00 00 |................| 000001d0 00 00 00 00 00 00 01 00 00 00 01 00 00 00 02 00 |................| 000001e0 00 00 00 00 00 00 00 00 04 00 02 00 00 00 00 00 |................| 000001f0 00 00 06 00 00 00 00 00 02 00 00 00 05 00 05 00 |................| 00000200 08 00 02 00 00 00 00 00 00 01 02 00 00 00 03 00 |................| 00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000260 00 00 00 00 00 00 00 00 00 00 02 00 04 00 00 00 |................| 00000270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000410 03 00 02 00 00 00 00 00 00 01 02 00 00 00 02 00 |................| 00000420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000470 00 00 00 00 00 00 00 00 00 00 01 00 03 00 04 00 |................| 00000480 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000610 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 end of container 00000610 08 |................| 00000620 Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Steve Beattie <steve@nxnw.org>
2014-01-09 17:43:59 -08:00
2007-02-27 02:29:16 +00:00
pad = align64((p->pos + 5) - p->buffer) - ((p->pos + 5) - p->buffer);
if (!sd_prepare_write(p, SD_BLOB, 4 + buf_size + pad))
return 0;
tmp = cpu_to_le32(buf_size + pad);
memcpy(p->pos, &tmp, sizeof(tmp));
sd_inc(p, sizeof(tmp));
memset(p->pos, 0, pad);
sd_inc(p, pad);
memcpy(p->pos, b, buf_size);
sd_inc(p, buf_size);
return 1;
}
static int sd_write_strn(sd_serialize *p, char *b, int size, char *name)
{
u16 tmp;
if (!sd_write_name(p, name))
return 0;
if (!sd_prepare_write(p, SD_STRING, SD_STR_LEN + size))
return 0;
tmp = cpu_to_le16(size);
memcpy(p->pos, &tmp, sizeof(tmp));
sd_inc(p, sizeof(tmp));
memcpy(p->pos, b, size);
sd_inc(p, size);
return 1;
}
static inline int sd_write_string(sd_serialize *p, char *b, char *name)
{
return sd_write_strn(p, b, strlen(b) + 1, name);
}
static inline int sd_write_struct(sd_serialize *p, char *name)
{
if (!sd_write_name(p, name))
return 0;
if (!sd_prepare_write(p, SD_STRUCT, 0))
return 0;
return 1;
}
static inline int sd_write_structend(sd_serialize *p)
{
if (!sd_prepare_write(p, SD_STRUCTEND, 0))
return 0;
return 1;
}
static inline int sd_write_array(sd_serialize *p, char *name, int size)
{
u16 tmp;
if (!sd_write_name(p, name))
return 0;
if (!sd_prepare_write(p, SD_ARRAY, 2))
return 0;
tmp = cpu_to_le16(size);
memcpy(p->pos, &tmp, sizeof(tmp));
sd_inc(p, sizeof(tmp));
return 1;
}
static inline int sd_write_arrayend(sd_serialize *p)
{
if (!sd_prepare_write(p, SD_ARRAYEND, 0))
return 0;
return 1;
}
static inline int sd_write_list(sd_serialize *p, char *name)
{
if (!sd_write_name(p, name))
return 0;
if (!sd_prepare_write(p, SD_LIST, 0))
return 0;
return 1;
}
static inline int sd_write_listend(sd_serialize *p)
{
if (!sd_prepare_write(p, SD_LISTEND, 0))
return 0;
return 1;
}
2007-02-27 02:29:16 +00:00
int sd_serialize_dfa(sd_serialize *p, void *dfa, size_t size)
{
if (dfa && !sd_write_aligned_blob(p, dfa, size, "aadfa"))
return 0;
return 1;
}
2008-04-06 18:55:46 +00:00
int sd_serialize_rlimits(sd_serialize *p, struct aa_rlimits *limits)
{
int i;
if (!limits->specified)
return 1;
if (!sd_write_struct(p, "rlimits"))
return 0;
if (!sd_write32(p, limits->specified))
return 0;
if (!sd_write_array(p, NULL, RLIM_NLIMITS))
return 0;
for (i = 0; i < RLIM_NLIMITS; i++) {
if (!sd_write64(p, limits->limits[i]))
return 0;
}
if (!sd_write_arrayend(p))
return 0;
if (!sd_write_structend(p))
return 0;
return 1;
}
int sd_serialize_xtable(sd_serialize *p, char **table)
{
int count, i;
if (!table[4])
return 1;
if (!sd_write_struct(p, "xtable"))
return 0;
count = 0;
for (i = 4; i < AA_EXEC_COUNT; i++) {
if (table[i])
count++;
}
if (!sd_write_array(p, NULL, count))
return 0;
for (i = 4; i < count + 4; i++) {
int len = strlen(table[i]) + 1;
/* if its a namespace make sure the second : is overwritten
* with 0, so that the namespace and name are \0 seperated
*/
if (*table[i] == ':') {
char *tmp = table[i] + 1;
strsep(&tmp, ":");
}
if (!sd_write_strn(p, table[i], len, NULL))
return 0;
}
if (!sd_write_arrayend(p))
return 0;
if (!sd_write_structend(p))
return 0;
return 1;
}
int count_file_ents(struct cod_entry *list)
{
2007-02-27 02:29:16 +00:00
struct cod_entry *entry;
int count = 0;
2007-02-27 02:29:16 +00:00
list_for_each(list, entry) {
if (entry->pattern_type == ePatternBasic) {
count++;
}
}
return count;
}
int count_tailglob_ents(struct cod_entry *list)
{
2007-02-27 02:29:16 +00:00
struct cod_entry *entry;
int count = 0;
2007-02-27 02:29:16 +00:00
list_for_each(list, entry) {
if (entry->pattern_type == ePatternTailGlob) {
count++;
}
}
return count;
}
int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
int flattened)
{
2009-08-20 15:27:12 +00:00
uint64_t allowed_caps;
if (!sd_write_struct(p, "profile"))
return 0;
if (flattened) {
assert(profile->parent);
int res;
char *name = malloc(3 + strlen(profile->name) +
strlen(profile->parent->name));
if (!name)
return 0;
sprintf(name, "%s//%s", profile->parent->name, profile->name);
res = sd_write_string(p, name, NULL);
free(name);
if (!res)
return 0;
} else {
if (!sd_write_string(p, profile->name, NULL))
return 0;
}
/* only emit this if current kernel at least supports "create" */
if (perms_create) {
if (regex_type == AARE_DFA && profile->xmatch) {
if (!sd_serialize_dfa(p, profile->xmatch, profile->xmatch_size))
return 0;
if (!sd_write32(p, profile->xmatch_len))
return 0;
}
}
if (!sd_write_struct(p, "flags"))
return 0;
/* used to be flags.debug, but that's no longer supported */
if (!sd_write32(p, profile->flags.hat))
return 0;
if (!sd_write32(p, profile->flags.complain))
return 0;
if (!sd_write32(p, profile->flags.audit))
return 0;
if (!sd_write_structend(p))
return 0;
if (profile->flags.path) {
int flags = 0;
if (profile->flags.path & PATH_CHROOT_REL)
flags |= 0x8;
if (profile->flags.path & PATH_MEDIATE_DELETED)
flags |= 0x10000;
if (profile->flags.path & PATH_ATTACH)
flags |= 0x4;
if (profile->flags.path & PATH_CHROOT_NSATTACH)
flags |= 0x10;
if (!sd_write_name(p, "path_flags") ||
!sd_write32(p, flags))
return 0;
}
2009-08-20 15:27:12 +00:00
#define low_caps(X) ((u32) ((X) & 0xffffffff))
#define high_caps(X) ((u32) (((X) >> 32) & 0xffffffff))
allowed_caps = (profile->capabilities) & ~profile->deny_caps;
2009-08-20 15:27:12 +00:00
if (!sd_write32(p, low_caps(allowed_caps)))
return 0;
if (!sd_write32(p, low_caps(allowed_caps & profile->audit_caps)))
return 0;
if (!sd_write32(p, low_caps(profile->deny_caps & profile->quiet_caps)))
Add Audit control to AppArmor through, the use of audit and deny key words. Deny is also used to subtract permissions from the profiles permission set. the audit key word can be prepended to any file, network, or capability rule, to force a selective audit when that rule is matched. Audit permissions accumulate just like standard permissions. eg. audit /bin/foo rw, will force an audit message when the file /bin/foo is opened for read or write. audit /etc/shadow w, /etc/shadow r, will force an audit message when /etc/shadow is opened for writing. The audit message is per permission bit so only opening the file for read access will not, force an audit message. audit can also be used in block form instead of prepending audit to every rule. audit { /bin/foo rw, /etc/shadow w, } /etc/shadow r, # don't audit r access to /etc/shadow the deny key word can be prepended to file, network and capability rules, to result in a denial of permissions when matching that rule. The deny rule specifically does 3 things - it gives AppArmor the ability to remember what has been denied so that the tools don't prompt for what has been denied in previous profiling sessions. - it subtracts globally from the allowed permissions. Deny permissions accumulate in the the deny set just as allow permissions accumulate then, the deny set is subtracted from the allow set. - it quiets known rejects. The default audit behavior of deny rules is to quiet known rejects so that audit logs are not flooded with already known rejects. To have known rejects logged prepend the audit keyword to the deny rule. Deny rules do not have a block form. eg. deny /foo/bar rw, audit deny /etc/shadow w, audit { deny owner /blah w, deny other /foo w, deny /etc/shadow w, }
2008-03-13 17:39:03 +00:00
return 0;
if (!sd_write32(p, 0))
2009-08-20 15:27:12 +00:00
return 0;
if (!sd_write_struct(p, "caps64"))
Add Audit control to AppArmor through, the use of audit and deny key words. Deny is also used to subtract permissions from the profiles permission set. the audit key word can be prepended to any file, network, or capability rule, to force a selective audit when that rule is matched. Audit permissions accumulate just like standard permissions. eg. audit /bin/foo rw, will force an audit message when the file /bin/foo is opened for read or write. audit /etc/shadow w, /etc/shadow r, will force an audit message when /etc/shadow is opened for writing. The audit message is per permission bit so only opening the file for read access will not, force an audit message. audit can also be used in block form instead of prepending audit to every rule. audit { /bin/foo rw, /etc/shadow w, } /etc/shadow r, # don't audit r access to /etc/shadow the deny key word can be prepended to file, network and capability rules, to result in a denial of permissions when matching that rule. The deny rule specifically does 3 things - it gives AppArmor the ability to remember what has been denied so that the tools don't prompt for what has been denied in previous profiling sessions. - it subtracts globally from the allowed permissions. Deny permissions accumulate in the the deny set just as allow permissions accumulate then, the deny set is subtracted from the allow set. - it quiets known rejects. The default audit behavior of deny rules is to quiet known rejects so that audit logs are not flooded with already known rejects. To have known rejects logged prepend the audit keyword to the deny rule. Deny rules do not have a block form. eg. deny /foo/bar rw, audit deny /etc/shadow w, audit { deny owner /blah w, deny other /foo w, deny /etc/shadow w, }
2008-03-13 17:39:03 +00:00
return 0;
2009-08-20 15:27:12 +00:00
if (!sd_write32(p, high_caps(allowed_caps)))
return 0;
2009-08-20 15:27:12 +00:00
if (!sd_write32(p, high_caps(allowed_caps & profile->audit_caps)))
return 0;
if (!sd_write32(p, high_caps(profile->deny_caps & profile->quiet_caps)))
return 0;
if (!sd_write32(p, 0))
2009-08-20 15:27:12 +00:00
return 0;
if (!sd_write_structend(p))
return 0;
2008-04-06 18:55:46 +00:00
if (!sd_serialize_rlimits(p, &profile->rlimits))
return 0;
if (profile->network_allowed && kernel_supports_network) {
size_t i;
if (!sd_write_array(p, "net_allowed_af", get_af_max()))
return 0;
for (i = 0; i < get_af_max(); i++) {
Add Audit control to AppArmor through, the use of audit and deny key words. Deny is also used to subtract permissions from the profiles permission set. the audit key word can be prepended to any file, network, or capability rule, to force a selective audit when that rule is matched. Audit permissions accumulate just like standard permissions. eg. audit /bin/foo rw, will force an audit message when the file /bin/foo is opened for read or write. audit /etc/shadow w, /etc/shadow r, will force an audit message when /etc/shadow is opened for writing. The audit message is per permission bit so only opening the file for read access will not, force an audit message. audit can also be used in block form instead of prepending audit to every rule. audit { /bin/foo rw, /etc/shadow w, } /etc/shadow r, # don't audit r access to /etc/shadow the deny key word can be prepended to file, network and capability rules, to result in a denial of permissions when matching that rule. The deny rule specifically does 3 things - it gives AppArmor the ability to remember what has been denied so that the tools don't prompt for what has been denied in previous profiling sessions. - it subtracts globally from the allowed permissions. Deny permissions accumulate in the the deny set just as allow permissions accumulate then, the deny set is subtracted from the allow set. - it quiets known rejects. The default audit behavior of deny rules is to quiet known rejects so that audit logs are not flooded with already known rejects. To have known rejects logged prepend the audit keyword to the deny rule. Deny rules do not have a block form. eg. deny /foo/bar rw, audit deny /etc/shadow w, audit { deny owner /blah w, deny other /foo w, deny /etc/shadow w, }
2008-03-13 17:39:03 +00:00
u16 allowed = profile->network_allowed[i] &
~profile->deny_network[i];
if (!sd_write16(p, allowed))
return 0;
if (!sd_write16(p, allowed & profile->audit_network[i]))
return 0;
if (!sd_write16(p, profile->deny_network[i] & profile->quiet_network[i]))
return 0;
}
if (!sd_write_arrayend(p))
return 0;
} else if (profile->network_allowed)
pwarn(_("profile %s network rules not enforced\n"), profile->name);
if (profile->policy_dfa && regex_type == AARE_DFA) {
if (!sd_write_struct(p, "policydb"))
return 0;
if (!sd_serialize_dfa(p, profile->policy_dfa, profile->policy_dfa_size))
return 0;
if (!sd_write_structend(p))
return 0;
}
2007-02-27 02:29:16 +00:00
/* either have a single dfa or lists of different entry types */
if (regex_type == AARE_DFA) {
if (!sd_serialize_dfa(p, profile->dfa, profile->dfa_size))
return 0;
if (!sd_serialize_xtable(p, profile->exec_table))
return 0;
2007-02-27 02:29:16 +00:00
} else {
PERROR(_("Unknown pattern type\n"));
return 1;
}
if (profile->hat_table && regex_type != AARE_DFA) {
if (!sd_write_list(p, "hats"))
return 0;
if (load_hats(p, profile) != 0)
return 0;
if (!sd_write_listend(p))
return 0;
}
if (!sd_write_structend(p))
return 0;
return 1;
}
int sd_serialize_top_profile(sd_serialize *p, struct codomain *profile)
{
2007-02-27 02:29:16 +00:00
int version;
if (regex_type == AARE_DFA) {
/* Not yet
if (profile->policy_dfa)
version = SUBDOMAIN_INTERFACE_POLICYDB;
else */
version = SUBDOMAIN_INTERFACE_DFA_VERSION;
} else
2007-02-27 02:29:16 +00:00
version = SUBDOMAIN_INTERFACE_VERSION;
if (!sd_write_name(p, "version"))
return 0;
2007-02-27 02:29:16 +00:00
if (!sd_write32(p, version))
return 0;
if (profile_namespace) {
if (!sd_write_string(p, profile_namespace, "namespace"))
return 0;
} else if (profile->namespace) {
if (!sd_write_string(p, profile->namespace, "namespace"))
return 0;
}
return sd_serialize_profile(p, profile, profile->parent ? 1 : 0);
}
int cache_fd = -1;
int sd_serialize_codomain(int option, struct codomain *cod)
{
int fd = -1;
int error = -ENOMEM, size, wsize;
sd_serialize *work_area;
char *filename = NULL;
switch (option) {
case OPTION_ADD:
if (asprintf(&filename, "%s/.load", subdomainbase) == -1)
goto exit;
if (kernel_load) fd = open(filename, O_WRONLY);
break;
case OPTION_REPLACE:
if (asprintf(&filename, "%s/.replace", subdomainbase) == -1)
goto exit;
if (kernel_load) fd = open(filename, O_WRONLY);
break;
case OPTION_REMOVE:
if (asprintf(&filename, "%s/.remove", subdomainbase) == -1)
goto exit;
if (kernel_load) fd = open(filename, O_WRONLY);
break;
case OPTION_STDOUT:
filename = "stdout";
fd = dup(1);
break;
case OPTION_OFILE:
fd = dup(fileno(ofile));
break;
default:
error = -EINVAL;
goto exit;
break;
}
if (fd < 0 && (kernel_load || option == OPTION_OFILE || option == OPTION_STDOUT)) {
PERROR(_("Unable to open %s - %s\n"), filename,
strerror(errno));
error = -errno;
goto exit;
}
error = 0;
if (option != OPTION_STDOUT && option != OPTION_OFILE)
2006-12-19 17:44:53 +00:00
free(filename);
if (option == OPTION_REMOVE) {
char *name, *ns = NULL;
int len = 0;
if (profile_namespace) {
len += strlen(profile_namespace) + 2;
ns = profile_namespace;
} else if (cod->namespace) {
len += strlen(cod->namespace) + 2;
ns = cod->namespace;
}
if (cod->parent) {
name = malloc(strlen(cod->name) + 3 +
strlen(cod->parent->name) + len);
if (!name) {
PERROR(_("Memory Allocation Error: Unable to remove ^%s\n"), cod->name);
error = -errno;
goto exit;
}
if (ns)
sprintf(name, ":%s:%s//%s", ns,
cod->parent->name, cod->name);
else
sprintf(name, "%s//%s", cod->parent->name,
cod->name);
} else if (ns) {
name = malloc(len + strlen(cod->name) + 1);
if (!name) {
PERROR(_("Memory Allocation Error: Unable to remove %s:%s."), ns, cod->name);
error = -errno;
goto exit;
}
sprintf(name, ":%s:%s", ns, cod->name);
} else {
name = cod->name;
}
size = strlen(name) + 1;
if (kernel_load) {
wsize = write(fd, name, size);
if (wsize < 0)
error = -errno;
}
if (cod->parent || ns)
free(name);
} else {
work_area = alloc_sd_serial();
if (!work_area) {
close(fd);
PERROR(_("unable to create work area\n"));
error = -ENOMEM;
goto exit;
}
if (!sd_serialize_top_profile(work_area, cod)) {
close(fd);
free_sd_serial(work_area);
PERROR(_("unable to serialize profile %s\n"),
cod->name);
goto exit;
}
size = work_area->pos - work_area->buffer;
if (kernel_load || option == OPTION_STDOUT || option == OPTION_OFILE) {
wsize = write(fd, work_area->buffer, size);
if (wsize < 0) {
error = -errno;
} else if (wsize < size) {
PERROR(_("%s: Unable to write entire profile entry\n"),
progname);
error = -EIO;
}
}
if (cache_fd != -1) {
wsize = write(cache_fd, work_area->buffer, size);
if (wsize < 0) {
error = -errno;
} else if (wsize < size) {
PERROR(_("%s: Unable to write entire profile entry to cache\n"),
progname);
error = -EIO;
}
}
free_sd_serial(work_area);
}
close(fd);
if (cod->hat_table && regex_type == AARE_DFA && option != OPTION_REMOVE) {
if (load_flattened_hats(cod) != 0)
return 0;
}
exit:
return error;
}
/* bleah the kernel should just loop and do multiple load, but to support
* older systems we need to do this
*/
#define PROFILE_HEADER_SIZE
static char header_version[] = "\x04\x08\x00version";
static char *next_profile_buffer(char *buffer, int size)
{
char *b = buffer;
for (; size - sizeof(header_version); b++, size--) {
if (memcmp(b, header_version, sizeof(header_version)) == 0) {
return b;
}
}
return NULL;
}
static int write_buffer(int fd, char *buffer, int size, int set)
Fix profile loads from cache files that contain multiple profiles backport of dev commit 2510 v3: fix freeing of filename when undefined v2: address tyhicks feedback refactor to have a common write routine fix issue with set profile load being done even if !kernel_load Profile loads from cache files that contain multiple profiles can result in multiple reloads of the same profile or error messages about failure to load profiles if the --add option is used. eg. apparmor="STATUS" operation="profile_load" name="/usr/lib/apache2/mpm-prefork/apache2" pid=8631 comm="apparmor_parser" <sth0R> [82932.058388] type=1400 audit(1395415826.937:616): apparmor="STATUS" operation="profile_load" name="DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.058391] type=1400 audit(1395415826.937:617): apparmor="STATUS" operation="profile_load" name="HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" <sth0R> [82932.058394] type=1400 audit(1395415826.937:618): apparmor="STATUS" operation="profile_load" name="phpsysinfo" pid=8631 comm="apparmor_parser" <sth0R> [82932.059058] type=1400 audit(1395415826.937:619): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.059574] type=1400 audit(1395415826.937:620): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" The reason this happens is that the cache file is a container that can contain multiple profiles in sequential order profile1 profile2 profile3 The parser loads the entire cache file to memory and the writes the whole file to the kernel interface. It then skips foward in the file to the next profile and reloads the file from that profile into the kernel. eg. First load profile1 profile2 profile3 advance to profile2, do second load profile2 profile3 advance to profile3, do third load profile3 With older kernels the interface would stop after the first profile and return that it had processed the whole file, thus while wasting compute resources copying extra data no errors occurred. However newer kernels now support atomic loading of multipe profiles, so that all the profiles passed in to the interface get processed. This means on newer kernels the current parser load behavior results in multiple loads/replacements when a cache file contains more than one profile (note: loads from a compile do not have this problem). To fix this, detect if the kernel supports atomic set loads, and load the cache file once. If it doesn't only load one profile section from a cache file at a time. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-05-08 09:37:01 -07:00
{
const char *err_str = set ? "profile set" : "profile";
int wsize = write(fd, buffer, size);
if (wsize < 0) {
PERROR(_("%s: Unable to write %s\n"), progname, err_str);
return -errno;
} else if (wsize < size) {
PERROR(_("%s: Unable to write %s\n"), progname, err_str);
return -EPROTO;
}
return 0;
}
int sd_load_buffer(int option, char *buffer, int size)
{
int fd = -1;
Fix profile loads from cache files that contain multiple profiles backport of dev commit 2510 v3: fix freeing of filename when undefined v2: address tyhicks feedback refactor to have a common write routine fix issue with set profile load being done even if !kernel_load Profile loads from cache files that contain multiple profiles can result in multiple reloads of the same profile or error messages about failure to load profiles if the --add option is used. eg. apparmor="STATUS" operation="profile_load" name="/usr/lib/apache2/mpm-prefork/apache2" pid=8631 comm="apparmor_parser" <sth0R> [82932.058388] type=1400 audit(1395415826.937:616): apparmor="STATUS" operation="profile_load" name="DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.058391] type=1400 audit(1395415826.937:617): apparmor="STATUS" operation="profile_load" name="HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" <sth0R> [82932.058394] type=1400 audit(1395415826.937:618): apparmor="STATUS" operation="profile_load" name="phpsysinfo" pid=8631 comm="apparmor_parser" <sth0R> [82932.059058] type=1400 audit(1395415826.937:619): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.059574] type=1400 audit(1395415826.937:620): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" The reason this happens is that the cache file is a container that can contain multiple profiles in sequential order profile1 profile2 profile3 The parser loads the entire cache file to memory and the writes the whole file to the kernel interface. It then skips foward in the file to the next profile and reloads the file from that profile into the kernel. eg. First load profile1 profile2 profile3 advance to profile2, do second load profile2 profile3 advance to profile3, do third load profile3 With older kernels the interface would stop after the first profile and return that it had processed the whole file, thus while wasting compute resources copying extra data no errors occurred. However newer kernels now support atomic loading of multipe profiles, so that all the profiles passed in to the interface get processed. This means on newer kernels the current parser load behavior results in multiple loads/replacements when a cache file contains more than one profile (note: loads from a compile do not have this problem). To fix this, detect if the kernel supports atomic set loads, and load the cache file once. If it doesn't only load one profile section from a cache file at a time. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-05-08 09:37:01 -07:00
int error, bsize;
char *filename = NULL;
Fix profile loads from cache files that contain multiple profiles backport of dev commit 2510 v3: fix freeing of filename when undefined v2: address tyhicks feedback refactor to have a common write routine fix issue with set profile load being done even if !kernel_load Profile loads from cache files that contain multiple profiles can result in multiple reloads of the same profile or error messages about failure to load profiles if the --add option is used. eg. apparmor="STATUS" operation="profile_load" name="/usr/lib/apache2/mpm-prefork/apache2" pid=8631 comm="apparmor_parser" <sth0R> [82932.058388] type=1400 audit(1395415826.937:616): apparmor="STATUS" operation="profile_load" name="DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.058391] type=1400 audit(1395415826.937:617): apparmor="STATUS" operation="profile_load" name="HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" <sth0R> [82932.058394] type=1400 audit(1395415826.937:618): apparmor="STATUS" operation="profile_load" name="phpsysinfo" pid=8631 comm="apparmor_parser" <sth0R> [82932.059058] type=1400 audit(1395415826.937:619): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.059574] type=1400 audit(1395415826.937:620): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" The reason this happens is that the cache file is a container that can contain multiple profiles in sequential order profile1 profile2 profile3 The parser loads the entire cache file to memory and the writes the whole file to the kernel interface. It then skips foward in the file to the next profile and reloads the file from that profile into the kernel. eg. First load profile1 profile2 profile3 advance to profile2, do second load profile2 profile3 advance to profile3, do third load profile3 With older kernels the interface would stop after the first profile and return that it had processed the whole file, thus while wasting compute resources copying extra data no errors occurred. However newer kernels now support atomic loading of multipe profiles, so that all the profiles passed in to the interface get processed. This means on newer kernels the current parser load behavior results in multiple loads/replacements when a cache file contains more than one profile (note: loads from a compile do not have this problem). To fix this, detect if the kernel supports atomic set loads, and load the cache file once. If it doesn't only load one profile section from a cache file at a time. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-05-08 09:37:01 -07:00
/* TODO: push backup into caller */
if (!kernel_load)
return 0;
switch (option) {
case OPTION_ADD:
if (asprintf(&filename, "%s/.load", subdomainbase) == -1)
Fix profile loads from cache files that contain multiple profiles backport of dev commit 2510 v3: fix freeing of filename when undefined v2: address tyhicks feedback refactor to have a common write routine fix issue with set profile load being done even if !kernel_load Profile loads from cache files that contain multiple profiles can result in multiple reloads of the same profile or error messages about failure to load profiles if the --add option is used. eg. apparmor="STATUS" operation="profile_load" name="/usr/lib/apache2/mpm-prefork/apache2" pid=8631 comm="apparmor_parser" <sth0R> [82932.058388] type=1400 audit(1395415826.937:616): apparmor="STATUS" operation="profile_load" name="DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.058391] type=1400 audit(1395415826.937:617): apparmor="STATUS" operation="profile_load" name="HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" <sth0R> [82932.058394] type=1400 audit(1395415826.937:618): apparmor="STATUS" operation="profile_load" name="phpsysinfo" pid=8631 comm="apparmor_parser" <sth0R> [82932.059058] type=1400 audit(1395415826.937:619): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.059574] type=1400 audit(1395415826.937:620): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" The reason this happens is that the cache file is a container that can contain multiple profiles in sequential order profile1 profile2 profile3 The parser loads the entire cache file to memory and the writes the whole file to the kernel interface. It then skips foward in the file to the next profile and reloads the file from that profile into the kernel. eg. First load profile1 profile2 profile3 advance to profile2, do second load profile2 profile3 advance to profile3, do third load profile3 With older kernels the interface would stop after the first profile and return that it had processed the whole file, thus while wasting compute resources copying extra data no errors occurred. However newer kernels now support atomic loading of multipe profiles, so that all the profiles passed in to the interface get processed. This means on newer kernels the current parser load behavior results in multiple loads/replacements when a cache file contains more than one profile (note: loads from a compile do not have this problem). To fix this, detect if the kernel supports atomic set loads, and load the cache file once. If it doesn't only load one profile section from a cache file at a time. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-05-08 09:37:01 -07:00
return -ENOMEM;
break;
case OPTION_REPLACE:
if (asprintf(&filename, "%s/.replace", subdomainbase) == -1)
Fix profile loads from cache files that contain multiple profiles backport of dev commit 2510 v3: fix freeing of filename when undefined v2: address tyhicks feedback refactor to have a common write routine fix issue with set profile load being done even if !kernel_load Profile loads from cache files that contain multiple profiles can result in multiple reloads of the same profile or error messages about failure to load profiles if the --add option is used. eg. apparmor="STATUS" operation="profile_load" name="/usr/lib/apache2/mpm-prefork/apache2" pid=8631 comm="apparmor_parser" <sth0R> [82932.058388] type=1400 audit(1395415826.937:616): apparmor="STATUS" operation="profile_load" name="DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.058391] type=1400 audit(1395415826.937:617): apparmor="STATUS" operation="profile_load" name="HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" <sth0R> [82932.058394] type=1400 audit(1395415826.937:618): apparmor="STATUS" operation="profile_load" name="phpsysinfo" pid=8631 comm="apparmor_parser" <sth0R> [82932.059058] type=1400 audit(1395415826.937:619): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.059574] type=1400 audit(1395415826.937:620): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" The reason this happens is that the cache file is a container that can contain multiple profiles in sequential order profile1 profile2 profile3 The parser loads the entire cache file to memory and the writes the whole file to the kernel interface. It then skips foward in the file to the next profile and reloads the file from that profile into the kernel. eg. First load profile1 profile2 profile3 advance to profile2, do second load profile2 profile3 advance to profile3, do third load profile3 With older kernels the interface would stop after the first profile and return that it had processed the whole file, thus while wasting compute resources copying extra data no errors occurred. However newer kernels now support atomic loading of multipe profiles, so that all the profiles passed in to the interface get processed. This means on newer kernels the current parser load behavior results in multiple loads/replacements when a cache file contains more than one profile (note: loads from a compile do not have this problem). To fix this, detect if the kernel supports atomic set loads, and load the cache file once. If it doesn't only load one profile section from a cache file at a time. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-05-08 09:37:01 -07:00
return -ENOMEM;
break;
default:
Fix profile loads from cache files that contain multiple profiles backport of dev commit 2510 v3: fix freeing of filename when undefined v2: address tyhicks feedback refactor to have a common write routine fix issue with set profile load being done even if !kernel_load Profile loads from cache files that contain multiple profiles can result in multiple reloads of the same profile or error messages about failure to load profiles if the --add option is used. eg. apparmor="STATUS" operation="profile_load" name="/usr/lib/apache2/mpm-prefork/apache2" pid=8631 comm="apparmor_parser" <sth0R> [82932.058388] type=1400 audit(1395415826.937:616): apparmor="STATUS" operation="profile_load" name="DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.058391] type=1400 audit(1395415826.937:617): apparmor="STATUS" operation="profile_load" name="HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" <sth0R> [82932.058394] type=1400 audit(1395415826.937:618): apparmor="STATUS" operation="profile_load" name="phpsysinfo" pid=8631 comm="apparmor_parser" <sth0R> [82932.059058] type=1400 audit(1395415826.937:619): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.059574] type=1400 audit(1395415826.937:620): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" The reason this happens is that the cache file is a container that can contain multiple profiles in sequential order profile1 profile2 profile3 The parser loads the entire cache file to memory and the writes the whole file to the kernel interface. It then skips foward in the file to the next profile and reloads the file from that profile into the kernel. eg. First load profile1 profile2 profile3 advance to profile2, do second load profile2 profile3 advance to profile3, do third load profile3 With older kernels the interface would stop after the first profile and return that it had processed the whole file, thus while wasting compute resources copying extra data no errors occurred. However newer kernels now support atomic loading of multipe profiles, so that all the profiles passed in to the interface get processed. This means on newer kernels the current parser load behavior results in multiple loads/replacements when a cache file contains more than one profile (note: loads from a compile do not have this problem). To fix this, detect if the kernel supports atomic set loads, and load the cache file once. If it doesn't only load one profile section from a cache file at a time. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-05-08 09:37:01 -07:00
return -EINVAL;
}
Fix profile loads from cache files that contain multiple profiles backport of dev commit 2510 v3: fix freeing of filename when undefined v2: address tyhicks feedback refactor to have a common write routine fix issue with set profile load being done even if !kernel_load Profile loads from cache files that contain multiple profiles can result in multiple reloads of the same profile or error messages about failure to load profiles if the --add option is used. eg. apparmor="STATUS" operation="profile_load" name="/usr/lib/apache2/mpm-prefork/apache2" pid=8631 comm="apparmor_parser" <sth0R> [82932.058388] type=1400 audit(1395415826.937:616): apparmor="STATUS" operation="profile_load" name="DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.058391] type=1400 audit(1395415826.937:617): apparmor="STATUS" operation="profile_load" name="HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" <sth0R> [82932.058394] type=1400 audit(1395415826.937:618): apparmor="STATUS" operation="profile_load" name="phpsysinfo" pid=8631 comm="apparmor_parser" <sth0R> [82932.059058] type=1400 audit(1395415826.937:619): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.059574] type=1400 audit(1395415826.937:620): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" The reason this happens is that the cache file is a container that can contain multiple profiles in sequential order profile1 profile2 profile3 The parser loads the entire cache file to memory and the writes the whole file to the kernel interface. It then skips foward in the file to the next profile and reloads the file from that profile into the kernel. eg. First load profile1 profile2 profile3 advance to profile2, do second load profile2 profile3 advance to profile3, do third load profile3 With older kernels the interface would stop after the first profile and return that it had processed the whole file, thus while wasting compute resources copying extra data no errors occurred. However newer kernels now support atomic loading of multipe profiles, so that all the profiles passed in to the interface get processed. This means on newer kernels the current parser load behavior results in multiple loads/replacements when a cache file contains more than one profile (note: loads from a compile do not have this problem). To fix this, detect if the kernel supports atomic set loads, and load the cache file once. If it doesn't only load one profile section from a cache file at a time. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-05-08 09:37:01 -07:00
fd = open(filename, O_WRONLY);
if (fd < 0) {
PERROR(_("Unable to open %s - %s\n"), filename,
strerror(errno));
error = -errno;
Fix profile loads from cache files that contain multiple profiles backport of dev commit 2510 v3: fix freeing of filename when undefined v2: address tyhicks feedback refactor to have a common write routine fix issue with set profile load being done even if !kernel_load Profile loads from cache files that contain multiple profiles can result in multiple reloads of the same profile or error messages about failure to load profiles if the --add option is used. eg. apparmor="STATUS" operation="profile_load" name="/usr/lib/apache2/mpm-prefork/apache2" pid=8631 comm="apparmor_parser" <sth0R> [82932.058388] type=1400 audit(1395415826.937:616): apparmor="STATUS" operation="profile_load" name="DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.058391] type=1400 audit(1395415826.937:617): apparmor="STATUS" operation="profile_load" name="HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" <sth0R> [82932.058394] type=1400 audit(1395415826.937:618): apparmor="STATUS" operation="profile_load" name="phpsysinfo" pid=8631 comm="apparmor_parser" <sth0R> [82932.059058] type=1400 audit(1395415826.937:619): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.059574] type=1400 audit(1395415826.937:620): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" The reason this happens is that the cache file is a container that can contain multiple profiles in sequential order profile1 profile2 profile3 The parser loads the entire cache file to memory and the writes the whole file to the kernel interface. It then skips foward in the file to the next profile and reloads the file from that profile into the kernel. eg. First load profile1 profile2 profile3 advance to profile2, do second load profile2 profile3 advance to profile3, do third load profile3 With older kernels the interface would stop after the first profile and return that it had processed the whole file, thus while wasting compute resources copying extra data no errors occurred. However newer kernels now support atomic loading of multipe profiles, so that all the profiles passed in to the interface get processed. This means on newer kernels the current parser load behavior results in multiple loads/replacements when a cache file contains more than one profile (note: loads from a compile do not have this problem). To fix this, detect if the kernel supports atomic set loads, and load the cache file once. If it doesn't only load one profile section from a cache file at a time. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-05-08 09:37:01 -07:00
goto out;
}
Fix profile loads from cache files that contain multiple profiles backport of dev commit 2510 v3: fix freeing of filename when undefined v2: address tyhicks feedback refactor to have a common write routine fix issue with set profile load being done even if !kernel_load Profile loads from cache files that contain multiple profiles can result in multiple reloads of the same profile or error messages about failure to load profiles if the --add option is used. eg. apparmor="STATUS" operation="profile_load" name="/usr/lib/apache2/mpm-prefork/apache2" pid=8631 comm="apparmor_parser" <sth0R> [82932.058388] type=1400 audit(1395415826.937:616): apparmor="STATUS" operation="profile_load" name="DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.058391] type=1400 audit(1395415826.937:617): apparmor="STATUS" operation="profile_load" name="HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" <sth0R> [82932.058394] type=1400 audit(1395415826.937:618): apparmor="STATUS" operation="profile_load" name="phpsysinfo" pid=8631 comm="apparmor_parser" <sth0R> [82932.059058] type=1400 audit(1395415826.937:619): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.059574] type=1400 audit(1395415826.937:620): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" The reason this happens is that the cache file is a container that can contain multiple profiles in sequential order profile1 profile2 profile3 The parser loads the entire cache file to memory and the writes the whole file to the kernel interface. It then skips foward in the file to the next profile and reloads the file from that profile into the kernel. eg. First load profile1 profile2 profile3 advance to profile2, do second load profile2 profile3 advance to profile3, do third load profile3 With older kernels the interface would stop after the first profile and return that it had processed the whole file, thus while wasting compute resources copying extra data no errors occurred. However newer kernels now support atomic loading of multipe profiles, so that all the profiles passed in to the interface get processed. This means on newer kernels the current parser load behavior results in multiple loads/replacements when a cache file contains more than one profile (note: loads from a compile do not have this problem). To fix this, detect if the kernel supports atomic set loads, and load the cache file once. If it doesn't only load one profile section from a cache file at a time. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-05-08 09:37:01 -07:00
if (kernel_supports_setload) {
error = write_buffer(fd, buffer, size, TRUE);
Fix profile loads from cache files that contain multiple profiles backport of dev commit 2510 v3: fix freeing of filename when undefined v2: address tyhicks feedback refactor to have a common write routine fix issue with set profile load being done even if !kernel_load Profile loads from cache files that contain multiple profiles can result in multiple reloads of the same profile or error messages about failure to load profiles if the --add option is used. eg. apparmor="STATUS" operation="profile_load" name="/usr/lib/apache2/mpm-prefork/apache2" pid=8631 comm="apparmor_parser" <sth0R> [82932.058388] type=1400 audit(1395415826.937:616): apparmor="STATUS" operation="profile_load" name="DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.058391] type=1400 audit(1395415826.937:617): apparmor="STATUS" operation="profile_load" name="HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" <sth0R> [82932.058394] type=1400 audit(1395415826.937:618): apparmor="STATUS" operation="profile_load" name="phpsysinfo" pid=8631 comm="apparmor_parser" <sth0R> [82932.059058] type=1400 audit(1395415826.937:619): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.059574] type=1400 audit(1395415826.937:620): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" The reason this happens is that the cache file is a container that can contain multiple profiles in sequential order profile1 profile2 profile3 The parser loads the entire cache file to memory and the writes the whole file to the kernel interface. It then skips foward in the file to the next profile and reloads the file from that profile into the kernel. eg. First load profile1 profile2 profile3 advance to profile2, do second load profile2 profile3 advance to profile3, do third load profile3 With older kernels the interface would stop after the first profile and return that it had processed the whole file, thus while wasting compute resources copying extra data no errors occurred. However newer kernels now support atomic loading of multipe profiles, so that all the profiles passed in to the interface get processed. This means on newer kernels the current parser load behavior results in multiple loads/replacements when a cache file contains more than one profile (note: loads from a compile do not have this problem). To fix this, detect if the kernel supports atomic set loads, and load the cache file once. If it doesn't only load one profile section from a cache file at a time. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-05-08 09:37:01 -07:00
} else {
char *b, *next;
error = 0; /* in case there are no profiles */
for (b = buffer; b; b = next, size -= bsize) {
next = next_profile_buffer(b + sizeof(header_version),
size);
if (next)
bsize = next - b;
else
bsize = size;
error = write_buffer(fd, b, bsize, FALSE);
Fix profile loads from cache files that contain multiple profiles backport of dev commit 2510 v3: fix freeing of filename when undefined v2: address tyhicks feedback refactor to have a common write routine fix issue with set profile load being done even if !kernel_load Profile loads from cache files that contain multiple profiles can result in multiple reloads of the same profile or error messages about failure to load profiles if the --add option is used. eg. apparmor="STATUS" operation="profile_load" name="/usr/lib/apache2/mpm-prefork/apache2" pid=8631 comm="apparmor_parser" <sth0R> [82932.058388] type=1400 audit(1395415826.937:616): apparmor="STATUS" operation="profile_load" name="DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.058391] type=1400 audit(1395415826.937:617): apparmor="STATUS" operation="profile_load" name="HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" <sth0R> [82932.058394] type=1400 audit(1395415826.937:618): apparmor="STATUS" operation="profile_load" name="phpsysinfo" pid=8631 comm="apparmor_parser" <sth0R> [82932.059058] type=1400 audit(1395415826.937:619): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.059574] type=1400 audit(1395415826.937:620): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" The reason this happens is that the cache file is a container that can contain multiple profiles in sequential order profile1 profile2 profile3 The parser loads the entire cache file to memory and the writes the whole file to the kernel interface. It then skips foward in the file to the next profile and reloads the file from that profile into the kernel. eg. First load profile1 profile2 profile3 advance to profile2, do second load profile2 profile3 advance to profile3, do third load profile3 With older kernels the interface would stop after the first profile and return that it had processed the whole file, thus while wasting compute resources copying extra data no errors occurred. However newer kernels now support atomic loading of multipe profiles, so that all the profiles passed in to the interface get processed. This means on newer kernels the current parser load behavior results in multiple loads/replacements when a cache file contains more than one profile (note: loads from a compile do not have this problem). To fix this, detect if the kernel supports atomic set loads, and load the cache file once. If it doesn't only load one profile section from a cache file at a time. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-05-08 09:37:01 -07:00
if (error)
break;
}
}
Fix profile loads from cache files that contain multiple profiles backport of dev commit 2510 v3: fix freeing of filename when undefined v2: address tyhicks feedback refactor to have a common write routine fix issue with set profile load being done even if !kernel_load Profile loads from cache files that contain multiple profiles can result in multiple reloads of the same profile or error messages about failure to load profiles if the --add option is used. eg. apparmor="STATUS" operation="profile_load" name="/usr/lib/apache2/mpm-prefork/apache2" pid=8631 comm="apparmor_parser" <sth0R> [82932.058388] type=1400 audit(1395415826.937:616): apparmor="STATUS" operation="profile_load" name="DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.058391] type=1400 audit(1395415826.937:617): apparmor="STATUS" operation="profile_load" name="HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" <sth0R> [82932.058394] type=1400 audit(1395415826.937:618): apparmor="STATUS" operation="profile_load" name="phpsysinfo" pid=8631 comm="apparmor_parser" <sth0R> [82932.059058] type=1400 audit(1395415826.937:619): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.059574] type=1400 audit(1395415826.937:620): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" The reason this happens is that the cache file is a container that can contain multiple profiles in sequential order profile1 profile2 profile3 The parser loads the entire cache file to memory and the writes the whole file to the kernel interface. It then skips foward in the file to the next profile and reloads the file from that profile into the kernel. eg. First load profile1 profile2 profile3 advance to profile2, do second load profile2 profile3 advance to profile3, do third load profile3 With older kernels the interface would stop after the first profile and return that it had processed the whole file, thus while wasting compute resources copying extra data no errors occurred. However newer kernels now support atomic loading of multipe profiles, so that all the profiles passed in to the interface get processed. This means on newer kernels the current parser load behavior results in multiple loads/replacements when a cache file contains more than one profile (note: loads from a compile do not have this problem). To fix this, detect if the kernel supports atomic set loads, and load the cache file once. If it doesn't only load one profile section from a cache file at a time. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-05-08 09:37:01 -07:00
close(fd);
out:
free(filename);
Fix profile loads from cache files that contain multiple profiles backport of dev commit 2510 v3: fix freeing of filename when undefined v2: address tyhicks feedback refactor to have a common write routine fix issue with set profile load being done even if !kernel_load Profile loads from cache files that contain multiple profiles can result in multiple reloads of the same profile or error messages about failure to load profiles if the --add option is used. eg. apparmor="STATUS" operation="profile_load" name="/usr/lib/apache2/mpm-prefork/apache2" pid=8631 comm="apparmor_parser" <sth0R> [82932.058388] type=1400 audit(1395415826.937:616): apparmor="STATUS" operation="profile_load" name="DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.058391] type=1400 audit(1395415826.937:617): apparmor="STATUS" operation="profile_load" name="HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" <sth0R> [82932.058394] type=1400 audit(1395415826.937:618): apparmor="STATUS" operation="profile_load" name="phpsysinfo" pid=8631 comm="apparmor_parser" <sth0R> [82932.059058] type=1400 audit(1395415826.937:619): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//DEFAULT_URI" pid=8631 comm="apparmor_parser" <sth0R> [82932.059574] type=1400 audit(1395415826.937:620): apparmor="STATUS" operation="profile_replace" info="profile can not be replaced" error=-17 name="/usr/lib/apache2/mpm-prefork/apache2//HANDLING_UNTRUSTED_INPUT" pid=8631 comm="apparmor_parser" The reason this happens is that the cache file is a container that can contain multiple profiles in sequential order profile1 profile2 profile3 The parser loads the entire cache file to memory and the writes the whole file to the kernel interface. It then skips foward in the file to the next profile and reloads the file from that profile into the kernel. eg. First load profile1 profile2 profile3 advance to profile2, do second load profile2 profile3 advance to profile3, do third load profile3 With older kernels the interface would stop after the first profile and return that it had processed the whole file, thus while wasting compute resources copying extra data no errors occurred. However newer kernels now support atomic loading of multipe profiles, so that all the profiles passed in to the interface get processed. This means on newer kernels the current parser load behavior results in multiple loads/replacements when a cache file contains more than one profile (note: loads from a compile do not have this problem). To fix this, detect if the kernel supports atomic set loads, and load the cache file once. If it doesn't only load one profile section from a cache file at a time. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-05-08 09:37:01 -07:00
return error;
}