18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * AppArmor security module
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file contains AppArmor functions for unpacking policy loaded from
68c2ecf20Sopenharmony_ci * userspace.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (C) 1998-2008 Novell/SUSE
98c2ecf20Sopenharmony_ci * Copyright 2009-2010 Canonical Ltd.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * AppArmor uses a serialized binary format for loading policy. To find
128c2ecf20Sopenharmony_ci * policy format documentation see Documentation/admin-guide/LSM/apparmor.rst
138c2ecf20Sopenharmony_ci * All policy is validated before it is used.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
178c2ecf20Sopenharmony_ci#include <linux/ctype.h>
188c2ecf20Sopenharmony_ci#include <linux/errno.h>
198c2ecf20Sopenharmony_ci#include <linux/zlib.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "include/apparmor.h"
228c2ecf20Sopenharmony_ci#include "include/audit.h"
238c2ecf20Sopenharmony_ci#include "include/cred.h"
248c2ecf20Sopenharmony_ci#include "include/crypto.h"
258c2ecf20Sopenharmony_ci#include "include/match.h"
268c2ecf20Sopenharmony_ci#include "include/path.h"
278c2ecf20Sopenharmony_ci#include "include/policy.h"
288c2ecf20Sopenharmony_ci#include "include/policy_unpack.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define K_ABI_MASK 0x3ff
318c2ecf20Sopenharmony_ci#define FORCE_COMPLAIN_FLAG 0x800
328c2ecf20Sopenharmony_ci#define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK))
338c2ecf20Sopenharmony_ci#define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK))
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define v5	5	/* base version */
368c2ecf20Sopenharmony_ci#define v6	6	/* per entry policydb mediation check */
378c2ecf20Sopenharmony_ci#define v7	7
388c2ecf20Sopenharmony_ci#define v8	8	/* full network masking */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci * The AppArmor interface treats data as a type byte followed by the
428c2ecf20Sopenharmony_ci * actual data.  The interface has the notion of a a named entry
438c2ecf20Sopenharmony_ci * which has a name (AA_NAME typecode followed by name string) followed by
448c2ecf20Sopenharmony_ci * the entries typecode and data.  Named types allow for optional
458c2ecf20Sopenharmony_ci * elements and extensions to be added and tested for without breaking
468c2ecf20Sopenharmony_ci * backwards compatibility.
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cienum aa_code {
508c2ecf20Sopenharmony_ci	AA_U8,
518c2ecf20Sopenharmony_ci	AA_U16,
528c2ecf20Sopenharmony_ci	AA_U32,
538c2ecf20Sopenharmony_ci	AA_U64,
548c2ecf20Sopenharmony_ci	AA_NAME,		/* same as string except it is items name */
558c2ecf20Sopenharmony_ci	AA_STRING,
568c2ecf20Sopenharmony_ci	AA_BLOB,
578c2ecf20Sopenharmony_ci	AA_STRUCT,
588c2ecf20Sopenharmony_ci	AA_STRUCTEND,
598c2ecf20Sopenharmony_ci	AA_LIST,
608c2ecf20Sopenharmony_ci	AA_LISTEND,
618c2ecf20Sopenharmony_ci	AA_ARRAY,
628c2ecf20Sopenharmony_ci	AA_ARRAYEND,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/*
668c2ecf20Sopenharmony_ci * aa_ext is the read of the buffer containing the serialized profile.  The
678c2ecf20Sopenharmony_ci * data is copied into a kernel buffer in apparmorfs and then handed off to
688c2ecf20Sopenharmony_ci * the unpack routines.
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_cistruct aa_ext {
718c2ecf20Sopenharmony_ci	void *start;
728c2ecf20Sopenharmony_ci	void *end;
738c2ecf20Sopenharmony_ci	void *pos;		/* pointer to current position in the buffer */
748c2ecf20Sopenharmony_ci	u32 version;
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/* audit callback for unpack fields */
788c2ecf20Sopenharmony_cistatic void audit_cb(struct audit_buffer *ab, void *va)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct common_audit_data *sa = va;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (aad(sa)->iface.ns) {
838c2ecf20Sopenharmony_ci		audit_log_format(ab, " ns=");
848c2ecf20Sopenharmony_ci		audit_log_untrustedstring(ab, aad(sa)->iface.ns);
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci	if (aad(sa)->name) {
878c2ecf20Sopenharmony_ci		audit_log_format(ab, " name=");
888c2ecf20Sopenharmony_ci		audit_log_untrustedstring(ab, aad(sa)->name);
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci	if (aad(sa)->iface.pos)
918c2ecf20Sopenharmony_ci		audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos);
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/**
958c2ecf20Sopenharmony_ci * audit_iface - do audit message for policy unpacking/load/replace/remove
968c2ecf20Sopenharmony_ci * @new: profile if it has been allocated (MAYBE NULL)
978c2ecf20Sopenharmony_ci * @ns_name: name of the ns the profile is to be loaded to (MAY BE NULL)
988c2ecf20Sopenharmony_ci * @name: name of the profile being manipulated (MAYBE NULL)
998c2ecf20Sopenharmony_ci * @info: any extra info about the failure (MAYBE NULL)
1008c2ecf20Sopenharmony_ci * @e: buffer position info
1018c2ecf20Sopenharmony_ci * @error: error code
1028c2ecf20Sopenharmony_ci *
1038c2ecf20Sopenharmony_ci * Returns: %0 or error
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_cistatic int audit_iface(struct aa_profile *new, const char *ns_name,
1068c2ecf20Sopenharmony_ci		       const char *name, const char *info, struct aa_ext *e,
1078c2ecf20Sopenharmony_ci		       int error)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct aa_profile *profile = labels_profile(aa_current_raw_label());
1108c2ecf20Sopenharmony_ci	DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
1118c2ecf20Sopenharmony_ci	if (e)
1128c2ecf20Sopenharmony_ci		aad(&sa)->iface.pos = e->pos - e->start;
1138c2ecf20Sopenharmony_ci	aad(&sa)->iface.ns = ns_name;
1148c2ecf20Sopenharmony_ci	if (new)
1158c2ecf20Sopenharmony_ci		aad(&sa)->name = new->base.hname;
1168c2ecf20Sopenharmony_ci	else
1178c2ecf20Sopenharmony_ci		aad(&sa)->name = name;
1188c2ecf20Sopenharmony_ci	aad(&sa)->info = info;
1198c2ecf20Sopenharmony_ci	aad(&sa)->error = error;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_civoid __aa_loaddata_update(struct aa_loaddata *data, long revision)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	AA_BUG(!data);
1278c2ecf20Sopenharmony_ci	AA_BUG(!data->ns);
1288c2ecf20Sopenharmony_ci	AA_BUG(!data->dents[AAFS_LOADDATA_REVISION]);
1298c2ecf20Sopenharmony_ci	AA_BUG(!mutex_is_locked(&data->ns->lock));
1308c2ecf20Sopenharmony_ci	AA_BUG(data->revision > revision);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	data->revision = revision;
1338c2ecf20Sopenharmony_ci	d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime =
1348c2ecf20Sopenharmony_ci		current_time(d_inode(data->dents[AAFS_LOADDATA_DIR]));
1358c2ecf20Sopenharmony_ci	d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime =
1368c2ecf20Sopenharmony_ci		current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION]));
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cibool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	if (l->size != r->size)
1428c2ecf20Sopenharmony_ci		return false;
1438c2ecf20Sopenharmony_ci	if (l->compressed_size != r->compressed_size)
1448c2ecf20Sopenharmony_ci		return false;
1458c2ecf20Sopenharmony_ci	if (aa_g_hash_policy && memcmp(l->hash, r->hash, aa_hash_size()) != 0)
1468c2ecf20Sopenharmony_ci		return false;
1478c2ecf20Sopenharmony_ci	return memcmp(l->data, r->data, r->compressed_size ?: r->size) == 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/*
1518c2ecf20Sopenharmony_ci * need to take the ns mutex lock which is NOT safe most places that
1528c2ecf20Sopenharmony_ci * put_loaddata is called, so we have to delay freeing it
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_cistatic void do_loaddata_free(struct work_struct *work)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct aa_loaddata *d = container_of(work, struct aa_loaddata, work);
1578c2ecf20Sopenharmony_ci	struct aa_ns *ns = aa_get_ns(d->ns);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (ns) {
1608c2ecf20Sopenharmony_ci		mutex_lock_nested(&ns->lock, ns->level);
1618c2ecf20Sopenharmony_ci		__aa_fs_remove_rawdata(d);
1628c2ecf20Sopenharmony_ci		mutex_unlock(&ns->lock);
1638c2ecf20Sopenharmony_ci		aa_put_ns(ns);
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	kfree_sensitive(d->hash);
1678c2ecf20Sopenharmony_ci	kfree_sensitive(d->name);
1688c2ecf20Sopenharmony_ci	kvfree(d->data);
1698c2ecf20Sopenharmony_ci	kfree_sensitive(d);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_civoid aa_loaddata_kref(struct kref *kref)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (d) {
1778c2ecf20Sopenharmony_ci		INIT_WORK(&d->work, do_loaddata_free);
1788c2ecf20Sopenharmony_ci		schedule_work(&d->work);
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistruct aa_loaddata *aa_loaddata_alloc(size_t size)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct aa_loaddata *d;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	d = kzalloc(sizeof(*d), GFP_KERNEL);
1878c2ecf20Sopenharmony_ci	if (d == NULL)
1888c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1898c2ecf20Sopenharmony_ci	d->data = kvzalloc(size, GFP_KERNEL);
1908c2ecf20Sopenharmony_ci	if (!d->data) {
1918c2ecf20Sopenharmony_ci		kfree(d);
1928c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci	kref_init(&d->count);
1958c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&d->list);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return d;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci/* test if read will be in packed data bounds */
2018c2ecf20Sopenharmony_cistatic bool inbounds(struct aa_ext *e, size_t size)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	return (size <= e->end - e->pos);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic void *kvmemdup(const void *src, size_t len)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	void *p = kvmalloc(len, GFP_KERNEL);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (p)
2118c2ecf20Sopenharmony_ci		memcpy(p, src, len);
2128c2ecf20Sopenharmony_ci	return p;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci/**
2168c2ecf20Sopenharmony_ci * aa_u16_chunck - test and do bounds checking for a u16 size based chunk
2178c2ecf20Sopenharmony_ci * @e: serialized data read head (NOT NULL)
2188c2ecf20Sopenharmony_ci * @chunk: start address for chunk of data (NOT NULL)
2198c2ecf20Sopenharmony_ci *
2208c2ecf20Sopenharmony_ci * Returns: the size of chunk found with the read head at the end of the chunk.
2218c2ecf20Sopenharmony_ci */
2228c2ecf20Sopenharmony_cistatic size_t unpack_u16_chunk(struct aa_ext *e, char **chunk)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	size_t size = 0;
2258c2ecf20Sopenharmony_ci	void *pos = e->pos;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (!inbounds(e, sizeof(u16)))
2288c2ecf20Sopenharmony_ci		goto fail;
2298c2ecf20Sopenharmony_ci	size = le16_to_cpu(get_unaligned((__le16 *) e->pos));
2308c2ecf20Sopenharmony_ci	e->pos += sizeof(__le16);
2318c2ecf20Sopenharmony_ci	if (!inbounds(e, size))
2328c2ecf20Sopenharmony_ci		goto fail;
2338c2ecf20Sopenharmony_ci	*chunk = e->pos;
2348c2ecf20Sopenharmony_ci	e->pos += size;
2358c2ecf20Sopenharmony_ci	return size;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cifail:
2388c2ecf20Sopenharmony_ci	e->pos = pos;
2398c2ecf20Sopenharmony_ci	return 0;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci/* unpack control byte */
2438c2ecf20Sopenharmony_cistatic bool unpack_X(struct aa_ext *e, enum aa_code code)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	if (!inbounds(e, 1))
2468c2ecf20Sopenharmony_ci		return false;
2478c2ecf20Sopenharmony_ci	if (*(u8 *) e->pos != code)
2488c2ecf20Sopenharmony_ci		return false;
2498c2ecf20Sopenharmony_ci	e->pos++;
2508c2ecf20Sopenharmony_ci	return true;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/**
2548c2ecf20Sopenharmony_ci * unpack_nameX - check is the next element is of type X with a name of @name
2558c2ecf20Sopenharmony_ci * @e: serialized data extent information  (NOT NULL)
2568c2ecf20Sopenharmony_ci * @code: type code
2578c2ecf20Sopenharmony_ci * @name: name to match to the serialized element.  (MAYBE NULL)
2588c2ecf20Sopenharmony_ci *
2598c2ecf20Sopenharmony_ci * check that the next serialized data element is of type X and has a tag
2608c2ecf20Sopenharmony_ci * name @name.  If @name is specified then there must be a matching
2618c2ecf20Sopenharmony_ci * name element in the stream.  If @name is NULL any name element will be
2628c2ecf20Sopenharmony_ci * skipped and only the typecode will be tested.
2638c2ecf20Sopenharmony_ci *
2648c2ecf20Sopenharmony_ci * Returns true on success (both type code and name tests match) and the read
2658c2ecf20Sopenharmony_ci * head is advanced past the headers
2668c2ecf20Sopenharmony_ci *
2678c2ecf20Sopenharmony_ci * Returns: false if either match fails, the read head does not move
2688c2ecf20Sopenharmony_ci */
2698c2ecf20Sopenharmony_cistatic bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	/*
2728c2ecf20Sopenharmony_ci	 * May need to reset pos if name or type doesn't match
2738c2ecf20Sopenharmony_ci	 */
2748c2ecf20Sopenharmony_ci	void *pos = e->pos;
2758c2ecf20Sopenharmony_ci	/*
2768c2ecf20Sopenharmony_ci	 * Check for presence of a tagname, and if present name size
2778c2ecf20Sopenharmony_ci	 * AA_NAME tag value is a u16.
2788c2ecf20Sopenharmony_ci	 */
2798c2ecf20Sopenharmony_ci	if (unpack_X(e, AA_NAME)) {
2808c2ecf20Sopenharmony_ci		char *tag = NULL;
2818c2ecf20Sopenharmony_ci		size_t size = unpack_u16_chunk(e, &tag);
2828c2ecf20Sopenharmony_ci		/* if a name is specified it must match. otherwise skip tag */
2838c2ecf20Sopenharmony_ci		if (name && (!size || tag[size-1] != '\0' || strcmp(name, tag)))
2848c2ecf20Sopenharmony_ci			goto fail;
2858c2ecf20Sopenharmony_ci	} else if (name) {
2868c2ecf20Sopenharmony_ci		/* if a name is specified and there is no name tag fail */
2878c2ecf20Sopenharmony_ci		goto fail;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/* now check if type code matches */
2918c2ecf20Sopenharmony_ci	if (unpack_X(e, code))
2928c2ecf20Sopenharmony_ci		return true;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cifail:
2958c2ecf20Sopenharmony_ci	e->pos = pos;
2968c2ecf20Sopenharmony_ci	return false;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic bool unpack_u8(struct aa_ext *e, u8 *data, const char *name)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	void *pos = e->pos;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_U8, name)) {
3048c2ecf20Sopenharmony_ci		if (!inbounds(e, sizeof(u8)))
3058c2ecf20Sopenharmony_ci			goto fail;
3068c2ecf20Sopenharmony_ci		if (data)
3078c2ecf20Sopenharmony_ci			*data = get_unaligned((u8 *)e->pos);
3088c2ecf20Sopenharmony_ci		e->pos += sizeof(u8);
3098c2ecf20Sopenharmony_ci		return true;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cifail:
3138c2ecf20Sopenharmony_ci	e->pos = pos;
3148c2ecf20Sopenharmony_ci	return false;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	void *pos = e->pos;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_U32, name)) {
3228c2ecf20Sopenharmony_ci		if (!inbounds(e, sizeof(u32)))
3238c2ecf20Sopenharmony_ci			goto fail;
3248c2ecf20Sopenharmony_ci		if (data)
3258c2ecf20Sopenharmony_ci			*data = le32_to_cpu(get_unaligned((__le32 *) e->pos));
3268c2ecf20Sopenharmony_ci		e->pos += sizeof(u32);
3278c2ecf20Sopenharmony_ci		return true;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cifail:
3318c2ecf20Sopenharmony_ci	e->pos = pos;
3328c2ecf20Sopenharmony_ci	return false;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic bool unpack_u64(struct aa_ext *e, u64 *data, const char *name)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	void *pos = e->pos;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_U64, name)) {
3408c2ecf20Sopenharmony_ci		if (!inbounds(e, sizeof(u64)))
3418c2ecf20Sopenharmony_ci			goto fail;
3428c2ecf20Sopenharmony_ci		if (data)
3438c2ecf20Sopenharmony_ci			*data = le64_to_cpu(get_unaligned((__le64 *) e->pos));
3448c2ecf20Sopenharmony_ci		e->pos += sizeof(u64);
3458c2ecf20Sopenharmony_ci		return true;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cifail:
3498c2ecf20Sopenharmony_ci	e->pos = pos;
3508c2ecf20Sopenharmony_ci	return false;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic size_t unpack_array(struct aa_ext *e, const char *name)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	void *pos = e->pos;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_ARRAY, name)) {
3588c2ecf20Sopenharmony_ci		int size;
3598c2ecf20Sopenharmony_ci		if (!inbounds(e, sizeof(u16)))
3608c2ecf20Sopenharmony_ci			goto fail;
3618c2ecf20Sopenharmony_ci		size = (int)le16_to_cpu(get_unaligned((__le16 *) e->pos));
3628c2ecf20Sopenharmony_ci		e->pos += sizeof(u16);
3638c2ecf20Sopenharmony_ci		return size;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cifail:
3678c2ecf20Sopenharmony_ci	e->pos = pos;
3688c2ecf20Sopenharmony_ci	return 0;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic size_t unpack_blob(struct aa_ext *e, char **blob, const char *name)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	void *pos = e->pos;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_BLOB, name)) {
3768c2ecf20Sopenharmony_ci		u32 size;
3778c2ecf20Sopenharmony_ci		if (!inbounds(e, sizeof(u32)))
3788c2ecf20Sopenharmony_ci			goto fail;
3798c2ecf20Sopenharmony_ci		size = le32_to_cpu(get_unaligned((__le32 *) e->pos));
3808c2ecf20Sopenharmony_ci		e->pos += sizeof(u32);
3818c2ecf20Sopenharmony_ci		if (inbounds(e, (size_t) size)) {
3828c2ecf20Sopenharmony_ci			*blob = e->pos;
3838c2ecf20Sopenharmony_ci			e->pos += size;
3848c2ecf20Sopenharmony_ci			return size;
3858c2ecf20Sopenharmony_ci		}
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cifail:
3898c2ecf20Sopenharmony_ci	e->pos = pos;
3908c2ecf20Sopenharmony_ci	return 0;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int unpack_str(struct aa_ext *e, const char **string, const char *name)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	char *src_str;
3968c2ecf20Sopenharmony_ci	size_t size = 0;
3978c2ecf20Sopenharmony_ci	void *pos = e->pos;
3988c2ecf20Sopenharmony_ci	*string = NULL;
3998c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_STRING, name)) {
4008c2ecf20Sopenharmony_ci		size = unpack_u16_chunk(e, &src_str);
4018c2ecf20Sopenharmony_ci		if (size) {
4028c2ecf20Sopenharmony_ci			/* strings are null terminated, length is size - 1 */
4038c2ecf20Sopenharmony_ci			if (src_str[size - 1] != 0)
4048c2ecf20Sopenharmony_ci				goto fail;
4058c2ecf20Sopenharmony_ci			*string = src_str;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci			return size;
4088c2ecf20Sopenharmony_ci		}
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cifail:
4128c2ecf20Sopenharmony_ci	e->pos = pos;
4138c2ecf20Sopenharmony_ci	return 0;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int unpack_strdup(struct aa_ext *e, char **string, const char *name)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	const char *tmp;
4198c2ecf20Sopenharmony_ci	void *pos = e->pos;
4208c2ecf20Sopenharmony_ci	int res = unpack_str(e, &tmp, name);
4218c2ecf20Sopenharmony_ci	*string = NULL;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (!res)
4248c2ecf20Sopenharmony_ci		return 0;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	*string = kmemdup(tmp, res, GFP_KERNEL);
4278c2ecf20Sopenharmony_ci	if (!*string) {
4288c2ecf20Sopenharmony_ci		e->pos = pos;
4298c2ecf20Sopenharmony_ci		return 0;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return res;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci/**
4378c2ecf20Sopenharmony_ci * unpack_dfa - unpack a file rule dfa
4388c2ecf20Sopenharmony_ci * @e: serialized data extent information (NOT NULL)
4398c2ecf20Sopenharmony_ci *
4408c2ecf20Sopenharmony_ci * returns dfa or ERR_PTR or NULL if no dfa
4418c2ecf20Sopenharmony_ci */
4428c2ecf20Sopenharmony_cistatic struct aa_dfa *unpack_dfa(struct aa_ext *e)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	char *blob = NULL;
4458c2ecf20Sopenharmony_ci	size_t size;
4468c2ecf20Sopenharmony_ci	struct aa_dfa *dfa = NULL;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	size = unpack_blob(e, &blob, "aadfa");
4498c2ecf20Sopenharmony_ci	if (size) {
4508c2ecf20Sopenharmony_ci		/*
4518c2ecf20Sopenharmony_ci		 * The dfa is aligned with in the blob to 8 bytes
4528c2ecf20Sopenharmony_ci		 * from the beginning of the stream.
4538c2ecf20Sopenharmony_ci		 * alignment adjust needed by dfa unpack
4548c2ecf20Sopenharmony_ci		 */
4558c2ecf20Sopenharmony_ci		size_t sz = blob - (char *) e->start -
4568c2ecf20Sopenharmony_ci			((e->pos - e->start) & 7);
4578c2ecf20Sopenharmony_ci		size_t pad = ALIGN(sz, 8) - sz;
4588c2ecf20Sopenharmony_ci		int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) |
4598c2ecf20Sopenharmony_ci			TO_ACCEPT2_FLAG(YYTD_DATA32) | DFA_FLAG_VERIFY_STATES;
4608c2ecf20Sopenharmony_ci		dfa = aa_dfa_unpack(blob + pad, size - pad, flags);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		if (IS_ERR(dfa))
4638c2ecf20Sopenharmony_ci			return dfa;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	return dfa;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci/**
4718c2ecf20Sopenharmony_ci * unpack_trans_table - unpack a profile transition table
4728c2ecf20Sopenharmony_ci * @e: serialized data extent information  (NOT NULL)
4738c2ecf20Sopenharmony_ci * @profile: profile to add the accept table to (NOT NULL)
4748c2ecf20Sopenharmony_ci *
4758c2ecf20Sopenharmony_ci * Returns: true if table successfully unpacked
4768c2ecf20Sopenharmony_ci */
4778c2ecf20Sopenharmony_cistatic bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	void *saved_pos = e->pos;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	/* exec table is optional */
4828c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_STRUCT, "xtable")) {
4838c2ecf20Sopenharmony_ci		int i, size;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		size = unpack_array(e, NULL);
4868c2ecf20Sopenharmony_ci		/* currently 4 exec bits and entries 0-3 are reserved iupcx */
4878c2ecf20Sopenharmony_ci		if (size > 16 - 4)
4888c2ecf20Sopenharmony_ci			goto fail;
4898c2ecf20Sopenharmony_ci		profile->file.trans.table = kcalloc(size, sizeof(char *),
4908c2ecf20Sopenharmony_ci						    GFP_KERNEL);
4918c2ecf20Sopenharmony_ci		if (!profile->file.trans.table)
4928c2ecf20Sopenharmony_ci			goto fail;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci		profile->file.trans.size = size;
4958c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++) {
4968c2ecf20Sopenharmony_ci			char *str;
4978c2ecf20Sopenharmony_ci			int c, j, pos, size2 = unpack_strdup(e, &str, NULL);
4988c2ecf20Sopenharmony_ci			/* unpack_strdup verifies that the last character is
4998c2ecf20Sopenharmony_ci			 * null termination byte.
5008c2ecf20Sopenharmony_ci			 */
5018c2ecf20Sopenharmony_ci			if (!size2)
5028c2ecf20Sopenharmony_ci				goto fail;
5038c2ecf20Sopenharmony_ci			profile->file.trans.table[i] = str;
5048c2ecf20Sopenharmony_ci			/* verify that name doesn't start with space */
5058c2ecf20Sopenharmony_ci			if (isspace(*str))
5068c2ecf20Sopenharmony_ci				goto fail;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci			/* count internal #  of internal \0 */
5098c2ecf20Sopenharmony_ci			for (c = j = 0; j < size2 - 1; j++) {
5108c2ecf20Sopenharmony_ci				if (!str[j]) {
5118c2ecf20Sopenharmony_ci					pos = j;
5128c2ecf20Sopenharmony_ci					c++;
5138c2ecf20Sopenharmony_ci				}
5148c2ecf20Sopenharmony_ci			}
5158c2ecf20Sopenharmony_ci			if (*str == ':') {
5168c2ecf20Sopenharmony_ci				/* first character after : must be valid */
5178c2ecf20Sopenharmony_ci				if (!str[1])
5188c2ecf20Sopenharmony_ci					goto fail;
5198c2ecf20Sopenharmony_ci				/* beginning with : requires an embedded \0,
5208c2ecf20Sopenharmony_ci				 * verify that exactly 1 internal \0 exists
5218c2ecf20Sopenharmony_ci				 * trailing \0 already verified by unpack_strdup
5228c2ecf20Sopenharmony_ci				 *
5238c2ecf20Sopenharmony_ci				 * convert \0 back to : for label_parse
5248c2ecf20Sopenharmony_ci				 */
5258c2ecf20Sopenharmony_ci				if (c == 1)
5268c2ecf20Sopenharmony_ci					str[pos] = ':';
5278c2ecf20Sopenharmony_ci				else if (c > 1)
5288c2ecf20Sopenharmony_ci					goto fail;
5298c2ecf20Sopenharmony_ci			} else if (c)
5308c2ecf20Sopenharmony_ci				/* fail - all other cases with embedded \0 */
5318c2ecf20Sopenharmony_ci				goto fail;
5328c2ecf20Sopenharmony_ci		}
5338c2ecf20Sopenharmony_ci		if (!unpack_nameX(e, AA_ARRAYEND, NULL))
5348c2ecf20Sopenharmony_ci			goto fail;
5358c2ecf20Sopenharmony_ci		if (!unpack_nameX(e, AA_STRUCTEND, NULL))
5368c2ecf20Sopenharmony_ci			goto fail;
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci	return true;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cifail:
5418c2ecf20Sopenharmony_ci	aa_free_domain_entries(&profile->file.trans);
5428c2ecf20Sopenharmony_ci	e->pos = saved_pos;
5438c2ecf20Sopenharmony_ci	return false;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	void *pos = e->pos;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_STRUCT, "xattrs")) {
5518c2ecf20Sopenharmony_ci		int i, size;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci		size = unpack_array(e, NULL);
5548c2ecf20Sopenharmony_ci		profile->xattr_count = size;
5558c2ecf20Sopenharmony_ci		profile->xattrs = kcalloc(size, sizeof(char *), GFP_KERNEL);
5568c2ecf20Sopenharmony_ci		if (!profile->xattrs)
5578c2ecf20Sopenharmony_ci			goto fail;
5588c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++) {
5598c2ecf20Sopenharmony_ci			if (!unpack_strdup(e, &profile->xattrs[i], NULL))
5608c2ecf20Sopenharmony_ci				goto fail;
5618c2ecf20Sopenharmony_ci		}
5628c2ecf20Sopenharmony_ci		if (!unpack_nameX(e, AA_ARRAYEND, NULL))
5638c2ecf20Sopenharmony_ci			goto fail;
5648c2ecf20Sopenharmony_ci		if (!unpack_nameX(e, AA_STRUCTEND, NULL))
5658c2ecf20Sopenharmony_ci			goto fail;
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	return true;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cifail:
5718c2ecf20Sopenharmony_ci	e->pos = pos;
5728c2ecf20Sopenharmony_ci	return false;
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_cistatic bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	void *pos = e->pos;
5788c2ecf20Sopenharmony_ci	int i, size;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_STRUCT, "secmark")) {
5818c2ecf20Sopenharmony_ci		size = unpack_array(e, NULL);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci		profile->secmark = kcalloc(size, sizeof(struct aa_secmark),
5848c2ecf20Sopenharmony_ci					   GFP_KERNEL);
5858c2ecf20Sopenharmony_ci		if (!profile->secmark)
5868c2ecf20Sopenharmony_ci			goto fail;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci		profile->secmark_count = size;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++) {
5918c2ecf20Sopenharmony_ci			if (!unpack_u8(e, &profile->secmark[i].audit, NULL))
5928c2ecf20Sopenharmony_ci				goto fail;
5938c2ecf20Sopenharmony_ci			if (!unpack_u8(e, &profile->secmark[i].deny, NULL))
5948c2ecf20Sopenharmony_ci				goto fail;
5958c2ecf20Sopenharmony_ci			if (!unpack_strdup(e, &profile->secmark[i].label, NULL))
5968c2ecf20Sopenharmony_ci				goto fail;
5978c2ecf20Sopenharmony_ci		}
5988c2ecf20Sopenharmony_ci		if (!unpack_nameX(e, AA_ARRAYEND, NULL))
5998c2ecf20Sopenharmony_ci			goto fail;
6008c2ecf20Sopenharmony_ci		if (!unpack_nameX(e, AA_STRUCTEND, NULL))
6018c2ecf20Sopenharmony_ci			goto fail;
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	return true;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cifail:
6078c2ecf20Sopenharmony_ci	if (profile->secmark) {
6088c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++)
6098c2ecf20Sopenharmony_ci			kfree(profile->secmark[i].label);
6108c2ecf20Sopenharmony_ci		kfree(profile->secmark);
6118c2ecf20Sopenharmony_ci		profile->secmark_count = 0;
6128c2ecf20Sopenharmony_ci		profile->secmark = NULL;
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	e->pos = pos;
6168c2ecf20Sopenharmony_ci	return false;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	void *pos = e->pos;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	/* rlimits are optional */
6248c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_STRUCT, "rlimits")) {
6258c2ecf20Sopenharmony_ci		int i, size;
6268c2ecf20Sopenharmony_ci		u32 tmp = 0;
6278c2ecf20Sopenharmony_ci		if (!unpack_u32(e, &tmp, NULL))
6288c2ecf20Sopenharmony_ci			goto fail;
6298c2ecf20Sopenharmony_ci		profile->rlimits.mask = tmp;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci		size = unpack_array(e, NULL);
6328c2ecf20Sopenharmony_ci		if (size > RLIM_NLIMITS)
6338c2ecf20Sopenharmony_ci			goto fail;
6348c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++) {
6358c2ecf20Sopenharmony_ci			u64 tmp2 = 0;
6368c2ecf20Sopenharmony_ci			int a = aa_map_resource(i);
6378c2ecf20Sopenharmony_ci			if (!unpack_u64(e, &tmp2, NULL))
6388c2ecf20Sopenharmony_ci				goto fail;
6398c2ecf20Sopenharmony_ci			profile->rlimits.limits[a].rlim_max = tmp2;
6408c2ecf20Sopenharmony_ci		}
6418c2ecf20Sopenharmony_ci		if (!unpack_nameX(e, AA_ARRAYEND, NULL))
6428c2ecf20Sopenharmony_ci			goto fail;
6438c2ecf20Sopenharmony_ci		if (!unpack_nameX(e, AA_STRUCTEND, NULL))
6448c2ecf20Sopenharmony_ci			goto fail;
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci	return true;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_cifail:
6498c2ecf20Sopenharmony_ci	e->pos = pos;
6508c2ecf20Sopenharmony_ci	return false;
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_cistatic u32 strhash(const void *data, u32 len, u32 seed)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	const char * const *key = data;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	return jhash(*key, strlen(*key), seed);
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic int datacmp(struct rhashtable_compare_arg *arg, const void *obj)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	const struct aa_data *data = obj;
6638c2ecf20Sopenharmony_ci	const char * const *key = arg->key;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	return strcmp(data->key, *key);
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci/**
6698c2ecf20Sopenharmony_ci * unpack_profile - unpack a serialized profile
6708c2ecf20Sopenharmony_ci * @e: serialized data extent information (NOT NULL)
6718c2ecf20Sopenharmony_ci *
6728c2ecf20Sopenharmony_ci * NOTE: unpack profile sets audit struct if there is a failure
6738c2ecf20Sopenharmony_ci */
6748c2ecf20Sopenharmony_cistatic struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	struct aa_profile *profile = NULL;
6778c2ecf20Sopenharmony_ci	const char *tmpname, *tmpns = NULL, *name = NULL;
6788c2ecf20Sopenharmony_ci	const char *info = "failed to unpack profile";
6798c2ecf20Sopenharmony_ci	size_t ns_len;
6808c2ecf20Sopenharmony_ci	struct rhashtable_params params = { 0 };
6818c2ecf20Sopenharmony_ci	char *key = NULL;
6828c2ecf20Sopenharmony_ci	struct aa_data *data;
6838c2ecf20Sopenharmony_ci	int i, error = -EPROTO;
6848c2ecf20Sopenharmony_ci	kernel_cap_t tmpcap;
6858c2ecf20Sopenharmony_ci	u32 tmp;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	*ns_name = NULL;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	/* check that we have the right struct being passed */
6908c2ecf20Sopenharmony_ci	if (!unpack_nameX(e, AA_STRUCT, "profile"))
6918c2ecf20Sopenharmony_ci		goto fail;
6928c2ecf20Sopenharmony_ci	if (!unpack_str(e, &name, NULL))
6938c2ecf20Sopenharmony_ci		goto fail;
6948c2ecf20Sopenharmony_ci	if (*name == '\0')
6958c2ecf20Sopenharmony_ci		goto fail;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len);
6988c2ecf20Sopenharmony_ci	if (tmpns) {
6998c2ecf20Sopenharmony_ci		if (!tmpname) {
7008c2ecf20Sopenharmony_ci			info = "empty profile name";
7018c2ecf20Sopenharmony_ci			goto fail;
7028c2ecf20Sopenharmony_ci		}
7038c2ecf20Sopenharmony_ci		*ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL);
7048c2ecf20Sopenharmony_ci		if (!*ns_name) {
7058c2ecf20Sopenharmony_ci			info = "out of memory";
7068c2ecf20Sopenharmony_ci			goto fail;
7078c2ecf20Sopenharmony_ci		}
7088c2ecf20Sopenharmony_ci		name = tmpname;
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	profile = aa_alloc_profile(name, NULL, GFP_KERNEL);
7128c2ecf20Sopenharmony_ci	if (!profile)
7138c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	/* profile renaming is optional */
7168c2ecf20Sopenharmony_ci	(void) unpack_str(e, &profile->rename, "rename");
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	/* attachment string is optional */
7198c2ecf20Sopenharmony_ci	(void) unpack_str(e, &profile->attach, "attach");
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	/* xmatch is optional and may be NULL */
7228c2ecf20Sopenharmony_ci	profile->xmatch = unpack_dfa(e);
7238c2ecf20Sopenharmony_ci	if (IS_ERR(profile->xmatch)) {
7248c2ecf20Sopenharmony_ci		error = PTR_ERR(profile->xmatch);
7258c2ecf20Sopenharmony_ci		profile->xmatch = NULL;
7268c2ecf20Sopenharmony_ci		info = "bad xmatch";
7278c2ecf20Sopenharmony_ci		goto fail;
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci	/* xmatch_len is not optional if xmatch is set */
7308c2ecf20Sopenharmony_ci	if (profile->xmatch) {
7318c2ecf20Sopenharmony_ci		if (!unpack_u32(e, &tmp, NULL)) {
7328c2ecf20Sopenharmony_ci			info = "missing xmatch len";
7338c2ecf20Sopenharmony_ci			goto fail;
7348c2ecf20Sopenharmony_ci		}
7358c2ecf20Sopenharmony_ci		profile->xmatch_len = tmp;
7368c2ecf20Sopenharmony_ci	}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	/* disconnected attachment string is optional */
7398c2ecf20Sopenharmony_ci	(void) unpack_str(e, &profile->disconnected, "disconnected");
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	/* per profile debug flags (complain, audit) */
7428c2ecf20Sopenharmony_ci	if (!unpack_nameX(e, AA_STRUCT, "flags")) {
7438c2ecf20Sopenharmony_ci		info = "profile missing flags";
7448c2ecf20Sopenharmony_ci		goto fail;
7458c2ecf20Sopenharmony_ci	}
7468c2ecf20Sopenharmony_ci	info = "failed to unpack profile flags";
7478c2ecf20Sopenharmony_ci	if (!unpack_u32(e, &tmp, NULL))
7488c2ecf20Sopenharmony_ci		goto fail;
7498c2ecf20Sopenharmony_ci	if (tmp & PACKED_FLAG_HAT)
7508c2ecf20Sopenharmony_ci		profile->label.flags |= FLAG_HAT;
7518c2ecf20Sopenharmony_ci	if (!unpack_u32(e, &tmp, NULL))
7528c2ecf20Sopenharmony_ci		goto fail;
7538c2ecf20Sopenharmony_ci	if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) {
7548c2ecf20Sopenharmony_ci		profile->mode = APPARMOR_COMPLAIN;
7558c2ecf20Sopenharmony_ci	} else if (tmp == PACKED_MODE_ENFORCE) {
7568c2ecf20Sopenharmony_ci		profile->mode = APPARMOR_ENFORCE;
7578c2ecf20Sopenharmony_ci	} else if (tmp == PACKED_MODE_KILL) {
7588c2ecf20Sopenharmony_ci		profile->mode = APPARMOR_KILL;
7598c2ecf20Sopenharmony_ci	} else if (tmp == PACKED_MODE_UNCONFINED) {
7608c2ecf20Sopenharmony_ci		profile->mode = APPARMOR_UNCONFINED;
7618c2ecf20Sopenharmony_ci		profile->label.flags |= FLAG_UNCONFINED;
7628c2ecf20Sopenharmony_ci	} else {
7638c2ecf20Sopenharmony_ci		goto fail;
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci	if (!unpack_u32(e, &tmp, NULL))
7668c2ecf20Sopenharmony_ci		goto fail;
7678c2ecf20Sopenharmony_ci	if (tmp)
7688c2ecf20Sopenharmony_ci		profile->audit = AUDIT_ALL;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	if (!unpack_nameX(e, AA_STRUCTEND, NULL))
7718c2ecf20Sopenharmony_ci		goto fail;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	/* path_flags is optional */
7748c2ecf20Sopenharmony_ci	if (unpack_u32(e, &profile->path_flags, "path_flags"))
7758c2ecf20Sopenharmony_ci		profile->path_flags |= profile->label.flags &
7768c2ecf20Sopenharmony_ci			PATH_MEDIATE_DELETED;
7778c2ecf20Sopenharmony_ci	else
7788c2ecf20Sopenharmony_ci		/* set a default value if path_flags field is not present */
7798c2ecf20Sopenharmony_ci		profile->path_flags = PATH_MEDIATE_DELETED;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	info = "failed to unpack profile capabilities";
7828c2ecf20Sopenharmony_ci	if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL))
7838c2ecf20Sopenharmony_ci		goto fail;
7848c2ecf20Sopenharmony_ci	if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL))
7858c2ecf20Sopenharmony_ci		goto fail;
7868c2ecf20Sopenharmony_ci	if (!unpack_u32(e, &(profile->caps.quiet.cap[0]), NULL))
7878c2ecf20Sopenharmony_ci		goto fail;
7888c2ecf20Sopenharmony_ci	if (!unpack_u32(e, &tmpcap.cap[0], NULL))
7898c2ecf20Sopenharmony_ci		goto fail;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	info = "failed to unpack upper profile capabilities";
7928c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_STRUCT, "caps64")) {
7938c2ecf20Sopenharmony_ci		/* optional upper half of 64 bit caps */
7948c2ecf20Sopenharmony_ci		if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL))
7958c2ecf20Sopenharmony_ci			goto fail;
7968c2ecf20Sopenharmony_ci		if (!unpack_u32(e, &(profile->caps.audit.cap[1]), NULL))
7978c2ecf20Sopenharmony_ci			goto fail;
7988c2ecf20Sopenharmony_ci		if (!unpack_u32(e, &(profile->caps.quiet.cap[1]), NULL))
7998c2ecf20Sopenharmony_ci			goto fail;
8008c2ecf20Sopenharmony_ci		if (!unpack_u32(e, &(tmpcap.cap[1]), NULL))
8018c2ecf20Sopenharmony_ci			goto fail;
8028c2ecf20Sopenharmony_ci		if (!unpack_nameX(e, AA_STRUCTEND, NULL))
8038c2ecf20Sopenharmony_ci			goto fail;
8048c2ecf20Sopenharmony_ci	}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	info = "failed to unpack extended profile capabilities";
8078c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_STRUCT, "capsx")) {
8088c2ecf20Sopenharmony_ci		/* optional extended caps mediation mask */
8098c2ecf20Sopenharmony_ci		if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL))
8108c2ecf20Sopenharmony_ci			goto fail;
8118c2ecf20Sopenharmony_ci		if (!unpack_u32(e, &(profile->caps.extended.cap[1]), NULL))
8128c2ecf20Sopenharmony_ci			goto fail;
8138c2ecf20Sopenharmony_ci		if (!unpack_nameX(e, AA_STRUCTEND, NULL))
8148c2ecf20Sopenharmony_ci			goto fail;
8158c2ecf20Sopenharmony_ci	}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	if (!unpack_xattrs(e, profile)) {
8188c2ecf20Sopenharmony_ci		info = "failed to unpack profile xattrs";
8198c2ecf20Sopenharmony_ci		goto fail;
8208c2ecf20Sopenharmony_ci	}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	if (!unpack_rlimits(e, profile)) {
8238c2ecf20Sopenharmony_ci		info = "failed to unpack profile rlimits";
8248c2ecf20Sopenharmony_ci		goto fail;
8258c2ecf20Sopenharmony_ci	}
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	if (!unpack_secmark(e, profile)) {
8288c2ecf20Sopenharmony_ci		info = "failed to unpack profile secmark rules";
8298c2ecf20Sopenharmony_ci		goto fail;
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_STRUCT, "policydb")) {
8338c2ecf20Sopenharmony_ci		/* generic policy dfa - optional and may be NULL */
8348c2ecf20Sopenharmony_ci		info = "failed to unpack policydb";
8358c2ecf20Sopenharmony_ci		profile->policy.dfa = unpack_dfa(e);
8368c2ecf20Sopenharmony_ci		if (IS_ERR(profile->policy.dfa)) {
8378c2ecf20Sopenharmony_ci			error = PTR_ERR(profile->policy.dfa);
8388c2ecf20Sopenharmony_ci			profile->policy.dfa = NULL;
8398c2ecf20Sopenharmony_ci			goto fail;
8408c2ecf20Sopenharmony_ci		} else if (!profile->policy.dfa) {
8418c2ecf20Sopenharmony_ci			error = -EPROTO;
8428c2ecf20Sopenharmony_ci			goto fail;
8438c2ecf20Sopenharmony_ci		}
8448c2ecf20Sopenharmony_ci		if (!unpack_u32(e, &profile->policy.start[0], "start"))
8458c2ecf20Sopenharmony_ci			/* default start state */
8468c2ecf20Sopenharmony_ci			profile->policy.start[0] = DFA_START;
8478c2ecf20Sopenharmony_ci		/* setup class index */
8488c2ecf20Sopenharmony_ci		for (i = AA_CLASS_FILE; i <= AA_CLASS_LAST; i++) {
8498c2ecf20Sopenharmony_ci			profile->policy.start[i] =
8508c2ecf20Sopenharmony_ci				aa_dfa_next(profile->policy.dfa,
8518c2ecf20Sopenharmony_ci					    profile->policy.start[0],
8528c2ecf20Sopenharmony_ci					    i);
8538c2ecf20Sopenharmony_ci		}
8548c2ecf20Sopenharmony_ci		if (!unpack_nameX(e, AA_STRUCTEND, NULL))
8558c2ecf20Sopenharmony_ci			goto fail;
8568c2ecf20Sopenharmony_ci	} else
8578c2ecf20Sopenharmony_ci		profile->policy.dfa = aa_get_dfa(nulldfa);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	/* get file rules */
8608c2ecf20Sopenharmony_ci	profile->file.dfa = unpack_dfa(e);
8618c2ecf20Sopenharmony_ci	if (IS_ERR(profile->file.dfa)) {
8628c2ecf20Sopenharmony_ci		error = PTR_ERR(profile->file.dfa);
8638c2ecf20Sopenharmony_ci		profile->file.dfa = NULL;
8648c2ecf20Sopenharmony_ci		info = "failed to unpack profile file rules";
8658c2ecf20Sopenharmony_ci		goto fail;
8668c2ecf20Sopenharmony_ci	} else if (profile->file.dfa) {
8678c2ecf20Sopenharmony_ci		if (!unpack_u32(e, &profile->file.start, "dfa_start"))
8688c2ecf20Sopenharmony_ci			/* default start state */
8698c2ecf20Sopenharmony_ci			profile->file.start = DFA_START;
8708c2ecf20Sopenharmony_ci	} else if (profile->policy.dfa &&
8718c2ecf20Sopenharmony_ci		   profile->policy.start[AA_CLASS_FILE]) {
8728c2ecf20Sopenharmony_ci		profile->file.dfa = aa_get_dfa(profile->policy.dfa);
8738c2ecf20Sopenharmony_ci		profile->file.start = profile->policy.start[AA_CLASS_FILE];
8748c2ecf20Sopenharmony_ci	} else
8758c2ecf20Sopenharmony_ci		profile->file.dfa = aa_get_dfa(nulldfa);
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	if (!unpack_trans_table(e, profile)) {
8788c2ecf20Sopenharmony_ci		info = "failed to unpack profile transition table";
8798c2ecf20Sopenharmony_ci		goto fail;
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	if (unpack_nameX(e, AA_STRUCT, "data")) {
8838c2ecf20Sopenharmony_ci		info = "out of memory";
8848c2ecf20Sopenharmony_ci		profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
8858c2ecf20Sopenharmony_ci		if (!profile->data)
8868c2ecf20Sopenharmony_ci			goto fail;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci		params.nelem_hint = 3;
8898c2ecf20Sopenharmony_ci		params.key_len = sizeof(void *);
8908c2ecf20Sopenharmony_ci		params.key_offset = offsetof(struct aa_data, key);
8918c2ecf20Sopenharmony_ci		params.head_offset = offsetof(struct aa_data, head);
8928c2ecf20Sopenharmony_ci		params.hashfn = strhash;
8938c2ecf20Sopenharmony_ci		params.obj_cmpfn = datacmp;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci		if (rhashtable_init(profile->data, &params)) {
8968c2ecf20Sopenharmony_ci			info = "failed to init key, value hash table";
8978c2ecf20Sopenharmony_ci			goto fail;
8988c2ecf20Sopenharmony_ci		}
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci		while (unpack_strdup(e, &key, NULL)) {
9018c2ecf20Sopenharmony_ci			data = kzalloc(sizeof(*data), GFP_KERNEL);
9028c2ecf20Sopenharmony_ci			if (!data) {
9038c2ecf20Sopenharmony_ci				kfree_sensitive(key);
9048c2ecf20Sopenharmony_ci				goto fail;
9058c2ecf20Sopenharmony_ci			}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci			data->key = key;
9088c2ecf20Sopenharmony_ci			data->size = unpack_blob(e, &data->data, NULL);
9098c2ecf20Sopenharmony_ci			data->data = kvmemdup(data->data, data->size);
9108c2ecf20Sopenharmony_ci			if (data->size && !data->data) {
9118c2ecf20Sopenharmony_ci				kfree_sensitive(data->key);
9128c2ecf20Sopenharmony_ci				kfree_sensitive(data);
9138c2ecf20Sopenharmony_ci				goto fail;
9148c2ecf20Sopenharmony_ci			}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci			if (rhashtable_insert_fast(profile->data, &data->head,
9178c2ecf20Sopenharmony_ci						   profile->data->p)) {
9188c2ecf20Sopenharmony_ci				kfree_sensitive(data->key);
9198c2ecf20Sopenharmony_ci				kfree_sensitive(data);
9208c2ecf20Sopenharmony_ci				info = "failed to insert data to table";
9218c2ecf20Sopenharmony_ci				goto fail;
9228c2ecf20Sopenharmony_ci			}
9238c2ecf20Sopenharmony_ci		}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci		if (!unpack_nameX(e, AA_STRUCTEND, NULL)) {
9268c2ecf20Sopenharmony_ci			info = "failed to unpack end of key, value data table";
9278c2ecf20Sopenharmony_ci			goto fail;
9288c2ecf20Sopenharmony_ci		}
9298c2ecf20Sopenharmony_ci	}
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	if (!unpack_nameX(e, AA_STRUCTEND, NULL)) {
9328c2ecf20Sopenharmony_ci		info = "failed to unpack end of profile";
9338c2ecf20Sopenharmony_ci		goto fail;
9348c2ecf20Sopenharmony_ci	}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	return profile;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_cifail:
9398c2ecf20Sopenharmony_ci	if (profile)
9408c2ecf20Sopenharmony_ci		name = NULL;
9418c2ecf20Sopenharmony_ci	else if (!name)
9428c2ecf20Sopenharmony_ci		name = "unknown";
9438c2ecf20Sopenharmony_ci	audit_iface(profile, NULL, name, info, e, error);
9448c2ecf20Sopenharmony_ci	aa_free_profile(profile);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	return ERR_PTR(error);
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci/**
9508c2ecf20Sopenharmony_ci * verify_head - unpack serialized stream header
9518c2ecf20Sopenharmony_ci * @e: serialized data read head (NOT NULL)
9528c2ecf20Sopenharmony_ci * @required: whether the header is required or optional
9538c2ecf20Sopenharmony_ci * @ns: Returns - namespace if one is specified else NULL (NOT NULL)
9548c2ecf20Sopenharmony_ci *
9558c2ecf20Sopenharmony_ci * Returns: error or 0 if header is good
9568c2ecf20Sopenharmony_ci */
9578c2ecf20Sopenharmony_cistatic int verify_header(struct aa_ext *e, int required, const char **ns)
9588c2ecf20Sopenharmony_ci{
9598c2ecf20Sopenharmony_ci	int error = -EPROTONOSUPPORT;
9608c2ecf20Sopenharmony_ci	const char *name = NULL;
9618c2ecf20Sopenharmony_ci	*ns = NULL;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	/* get the interface version */
9648c2ecf20Sopenharmony_ci	if (!unpack_u32(e, &e->version, "version")) {
9658c2ecf20Sopenharmony_ci		if (required) {
9668c2ecf20Sopenharmony_ci			audit_iface(NULL, NULL, NULL, "invalid profile format",
9678c2ecf20Sopenharmony_ci				    e, error);
9688c2ecf20Sopenharmony_ci			return error;
9698c2ecf20Sopenharmony_ci		}
9708c2ecf20Sopenharmony_ci	}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	/* Check that the interface version is currently supported.
9738c2ecf20Sopenharmony_ci	 * if not specified use previous version
9748c2ecf20Sopenharmony_ci	 * Mask off everything that is not kernel abi version
9758c2ecf20Sopenharmony_ci	 */
9768c2ecf20Sopenharmony_ci	if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v8)) {
9778c2ecf20Sopenharmony_ci		audit_iface(NULL, NULL, NULL, "unsupported interface version",
9788c2ecf20Sopenharmony_ci			    e, error);
9798c2ecf20Sopenharmony_ci		return error;
9808c2ecf20Sopenharmony_ci	}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	/* read the namespace if present */
9838c2ecf20Sopenharmony_ci	if (unpack_str(e, &name, "namespace")) {
9848c2ecf20Sopenharmony_ci		if (*name == '\0') {
9858c2ecf20Sopenharmony_ci			audit_iface(NULL, NULL, NULL, "invalid namespace name",
9868c2ecf20Sopenharmony_ci				    e, error);
9878c2ecf20Sopenharmony_ci			return error;
9888c2ecf20Sopenharmony_ci		}
9898c2ecf20Sopenharmony_ci		if (*ns && strcmp(*ns, name)) {
9908c2ecf20Sopenharmony_ci			audit_iface(NULL, NULL, NULL, "invalid ns change", e,
9918c2ecf20Sopenharmony_ci				    error);
9928c2ecf20Sopenharmony_ci		} else if (!*ns) {
9938c2ecf20Sopenharmony_ci			*ns = kstrdup(name, GFP_KERNEL);
9948c2ecf20Sopenharmony_ci			if (!*ns)
9958c2ecf20Sopenharmony_ci				return -ENOMEM;
9968c2ecf20Sopenharmony_ci		}
9978c2ecf20Sopenharmony_ci	}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	return 0;
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cistatic bool verify_xindex(int xindex, int table_size)
10038c2ecf20Sopenharmony_ci{
10048c2ecf20Sopenharmony_ci	int index, xtype;
10058c2ecf20Sopenharmony_ci	xtype = xindex & AA_X_TYPE_MASK;
10068c2ecf20Sopenharmony_ci	index = xindex & AA_X_INDEX_MASK;
10078c2ecf20Sopenharmony_ci	if (xtype == AA_X_TABLE && index >= table_size)
10088c2ecf20Sopenharmony_ci		return false;
10098c2ecf20Sopenharmony_ci	return true;
10108c2ecf20Sopenharmony_ci}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci/* verify dfa xindexes are in range of transition tables */
10138c2ecf20Sopenharmony_cistatic bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size)
10148c2ecf20Sopenharmony_ci{
10158c2ecf20Sopenharmony_ci	int i;
10168c2ecf20Sopenharmony_ci	for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) {
10178c2ecf20Sopenharmony_ci		if (!verify_xindex(dfa_user_xindex(dfa, i), table_size))
10188c2ecf20Sopenharmony_ci			return false;
10198c2ecf20Sopenharmony_ci		if (!verify_xindex(dfa_other_xindex(dfa, i), table_size))
10208c2ecf20Sopenharmony_ci			return false;
10218c2ecf20Sopenharmony_ci	}
10228c2ecf20Sopenharmony_ci	return true;
10238c2ecf20Sopenharmony_ci}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci/**
10268c2ecf20Sopenharmony_ci * verify_profile - Do post unpack analysis to verify profile consistency
10278c2ecf20Sopenharmony_ci * @profile: profile to verify (NOT NULL)
10288c2ecf20Sopenharmony_ci *
10298c2ecf20Sopenharmony_ci * Returns: 0 if passes verification else error
10308c2ecf20Sopenharmony_ci */
10318c2ecf20Sopenharmony_cistatic int verify_profile(struct aa_profile *profile)
10328c2ecf20Sopenharmony_ci{
10338c2ecf20Sopenharmony_ci	if (profile->file.dfa &&
10348c2ecf20Sopenharmony_ci	    !verify_dfa_xindex(profile->file.dfa,
10358c2ecf20Sopenharmony_ci			       profile->file.trans.size)) {
10368c2ecf20Sopenharmony_ci		audit_iface(profile, NULL, NULL, "Invalid named transition",
10378c2ecf20Sopenharmony_ci			    NULL, -EPROTO);
10388c2ecf20Sopenharmony_ci		return -EPROTO;
10398c2ecf20Sopenharmony_ci	}
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	return 0;
10428c2ecf20Sopenharmony_ci}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_civoid aa_load_ent_free(struct aa_load_ent *ent)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	if (ent) {
10478c2ecf20Sopenharmony_ci		aa_put_profile(ent->rename);
10488c2ecf20Sopenharmony_ci		aa_put_profile(ent->old);
10498c2ecf20Sopenharmony_ci		aa_put_profile(ent->new);
10508c2ecf20Sopenharmony_ci		kfree(ent->ns_name);
10518c2ecf20Sopenharmony_ci		kfree_sensitive(ent);
10528c2ecf20Sopenharmony_ci	}
10538c2ecf20Sopenharmony_ci}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_cistruct aa_load_ent *aa_load_ent_alloc(void)
10568c2ecf20Sopenharmony_ci{
10578c2ecf20Sopenharmony_ci	struct aa_load_ent *ent = kzalloc(sizeof(*ent), GFP_KERNEL);
10588c2ecf20Sopenharmony_ci	if (ent)
10598c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&ent->list);
10608c2ecf20Sopenharmony_ci	return ent;
10618c2ecf20Sopenharmony_ci}
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_cistatic int deflate_compress(const char *src, size_t slen, char **dst,
10648c2ecf20Sopenharmony_ci			    size_t *dlen)
10658c2ecf20Sopenharmony_ci{
10668c2ecf20Sopenharmony_ci	int error;
10678c2ecf20Sopenharmony_ci	struct z_stream_s strm;
10688c2ecf20Sopenharmony_ci	void *stgbuf, *dstbuf;
10698c2ecf20Sopenharmony_ci	size_t stglen = deflateBound(slen);
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	memset(&strm, 0, sizeof(strm));
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	if (stglen < slen)
10748c2ecf20Sopenharmony_ci		return -EFBIG;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	strm.workspace = kvzalloc(zlib_deflate_workspacesize(MAX_WBITS,
10778c2ecf20Sopenharmony_ci							     MAX_MEM_LEVEL),
10788c2ecf20Sopenharmony_ci				  GFP_KERNEL);
10798c2ecf20Sopenharmony_ci	if (!strm.workspace)
10808c2ecf20Sopenharmony_ci		return -ENOMEM;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	error = zlib_deflateInit(&strm, aa_g_rawdata_compression_level);
10838c2ecf20Sopenharmony_ci	if (error != Z_OK) {
10848c2ecf20Sopenharmony_ci		error = -ENOMEM;
10858c2ecf20Sopenharmony_ci		goto fail_deflate_init;
10868c2ecf20Sopenharmony_ci	}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	stgbuf = kvzalloc(stglen, GFP_KERNEL);
10898c2ecf20Sopenharmony_ci	if (!stgbuf) {
10908c2ecf20Sopenharmony_ci		error = -ENOMEM;
10918c2ecf20Sopenharmony_ci		goto fail_stg_alloc;
10928c2ecf20Sopenharmony_ci	}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	strm.next_in = src;
10958c2ecf20Sopenharmony_ci	strm.avail_in = slen;
10968c2ecf20Sopenharmony_ci	strm.next_out = stgbuf;
10978c2ecf20Sopenharmony_ci	strm.avail_out = stglen;
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	error = zlib_deflate(&strm, Z_FINISH);
11008c2ecf20Sopenharmony_ci	if (error != Z_STREAM_END) {
11018c2ecf20Sopenharmony_ci		error = -EINVAL;
11028c2ecf20Sopenharmony_ci		goto fail_deflate;
11038c2ecf20Sopenharmony_ci	}
11048c2ecf20Sopenharmony_ci	error = 0;
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	if (is_vmalloc_addr(stgbuf)) {
11078c2ecf20Sopenharmony_ci		dstbuf = kvzalloc(strm.total_out, GFP_KERNEL);
11088c2ecf20Sopenharmony_ci		if (dstbuf) {
11098c2ecf20Sopenharmony_ci			memcpy(dstbuf, stgbuf, strm.total_out);
11108c2ecf20Sopenharmony_ci			kvfree(stgbuf);
11118c2ecf20Sopenharmony_ci		}
11128c2ecf20Sopenharmony_ci	} else
11138c2ecf20Sopenharmony_ci		/*
11148c2ecf20Sopenharmony_ci		 * If the staging buffer was kmalloc'd, then using krealloc is
11158c2ecf20Sopenharmony_ci		 * probably going to be faster. The destination buffer will
11168c2ecf20Sopenharmony_ci		 * always be smaller, so it's just shrunk, avoiding a memcpy
11178c2ecf20Sopenharmony_ci		 */
11188c2ecf20Sopenharmony_ci		dstbuf = krealloc(stgbuf, strm.total_out, GFP_KERNEL);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	if (!dstbuf) {
11218c2ecf20Sopenharmony_ci		error = -ENOMEM;
11228c2ecf20Sopenharmony_ci		goto fail_deflate;
11238c2ecf20Sopenharmony_ci	}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	*dst = dstbuf;
11268c2ecf20Sopenharmony_ci	*dlen = strm.total_out;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_cifail_stg_alloc:
11298c2ecf20Sopenharmony_ci	zlib_deflateEnd(&strm);
11308c2ecf20Sopenharmony_cifail_deflate_init:
11318c2ecf20Sopenharmony_ci	kvfree(strm.workspace);
11328c2ecf20Sopenharmony_ci	return error;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_cifail_deflate:
11358c2ecf20Sopenharmony_ci	kvfree(stgbuf);
11368c2ecf20Sopenharmony_ci	goto fail_stg_alloc;
11378c2ecf20Sopenharmony_ci}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_cistatic int compress_loaddata(struct aa_loaddata *data)
11408c2ecf20Sopenharmony_ci{
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	AA_BUG(data->compressed_size > 0);
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	/*
11458c2ecf20Sopenharmony_ci	 * Shortcut the no compression case, else we increase the amount of
11468c2ecf20Sopenharmony_ci	 * storage required by a small amount
11478c2ecf20Sopenharmony_ci	 */
11488c2ecf20Sopenharmony_ci	if (aa_g_rawdata_compression_level != 0) {
11498c2ecf20Sopenharmony_ci		void *udata = data->data;
11508c2ecf20Sopenharmony_ci		int error = deflate_compress(udata, data->size, &data->data,
11518c2ecf20Sopenharmony_ci					     &data->compressed_size);
11528c2ecf20Sopenharmony_ci		if (error)
11538c2ecf20Sopenharmony_ci			return error;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci		kvfree(udata);
11568c2ecf20Sopenharmony_ci	} else
11578c2ecf20Sopenharmony_ci		data->compressed_size = data->size;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	return 0;
11608c2ecf20Sopenharmony_ci}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci/**
11638c2ecf20Sopenharmony_ci * aa_unpack - unpack packed binary profile(s) data loaded from user space
11648c2ecf20Sopenharmony_ci * @udata: user data copied to kmem  (NOT NULL)
11658c2ecf20Sopenharmony_ci * @lh: list to place unpacked profiles in a aa_repl_ws
11668c2ecf20Sopenharmony_ci * @ns: Returns namespace profile is in if specified else NULL (NOT NULL)
11678c2ecf20Sopenharmony_ci *
11688c2ecf20Sopenharmony_ci * Unpack user data and return refcounted allocated profile(s) stored in
11698c2ecf20Sopenharmony_ci * @lh in order of discovery, with the list chain stored in base.list
11708c2ecf20Sopenharmony_ci * or error
11718c2ecf20Sopenharmony_ci *
11728c2ecf20Sopenharmony_ci * Returns: profile(s) on @lh else error pointer if fails to unpack
11738c2ecf20Sopenharmony_ci */
11748c2ecf20Sopenharmony_ciint aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
11758c2ecf20Sopenharmony_ci	      const char **ns)
11768c2ecf20Sopenharmony_ci{
11778c2ecf20Sopenharmony_ci	struct aa_load_ent *tmp, *ent;
11788c2ecf20Sopenharmony_ci	struct aa_profile *profile = NULL;
11798c2ecf20Sopenharmony_ci	int error;
11808c2ecf20Sopenharmony_ci	struct aa_ext e = {
11818c2ecf20Sopenharmony_ci		.start = udata->data,
11828c2ecf20Sopenharmony_ci		.end = udata->data + udata->size,
11838c2ecf20Sopenharmony_ci		.pos = udata->data,
11848c2ecf20Sopenharmony_ci	};
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	*ns = NULL;
11878c2ecf20Sopenharmony_ci	while (e.pos < e.end) {
11888c2ecf20Sopenharmony_ci		char *ns_name = NULL;
11898c2ecf20Sopenharmony_ci		void *start;
11908c2ecf20Sopenharmony_ci		error = verify_header(&e, e.pos == e.start, ns);
11918c2ecf20Sopenharmony_ci		if (error)
11928c2ecf20Sopenharmony_ci			goto fail;
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci		start = e.pos;
11958c2ecf20Sopenharmony_ci		profile = unpack_profile(&e, &ns_name);
11968c2ecf20Sopenharmony_ci		if (IS_ERR(profile)) {
11978c2ecf20Sopenharmony_ci			error = PTR_ERR(profile);
11988c2ecf20Sopenharmony_ci			goto fail;
11998c2ecf20Sopenharmony_ci		}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci		error = verify_profile(profile);
12028c2ecf20Sopenharmony_ci		if (error)
12038c2ecf20Sopenharmony_ci			goto fail_profile;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci		if (aa_g_hash_policy)
12068c2ecf20Sopenharmony_ci			error = aa_calc_profile_hash(profile, e.version, start,
12078c2ecf20Sopenharmony_ci						     e.pos - start);
12088c2ecf20Sopenharmony_ci		if (error)
12098c2ecf20Sopenharmony_ci			goto fail_profile;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci		ent = aa_load_ent_alloc();
12128c2ecf20Sopenharmony_ci		if (!ent) {
12138c2ecf20Sopenharmony_ci			error = -ENOMEM;
12148c2ecf20Sopenharmony_ci			goto fail_profile;
12158c2ecf20Sopenharmony_ci		}
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci		ent->new = profile;
12188c2ecf20Sopenharmony_ci		ent->ns_name = ns_name;
12198c2ecf20Sopenharmony_ci		list_add_tail(&ent->list, lh);
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci	udata->abi = e.version & K_ABI_MASK;
12228c2ecf20Sopenharmony_ci	if (aa_g_hash_policy) {
12238c2ecf20Sopenharmony_ci		udata->hash = aa_calc_hash(udata->data, udata->size);
12248c2ecf20Sopenharmony_ci		if (IS_ERR(udata->hash)) {
12258c2ecf20Sopenharmony_ci			error = PTR_ERR(udata->hash);
12268c2ecf20Sopenharmony_ci			udata->hash = NULL;
12278c2ecf20Sopenharmony_ci			goto fail;
12288c2ecf20Sopenharmony_ci		}
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci	error = compress_loaddata(udata);
12318c2ecf20Sopenharmony_ci	if (error)
12328c2ecf20Sopenharmony_ci		goto fail;
12338c2ecf20Sopenharmony_ci	return 0;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_cifail_profile:
12368c2ecf20Sopenharmony_ci	aa_put_profile(profile);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_cifail:
12398c2ecf20Sopenharmony_ci	list_for_each_entry_safe(ent, tmp, lh, list) {
12408c2ecf20Sopenharmony_ci		list_del_init(&ent->list);
12418c2ecf20Sopenharmony_ci		aa_load_ent_free(ent);
12428c2ecf20Sopenharmony_ci	}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	return error;
12458c2ecf20Sopenharmony_ci}
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci#ifdef CONFIG_SECURITY_APPARMOR_KUNIT_TEST
12488c2ecf20Sopenharmony_ci#include "policy_unpack_test.c"
12498c2ecf20Sopenharmony_ci#endif /* CONFIG_SECURITY_APPARMOR_KUNIT_TEST */
1250