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, ¶ms)) { 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