2010-07-30 01:48:02 +04:00
/*
* AppArmor security module
*
* This file contains AppArmor functions for unpacking policy loaded from
* userspace .
*
* Copyright ( C ) 1998 - 2008 Novell / SUSE
* Copyright 2009 - 2010 Canonical Ltd .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation , version 2 of the
* License .
*
2011-05-20 02:59:38 +04:00
* AppArmor uses a serialized binary format for loading policy . To find
2017-05-13 14:51:45 +03:00
* policy format documentation see Documentation / admin - guide / LSM / apparmor . rst
2010-07-30 01:48:02 +04:00
* All policy is validated before it is used .
*/
# include <asm/unaligned.h>
# include <linux/ctype.h>
# include <linux/errno.h>
# include "include/apparmor.h"
# include "include/audit.h"
2017-10-11 11:04:48 +03:00
# include "include/cred.h"
2013-08-14 22:27:36 +04:00
# include "include/crypto.h"
2010-07-30 01:48:02 +04:00
# include "include/match.h"
2017-06-09 18:14:28 +03:00
# include "include/path.h"
2010-07-30 01:48:02 +04:00
# include "include/policy.h"
# include "include/policy_unpack.h"
2017-01-16 11:42:39 +03:00
# define K_ABI_MASK 0x3ff
2017-01-16 11:42:38 +03:00
# define FORCE_COMPLAIN_FLAG 0x800
2017-01-16 11:42:39 +03:00
# define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK))
# define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK))
# define v5 5 /* base version */
# define v6 6 /* per entry policydb mediation check */
2017-07-19 09:18:33 +03:00
# define v7 7
# define v8 8 /* full network masking */
2017-01-16 11:42:38 +03:00
2010-07-30 01:48:02 +04:00
/*
* The AppArmor interface treats data as a type byte followed by the
* actual data . The interface has the notion of a a named entry
* which has a name ( AA_NAME typecode followed by name string ) followed by
* the entries typecode and data . Named types allow for optional
* elements and extensions to be added and tested for without breaking
* backwards compatibility .
*/
enum aa_code {
AA_U8 ,
AA_U16 ,
AA_U32 ,
AA_U64 ,
AA_NAME , /* same as string except it is items name */
AA_STRING ,
AA_BLOB ,
AA_STRUCT ,
AA_STRUCTEND ,
AA_LIST ,
AA_LISTEND ,
AA_ARRAY ,
AA_ARRAYEND ,
} ;
/*
* aa_ext is the read of the buffer containing the serialized profile . The
* data is copied into a kernel buffer in apparmorfs and then handed off to
* the unpack routines .
*/
struct aa_ext {
void * start ;
void * end ;
void * pos ; /* pointer to current position in the buffer */
u32 version ;
} ;
/* audit callback for unpack fields */
static void audit_cb ( struct audit_buffer * ab , void * va )
{
struct common_audit_data * sa = va ;
2017-01-16 11:43:02 +03:00
if ( aad ( sa ) - > iface . ns ) {
audit_log_format ( ab , " ns= " ) ;
audit_log_untrustedstring ( ab , aad ( sa ) - > iface . ns ) ;
}
2017-07-19 09:37:18 +03:00
if ( aad ( sa ) - > name ) {
2010-07-30 01:48:02 +04:00
audit_log_format ( ab , " name= " ) ;
2017-07-19 09:37:18 +03:00
audit_log_untrustedstring ( ab , aad ( sa ) - > name ) ;
2010-07-30 01:48:02 +04:00
}
2017-01-16 11:43:02 +03:00
if ( aad ( sa ) - > iface . pos )
audit_log_format ( ab , " offset=%ld " , aad ( sa ) - > iface . pos ) ;
2010-07-30 01:48:02 +04:00
}
/**
* audit_iface - do audit message for policy unpacking / load / replace / remove
* @ new : profile if it has been allocated ( MAYBE NULL )
2017-01-16 11:42:56 +03:00
* @ ns_name : name of the ns the profile is to be loaded to ( MAY BE NULL )
2010-07-30 01:48:02 +04:00
* @ name : name of the profile being manipulated ( MAYBE NULL )
* @ info : any extra info about the failure ( MAYBE NULL )
2012-03-10 23:25:30 +04:00
* @ e : buffer position info
2010-07-30 01:48:02 +04:00
* @ error : error code
*
* Returns : % 0 or error
*/
2017-01-16 11:42:56 +03:00
static int audit_iface ( struct aa_profile * new , const char * ns_name ,
const char * name , const char * info , struct aa_ext * e ,
int error )
2010-07-30 01:48:02 +04:00
{
2017-06-09 18:14:28 +03:00
struct aa_profile * profile = labels_profile ( aa_current_raw_label ( ) ) ;
2017-01-16 11:43:02 +03:00
DEFINE_AUDIT_DATA ( sa , LSM_AUDIT_DATA_NONE , NULL ) ;
2012-03-10 23:25:30 +04:00
if ( e )
2017-01-16 11:43:02 +03:00
aad ( & sa ) - > iface . pos = e - > pos - e - > start ;
aad ( & sa ) - > iface . ns = ns_name ;
if ( new )
2017-07-19 09:37:18 +03:00
aad ( & sa ) - > name = new - > base . hname ;
2017-01-16 11:43:02 +03:00
else
2017-07-19 09:37:18 +03:00
aad ( & sa ) - > name = name ;
2017-01-16 11:43:02 +03:00
aad ( & sa ) - > info = info ;
aad ( & sa ) - > error = error ;
2010-07-30 01:48:02 +04:00
2017-01-16 11:43:02 +03:00
return aa_audit ( AUDIT_APPARMOR_STATUS , profile , & sa , audit_cb ) ;
2010-07-30 01:48:02 +04:00
}
2017-05-09 10:08:41 +03:00
void __aa_loaddata_update ( struct aa_loaddata * data , long revision )
{
AA_BUG ( ! data ) ;
AA_BUG ( ! data - > ns ) ;
AA_BUG ( ! data - > dents [ AAFS_LOADDATA_REVISION ] ) ;
AA_BUG ( ! mutex_is_locked ( & data - > ns - > lock ) ) ;
AA_BUG ( data - > revision > revision ) ;
data - > revision = revision ;
d_inode ( data - > dents [ AAFS_LOADDATA_DIR ] ) - > i_mtime =
current_time ( d_inode ( data - > dents [ AAFS_LOADDATA_DIR ] ) ) ;
d_inode ( data - > dents [ AAFS_LOADDATA_REVISION ] ) - > i_mtime =
current_time ( d_inode ( data - > dents [ AAFS_LOADDATA_REVISION ] ) ) ;
}
bool aa_rawdata_eq ( struct aa_loaddata * l , struct aa_loaddata * r )
{
if ( l - > size ! = r - > size )
return false ;
if ( aa_g_hash_policy & & memcmp ( l - > hash , r - > hash , aa_hash_size ( ) ) ! = 0 )
return false ;
return memcmp ( l - > data , r - > data , r - > size ) = = 0 ;
}
/*
* need to take the ns mutex lock which is NOT safe most places that
* put_loaddata is called , so we have to delay freeing it
*/
static void do_loaddata_free ( struct work_struct * work )
{
struct aa_loaddata * d = container_of ( work , struct aa_loaddata , work ) ;
struct aa_ns * ns = aa_get_ns ( d - > ns ) ;
if ( ns ) {
2017-11-21 10:24:09 +03:00
mutex_lock_nested ( & ns - > lock , ns - > level ) ;
2017-05-09 10:08:41 +03:00
__aa_fs_remove_rawdata ( d ) ;
mutex_unlock ( & ns - > lock ) ;
aa_put_ns ( ns ) ;
}
kzfree ( d - > hash ) ;
2018-02-03 22:08:28 +03:00
kzfree ( d - > name ) ;
kvfree ( d - > data ) ;
kzfree ( d ) ;
2017-05-09 10:08:41 +03:00
}
2017-01-16 11:42:55 +03:00
void aa_loaddata_kref ( struct kref * kref )
{
struct aa_loaddata * d = container_of ( kref , struct aa_loaddata , count ) ;
if ( d ) {
2017-05-09 10:08:41 +03:00
INIT_WORK ( & d - > work , do_loaddata_free ) ;
schedule_work ( & d - > work ) ;
2017-01-16 11:42:55 +03:00
}
}
2017-05-09 10:08:41 +03:00
struct aa_loaddata * aa_loaddata_alloc ( size_t size )
{
2018-02-03 22:08:28 +03:00
struct aa_loaddata * d ;
2017-05-09 10:08:41 +03:00
2018-02-03 22:08:28 +03:00
d = kzalloc ( sizeof ( * d ) , GFP_KERNEL ) ;
2017-05-09 10:08:41 +03:00
if ( d = = NULL )
return ERR_PTR ( - ENOMEM ) ;
2018-02-03 22:08:28 +03:00
d - > data = kvzalloc ( size , GFP_KERNEL ) ;
if ( ! d - > data ) {
kfree ( d ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2017-05-09 10:08:41 +03:00
kref_init ( & d - > count ) ;
INIT_LIST_HEAD ( & d - > list ) ;
return d ;
}
2010-07-30 01:48:02 +04:00
/* test if read will be in packed data bounds */
static bool inbounds ( struct aa_ext * e , size_t size )
{
return ( size < = e - > end - e - > pos ) ;
}
2018-02-08 23:37:19 +03:00
static void * kvmemdup ( const void * src , size_t len )
{
void * p = kvmalloc ( len , GFP_KERNEL ) ;
if ( p )
memcpy ( p , src , len ) ;
return p ;
}
2010-07-30 01:48:02 +04:00
/**
* aa_u16_chunck - test and do bounds checking for a u16 size based chunk
* @ e : serialized data read head ( NOT NULL )
* @ chunk : start address for chunk of data ( NOT NULL )
*
* Returns : the size of chunk found with the read head at the end of the chunk .
*/
static size_t unpack_u16_chunk ( struct aa_ext * e , char * * chunk )
{
size_t size = 0 ;
if ( ! inbounds ( e , sizeof ( u16 ) ) )
return 0 ;
2017-01-16 11:43:14 +03:00
size = le16_to_cpu ( get_unaligned ( ( __le16 * ) e - > pos ) ) ;
e - > pos + = sizeof ( __le16 ) ;
2010-07-30 01:48:02 +04:00
if ( ! inbounds ( e , size ) )
return 0 ;
* chunk = e - > pos ;
e - > pos + = size ;
return size ;
}
/* unpack control byte */
static bool unpack_X ( struct aa_ext * e , enum aa_code code )
{
if ( ! inbounds ( e , 1 ) )
return 0 ;
if ( * ( u8 * ) e - > pos ! = code )
return 0 ;
e - > pos + + ;
return 1 ;
}
/**
* unpack_nameX - check is the next element is of type X with a name of @ name
* @ e : serialized data extent information ( NOT NULL )
* @ code : type code
* @ name : name to match to the serialized element . ( MAYBE NULL )
*
* check that the next serialized data element is of type X and has a tag
* name @ name . If @ name is specified then there must be a matching
* name element in the stream . If @ name is NULL any name element will be
* skipped and only the typecode will be tested .
*
* Returns 1 on success ( both type code and name tests match ) and the read
* head is advanced past the headers
*
* Returns : 0 if either match fails , the read head does not move
*/
static bool unpack_nameX ( struct aa_ext * e , enum aa_code code , const char * name )
{
/*
* May need to reset pos if name or type doesn ' t match
*/
void * pos = e - > pos ;
/*
* Check for presence of a tagname , and if present name size
* AA_NAME tag value is a u16 .
*/
if ( unpack_X ( e , AA_NAME ) ) {
char * tag = NULL ;
size_t size = unpack_u16_chunk ( e , & tag ) ;
/* if a name is specified it must match. otherwise skip tag */
if ( name & & ( ! size | | strcmp ( name , tag ) ) )
goto fail ;
} else if ( name ) {
/* if a name is specified and there is no name tag fail */
goto fail ;
}
/* now check if type code matches */
if ( unpack_X ( e , code ) )
return 1 ;
fail :
e - > pos = pos ;
return 0 ;
}
2018-05-24 23:27:46 +03:00
static bool unpack_u8 ( struct aa_ext * e , u8 * data , const char * name )
{
if ( unpack_nameX ( e , AA_U8 , name ) ) {
if ( ! inbounds ( e , sizeof ( u8 ) ) )
return 0 ;
if ( data )
* data = get_unaligned ( ( u8 * ) e - > pos ) ;
e - > pos + = sizeof ( u8 ) ;
return 1 ;
}
return 0 ;
}
2010-07-30 01:48:02 +04:00
static bool unpack_u32 ( struct aa_ext * e , u32 * data , const char * name )
{
if ( unpack_nameX ( e , AA_U32 , name ) ) {
if ( ! inbounds ( e , sizeof ( u32 ) ) )
return 0 ;
if ( data )
2017-01-16 11:43:14 +03:00
* data = le32_to_cpu ( get_unaligned ( ( __le32 * ) e - > pos ) ) ;
2010-07-30 01:48:02 +04:00
e - > pos + = sizeof ( u32 ) ;
return 1 ;
}
return 0 ;
}
static bool unpack_u64 ( struct aa_ext * e , u64 * data , const char * name )
{
if ( unpack_nameX ( e , AA_U64 , name ) ) {
if ( ! inbounds ( e , sizeof ( u64 ) ) )
return 0 ;
if ( data )
2017-01-16 11:43:14 +03:00
* data = le64_to_cpu ( get_unaligned ( ( __le64 * ) e - > pos ) ) ;
2010-07-30 01:48:02 +04:00
e - > pos + = sizeof ( u64 ) ;
return 1 ;
}
return 0 ;
}
static size_t unpack_array ( struct aa_ext * e , const char * name )
{
if ( unpack_nameX ( e , AA_ARRAY , name ) ) {
int size ;
if ( ! inbounds ( e , sizeof ( u16 ) ) )
return 0 ;
2017-01-16 11:43:14 +03:00
size = ( int ) le16_to_cpu ( get_unaligned ( ( __le16 * ) e - > pos ) ) ;
2010-07-30 01:48:02 +04:00
e - > pos + = sizeof ( u16 ) ;
return size ;
}
return 0 ;
}
static size_t unpack_blob ( struct aa_ext * e , char * * blob , const char * name )
{
if ( unpack_nameX ( e , AA_BLOB , name ) ) {
u32 size ;
if ( ! inbounds ( e , sizeof ( u32 ) ) )
return 0 ;
2017-01-16 11:43:14 +03:00
size = le32_to_cpu ( get_unaligned ( ( __le32 * ) e - > pos ) ) ;
2010-07-30 01:48:02 +04:00
e - > pos + = sizeof ( u32 ) ;
if ( inbounds ( e , ( size_t ) size ) ) {
* blob = e - > pos ;
e - > pos + = size ;
return size ;
}
}
return 0 ;
}
static int unpack_str ( struct aa_ext * e , const char * * string , const char * name )
{
char * src_str ;
size_t size = 0 ;
void * pos = e - > pos ;
* string = NULL ;
if ( unpack_nameX ( e , AA_STRING , name ) ) {
size = unpack_u16_chunk ( e , & src_str ) ;
if ( size ) {
/* strings are null terminated, length is size - 1 */
if ( src_str [ size - 1 ] ! = 0 )
goto fail ;
* string = src_str ;
}
}
return size ;
fail :
e - > pos = pos ;
return 0 ;
}
static int unpack_strdup ( struct aa_ext * e , char * * string , const char * name )
{
const char * tmp ;
void * pos = e - > pos ;
int res = unpack_str ( e , & tmp , name ) ;
* string = NULL ;
if ( ! res )
return 0 ;
* string = kmemdup ( tmp , res , GFP_KERNEL ) ;
if ( ! * string ) {
e - > pos = pos ;
return 0 ;
}
return res ;
}
/**
* unpack_dfa - unpack a file rule dfa
* @ e : serialized data extent information ( NOT NULL )
*
* returns dfa or ERR_PTR or NULL if no dfa
*/
static struct aa_dfa * unpack_dfa ( struct aa_ext * e )
{
char * blob = NULL ;
size_t size ;
struct aa_dfa * dfa = NULL ;
size = unpack_blob ( e , & blob , " aadfa " ) ;
if ( size ) {
/*
* The dfa is aligned with in the blob to 8 bytes
* from the beginning of the stream .
2013-07-11 08:05:43 +04:00
* alignment adjust needed by dfa unpack
2010-07-30 01:48:02 +04:00
*/
2013-07-11 08:05:43 +04:00
size_t sz = blob - ( char * ) e - > start -
( ( e - > pos - e - > start ) & 7 ) ;
2010-07-30 01:48:02 +04:00
size_t pad = ALIGN ( sz , 8 ) - sz ;
int flags = TO_ACCEPT1_FLAG ( YYTD_DATA32 ) |
2017-01-16 11:42:37 +03:00
TO_ACCEPT2_FLAG ( YYTD_DATA32 ) | DFA_FLAG_VERIFY_STATES ;
2010-07-30 01:48:02 +04:00
dfa = aa_dfa_unpack ( blob + pad , size - pad , flags ) ;
if ( IS_ERR ( dfa ) )
return dfa ;
}
return dfa ;
}
/**
* unpack_trans_table - unpack a profile transition table
* @ e : serialized data extent information ( NOT NULL )
* @ profile : profile to add the accept table to ( NOT NULL )
*
2011-03-31 05:57:33 +04:00
* Returns : 1 if table successfully unpacked
2010-07-30 01:48:02 +04:00
*/
static bool unpack_trans_table ( struct aa_ext * e , struct aa_profile * profile )
{
2017-07-06 11:56:21 +03:00
void * saved_pos = e - > pos ;
2010-07-30 01:48:02 +04:00
/* exec table is optional */
if ( unpack_nameX ( e , AA_STRUCT , " xtable " ) ) {
int i , size ;
size = unpack_array ( e , NULL ) ;
/* currently 4 exec bits and entries 0-3 are reserved iupcx */
if ( size > 16 - 4 )
goto fail ;
treewide: kzalloc() -> kcalloc()
The kzalloc() function has a 2-factor argument form, kcalloc(). This
patch replaces cases of:
kzalloc(a * b, gfp)
with:
kcalloc(a * b, gfp)
as well as handling cases of:
kzalloc(a * b * c, gfp)
with:
kzalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kzalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kzalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kzalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kzalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kzalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kzalloc
+ kcalloc
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kzalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kzalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kzalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kzalloc(sizeof(THING) * C2, ...)
|
kzalloc(sizeof(TYPE) * C2, ...)
|
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(C1 * C2, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * E2
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:03:40 +03:00
profile - > file . trans . table = kcalloc ( size , sizeof ( char * ) ,
2010-07-30 01:48:02 +04:00
GFP_KERNEL ) ;
if ( ! profile - > file . trans . table )
goto fail ;
profile - > file . trans . size = size ;
for ( i = 0 ; i < size ; i + + ) {
char * str ;
2017-06-10 03:29:12 +03:00
int c , j , pos , size2 = unpack_strdup ( e , & str , NULL ) ;
2010-07-30 01:48:02 +04:00
/* unpack_strdup verifies that the last character is
* null termination byte .
*/
2011-08-29 05:43:02 +04:00
if ( ! size2 )
2010-07-30 01:48:02 +04:00
goto fail ;
profile - > file . trans . table [ i ] = str ;
/* verify that name doesn't start with space */
if ( isspace ( * str ) )
goto fail ;
/* count internal # of internal \0 */
2017-06-10 03:29:12 +03:00
for ( c = j = 0 ; j < size2 - 1 ; j + + ) {
if ( ! str [ j ] ) {
pos = j ;
2010-07-30 01:48:02 +04:00
c + + ;
2017-06-10 03:29:12 +03:00
}
2010-07-30 01:48:02 +04:00
}
if ( * str = = ' : ' ) {
2017-06-10 03:29:12 +03:00
/* first character after : must be valid */
if ( ! str [ 1 ] )
goto fail ;
2010-07-30 01:48:02 +04:00
/* beginning with : requires an embedded \0,
* verify that exactly 1 internal \ 0 exists
* trailing \ 0 already verified by unpack_strdup
2017-06-10 03:29:12 +03:00
*
* convert \ 0 back to : for label_parse
2010-07-30 01:48:02 +04:00
*/
2017-06-10 03:29:12 +03:00
if ( c = = 1 )
str [ pos ] = ' : ' ;
else if ( c > 1 )
2010-07-30 01:48:02 +04:00
goto fail ;
} else if ( c )
/* fail - all other cases with embedded \0 */
goto fail ;
}
if ( ! unpack_nameX ( e , AA_ARRAYEND , NULL ) )
goto fail ;
if ( ! unpack_nameX ( e , AA_STRUCTEND , NULL ) )
goto fail ;
}
return 1 ;
fail :
aa_free_domain_entries ( & profile - > file . trans ) ;
2017-07-06 11:56:21 +03:00
e - > pos = saved_pos ;
2010-07-30 01:48:02 +04:00
return 0 ;
}
2018-02-08 23:37:19 +03:00
static bool unpack_xattrs ( struct aa_ext * e , struct aa_profile * profile )
{
void * pos = e - > pos ;
if ( unpack_nameX ( e , AA_STRUCT , " xattrs " ) ) {
int i , size ;
size = unpack_array ( e , NULL ) ;
profile - > xattr_count = size ;
2017-12-13 02:28:05 +03:00
profile - > xattrs = kcalloc ( size , sizeof ( char * ) , GFP_KERNEL ) ;
2018-02-08 23:37:19 +03:00
if ( ! profile - > xattrs )
goto fail ;
for ( i = 0 ; i < size ; i + + ) {
if ( ! unpack_strdup ( e , & profile - > xattrs [ i ] , NULL ) )
goto fail ;
}
if ( ! unpack_nameX ( e , AA_ARRAYEND , NULL ) )
goto fail ;
if ( ! unpack_nameX ( e , AA_STRUCTEND , NULL ) )
goto fail ;
}
return 1 ;
fail :
e - > pos = pos ;
return 0 ;
}
2018-05-24 23:27:46 +03:00
static bool unpack_secmark ( struct aa_ext * e , struct aa_profile * profile )
{
void * pos = e - > pos ;
int i , size ;
if ( unpack_nameX ( e , AA_STRUCT , " secmark " ) ) {
size = unpack_array ( e , NULL ) ;
profile - > secmark = kcalloc ( size , sizeof ( struct aa_secmark ) ,
GFP_KERNEL ) ;
if ( ! profile - > secmark )
goto fail ;
profile - > secmark_count = size ;
for ( i = 0 ; i < size ; i + + ) {
if ( ! unpack_u8 ( e , & profile - > secmark [ i ] . audit , NULL ) )
goto fail ;
if ( ! unpack_u8 ( e , & profile - > secmark [ i ] . deny , NULL ) )
goto fail ;
if ( ! unpack_strdup ( e , & profile - > secmark [ i ] . label , NULL ) )
goto fail ;
}
if ( ! unpack_nameX ( e , AA_ARRAYEND , NULL ) )
goto fail ;
if ( ! unpack_nameX ( e , AA_STRUCTEND , NULL ) )
goto fail ;
}
return 1 ;
fail :
if ( profile - > secmark ) {
for ( i = 0 ; i < size ; i + + )
kfree ( profile - > secmark [ i ] . label ) ;
kfree ( profile - > secmark ) ;
profile - > secmark_count = 0 ;
2019-02-12 14:35:40 +03:00
profile - > secmark = NULL ;
2018-05-24 23:27:46 +03:00
}
e - > pos = pos ;
return 0 ;
}
2010-07-30 01:48:02 +04:00
static bool unpack_rlimits ( struct aa_ext * e , struct aa_profile * profile )
{
void * pos = e - > pos ;
/* rlimits are optional */
if ( unpack_nameX ( e , AA_STRUCT , " rlimits " ) ) {
int i , size ;
u32 tmp = 0 ;
if ( ! unpack_u32 ( e , & tmp , NULL ) )
goto fail ;
profile - > rlimits . mask = tmp ;
size = unpack_array ( e , NULL ) ;
if ( size > RLIM_NLIMITS )
goto fail ;
for ( i = 0 ; i < size ; i + + ) {
2011-08-29 05:43:02 +04:00
u64 tmp2 = 0 ;
2010-07-30 01:48:02 +04:00
int a = aa_map_resource ( i ) ;
2011-08-29 05:43:02 +04:00
if ( ! unpack_u64 ( e , & tmp2 , NULL ) )
2010-07-30 01:48:02 +04:00
goto fail ;
2011-08-29 05:43:02 +04:00
profile - > rlimits . limits [ a ] . rlim_max = tmp2 ;
2010-07-30 01:48:02 +04:00
}
if ( ! unpack_nameX ( e , AA_ARRAYEND , NULL ) )
goto fail ;
if ( ! unpack_nameX ( e , AA_STRUCTEND , NULL ) )
goto fail ;
}
return 1 ;
fail :
e - > pos = pos ;
return 0 ;
}
2017-01-16 03:49:28 +03:00
static u32 strhash ( const void * data , u32 len , u32 seed )
{
const char * const * key = data ;
return jhash ( * key , strlen ( * key ) , seed ) ;
}
static int datacmp ( struct rhashtable_compare_arg * arg , const void * obj )
{
const struct aa_data * data = obj ;
const char * const * key = arg - > key ;
return strcmp ( data - > key , * key ) ;
}
2010-07-30 01:48:02 +04:00
/**
* unpack_profile - unpack a serialized profile
* @ e : serialized data extent information ( NOT NULL )
*
* NOTE : unpack profile sets audit struct if there is a failure
*/
2017-01-16 11:42:56 +03:00
static struct aa_profile * unpack_profile ( struct aa_ext * e , char * * ns_name )
2010-07-30 01:48:02 +04:00
{
struct aa_profile * profile = NULL ;
2017-01-16 11:42:56 +03:00
const char * tmpname , * tmpns = NULL , * name = NULL ;
2017-07-19 09:37:18 +03:00
const char * info = " failed to unpack profile " ;
2017-10-26 20:35:35 +03:00
size_t ns_len ;
2017-01-16 03:49:28 +03:00
struct rhashtable_params params = { 0 } ;
char * key = NULL ;
struct aa_data * data ;
2012-02-16 19:07:53 +04:00
int i , error = - EPROTO ;
2010-07-30 01:48:02 +04:00
kernel_cap_t tmpcap ;
u32 tmp ;
2017-01-16 11:42:56 +03:00
* ns_name = NULL ;
2010-07-30 01:48:02 +04:00
/* check that we have the right struct being passed */
if ( ! unpack_nameX ( e , AA_STRUCT , " profile " ) )
goto fail ;
if ( ! unpack_str ( e , & name , NULL ) )
goto fail ;
2017-01-16 11:42:56 +03:00
if ( * name = = ' \0 ' )
goto fail ;
tmpname = aa_splitn_fqname ( name , strlen ( name ) , & tmpns , & ns_len ) ;
if ( tmpns ) {
* ns_name = kstrndup ( tmpns , ns_len , GFP_KERNEL ) ;
2017-07-19 09:37:18 +03:00
if ( ! * ns_name ) {
info = " out of memory " ;
2017-01-16 11:42:56 +03:00
goto fail ;
2017-07-19 09:37:18 +03:00
}
2017-01-16 11:42:56 +03:00
name = tmpname ;
}
2010-07-30 01:48:02 +04:00
2017-06-09 18:14:28 +03:00
profile = aa_alloc_profile ( name , NULL , GFP_KERNEL ) ;
2010-07-30 01:48:02 +04:00
if ( ! profile )
return ERR_PTR ( - ENOMEM ) ;
/* profile renaming is optional */
( void ) unpack_str ( e , & profile - > rename , " rename " ) ;
2013-07-11 08:17:43 +04:00
/* attachment string is optional */
( void ) unpack_str ( e , & profile - > attach , " attach " ) ;
2010-07-30 01:48:02 +04:00
/* xmatch is optional and may be NULL */
profile - > xmatch = unpack_dfa ( e ) ;
if ( IS_ERR ( profile - > xmatch ) ) {
error = PTR_ERR ( profile - > xmatch ) ;
profile - > xmatch = NULL ;
2017-07-19 09:37:18 +03:00
info = " bad xmatch " ;
2010-07-30 01:48:02 +04:00
goto fail ;
}
/* xmatch_len is not optional if xmatch is set */
if ( profile - > xmatch ) {
2017-07-19 09:37:18 +03:00
if ( ! unpack_u32 ( e , & tmp , NULL ) ) {
info = " missing xmatch len " ;
2010-07-30 01:48:02 +04:00
goto fail ;
2017-07-19 09:37:18 +03:00
}
2010-07-30 01:48:02 +04:00
profile - > xmatch_len = tmp ;
}
2017-05-22 13:06:52 +03:00
/* disconnected attachment string is optional */
( void ) unpack_str ( e , & profile - > disconnected , " disconnected " ) ;
2010-07-30 01:48:02 +04:00
/* per profile debug flags (complain, audit) */
2017-07-19 09:37:18 +03:00
if ( ! unpack_nameX ( e , AA_STRUCT , " flags " ) ) {
info = " profile missing flags " ;
2010-07-30 01:48:02 +04:00
goto fail ;
2017-07-19 09:37:18 +03:00
}
info = " failed to unpack profile flags " ;
2010-07-30 01:48:02 +04:00
if ( ! unpack_u32 ( e , & tmp , NULL ) )
goto fail ;
2013-07-11 08:12:43 +04:00
if ( tmp & PACKED_FLAG_HAT )
2017-06-09 18:14:28 +03:00
profile - > label . flags | = FLAG_HAT ;
2010-07-30 01:48:02 +04:00
if ( ! unpack_u32 ( e , & tmp , NULL ) )
goto fail ;
2017-01-16 11:42:38 +03:00
if ( tmp = = PACKED_MODE_COMPLAIN | | ( e - > version & FORCE_COMPLAIN_FLAG ) )
2010-07-30 01:48:02 +04:00
profile - > mode = APPARMOR_COMPLAIN ;
2013-07-11 08:12:43 +04:00
else if ( tmp = = PACKED_MODE_KILL )
profile - > mode = APPARMOR_KILL ;
else if ( tmp = = PACKED_MODE_UNCONFINED )
profile - > mode = APPARMOR_UNCONFINED ;
2010-07-30 01:48:02 +04:00
if ( ! unpack_u32 ( e , & tmp , NULL ) )
goto fail ;
if ( tmp )
profile - > audit = AUDIT_ALL ;
if ( ! unpack_nameX ( e , AA_STRUCTEND , NULL ) )
goto fail ;
/* path_flags is optional */
if ( unpack_u32 ( e , & profile - > path_flags , " path_flags " ) )
2017-06-09 18:14:28 +03:00
profile - > path_flags | = profile - > label . flags &
PATH_MEDIATE_DELETED ;
2010-07-30 01:48:02 +04:00
else
/* set a default value if path_flags field is not present */
2017-06-09 18:14:28 +03:00
profile - > path_flags = PATH_MEDIATE_DELETED ;
2010-07-30 01:48:02 +04:00
2017-07-19 09:37:18 +03:00
info = " failed to unpack profile capabilities " ;
2010-07-30 01:48:02 +04:00
if ( ! unpack_u32 ( e , & ( profile - > caps . allow . cap [ 0 ] ) , NULL ) )
goto fail ;
if ( ! unpack_u32 ( e , & ( profile - > caps . audit . cap [ 0 ] ) , NULL ) )
goto fail ;
if ( ! unpack_u32 ( e , & ( profile - > caps . quiet . cap [ 0 ] ) , NULL ) )
goto fail ;
if ( ! unpack_u32 ( e , & tmpcap . cap [ 0 ] , NULL ) )
goto fail ;
2017-07-19 09:37:18 +03:00
info = " failed to unpack upper profile capabilities " ;
2010-07-30 01:48:02 +04:00
if ( unpack_nameX ( e , AA_STRUCT , " caps64 " ) ) {
/* optional upper half of 64 bit caps */
if ( ! unpack_u32 ( e , & ( profile - > caps . allow . cap [ 1 ] ) , NULL ) )
goto fail ;
if ( ! unpack_u32 ( e , & ( profile - > caps . audit . cap [ 1 ] ) , NULL ) )
goto fail ;
if ( ! unpack_u32 ( e , & ( profile - > caps . quiet . cap [ 1 ] ) , NULL ) )
goto fail ;
if ( ! unpack_u32 ( e , & ( tmpcap . cap [ 1 ] ) , NULL ) )
goto fail ;
if ( ! unpack_nameX ( e , AA_STRUCTEND , NULL ) )
goto fail ;
}
2017-07-19 09:37:18 +03:00
info = " failed to unpack extended profile capabilities " ;
2010-07-30 01:48:02 +04:00
if ( unpack_nameX ( e , AA_STRUCT , " capsx " ) ) {
/* optional extended caps mediation mask */
if ( ! unpack_u32 ( e , & ( profile - > caps . extended . cap [ 0 ] ) , NULL ) )
goto fail ;
if ( ! unpack_u32 ( e , & ( profile - > caps . extended . cap [ 1 ] ) , NULL ) )
goto fail ;
2012-02-16 19:06:41 +04:00
if ( ! unpack_nameX ( e , AA_STRUCTEND , NULL ) )
goto fail ;
2010-07-30 01:48:02 +04:00
}
2018-02-08 23:37:19 +03:00
if ( ! unpack_xattrs ( e , profile ) ) {
info = " failed to unpack profile xattrs " ;
goto fail ;
}
2017-07-19 09:37:18 +03:00
if ( ! unpack_rlimits ( e , profile ) ) {
info = " failed to unpack profile rlimits " ;
2010-07-30 01:48:02 +04:00
goto fail ;
2017-07-19 09:37:18 +03:00
}
2010-07-30 01:48:02 +04:00
2018-05-24 23:27:46 +03:00
if ( ! unpack_secmark ( e , profile ) ) {
info = " failed to unpack profile secmark rules " ;
goto fail ;
}
2012-02-16 19:07:53 +04:00
if ( unpack_nameX ( e , AA_STRUCT , " policydb " ) ) {
/* generic policy dfa - optional and may be NULL */
2017-07-19 09:37:18 +03:00
info = " failed to unpack policydb " ;
2012-02-16 19:07:53 +04:00
profile - > policy . dfa = unpack_dfa ( e ) ;
if ( IS_ERR ( profile - > policy . dfa ) ) {
error = PTR_ERR ( profile - > policy . dfa ) ;
profile - > policy . dfa = NULL ;
goto fail ;
2016-06-15 10:00:55 +03:00
} else if ( ! profile - > policy . dfa ) {
error = - EPROTO ;
goto fail ;
2012-02-16 19:07:53 +04:00
}
if ( ! unpack_u32 ( e , & profile - > policy . start [ 0 ] , " start " ) )
/* default start state */
profile - > policy . start [ 0 ] = DFA_START ;
/* setup class index */
for ( i = AA_CLASS_FILE ; i < = AA_CLASS_LAST ; i + + ) {
profile - > policy . start [ i ] =
aa_dfa_next ( profile - > policy . dfa ,
profile - > policy . start [ 0 ] ,
i ) ;
}
if ( ! unpack_nameX ( e , AA_STRUCTEND , NULL ) )
goto fail ;
2017-01-16 11:42:42 +03:00
} else
profile - > policy . dfa = aa_get_dfa ( nulldfa ) ;
2012-02-16 19:07:53 +04:00
2010-07-30 01:48:02 +04:00
/* get file rules */
profile - > file . dfa = unpack_dfa ( e ) ;
if ( IS_ERR ( profile - > file . dfa ) ) {
error = PTR_ERR ( profile - > file . dfa ) ;
profile - > file . dfa = NULL ;
2017-07-19 09:37:18 +03:00
info = " failed to unpack profile file rules " ;
2010-07-30 01:48:02 +04:00
goto fail ;
2017-01-16 11:42:41 +03:00
} else if ( profile - > file . dfa ) {
if ( ! unpack_u32 ( e , & profile - > file . start , " dfa_start " ) )
/* default start state */
profile - > file . start = DFA_START ;
} else if ( profile - > policy . dfa & &
profile - > policy . start [ AA_CLASS_FILE ] ) {
profile - > file . dfa = aa_get_dfa ( profile - > policy . dfa ) ;
profile - > file . start = profile - > policy . start [ AA_CLASS_FILE ] ;
2017-01-16 11:42:42 +03:00
} else
profile - > file . dfa = aa_get_dfa ( nulldfa ) ;
2010-07-30 01:48:02 +04:00
2017-07-19 09:37:18 +03:00
if ( ! unpack_trans_table ( e , profile ) ) {
info = " failed to unpack profile transition table " ;
2010-07-30 01:48:02 +04:00
goto fail ;
2017-07-19 09:37:18 +03:00
}
2010-07-30 01:48:02 +04:00
2017-01-16 03:49:28 +03:00
if ( unpack_nameX ( e , AA_STRUCT , " data " ) ) {
2017-07-19 09:37:18 +03:00
info = " out of memory " ;
2017-01-16 03:49:28 +03:00
profile - > data = kzalloc ( sizeof ( * profile - > data ) , GFP_KERNEL ) ;
if ( ! profile - > data )
goto fail ;
params . nelem_hint = 3 ;
params . key_len = sizeof ( void * ) ;
params . key_offset = offsetof ( struct aa_data , key ) ;
params . head_offset = offsetof ( struct aa_data , head ) ;
params . hashfn = strhash ;
params . obj_cmpfn = datacmp ;
2017-07-19 09:37:18 +03:00
if ( rhashtable_init ( profile - > data , & params ) ) {
info = " failed to init key, value hash table " ;
2017-01-16 03:49:28 +03:00
goto fail ;
2017-07-19 09:37:18 +03:00
}
2017-01-16 03:49:28 +03:00
while ( unpack_strdup ( e , & key , NULL ) ) {
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data ) {
kzfree ( key ) ;
goto fail ;
}
data - > key = key ;
data - > size = unpack_blob ( e , & data - > data , NULL ) ;
data - > data = kvmemdup ( data - > data , data - > size ) ;
if ( data - > size & & ! data - > data ) {
kzfree ( data - > key ) ;
kzfree ( data ) ;
goto fail ;
}
rhashtable_insert_fast ( profile - > data , & data - > head ,
profile - > data - > p ) ;
}
2017-07-19 09:37:18 +03:00
if ( ! unpack_nameX ( e , AA_STRUCTEND , NULL ) ) {
info = " failed to unpack end of key, value data table " ;
2017-01-16 03:49:28 +03:00
goto fail ;
2017-07-19 09:37:18 +03:00
}
2017-01-16 03:49:28 +03:00
}
2017-07-19 09:37:18 +03:00
if ( ! unpack_nameX ( e , AA_STRUCTEND , NULL ) ) {
info = " failed to unpack end of profile " ;
2010-07-30 01:48:02 +04:00
goto fail ;
2017-07-19 09:37:18 +03:00
}
2010-07-30 01:48:02 +04:00
return profile ;
fail :
if ( profile )
name = NULL ;
else if ( ! name )
name = " unknown " ;
2017-07-19 09:37:18 +03:00
audit_iface ( profile , NULL , name , info , e , error ) ;
2013-07-11 08:11:43 +04:00
aa_free_profile ( profile ) ;
2010-07-30 01:48:02 +04:00
return ERR_PTR ( error ) ;
}
/**
* verify_head - unpack serialized stream header
* @ e : serialized data read head ( NOT NULL )
2013-07-11 08:05:43 +04:00
* @ required : whether the header is required or optional
2010-07-30 01:48:02 +04:00
* @ ns : Returns - namespace if one is specified else NULL ( NOT NULL )
*
* Returns : error or 0 if header is good
*/
2013-07-11 08:05:43 +04:00
static int verify_header ( struct aa_ext * e , int required , const char * * ns )
2010-07-30 01:48:02 +04:00
{
int error = - EPROTONOSUPPORT ;
2013-07-11 08:05:43 +04:00
const char * name = NULL ;
* ns = NULL ;
2010-07-30 01:48:02 +04:00
/* get the interface version */
if ( ! unpack_u32 ( e , & e - > version , " version " ) ) {
2013-07-11 08:05:43 +04:00
if ( required ) {
2017-01-16 11:42:56 +03:00
audit_iface ( NULL , NULL , NULL , " invalid profile format " ,
2013-07-11 08:05:43 +04:00
e , error ) ;
return error ;
}
2010-07-30 01:48:02 +04:00
}
2017-01-16 11:42:39 +03:00
/* Check that the interface version is currently supported.
* if not specified use previous version
* Mask off everything that is not kernel abi version
*/
2017-07-08 22:50:21 +03:00
if ( VERSION_LT ( e - > version , v5 ) | | VERSION_GT ( e - > version , v7 ) ) {
2017-01-16 11:42:56 +03:00
audit_iface ( NULL , NULL , NULL , " unsupported interface version " ,
2017-01-16 11:42:39 +03:00
e , error ) ;
return error ;
}
2013-07-11 08:05:43 +04:00
2010-07-30 01:48:02 +04:00
/* read the namespace if present */
2013-07-11 08:05:43 +04:00
if ( unpack_str ( e , & name , " namespace " ) ) {
2017-01-16 11:42:56 +03:00
if ( * name = = ' \0 ' ) {
audit_iface ( NULL , NULL , NULL , " invalid namespace name " ,
e , error ) ;
return error ;
}
2013-07-11 08:05:43 +04:00
if ( * ns & & strcmp ( * ns , name ) )
2017-01-16 11:42:56 +03:00
audit_iface ( NULL , NULL , NULL , " invalid ns change " , e ,
error ) ;
2013-07-11 08:05:43 +04:00
else if ( ! * ns )
* ns = name ;
}
2010-07-30 01:48:02 +04:00
return 0 ;
}
static bool verify_xindex ( int xindex , int table_size )
{
int index , xtype ;
xtype = xindex & AA_X_TYPE_MASK ;
index = xindex & AA_X_INDEX_MASK ;
2016-03-17 22:02:54 +03:00
if ( xtype = = AA_X_TABLE & & index > = table_size )
2010-07-30 01:48:02 +04:00
return 0 ;
return 1 ;
}
/* verify dfa xindexes are in range of transition tables */
static bool verify_dfa_xindex ( struct aa_dfa * dfa , int table_size )
{
int i ;
for ( i = 0 ; i < dfa - > tables [ YYTD_ID_ACCEPT ] - > td_lolen ; i + + ) {
if ( ! verify_xindex ( dfa_user_xindex ( dfa , i ) , table_size ) )
return 0 ;
if ( ! verify_xindex ( dfa_other_xindex ( dfa , i ) , table_size ) )
return 0 ;
}
return 1 ;
}
/**
* verify_profile - Do post unpack analysis to verify profile consistency
* @ profile : profile to verify ( NOT NULL )
*
* Returns : 0 if passes verification else error
*/
static int verify_profile ( struct aa_profile * profile )
{
2017-01-16 11:42:37 +03:00
if ( profile - > file . dfa & &
! verify_dfa_xindex ( profile - > file . dfa ,
profile - > file . trans . size ) ) {
2017-01-16 11:42:56 +03:00
audit_iface ( profile , NULL , NULL , " Invalid named transition " ,
2017-01-16 11:42:37 +03:00
NULL , - EPROTO ) ;
return - EPROTO ;
2010-07-30 01:48:02 +04:00
}
return 0 ;
}
2013-07-11 08:05:43 +04:00
void aa_load_ent_free ( struct aa_load_ent * ent )
{
if ( ent ) {
aa_put_profile ( ent - > rename ) ;
aa_put_profile ( ent - > old ) ;
aa_put_profile ( ent - > new ) ;
2017-01-16 11:42:56 +03:00
kfree ( ent - > ns_name ) ;
2013-07-11 08:05:43 +04:00
kzfree ( ent ) ;
}
}
struct aa_load_ent * aa_load_ent_alloc ( void )
{
struct aa_load_ent * ent = kzalloc ( sizeof ( * ent ) , GFP_KERNEL ) ;
if ( ent )
INIT_LIST_HEAD ( & ent - > list ) ;
return ent ;
}
2010-07-30 01:48:02 +04:00
/**
2013-07-11 08:05:43 +04:00
* aa_unpack - unpack packed binary profile ( s ) data loaded from user space
2010-07-30 01:48:02 +04:00
* @ udata : user data copied to kmem ( NOT NULL )
2013-07-11 08:05:43 +04:00
* @ lh : list to place unpacked profiles in a aa_repl_ws
2010-07-30 01:48:02 +04:00
* @ ns : Returns namespace profile is in if specified else NULL ( NOT NULL )
*
2013-07-11 08:05:43 +04:00
* Unpack user data and return refcounted allocated profile ( s ) stored in
* @ lh in order of discovery , with the list chain stored in base . list
* or error
2010-07-30 01:48:02 +04:00
*
2013-07-11 08:05:43 +04:00
* Returns : profile ( s ) on @ lh else error pointer if fails to unpack
2010-07-30 01:48:02 +04:00
*/
2017-01-16 11:42:55 +03:00
int aa_unpack ( struct aa_loaddata * udata , struct list_head * lh ,
const char * * ns )
2010-07-30 01:48:02 +04:00
{
2013-07-11 08:05:43 +04:00
struct aa_load_ent * tmp , * ent ;
2010-07-30 01:48:02 +04:00
struct aa_profile * profile = NULL ;
int error ;
struct aa_ext e = {
2017-01-16 11:42:55 +03:00
. start = udata - > data ,
. end = udata - > data + udata - > size ,
. pos = udata - > data ,
2010-07-30 01:48:02 +04:00
} ;
2013-07-11 08:05:43 +04:00
* ns = NULL ;
while ( e . pos < e . end ) {
2017-01-16 11:42:56 +03:00
char * ns_name = NULL ;
2013-08-14 22:27:36 +04:00
void * start ;
2013-07-11 08:05:43 +04:00
error = verify_header ( & e , e . pos = = e . start , ns ) ;
if ( error )
goto fail ;
2010-07-30 01:48:02 +04:00
2013-08-14 22:27:36 +04:00
start = e . pos ;
2017-01-16 11:42:56 +03:00
profile = unpack_profile ( & e , & ns_name ) ;
2013-07-11 08:05:43 +04:00
if ( IS_ERR ( profile ) ) {
error = PTR_ERR ( profile ) ;
goto fail ;
}
error = verify_profile ( profile ) ;
2013-08-14 22:27:36 +04:00
if ( error )
goto fail_profile ;
2017-01-16 11:43:07 +03:00
if ( aa_g_hash_policy )
error = aa_calc_profile_hash ( profile , e . version , start ,
2014-10-24 20:16:14 +04:00
e . pos - start ) ;
2013-08-14 22:27:36 +04:00
if ( error )
goto fail_profile ;
2013-07-11 08:05:43 +04:00
ent = aa_load_ent_alloc ( ) ;
if ( ! ent ) {
error = - ENOMEM ;
2013-08-14 22:27:36 +04:00
goto fail_profile ;
2013-07-11 08:05:43 +04:00
}
2010-07-30 01:48:02 +04:00
2013-07-11 08:05:43 +04:00
ent - > new = profile ;
2017-01-16 11:42:56 +03:00
ent - > ns_name = ns_name ;
2013-07-11 08:05:43 +04:00
list_add_tail ( & ent - > list , lh ) ;
2010-07-30 01:48:02 +04:00
}
2017-01-16 11:42:55 +03:00
udata - > abi = e . version & K_ABI_MASK ;
2017-01-16 11:43:07 +03:00
if ( aa_g_hash_policy ) {
udata - > hash = aa_calc_hash ( udata - > data , udata - > size ) ;
if ( IS_ERR ( udata - > hash ) ) {
error = PTR_ERR ( udata - > hash ) ;
udata - > hash = NULL ;
goto fail ;
}
2017-01-16 11:42:55 +03:00
}
2013-07-11 08:05:43 +04:00
return 0 ;
2013-08-14 22:27:36 +04:00
fail_profile :
aa_put_profile ( profile ) ;
2013-07-11 08:05:43 +04:00
fail :
list_for_each_entry_safe ( ent , tmp , lh , list ) {
list_del_init ( & ent - > list ) ;
aa_load_ent_free ( ent ) ;
}
return error ;
2010-07-30 01:48:02 +04:00
}