apparmor/parser/parser_interface.c

596 lines
14 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)
*
* Copyright (c) 2013
* Canonical Ltd. (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 <string>
#include <sstream>
#include <sys/apparmor.h>
#include "lib.h"
#include "parser.h"
#include "profile.h"
2007-02-27 02:29:16 +00:00
#include "libapparmor_re/apparmor_re.h"
#include <unistd.h>
#include <linux/unistd.h>
#define SD_CODE_SIZE (sizeof(u8))
#define SD_STR_LEN (sizeof(u16))
int __sd_serialize_profile(int option, aa_kernel_interface *kernel_interface,
Profile *prof, int cache_fd);
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_profile(int option, aa_kernel_interface *kernel_interface,
Profile *prof, int cache_fd)
{
int retval = 0;
int error = 0;
PDEBUG("Serializing policy for %s.\n", prof->name);
retval = __sd_serialize_profile(option, kernel_interface, prof, cache_fd);
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, prof->name);
print_error(error);
break;
case OPTION_REPLACE:
PERROR(_("%s: Unable to replace \"%s\". "),
progname, prof->name);
print_error(error);
break;
case OPTION_REMOVE:
PERROR(_("%s: Unable to remove \"%s\". "),
progname, prof->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);
break;
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"),
prof->name);
break;
case OPTION_REPLACE:
printf(_("Replacement succeeded for \"%s\".\n"),
prof->name);
break;
case OPTION_REMOVE:
printf(_("Removal succeeded for \"%s\".\n"),
prof->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"
};
static inline void sd_write8(std::ostringstream &buf, u8 b)
{
buf.write((const char *) &b, 1);
}
static inline void sd_write16(std::ostringstream &buf, u16 b)
{
u16 tmp;
tmp = cpu_to_le16(b);
buf.write((const char *) &tmp, 2);
}
static inline void sd_write32(std::ostringstream &buf, u32 b)
{
u32 tmp;
tmp = cpu_to_le32(b);
buf.write((const char *) &tmp, 4);
}
static inline void sd_write64(std::ostringstream &buf, u64 b)
{
u64 tmp;
tmp = cpu_to_le64(b);
buf.write((const char *) &tmp, 8);
}
static inline void sd_write_uint8(std::ostringstream &buf, u8 b)
{
sd_write8(buf, SD_U8);
sd_write8(buf, b);
}
static inline void sd_write_uint16(std::ostringstream &buf, u16 b)
{
sd_write8(buf, SD_U16);
sd_write16(buf, b);
}
static inline void sd_write_uint32(std::ostringstream &buf, u32 b)
{
sd_write8(buf, SD_U32);
sd_write32(buf, b);
}
static inline void sd_write_uint64(std::ostringstream &buf, u64 b)
2008-04-06 18:55:46 +00:00
{
sd_write8(buf, SD_U64);
sd_write64(buf, b);
2008-04-06 18:55:46 +00:00
}
static inline void sd_write_name(std::ostringstream &buf, const char *name)
{
PDEBUG("Writing name '%s'\n", name);
if (name) {
sd_write8(buf, SD_NAME);
sd_write16(buf, strlen(name) + 1);
buf.write(name, strlen(name) + 1);
}
}
static inline void sd_write_blob(std::ostringstream &buf, void *b, int buf_size, char *name)
{
sd_write_name(buf, name);
sd_write8(buf, SD_BLOB);
sd_write32(buf, buf_size);
buf.write((const char *) b, buf_size);
}
static char zeros[64];
Fix policy generation for small dfas 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:09:54 -08:00
#define align64(X) (((X) + (typeof(X)) 7) & ~((typeof(X)) 7))
static inline void sd_write_aligned_blob(std::ostringstream &buf, void *b, int b_size,
const char *name)
2007-02-27 02:29:16 +00:00
{
sd_write_name(buf, name);
/* pad calculation MUST come after name is written */
size_t pad = align64(buf.tellp() + ((std::streamoff) 5l)) - (buf.tellp() + ((std::streamoff) 5l));
sd_write8(buf, SD_BLOB);
sd_write32(buf, b_size + pad);
buf.write(zeros, pad);
buf.write((const char *) b, b_size);
2007-02-27 02:29:16 +00:00
}
static void sd_write_strn(std::ostringstream &buf, char *b, int size, const char *name)
{
sd_write_name(buf, name);
sd_write8(buf, SD_STRING);
sd_write16(buf, size);
buf.write(b, size);
}
static inline void sd_write_string(std::ostringstream &buf, char *b, const char *name)
{
sd_write_strn(buf, b, strlen(b) + 1, name);
}
static inline void sd_write_struct(std::ostringstream &buf, const char *name)
{
sd_write_name(buf, name);
sd_write8(buf, SD_STRUCT);
}
static inline void sd_write_structend(std::ostringstream &buf)
{
sd_write8(buf, SD_STRUCTEND);
}
static inline void sd_write_array(std::ostringstream &buf, const char *name, int size)
{
sd_write_name(buf, name);
sd_write8(buf, SD_ARRAY);
sd_write16(buf, size);
}
static inline void sd_write_arrayend(std::ostringstream &buf)
{
sd_write8(buf, SD_ARRAYEND);
}
static inline void sd_write_list(std::ostringstream &buf, const char *name)
{
sd_write_name(buf, name);
sd_write8(buf, SD_LIST);
}
static inline void sd_write_listend(std::ostringstream &buf)
{
sd_write8(buf, SD_LISTEND);
}
void sd_serialize_dfa(std::ostringstream &buf, void *dfa, size_t size)
2007-02-27 02:29:16 +00:00
{
if (dfa)
sd_write_aligned_blob(buf, dfa, size, "aadfa");
2007-02-27 02:29:16 +00:00
}
void sd_serialize_rlimits(std::ostringstream &buf, struct aa_rlimits *limits)
2008-04-06 18:55:46 +00:00
{
if (!limits->specified)
return;
sd_write_struct(buf, "rlimits");
sd_write_uint32(buf, limits->specified);
sd_write_array(buf, NULL, RLIM_NLIMITS);
for (int i = 0; i < RLIM_NLIMITS; i++) {
sd_write_uint64(buf, limits->limits[i]);
2008-04-06 18:55:46 +00:00
}
sd_write_arrayend(buf);
sd_write_structend(buf);
2008-04-06 18:55:46 +00:00
}
void sd_serialize_xtable(std::ostringstream &buf, char **table)
{
int count;
if (!table[4])
return;
sd_write_struct(buf, "xtable");
count = 0;
for (int i = 4; i < AA_EXEC_COUNT; i++) {
if (table[i])
count++;
}
sd_write_array(buf, NULL, count);
for (int 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 separated
*/
if (*table[i] == ':') {
char *tmp = table[i] + 1;
strsep(&tmp, ":");
}
sd_write_strn(buf, table[i], len, NULL);
}
sd_write_arrayend(buf);
sd_write_structend(buf);
}
void sd_serialize_xattrs(std::ostringstream &buf, struct cond_entry_list xattrs)
{
int count;
struct cond_entry *entry;
if (!(xattrs.list))
return;
count = 0;
for (entry = xattrs.list; entry; entry = entry->next) {
count++;
}
sd_write_struct(buf, "xattrs");
sd_write_array(buf, NULL, count);
for (entry = xattrs.list; entry; entry = entry->next) {
sd_write_string(buf, entry->name, NULL);
}
sd_write_arrayend(buf);
sd_write_structend(buf);
}
void sd_serialize_profile(std::ostringstream &buf, Profile *profile,
int flattened)
{
2009-08-20 15:27:12 +00:00
uint64_t allowed_caps;
sd_write_struct(buf, "profile");
if (flattened) {
assert(profile->parent);
autofree char *name = (char *) malloc(3 + strlen(profile->name) + strlen(profile->parent->name));
if (!name)
return;
sprintf(name, "%s//%s", profile->parent->name, profile->name);
sd_write_string(buf, name, NULL);
} else {
sd_write_string(buf, profile->name, NULL);
}
/* only emit this if current kernel at least supports "create" */
if (perms_create) {
if (profile->xmatch) {
sd_serialize_dfa(buf, profile->xmatch, profile->xmatch_size);
sd_write_uint32(buf, profile->xmatch_len);
}
}
sd_write_struct(buf, "flags");
/* used to be flags.debug, but that's no longer supported */
sd_write_uint32(buf, profile->flags.flags);
sd_write_uint32(buf, profile_mode_packed(profile->flags.mode));
sd_write_uint32(buf, profile->flags.audit);
sd_write_structend(buf);
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;
sd_write_name(buf, "path_flags");
sd_write_uint32(buf, flags);
}
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->caps.allow) & ~profile->caps.deny;
sd_write_uint32(buf, low_caps(allowed_caps));
sd_write_uint32(buf, low_caps(allowed_caps & profile->caps.audit));
sd_write_uint32(buf, low_caps(profile->caps.deny & profile->caps.quiet));
sd_write_uint32(buf, 0);
sd_write_struct(buf, "caps64");
sd_write_uint32(buf, high_caps(allowed_caps));
sd_write_uint32(buf, high_caps(allowed_caps & profile->caps.audit));
sd_write_uint32(buf, high_caps(profile->caps.deny & profile->caps.quiet));
sd_write_uint32(buf, 0);
sd_write_structend(buf);
sd_serialize_xattrs(buf, profile->xattrs);
sd_serialize_rlimits(buf, &profile->rlimits);
2008-04-06 18:55:46 +00:00
/* choice to support / downgrade needs to already have been made */
if (features_supports_networkv8) {
/* nothing - encoded in policydb */
} else if (profile->net.allow && features_supports_network) {
size_t i;
sd_write_array(buf, "net_allowed_af", get_af_max());
for (i = 0; i < get_af_max(); i++) {
u16 allowed = profile->net.allow[i] &
~profile->net.deny[i];
sd_write_uint16(buf, allowed);
sd_write_uint16(buf, allowed & profile->net.audit[i]);
sd_write_uint16(buf, profile->net.deny[i] & profile->net.quiet[i]);
}
sd_write_arrayend(buf);
} else if (profile->net.allow)
pwarn(WARN_RULE_NOT_ENFORCED, _("profile %s network rules not enforced\n"), profile->name);
if (profile->policy.dfa) {
sd_write_struct(buf, "policydb");
sd_serialize_dfa(buf, profile->policy.dfa, profile->policy.size);
sd_write_structend(buf);
}
2007-02-27 02:29:16 +00:00
/* either have a single dfa or lists of different entry types */
sd_serialize_dfa(buf, profile->dfa.dfa, profile->dfa.size);
sd_serialize_xtable(buf, profile->exec_table);
sd_write_structend(buf);
}
void sd_serialize_top_profile(std::ostringstream &buf, Profile *profile)
{
Add the ability to separate policy_version from kernel and parser abi This will allow for the parser to invalidate its caches separate of whether the kernel policy version has changed. This can be desirable if a parser bug is discovered, a new version the parser is shipped and we need to force cache files to be regenerated. Policy current stores a 32 bit version number in the header binary policy. For newer policy (> v5 kernel abi) split this number into 3 separate fields policy_version, parser_abi, kernel_abi. If binary policy with a split version number is loaded to an older kernel it will be correctly rejected as unsupported as those kernels will see it as a none v5 version. For kernels that only support v5 policy on the kernel abi version is written. The rules for policy versioning should be policy_version: Set by text policy language version. Parsers that don't understand a specified version may fail, or drop rules they are unaware of. parser_abi_version: gets bumped when a userspace bug is discovered that requires policy be recompiled. The policy version could be reset for each new kernel version but since the parser needs to support multiple kernel versions tracking this is extra work and should be avoided. kernel_abi_version: gets bumped when semantic changes need to be applied. Eg unix domain sockets being mediated at connect. the kernel abi version does not encapsulate all supported features. As kernels could have different sets of patches supplied. Basic feature support is determined by the policy_mediates() encoding in the policydb. As such comparing cache features to kernel features is still needed to determine if cached policy is best matched to the kernel. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-04-23 11:00:32 -07:00
uint32_t version;
2007-02-27 02:29:16 +00:00
Add the ability to separate policy_version from kernel and parser abi This will allow for the parser to invalidate its caches separate of whether the kernel policy version has changed. This can be desirable if a parser bug is discovered, a new version the parser is shipped and we need to force cache files to be regenerated. Policy current stores a 32 bit version number in the header binary policy. For newer policy (> v5 kernel abi) split this number into 3 separate fields policy_version, parser_abi, kernel_abi. If binary policy with a split version number is loaded to an older kernel it will be correctly rejected as unsupported as those kernels will see it as a none v5 version. For kernels that only support v5 policy on the kernel abi version is written. The rules for policy versioning should be policy_version: Set by text policy language version. Parsers that don't understand a specified version may fail, or drop rules they are unaware of. parser_abi_version: gets bumped when a userspace bug is discovered that requires policy be recompiled. The policy version could be reset for each new kernel version but since the parser needs to support multiple kernel versions tracking this is extra work and should be avoided. kernel_abi_version: gets bumped when semantic changes need to be applied. Eg unix domain sockets being mediated at connect. the kernel abi version does not encapsulate all supported features. As kernels could have different sets of patches supplied. Basic feature support is determined by the policy_mediates() encoding in the policydb. As such comparing cache features to kernel features is still needed to determine if cached policy is best matched to the kernel. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-04-23 11:00:32 -07:00
version = ENCODE_VERSION(force_complain, policy_version,
parser_abi_version, kernel_abi_version);
2007-02-27 02:29:16 +00:00
sd_write_name(buf, "version");
sd_write_uint32(buf, version);
if (profile->ns) {
sd_write_string(buf, profile->ns, "namespace");
}
sd_serialize_profile(buf, profile, profile->parent ? 1 : 0);
}
int __sd_serialize_profile(int option, aa_kernel_interface *kernel_interface,
Profile *prof, int cache_fd)
{
autoclose int fd = -1;
int error, size, wsize;
std::ostringstream work_area;
switch (option) {
case OPTION_ADD:
case OPTION_REPLACE:
case OPTION_REMOVE:
break;
case OPTION_STDOUT:
fd = dup(1);
if (fd < 0) {
error = -errno;
PERROR(_("Unable to open stdout - %s\n"),
strerror(errno));
goto exit;
}
break;
case OPTION_OFILE:
fd = dup(fileno(ofile));
if (fd < 0) {
error = -errno;
PERROR(_("Unable to open output file - %s\n"),
strerror(errno));
goto exit;
}
break;
default:
error = -EINVAL;
goto exit;
break;
}
error = 0;
if (option == OPTION_REMOVE) {
if (kernel_load) {
if (aa_kernel_interface_remove_policy(kernel_interface,
prof->fqname().c_str()) == -1)
error = -errno;
}
} else {
std::string tmp;
sd_serialize_top_profile(work_area, prof);
tmp = work_area.str();
size = (long) work_area.tellp();
if (kernel_load) {
if (option == OPTION_ADD &&
aa_kernel_interface_load_policy(kernel_interface,
tmp.c_str(), size) == -1) {
error = -errno;
} else if (option == OPTION_REPLACE &&
aa_kernel_interface_replace_policy(kernel_interface,
tmp.c_str(), size) == -1) {
error = -errno;
}
} else if ((option == OPTION_STDOUT || option == OPTION_OFILE) &&
aa_kernel_interface_write_policy(fd, tmp.c_str(), size) == -1) {
error = -errno;
}
if (cache_fd != -1) {
wsize = write(cache_fd, tmp.c_str(), size);
if (wsize < 0) {
error = -errno;
} else if (wsize < size) {
PERROR(_("%s: Unable to write entire profile entry to cache\n"),
progname);
error = -EIO;
}
}
}
if (!prof->hat_table.empty() && option != OPTION_REMOVE) {
if (load_flattened_hats(prof, option, kernel_interface, cache_fd) == 0)
return 0;
}
exit:
return error;
}