/*
* AppArmor security module
*
* This file contains AppArmor functions for unpacking policy loaded from
* userspace.
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2 of the
* License.
*
* AppArmor uses a serialized binary format for loading policy. To find
* policy format documentation see Documentation/admin-guide/LSM/apparmor.rst
* All policy is validated before it is used.
*/
#include <asm/unaligned.h>
#include <linux/ctype.h>
#include <linux/errno.h>
#include "include/apparmor.h"
#include "include/audit.h"
#include "include/context.h"
#include "include/crypto.h"
#include "include/match.h"
#include "include/path.h"
#include "include/policy.h"
#include "include/policy_unpack.h"
#define K_ABI_MASK 0x3ff
#define FORCE_COMPLAIN_FLAG 0x800
#define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK))
#define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK))
#define v5 5 /* base version */
#define v6 6 /* per entry policydb mediation check */
#define v7 7 /* full network masking */
/*
* The AppArmor interface treats data as a type byte followed by the
* actual data. The interface has the notion of a a named entry
* which has a name (AA_NAME typecode followed by name string) followed by
* the entries typecode and data. Named types allow for optional
* elements and extensions to be added and tested for without breaking
* backwards compatibility.
*/
enum aa_code {
AA_U8,
AA_U16,
AA_U32,
AA_U64,
AA_NAME, /* same as string except it is items name */
AA_STRING,
AA_BLOB,
AA_STRUCT,
AA_STRUCTEND,
AA_LIST,
AA_LISTEND,
AA_ARRAY,
AA_ARRAYEND,
};
/*
* aa_ext is the read of the buffer containing the serialized profile. The
* data is copied into a kernel buffer in apparmorfs and then handed off to
* the unpack routines.
*/
struct aa_ext {
void *start;
void *end;
void *pos; /* pointer to current position in the buffer */
u32 version;
};
/* audit callback for unpack fields */
static void audit_cb(struct audit_buffer *ab, void *va)
{
struct common_audit_data *sa = va;
if (aad(sa)->iface.ns) {
audit_log_format(ab, " ns=");
audit_log_untrustedstring(ab, aad(sa)->iface.ns);
}
if (aad(sa)->name) {
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, aad(sa)->name);
}
if (aad(sa)->iface.pos)
audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos);
}
/**
* audit_iface - do audit message for policy unpacking/load/replace/remove
* @new: profile if it has been allocated (MAYBE NULL)
* @ns_name: name of the ns the profile is to be loaded to (MAY BE NULL)
* @name: name of the profile being manipulated (MAYBE NULL)
* @info: any extra info about the failure (MAYBE NULL)
* @e: buffer position info
* @error: error code
*
* Returns: %0 or error
*/
static int audit_iface(struct aa_profile *new, const char *ns_name,
const char *name, const char *info, struct aa_ext *e,
int error)
{
struct aa_profile *profile = labels_profile(aa_current_raw_label());
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
if (e)
aad(&sa)->iface.pos = e->pos - e->start;
aad(&sa)->iface.ns = ns_name;
if (new)
aad(&sa)->name = new->base.hname;
else
aad(&sa)->name = name;
aad(&sa)->info = info;
aad(&sa)->error = error;
return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb);
}
void __aa_loaddata_update(struct aa_loaddata *data, long revision)
{
AA_BUG(!data);
AA_BUG(!data->ns);
AA_BUG(!data->dents[AAFS_LOADDATA_REVISION]);
AA_BUG(!mutex_is_locked(&data->ns->lock));
AA_BUG(data->revision > revision);
data->revision = revision;
d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime =
current_time(d_inode(data->dents[AAFS_LOADDATA_DIR]));
d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime =
current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION]));
}
bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r)
{
if (l->size != r->size)
return false;
if (aa_g_hash_policy && memcmp(l->hash, r->hash, aa_hash_size()) != 0)
return false;
return memcmp(l->data, r->data, r->size) == 0;
}
/*
* need to take the ns mutex lock which is NOT safe most places that
* put_loaddata is called, so we have to delay freeing it
*/
static void do_loaddata_free(struct work_struct *work)
{
struct aa_loaddata