diff options
Diffstat (limited to 'security')
38 files changed, 946 insertions, 279 deletions
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index d8b1a360a636..a422a349f926 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -6,6 +6,8 @@ config SECURITY_APPARMOR select SECURITY_PATH select SECURITYFS select SECURITY_NETWORK + select ZLIB_INFLATE + select ZLIB_DEFLATE default n help This enables the AppArmor security module. diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 45d13b6462aa..09996f2552ee 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -21,6 +21,7 @@ #include <linux/fs.h> #include <linux/fs_context.h> #include <linux/poll.h> +#include <linux/zlib.h> #include <uapi/linux/major.h> #include <uapi/linux/magic.h> @@ -65,6 +66,35 @@ * support fns */ +struct rawdata_f_data { + struct aa_loaddata *loaddata; +}; + +#define RAWDATA_F_DATA_BUF(p) (char *)(p + 1) + +static void rawdata_f_data_free(struct rawdata_f_data *private) +{ + if (!private) + return; + + aa_put_loaddata(private->loaddata); + kvfree(private); +} + +static struct rawdata_f_data *rawdata_f_data_alloc(size_t size) +{ + struct rawdata_f_data *ret; + + if (size > SIZE_MAX - sizeof(*ret)) + return ERR_PTR(-EINVAL); + + ret = kvzalloc(sizeof(*ret) + size, GFP_KERNEL); + if (!ret) + return ERR_PTR(-ENOMEM); + + return ret; +} + /** * aa_mangle_name - mangle a profile name to std profile layout form * @name: profile name to mangle (NOT NULL) @@ -1280,36 +1310,117 @@ static int seq_rawdata_hash_show(struct seq_file *seq, void *v) return 0; } +static int seq_rawdata_compressed_size_show(struct seq_file *seq, void *v) +{ + struct aa_loaddata *data = seq->private; + + seq_printf(seq, "%zu\n", data->compressed_size); + + return 0; +} + SEQ_RAWDATA_FOPS(abi); SEQ_RAWDATA_FOPS(revision); SEQ_RAWDATA_FOPS(hash); +SEQ_RAWDATA_FOPS(compressed_size); + +static int deflate_decompress(char *src, size_t slen, char *dst, size_t dlen) +{ + int error; + struct z_stream_s strm; + + if (aa_g_rawdata_compression_level == 0) { + if (dlen < slen) + return -EINVAL; + memcpy(dst, src, slen); + return 0; + } + + memset(&strm, 0, sizeof(strm)); + + strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL); + if (!strm.workspace) + return -ENOMEM; + + strm.next_in = src; + strm.avail_in = slen; + + error = zlib_inflateInit(&strm); + if (error != Z_OK) { + error = -ENOMEM; + goto fail_inflate_init; + } + + strm.next_out = dst; + strm.avail_out = dlen; + + error = zlib_inflate(&strm, Z_FINISH); + if (error != Z_STREAM_END) + error = -EINVAL; + else + error = 0; + + zlib_inflateEnd(&strm); +fail_inflate_init: + kvfree(strm.workspace); + return error; +} static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { - struct aa_loaddata *rawdata = file->private_data; + struct rawdata_f_data *private = file->private_data; - return simple_read_from_buffer(buf, size, ppos, rawdata->data, - rawdata->size); + return simple_read_from_buffer(buf, size, ppos, + RAWDATA_F_DATA_BUF(private), + private->loaddata->size); } static int rawdata_release(struct inode *inode, struct file *file) { - aa_put_loaddata(file->private_data); + rawdata_f_data_free(file->private_data); return 0; } static int rawdata_open(struct inode *inode, struct file *file) { + int error; + struct aa_loaddata *loaddata; + struct rawdata_f_data *private; + if (!policy_view_capable(NULL)) return -EACCES; - file->private_data = __aa_get_loaddata(inode->i_private); - if (!file->private_data) + + loaddata = __aa_get_loaddata(inode->i_private); + if (!loaddata) /* lost race: this entry is being reaped */ return -ENOENT; + private = rawdata_f_data_alloc(loaddata->size); + if (IS_ERR(private)) { + error = PTR_ERR(private); + goto fail_private_alloc; + } + + private->loaddata = loaddata; + + error = deflate_decompress(loaddata->data, loaddata->compressed_size, + RAWDATA_F_DATA_BUF(private), + loaddata->size); + if (error) + goto fail_decompress; + + file->private_data = private; return 0; + +fail_decompress: + rawdata_f_data_free(private); + return error; + +fail_private_alloc: + aa_put_loaddata(loaddata); + return error; } static const struct file_operations rawdata_fops = { @@ -1388,6 +1499,13 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata) rawdata->dents[AAFS_LOADDATA_HASH] = dent; } + dent = aafs_create_file("compressed_size", S_IFREG | 0444, dir, + rawdata, + &seq_rawdata_compressed_size_fops); + if (IS_ERR(dent)) + goto fail; + rawdata->dents[AAFS_LOADDATA_COMPRESSED_SIZE] = dent; + dent = aafs_create_file("raw_data", S_IFREG | 0444, dir, rawdata, &rawdata_fops); if (IS_ERR(dent)) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 9e0492795267..9be7ccb8379e 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -520,7 +520,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, label = &new_profile->label; continue; } - label = aa_label_parse(&profile->label, *name, GFP_ATOMIC, + label = aa_label_parse(&profile->label, *name, GFP_KERNEL, true, false); if (IS_ERR(label)) label = NULL; @@ -600,7 +600,7 @@ static struct aa_label *x_to_label(struct aa_profile *profile, /* base the stack on post domain transition */ struct aa_label *base = new; - new = aa_label_parse(base, stack, GFP_ATOMIC, true, false); + new = aa_label_parse(base, stack, GFP_KERNEL, true, false); if (IS_ERR(new)) new = NULL; aa_put_label(base); @@ -685,20 +685,9 @@ static struct aa_label *profile_transition(struct aa_profile *profile, } else if (COMPLAIN_MODE(profile)) { /* no exec permission - learning mode */ struct aa_profile *new_profile = NULL; - char *n = kstrdup(name, GFP_ATOMIC); - - if (n) { - /* name is ptr into buffer */ - long pos = name - buffer; - /* break per cpu buffer hold */ - put_buffers(buffer); - new_profile = aa_new_null_profile(profile, false, n, - GFP_KERNEL); - get_buffers(buffer); - name = buffer + pos; - strcpy((char *)name, n); - kfree(n); - } + + new_profile = aa_new_null_profile(profile, false, name, + GFP_KERNEL); if (!new_profile) { error = -ENOMEM; info = "could not create null profile"; @@ -719,7 +708,7 @@ static struct aa_label *profile_transition(struct aa_profile *profile, if (DEBUG_ON) { dbg_printk("apparmor: scrubbing environment variables" " for %s profile=", name); - aa_label_printk(new, GFP_ATOMIC); + aa_label_printk(new, GFP_KERNEL); dbg_printk("\n"); } *secure_exec = true; @@ -795,7 +784,7 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec, if (DEBUG_ON) { dbg_printk("apparmor: scrubbing environment " "variables for %s label=", xname); - aa_label_printk(onexec, GFP_ATOMIC); + aa_label_printk(onexec, GFP_KERNEL); dbg_printk("\n"); } *secure_exec = true; @@ -829,7 +818,7 @@ static struct aa_label *handle_onexec(struct aa_label *label, bprm, buffer, cond, unsafe)); if (error) return ERR_PTR(error); - new = fn_label_build_in_ns(label, profile, GFP_ATOMIC, + new = fn_label_build_in_ns(label, profile, GFP_KERNEL, aa_get_newest_label(onexec), profile_transition(profile, bprm, buffer, cond, unsafe)); @@ -841,9 +830,9 @@ static struct aa_label *handle_onexec(struct aa_label *label, buffer, cond, unsafe)); if (error) return ERR_PTR(error); - new = fn_label_build_in_ns(label, profile, GFP_ATOMIC, + new = fn_label_build_in_ns(label, profile, GFP_KERNEL, aa_label_merge(&profile->label, onexec, - GFP_ATOMIC), + GFP_KERNEL), profile_transition(profile, bprm, buffer, cond, unsafe)); } @@ -903,13 +892,18 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) ctx->nnp = aa_get_label(label); /* buffer freed below, name is pointer into buffer */ - get_buffers(buffer); + buffer = aa_get_buffer(false); + if (!buffer) { + error = -ENOMEM; + goto done; + } + /* Test for onexec first as onexec override other x transitions. */ if (ctx->onexec) new = handle_onexec(label, ctx->onexec, ctx->token, bprm, buffer, &cond, &unsafe); else - new = fn_label_build(label, profile, GFP_ATOMIC, + new = fn_label_build(label, profile, GFP_KERNEL, profile_transition(profile, bprm, buffer, &cond, &unsafe)); @@ -953,7 +947,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (DEBUG_ON) { dbg_printk("scrubbing environment variables for %s " "label=", bprm->filename); - aa_label_printk(new, GFP_ATOMIC); + aa_label_printk(new, GFP_KERNEL); dbg_printk("\n"); } bprm->secureexec = 1; @@ -964,7 +958,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (DEBUG_ON) { dbg_printk("apparmor: clearing unsafe personality " "bits. %s label=", bprm->filename); - aa_label_printk(new, GFP_ATOMIC); + aa_label_printk(new, GFP_KERNEL); dbg_printk("\n"); } bprm->per_clear |= PER_CLEAR_ON_SETID; @@ -975,7 +969,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) done: aa_put_label(label); - put_buffers(buffer); + aa_put_buffer(buffer); return error; diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 4c1b05eb130c..fe2ebe5e865e 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -76,7 +76,7 @@ static void file_audit_cb(struct audit_buffer *ab, void *va) if (aad(sa)->peer) { audit_log_format(ab, " target="); aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, - FLAG_VIEW_SUBNS, GFP_ATOMIC); + FLAG_VIEW_SUBNS, GFP_KERNEL); } else if (aad(sa)->fs.target) { audit_log_format(ab, " target="); audit_log_untrustedstring(ab, aad(sa)->fs.target); @@ -332,12 +332,14 @@ int aa_path_perm(const char *op, struct aa_label *label, flags |= PATH_DELEGATE_DELETED | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0); - get_buffers(buffer); + buffer = aa_get_buffer(false); + if (!buffer) + return -ENOMEM; error = fn_for_each_confined(label, profile, profile_path_perm(op, profile, path, buffer, request, cond, flags, &perms)); - put_buffers(buffer); + aa_put_buffer(buffer); return error; } @@ -475,12 +477,18 @@ int aa_path_link(struct aa_label *label, struct dentry *old_dentry, int error; /* buffer freed below, lname is pointer in buffer */ - get_buffers(buffer, buffer2); + buffer = aa_get_buffer(false); + buffer2 = aa_get_buffer(false); + error = -ENOMEM; + if (!buffer || !buffer2) + goto out; + error = fn_for_each_confined(label, profile, profile_path_link(profile, &link, buffer, &target, buffer2, &cond)); - put_buffers(buffer, buffer2); - +out: + aa_put_buffer(buffer); + aa_put_buffer(buffer2); return error; } @@ -507,7 +515,7 @@ static void update_file_ctx(struct aa_file_ctx *fctx, struct aa_label *label, static int __file_path_perm(const char *op, struct aa_label *label, struct aa_label *flabel, struct file *file, - u32 request, u32 denied) + u32 request, u32 denied, bool in_atomic) { struct aa_profile *profile; struct aa_perms perms = {}; @@ -524,7 +532,9 @@ static int __file_path_perm(const char *op, struct aa_label *label, return 0; flags = PATH_DELEGATE_DELETED | (S_ISDIR(cond.mode) ? PATH_IS_DIR : 0); - get_buffers(buffer); + buffer = aa_get_buffer(in_atomic); + if (!buffer) + return -ENOMEM; /* check every profile in task label not in current cache */ error = fn_for_each_not_in_set(flabel, label, profile, @@ -553,7 +563,7 @@ static int __file_path_perm(const char *op, struct aa_label *label, if (!error) update_file_ctx(file_ctx(file), label, request); - put_buffers(buffer); + aa_put_buffer(buffer); return error; } @@ -590,11 +600,12 @@ static int __file_sock_perm(const char *op, struct aa_label *label, * @label: label being enforced (NOT NULL) * @file: file to revalidate access permissions on (NOT NULL) * @request: requested permissions + * @in_atomic: whether allocations need to be done in atomic context * * Returns: %0 if access allowed else error */ int aa_file_perm(const char *op, struct aa_label *label, struct file *file, - u32 request) + u32 request, bool in_atomic) { struct aa_file_ctx *fctx; struct aa_label *flabel; @@ -607,7 +618,8 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file, fctx = file_ctx(file); rcu_read_lock(); - flabel = rcu_dereference(fctx->label); + flabel = aa_get_newest_label(rcu_dereference(fctx->label)); + rcu_read_unlock(); AA_BUG(!flabel); /* revalidate access, if task is unconfined, or the cached cred @@ -626,14 +638,13 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file, if (file->f_path.mnt && path_mediated_fs(file->f_path.dentry)) error = __file_path_perm(op, label, flabel, file, request, - denied); + denied, in_atomic); else if (S_ISSOCK(file_inode(file)->i_mode)) error = __file_sock_perm(op, label, flabel, file, request, denied); done: - rcu_read_unlock(); - + aa_put_label(flabel); return error; } @@ -655,7 +666,8 @@ static void revalidate_tty(struct aa_label *label) struct tty_file_private, list); file = file_priv->file; - if (aa_file_perm(OP_INHERIT, label, file, MAY_READ | MAY_WRITE)) + if (aa_file_perm(OP_INHERIT, label, file, MAY_READ | MAY_WRITE, + IN_ATOMIC)) drop_tty = 1; } spin_unlock(&tty->files_lock); @@ -669,7 +681,8 @@ static int match_file(const void *p, struct file *file, unsigned int fd) { struct aa_label *label = (struct aa_label *)p; - if (aa_file_perm(OP_INHERIT, label, file, aa_map_file_to_perms(file))) + if (aa_file_perm(OP_INHERIT, label, file, aa_map_file_to_perms(file), + IN_ATOMIC)) return fd + 1; return 0; } diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 6b7e6e13176e..1fbabdb565a8 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -36,6 +36,7 @@ extern enum audit_mode aa_g_audit; extern bool aa_g_audit_header; extern bool aa_g_debug; extern bool aa_g_hash_policy; +extern int aa_g_rawdata_compression_level; extern bool aa_g_lock_policy; extern bool aa_g_logsyscall; extern bool aa_g_paranoid_load; diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index a852be89a7dc..aff26fc71407 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -197,7 +197,7 @@ int aa_path_link(struct aa_label *label, struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry); int aa_file_perm(const char *op, struct aa_label *label, struct file *file, - u32 request); + u32 request, bool in_atomic); void aa_inherit_files(const struct cred *cred, struct files_struct *files); diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index 6b0af638a18d..e23f4aadc1ff 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -134,7 +134,7 @@ unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start, void aa_dfa_free_kref(struct kref *kref); -#define WB_HISTORY_SIZE 8 +#define WB_HISTORY_SIZE 24 struct match_workbuf { unsigned int count; unsigned int pos; @@ -147,7 +147,6 @@ struct match_workbuf N = { \ .count = 0, \ .pos = 0, \ .len = 0, \ - .size = WB_HISTORY_SIZE, \ } unsigned int aa_dfa_leftmatch(struct aa_dfa *dfa, unsigned int start, diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 35a8295e8f3a..44a7945fbe3c 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -11,7 +11,6 @@ #ifndef __AA_PATH_H #define __AA_PATH_H - enum path_flags { PATH_IS_DIR = 0x1, /* path is a directory */ PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ @@ -26,51 +25,8 @@ int aa_path_name(const struct path *path, int flags, char *buffer, const char **name, const char **info, const char *disconnected); -#define MAX_PATH_BUFFERS 2 - -/* Per cpu buffers used during mediation */ -/* preallocated buffers to use during path lookups */ -struct aa_buffers { - char *buf[MAX_PATH_BUFFERS]; -}; - -#include <linux/percpu.h> -#include <linux/preempt.h> - -DECLARE_PER_CPU(struct aa_buffers, aa_buffers); - -#define ASSIGN(FN, A, X, N) ((X) = FN(A, N)) -#define EVAL1(FN, A, X) ASSIGN(FN, A, X, 0) /*X = FN(0)*/ -#define EVAL2(FN, A, X, Y...) \ - do { ASSIGN(FN, A, X, 1); EVAL1(FN, A, Y); } while (0) -#define EVAL(FN, A, X...) CONCATENATE(EVAL, COUNT_ARGS(X))(FN, A, X) - -#define for_each_cpu_buffer(I) for ((I) = 0; (I) < MAX_PATH_BUFFERS; (I)++) - -#ifdef CONFIG_DEBUG_PREEMPT -#define AA_BUG_PREEMPT_ENABLED(X) AA_BUG(preempt_count() <= 0, X) -#else -#define AA_BUG_PREEMPT_ENABLED(X) /* nop */ -#endif - -#define __get_buffer(C, N) ({ \ - AA_BUG_PREEMPT_ENABLED("__get_buffer without preempt disabled"); \ - (C)->buf[(N)]; }) - -#define __get_buffers(C, X...) EVAL(__get_buffer, C, X) - -#define __put_buffers(X, Y...) ((void)&(X)) - -#define get_buffers(X...) \ -do { \ - struct aa_buffers *__cpu_var = get_cpu_ptr(&aa_buffers); \ - __get_buffers(__cpu_var, X); \ -} while (0) - -#define put_buffers(X, Y...) \ -do { \ - __put_buffers(X, Y); \ - put_cpu_ptr(&aa_buffers); \ -} while (0) +#define IN_ATOMIC true +char *aa_get_buffer(bool in_atomic); +void aa_put_buffer(char *buf); #endif /* __AA_PATH_H */ diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index 46aefae918f5..e0e1ca7ebc38 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -41,6 +41,7 @@ enum { AAFS_LOADDATA_REVISION, AAFS_LOADDATA_HASH, AAFS_LOADDATA_DATA, + AAFS_LOADDATA_COMPRESSED_SIZE, AAFS_LOADDATA_DIR, /* must be last actual entry */ AAFS_LOADDATA_NDENTS /* count of entries */ }; @@ -61,11 +62,16 @@ struct aa_loaddata { struct dentry *dents[AAFS_LOADDATA_NDENTS]; struct aa_ns *ns; char *name; - size_t size; + size_t size; /* the original size of the payload */ + size_t compressed_size; /* the compressed size of the payload */ long revision; /* the ns policy revision this caused */ int abi; unsigned char *hash; + /* Pointer to payload. If @compressed_size > 0, then this is the + * compressed version of the payload, else it is the uncompressed + * version (with the size indicated by @size). + */ char *data; }; diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 59f1cc2557a7..470693239e64 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -1458,11 +1458,13 @@ static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label, /* helper macro for snprint routines */ #define update_for_len(total, len, size, str) \ do { \ + size_t ulen = len; \ + \ AA_BUG(len < 0); \ - total += len; \ - len = min(len, size); \ - size -= len; \ - str += len; \ + total += ulen; \ + ulen = min(ulen, size); \ + size -= ulen; \ + str += ulen; \ } while (0) /** @@ -1597,7 +1599,7 @@ int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns, struct aa_ns *prev_ns = NULL; struct label_it i; int count = 0, total = 0; - size_t len; + ssize_t len; AA_BUG(!str && size != 0); AA_BUG(!label); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index ec3a928af829..b621ad74f54a 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -21,6 +21,7 @@ #include <linux/user_namespace.h> #include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv6.h> +#include <linux/zlib.h> #include <net/sock.h> #include <uapi/linux/mount.h> @@ -43,8 +44,17 @@ /* Flag indicating whether initialization completed */ int apparmor_initialized; -DEFINE_PER_CPU(struct aa_buffers, aa_buffers); +union aa_buffer { + struct list_head list; + char buffer[1]; +}; + +#define RESERVE_COUNT 2 +static int reserve_count = RESERVE_COUNT; +static int buffer_count; +static LIST_HEAD(aa_global_buffers); +static DEFINE_SPINLOCK(aa_buffers_lock); /* * LSM hook functions @@ -442,7 +452,8 @@ static void apparmor_file_free_security(struct file *file) aa_put_label(rcu_access_pointer(ctx->label)); } -static int common_file_perm(const char *op, struct file *file, u32 mask) +static int common_file_perm(const char *op, struct file *file, u32 mask, + bool in_atomic) { struct aa_label *label; int error = 0; @@ -452,7 +463,7 @@ static int common_file_perm(const char *op, struct file *file, u32 mask) return -EACCES; label = __begin_current_label_crit_section(); - error = aa_file_perm(op, label, file, mask); + error = aa_file_perm(op, label, file, mask, in_atomic); __end_current_label_crit_section(label); return error; @@ -460,12 +471,13 @@ static int common_file_perm(const char *op, struct file *file, u32 mask) static int apparmor_file_receive(struct file *file) { - return common_file_perm(OP_FRECEIVE, file, aa_map_file_to_perms(file)); + return common_file_perm(OP_FRECEIVE, file, aa_map_file_to_perms(file), + false); } static int apparmor_file_permission(struct file *file, int mask) { - return common_file_perm(OP_FPERM, file, mask); + return common_file_perm(OP_FPERM, file, mask, false); } static int apparmor_file_lock(struct file *file, unsigned int cmd) @@ -475,11 +487,11 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd) if (cmd == F_WRLCK) mask |= MAY_WRITE; - return common_file_perm(OP_FLOCK, file, mask); + return common_file_perm(OP_FLOCK, file, mask, false); } static int common_mmap(const char *op, struct file *file, unsigned long prot, - unsigned long flags) + unsigned long flags, bool in_atomic) { int mask = 0; @@ -497,20 +509,21 @@ static int common_mmap(const char *op, struct file *file, unsigned long prot, if (prot & PROT_EXEC) mask |= AA_EXEC_MMAP; - return common_file_perm(op, file, mask); + return common_file_perm(op, file, mask, in_atomic); } static int apparmor_mmap_file(struct file *file, unsigned long reqprot, unsigned long prot, unsigned long flags) { - return common_mmap(OP_FMMAP, file, prot, flags); + return common_mmap(OP_FMMAP, file, prot, flags, GFP_ATOMIC); } static int apparmor_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot) { return common_mmap(OP_FMPROT, vma->vm_file, prot, - !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); + !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0, + false); } static int apparmor_sb_mount(const char *dev_name, const struct path *path, @@ -1262,6 +1275,16 @@ static const struct kernel_param_ops param_ops_aauint = { .get = param_get_aauint }; +static int param_set_aacompressionlevel(const char *val, + const struct kernel_param *kp); +static int param_get_aacompressionlevel(char *buffer, + const struct kernel_param *kp); +#define param_check_aacompressionlevel param_check_int +static const struct kernel_param_ops param_ops_aacompressionlevel = { + .set = param_set_aacompressionlevel, + .get = param_get_aacompressionlevel +}; + static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp); static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp); #define param_check_aalockpolicy param_check_bool @@ -1292,6 +1315,11 @@ bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT); module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR); #endif +/* policy loaddata compression level */ +int aa_g_rawdata_compression_level = Z_DEFAULT_COMPRESSION; +module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level, + aacompressionlevel, 0400); + /* Debug mode */ bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_APPARMOR_DEBUG_MESSAGES); module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); @@ -1402,6 +1430,7 @@ static int param_set_aauint(const char *val, const struct kernel_param *kp) return -EPERM; error = param_set_uint(val, kp); + aa_g_path_max = max_t(uint32_t, aa_g_path_max, sizeof(union aa_buffer)); pr_info("AppArmor: buffer size set to %d bytes\n", aa_g_path_max); return error; @@ -1456,6 +1485,37 @@ static int param_get_aaintbool(char *buffer, const struct kernel_param *kp) return param_get_bool(buffer, &kp_local); } +static int param_set_aacompressionlevel(const char *val, + const struct kernel_param *kp) +{ + int error; + + if (!apparmor_enabled) + return -EINVAL; + if (apparmor_initialized) + return -EPERM; + + error = param_set_int(val, kp); + + aa_g_rawdata_compression_level = clamp(aa_g_rawdata_compression_level, + Z_NO_COMPRESSION, + Z_BEST_COMPRESSION); + pr_info("AppArmor: policy rawdata compression level set to %u\n", + aa_g_rawdata_compression_level); + + return error; +} + +static int param_get_aacompressionlevel(char *buffer, + const struct kernel_param *kp) +{ + if (!apparmor_enabled) + return -EINVAL; + if (apparmor_initialized && !policy_view_capable(NULL)) + return -EPERM; + return param_get_int(buffer, kp); +} + static int param_get_audit(char *buffer, const struct kernel_param *kp) { if (!apparmor_enabled) @@ -1514,6 +1574,61 @@ static int param_set_mode(const char *val, const struct kernel_param *kp) return 0; } +char *aa_get_buffer(bool in_atomic) +{ + union aa_buffer *aa_buf; + bool try_again = true; + gfp_t flags = (GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_NOWARN); + +retry: + spin_lock(&aa_buffers_lock); + if (buffer_count > reserve_count || + (in_atomic && !list_empty(&aa_global_buffers))) { + aa_buf = list_first_entry(&aa_global_buffers, union aa_buffer, + list); + list_del(&aa_buf->list); + buffer_count--; + spin_unlock(&aa_buffers_lock); + return &aa_buf->buffer[0]; + } + if (in_atomic) { + /* + * out of reserve buffers and in atomic context so increase + * how many buffers to keep in reserve + */ + reserve_count++; + flags = GFP_ATOMIC; + } + spin_unlock(&aa_buffers_lock); + + if (!in_atomic) + might_sleep(); + aa_buf = kmalloc(aa_g_path_max, flags); + if (!aa_buf) { + if (try_again) { + try_again = false; + goto retry; + } + pr_warn_once("AppArmor: Failed to allocate a memory buffer.\n"); + return NULL; + } + return &aa_buf->buffer[0]; +} + +void aa_put_buffer(char *buf) +{ + union aa_buffer *aa_buf; + + if (!buf) + return; + aa_buf = container_of(buf, union aa_buffer, buffer[0]); + + spin_lock(&aa_buffers_lock); + list_add(&aa_buf->list, &aa_global_buffers); + buffer_count++; + spin_unlock(&aa_buffers_lock); +} + /* * AppArmor init functions */ @@ -1525,7 +1640,7 @@ static int param_set_mode(const char *val, const struct kernel_param *kp) */ static int __init set_init_ctx(void) { - struct cred *cred = (struct cred *)current->real_cred; + struct cred *cred = (__force struct cred *)current->real_cred; set_cred_label(cred, aa_get_label(ns_unconfined(root_ns))); @@ -1534,38 +1649,48 @@ static int __init set_init_ctx(void) static void destroy_buffers(void) { - u32 i, j; + union aa_buffer *aa_buf; - for_each_possible_cpu(i) { - for_each_cpu_buffer(j) { - kfree(per_cpu(aa_buffers, i).buf[j]); - per_cpu(aa_buffers, i).buf[j] = NULL; - } + spin_lock(&aa_buffers_lock); + while (!list_empty(&aa_global_buffers)) { + aa_buf = list_first_entry(&aa_global_buffers, union aa_buffer, + list); + list_del(&aa_buf->list); + spin_unlock(&aa_buffers_lock); + kfree(aa_buf); + spin_lock(&aa_buffers_lock); } + spin_unlock(&aa_buffers_lock); } static int __init alloc_buffers(void) { - u32 i, j; - - for_each_possible_cpu(i) { - for_each_cpu_buffer(j) { - char *buffer; - - if (cpu_to_node(i) > num_online_nodes()) - /* fallback to kmalloc for offline nodes */ - buffer = kmalloc(aa_g_path_max, GFP_KERNEL); - else - buffer = kmalloc_node(aa_g_path_max, GFP_KERNEL, - cpu_to_node(i)); - if (!buffer) { - destroy_buffers(); - return -ENOMEM; - } - per_cpu(aa_buffers, i).buf[j] = buffer; + union aa_buffer *aa_buf; + int i, num; + + /* + * A function may require two buffers at once. Usually the buffers are + * used for a short period of time and are shared. On UP kernel buffers + * two should be enough, with more CPUs it is possible that more + * buffers will be used simultaneously. The preallocated pool may grow. + * This preallocation has also the side-effect that AppArmor will be + * disabled early at boot if aa_g_path_max is extremly high. + */ + if (num_online_cpus() > 1) + num = 4 + RESERVE_COUNT; + else + num = 2 + RESERVE_COUNT; + + for (i = 0; i < num; i++) { + + aa_buf = kmalloc(aa_g_path_max, GFP_KERNEL | + __GFP_RETRY_MAYFAIL | __GFP_NOWARN); + if (!aa_buf) { + destroy_buffers(); + return -ENOMEM; } + aa_put_buffer(&aa_buf->buffer[0]); } - return 0; } @@ -1730,7 +1855,7 @@ static int __init apparmor_init(void) error = alloc_buffers(); if (error) { AA_ERROR("Unable to allocate work buffers\n"); - goto buffers_out; + goto alloc_out; } error = set_init_ctx(); @@ -1755,7 +1880,6 @@ static int __init apparmor_init(void) buffers_out: destroy_buffers(); - alloc_out: aa_destroy_aafs(); aa_teardown_dfa_engine(); diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 6ccd3734a841..525ce22dc0e9 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -616,8 +616,8 @@ unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start, #define inc_wb_pos(wb) \ do { \ - wb->pos = (wb->pos + 1) & (wb->size - 1); \ - wb->len = (wb->len + 1) & (wb->size - 1); \ + wb->pos = (wb->pos + 1) & (WB_HISTORY_SIZE - 1); \ + wb->len = (wb->len + 1) & (WB_HISTORY_SIZE - 1); \ } while (0) /* For DFAs that don't support extended tagging of states */ @@ -636,7 +636,7 @@ static bool is_loop(struct match_workbuf *wb, unsigned int state, return true; } if (pos == 0) - pos = wb->size; + pos = WB_HISTORY_SIZE; pos--; } diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c index 17081c8dbefa..4ed6688f9d40 100644 --- a/security/apparmor/mount.c +++ b/security/apparmor/mount.c @@ -408,11 +408,13 @@ int aa_remount(struct aa_label *label, const struct path *path, binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; - get_buffers(buffer); + buffer = aa_get_buffer(false); + if (!buffer) + return -ENOMEM; error = fn_for_each_confined(label, profile, match_mnt(profile, path, buffer, NULL, NULL, NULL, flags, data, binary)); - put_buffers(buffer); + aa_put_buffer(buffer); return error; } @@ -437,11 +439,18 @@ int aa_bind_mount(struct aa_label *label, const struct path *path, if (error) return error; - get_buffers(buffer, old_buffer); + buffer = aa_get_buffer(false); + old_buffer = aa_get_buffer(false); + error = -ENOMEM; + if (!buffer || old_buffer) + goto out; + error = fn_for_each_confined(label, profile, match_mnt(profile, path, buffer, &old_path, old_buffer, NULL, flags, NULL, false)); - put_buffers(buffer, old_buffer); +out: + aa_put_buffer(buffer); + aa_put_buffer(old_buffer); path_put(&old_path); return error; @@ -461,11 +470,13 @@ int aa_mount_change_type(struct aa_label *label, const struct path *path, flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE); - get_buffers(buffer); + buffer = aa_get_buffer(false); + if (!buffer) + return -ENOMEM; error = fn_for_each_confined(label, profile, match_mnt(profile, path, buffer, NULL, NULL, NULL, flags, NULL, false)); - put_buffers(buffer); + aa_put_buffer(buffer); return error; } @@ -488,11 +499,17 @@ int aa_move_mount(struct aa_label *label, const struct path *path, if (error) return error; - get_buffers(buffer, old_buffer); + buffer = aa_get_buffer(false); + old_buffer = aa_get_buffer(false); + error = -ENOMEM; + if (!buffer || !old_buffer) + goto out; error = fn_for_each_confined(label, profile, match_mnt(profile, path, buffer, &old_path, old_buffer, NULL, MS_MOVE, NULL, false)); - put_buffers(buffer, old_buffer); +out: + aa_put_buffer(buffer); + aa_put_buffer(old_buffer); path_put(&old_path); return error; @@ -533,8 +550,17 @@ int aa_new_mount(struct aa_label *label, const char *dev_name, } } - get_buffers(buffer, dev_buffer); + buffer = aa_get_buffer(false); + if (!buffer) { + error = -ENOMEM; + goto out; + } if (dev_path) { + dev_buffer = aa_get_buffer(false); + if (!dev_buffer) { + error = -ENOMEM; + goto out; + } error = fn_for_each_confined(label, profile, match_mnt(profile, path, buffer, dev_path, dev_buffer, type, flags, data, binary)); @@ -543,7 +569,10 @@ int aa_new_mount(struct aa_label *label, const char *dev_name, match_mnt_path_str(profile, path, buffer, dev_name, type, flags, data, binary, NULL)); } - put_buffers(buffer, dev_buffer); + +out: + aa_put_buffer(buffer); + aa_put_buffer(dev_buffer); if (dev_path) path_put(dev_path); @@ -591,10 +620,13 @@ int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags) AA_BUG(!label); AA_BUG(!mnt); - get_buffers(buffer); + buffer = aa_get_buffer(false); + if (!buffer) + return -ENOMEM; + error = fn_for_each_confined(label, profile, profile_umount(profile, &path, buffer)); - put_buffers(buffer); + aa_put_buffer(buffer); return error; } @@ -667,8 +699,12 @@ int aa_pivotroot(struct aa_label *label, const struct path *old_path, AA_BUG(!old_path); AA_BUG(!new_path); - get_buffers(old_buffer, new_buffer); - target = fn_label_build(label, profile, GFP_ATOMIC, + old_buffer = aa_get_buffer(false); + new_buffer = aa_get_buffer(false); + error = -ENOMEM; + if (!old_buffer || !new_buffer) + goto out; + target = fn_label_build(label, profile, GFP_KERNEL, build_pivotroot(profile, new_path, new_buffer, old_path, old_buffer)); if (!target) { @@ -686,7 +722,8 @@ int aa_pivotroot(struct aa_label *label, const struct path *old_path, /* already audited error */ error = PTR_ERR(target); out: - put_buffers(old_buffer, new_buffer); + aa_put_buffer(old_buffer); + aa_put_buffer(new_buffer); return error; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index ade333074c8e..03104830c913 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -582,7 +582,7 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace, { if (profile) { if (profile->label.flags & FLAG_IMMUTIBLE) { - *info = "cannot replace immutible profile"; + *info = "cannot replace immutable profile"; return -EPERM; } else if (noreplace) { *info = "profile already exists"; @@ -856,7 +856,7 @@ static struct aa_profile *update_to_newest_parent(struct aa_profile *new) ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, u32 mask, struct aa_loaddata *udata) { - const char *ns_name, *info = NULL; + const char *ns_name = NULL, *info = NULL; struct aa_ns *ns = NULL; struct aa_load_ent *ent, *tmp; struct aa_loaddata *rawdata_ent; @@ -1043,6 +1043,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, out: aa_put_ns(ns); aa_put_loaddata(udata); + kfree(ns_name); if (error) return error; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 8cfc9493eefc..80364310fb1e 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -16,6 +16,7 @@ #include <asm/unaligned.h> #include <linux/ctype.h> #include <linux/errno.h> +#include <linux/zlib.h> #include "include/apparmor.h" #include "include/audit.h" @@ -139,9 +140,11 @@ bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r) { if (l->size != r->size) return false; + if (l->compressed_size != r->compressed_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; + return memcmp(l->data, r->data, r->compressed_size ?: r->size) == 0; } /* @@ -968,11 +971,14 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) e, error); return error; } - if (*ns && strcmp(*ns, name)) + if (*ns && strcmp(*ns, name)) { audit_iface(NULL, NULL, NULL, "invalid ns change", e, error); - else if (!*ns) - *ns = name; + } else if (!*ns) { + *ns = kstrdup(name, GFP_KERNEL); + if (!*ns) + return -ENOMEM; + } } return 0; @@ -1039,6 +1045,105 @@ struct aa_load_ent *aa_load_ent_alloc(void) return ent; } +static int deflate_compress(const char *src, size_t slen, char **dst, + size_t *dlen) +{ + int error; + struct z_stream_s strm; + void *stgbuf, *dstbuf; + size_t stglen = deflateBound(slen); + + memset(&strm, 0, sizeof(strm)); + + if (stglen < slen) + return -EFBIG; + + strm.workspace = kvzalloc(zlib_deflate_workspacesize(MAX_WBITS, + MAX_MEM_LEVEL), + GFP_KERNEL); + if (!strm.workspace) + return -ENOMEM; + + error = zlib_deflateInit(&strm, aa_g_rawdata_compression_level); + if (error != Z_OK) { + error = -ENOMEM; + goto fail_deflate_init; + } + + stgbuf = kvzalloc(stglen, GFP_KERNEL); + if (!stgbuf) { + error = -ENOMEM; + goto fail_stg_alloc; + } + + strm.next_in = src; + strm.avail_in = slen; + strm.next_out = stgbuf; + strm.avail_out = stglen; + + error = zlib_deflate(&strm, Z_FINISH); + if (error != Z_STREAM_END) { + error = -EINVAL; + goto fail_deflate; + } + error = 0; + + if (is_vmalloc_addr(stgbuf)) { + dstbuf = kvzalloc(strm.total_out, GFP_KERNEL); + if (dstbuf) { + memcpy(dstbuf, stgbuf, strm.total_out); + kvfree(stgbuf); + } + } else + /* + * If the staging buffer was kmalloc'd, then using krealloc is + * probably going to be faster. The destination buffer will + * always be smaller, so it's just shrunk, avoiding a memcpy + */ + dstbuf = krealloc(stgbuf, strm.total_out, GFP_KERNEL); + + if (!dstbuf) { + error = -ENOMEM; + goto fail_deflate; + } + + *dst = dstbuf; + *dlen = strm.total_out; + +fail_stg_alloc: + zlib_deflateEnd(&strm); +fail_deflate_init: + kvfree(strm.workspace); + return error; + +fail_deflate: + kvfree(stgbuf); + goto fail_stg_alloc; +} + +static int compress_loaddata(struct aa_loaddata *data) +{ + + AA_BUG(data->compressed_size > 0); + + /* + * Shortcut the no compression case, else we increase the amount of + * storage required by a small amount + */ + if (aa_g_rawdata_compression_level != 0) { + void *udata = data->data; + int error = deflate_compress(udata, data->size, &data->data, + &data->compressed_size); + if (error) + return error; + + kvfree(udata); + } else + data->compressed_size = data->size; + + return 0; +} + /** * aa_unpack - unpack packed binary profile(s) data loaded from user space * @udata: user data copied to kmem (NOT NULL) @@ -1107,6 +1212,9 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, goto fail; } } + error = compress_loaddata(udata); + if (error) + goto fail; return 0; fail_profile: diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 725674f3276d..7d0f8f7431ff 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -801,8 +801,8 @@ struct cgroup_subsys devices_cgrp_subsys = { * * returns 0 on success, -EPERM case the operation is not permitted */ -int __devcgroup_check_permission(short type, u32 major, u32 minor, - short access) +static int __devcgroup_check_permission(short type, u32 major, u32 minor, + short access) { struct dev_cgroup *dev_cgroup; bool rc; @@ -824,3 +824,14 @@ int __devcgroup_check_permission(short type, u32 major, u32 minor, return 0; } + +int devcgroup_check_permission(short type, u32 major, u32 minor, short access) +{ + int rc = BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access); + + if (rc) + return -EPERM; + + return __devcgroup_check_permission(type, major, minor, access); +} +EXPORT_SYMBOL(devcgroup_check_permission); diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 0bae6adb63a9..71f0177e8716 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -72,6 +72,15 @@ config LOAD_IPL_KEYS depends on S390 def_bool y +config LOAD_PPC_KEYS + bool "Enable loading of platform and blacklisted keys for POWER" + depends on INTEGRITY_PLATFORM_KEYRING + depends on PPC_SECURE_BOOT + default y + help + Enable loading of keys to the .platform keyring and blacklisted + hashes to the .blacklist keyring for powerpc based platforms. + config INTEGRITY_AUDIT bool "Enables integrity auditing support " depends on AUDIT diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 35e6ca773734..7ee39d66cf16 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -11,8 +11,11 @@ integrity-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyring.o integrity-$(CONFIG_LOAD_UEFI_KEYS) += platform_certs/efi_parser.o \ - platform_certs/load_uefi.o + platform_certs/load_uefi.o \ + platform_certs/keyring_handler.o integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o - +integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \ + platform_certs/load_powerpc.o \ + platform_certs/keyring_handler.o obj-$(CONFIG_IMA) += ima/ obj-$(CONFIG_EVM) += evm/ diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 3689081aaf38..df4ca482fb53 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -217,6 +217,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, struct evm_ima_xattr_data *xattr_value, int xattr_len, const struct modsig *modsig, int pcr, struct ima_template_desc *template_desc); +void process_buffer_measurement(const void *buf, int size, + const char *eventname, enum ima_hooks func, + int pcr); void ima_audit_measurement(struct integrity_iint_cache *iint, const unsigned char *filename); int ima_alloc_init_template(struct ima_event_data *event_data, @@ -253,6 +256,8 @@ int ima_policy_show(struct seq_file *m, void *v); #define IMA_APPRAISE_KEXEC 0x40 #ifdef CONFIG_IMA_APPRAISE +int ima_check_blacklist(struct integrity_iint_cache *iint, + const struct modsig *modsig, int pcr); int ima_appraise_measurement(enum ima_hooks func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, @@ -268,6 +273,12 @@ int ima_read_xattr(struct dentry *dentry, struct evm_ima_xattr_data **xattr_value); #else +static inline int ima_check_blacklist(struct integrity_iint_cache *iint, + const struct modsig *modsig, int pcr) +{ + return 0; +} + static inline int ima_appraise_measurement(enum ima_hooks func, struct integrity_iint_cache *iint, struct file *file, diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 136ae4e0ee92..300c8d2943c5 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -12,6 +12,7 @@ #include <linux/magic.h> #include <linux/ima.h> #include <linux/evm.h> +#include <keys/system_keyring.h> #include "ima.h" @@ -304,6 +305,38 @@ static int modsig_verify(enum ima_hooks func, const struct modsig *modsig, } /* + * ima_check_blacklist - determine if the binary is blacklisted. + * + * Add the hash of the blacklisted binary to the measurement list, based + * on policy. + * + * Returns -EPERM if the hash is blacklisted. + */ +int ima_check_blacklist(struct integrity_iint_cache *iint, + const struct modsig *modsig, int pcr) +{ + enum hash_algo hash_algo; + const u8 *digest = NULL; + u32 digestsize = 0; + int rc = 0; + + if (!(iint->flags & IMA_CHECK_BLACKLIST)) + return 0; + + if (iint->flags & IMA_MODSIG_ALLOWED && modsig) { + ima_get_modsig_digest(modsig, &hash_algo, &digest, &digestsize); + + rc = is_binary_blacklisted(digest, digestsize); + if ((rc == -EPERM) && (iint->flags & IMA_MEASURE)) + process_buffer_measurement(digest, digestsize, + "blacklisted-hash", NONE, + pcr); + } + + return rc; +} + +/* * ima_appraise_measurement - appraise file measurement * * Call evm_verifyxattr() to verify the integrity of 'security.ima'. diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 60027c643ecd..d7e987baf127 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -335,10 +335,14 @@ static int process_measurement(struct file *file, const struct cred *cred, xattr_value, xattr_len, modsig, pcr, template_desc); if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) { - inode_lock(inode); - rc = ima_appraise_measurement(func, iint, file, pathname, - xattr_value, xattr_len, modsig); - inode_unlock(inode); + rc = ima_check_blacklist(iint, modsig, pcr); + if (rc != -EPERM) { + inode_lock(inode); + rc = ima_appraise_measurement(func, iint, file, + pathname, xattr_value, + xattr_len, modsig); + inode_unlock(inode); + } if (!rc) rc = mmap_violation_check(func, file, &pathbuf, &pathname, filename); @@ -626,14 +630,14 @@ int ima_load_data(enum kernel_load_data_id id) * @buf: pointer to the buffer that needs to be added to the log. * @size: size of buffer(in bytes). * @eventname: event name to be used for the buffer entry. - * @cred: a pointer to a credentials structure for user validation. - * @secid: the secid of the task to be validated. + * @func: IMA hook + * @pcr: pcr to extend the measurement * * Based on policy, the buffer is measured into the ima log. */ -static void process_buffer_measurement(const void *buf, int size, - const char *eventname, - const struct cred *cred, u32 secid) +void process_buffer_measurement(const void *buf, int size, + const char *eventname, enum ima_hooks func, + int pcr) { int ret = 0; struct ima_template_entry *entry = NULL; @@ -642,19 +646,45 @@ static void process_buffer_measurement(const void *buf, int size, .filename = eventname, .buf = buf, .buf_len = size}; - struct ima_template_desc *template_desc = NULL; + struct ima_template_desc *template = NULL; struct { struct ima_digest_data hdr; char digest[IMA_MAX_DIGEST_SIZE]; } hash = {}; int violation = 0; - int pcr = CONFIG_IMA_MEASURE_PCR_IDX; int action = 0; + u32 secid; - action = ima_get_action(NULL, cred, secid, 0, KEXEC_CMDLINE, &pcr, - &template_desc); - if (!(action & IMA_MEASURE)) - return; + /* + * Both LSM hooks and auxilary based buffer measurements are + * based on policy. To avoid code duplication, differentiate + * between the LSM hooks and auxilary buffer measurements, + * retrieving the policy rule information only for the LSM hook + * buffer measurements. + */ + if (func) { + security_task_getsecid(current, &secid); + action = ima_get_action(NULL, current_cred(), secid, 0, func, + &pcr, &template); + if (!(action & IMA_MEASURE)) + return; + } + + if (!pcr) + pcr = CONFIG_IMA_MEASURE_PCR_IDX; + + if (!template) { + template = lookup_template_desc("ima-buf"); + ret = template_desc_init_fields(template->fmt, + &(template->fields), + &(template->num_fields)); + if (ret < 0) { + pr_err("template %s init failed, result: %d\n", + (strlen(template->name) ? + template->name : template->fmt), ret); + return; + } + } iint.ima_hash = &hash.hdr; iint.ima_hash->algo = ima_hash_algo; @@ -664,7 +694,7 @@ static void process_buffer_measurement(const void *buf, int size, if (ret < 0) goto out; - ret = ima_alloc_init_template(&event_data, &entry, template_desc); + ret = ima_alloc_init_template(&event_data, &entry, template); if (ret < 0) goto out; @@ -686,13 +716,9 @@ out: */ void ima_kexec_cmdline(const void *buf, int size) { - u32 secid; - - if (buf && size != 0) { - security_task_getsecid(current, &secid); + if (buf && size != 0) process_buffer_measurement(buf, size, "kexec-cmdline", - current_cred(), secid); - } + KEXEC_CMDLINE, 0); } static int __init init_ima(void) diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 5380aca2b351..f19a895ad7cd 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -765,8 +765,8 @@ enum { Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq, Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt, Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt, - Opt_appraise_type, Opt_permit_directio, - Opt_pcr, Opt_template, Opt_err + Opt_appraise_type, Opt_appraise_flag, + Opt_permit_directio, Opt_pcr, Opt_template, Opt_err }; static const match_table_t policy_tokens = { @@ -798,6 +798,7 @@ static const match_table_t policy_tokens = { {Opt_euid_lt, "euid<%s"}, {Opt_fowner_lt, "fowner<%s"}, {Opt_appraise_type, "appraise_type=%s"}, + {Opt_appraise_flag, "appraise_flag=%s"}, {Opt_permit_directio, "permit_directio"}, {Opt_pcr, "pcr=%s"}, {Opt_template, "template=%s"}, @@ -1172,6 +1173,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) else result = -EINVAL; break; + case Opt_appraise_flag: + ima_log_string(ab, "appraise_flag", args[0].from); + if (strstr(args[0].from, "blacklist")) + entry->flags |= IMA_CHECK_BLACKLIST; + break; case Opt_permit_directio: entry->flags |= IMA_PERMIT_DIRECTIO; break; @@ -1500,6 +1506,8 @@ int ima_policy_show(struct seq_file *m, void *v) else seq_puts(m, "appraise_type=imasig "); } + if (entry->flags & IMA_CHECK_BLACKLIST) + seq_puts(m, "appraise_flag=check_blacklist "); if (entry->flags & IMA_PERMIT_DIRECTIO) seq_puts(m, "permit_directio "); rcu_read_unlock(); diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index d9323d31a3a8..73fc286834d7 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -32,6 +32,7 @@ #define EVM_IMMUTABLE_DIGSIG 0x08000000 #define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000 #define IMA_MODSIG_ALLOWED 0x20000000 +#define IMA_CHECK_BLACKLIST 0x40000000 #define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \ IMA_HASH | IMA_APPRAISE_SUBMASK) diff --git a/security/integrity/platform_certs/keyring_handler.c b/security/integrity/platform_certs/keyring_handler.c new file mode 100644 index 000000000000..c5ba695c10e3 --- /dev/null +++ b/security/integrity/platform_certs/keyring_handler.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/cred.h> +#include <linux/err.h> +#include <linux/efi.h> +#include <linux/slab.h> +#include <keys/asymmetric-type.h> +#include <keys/system_keyring.h> +#include "../integrity.h" + +static efi_guid_t efi_cert_x509_guid __initdata = EFI_CERT_X509_GUID; +static efi_guid_t efi_cert_x509_sha256_guid __initdata = + EFI_CERT_X509_SHA256_GUID; +static efi_guid_t efi_cert_sha256_guid __initdata = EFI_CERT_SHA256_GUID; + +/* + * Blacklist a hash. + */ +static __init void uefi_blacklist_hash(const char *source, const void *data, + size_t len, const char *type, + size_t type_len) +{ + char *hash, *p; + + hash = kmalloc(type_len + len * 2 + 1, GFP_KERNEL); + if (!hash) + return; + p = memcpy(hash, type, type_len); + p += type_len; + bin2hex(p, data, len); + p += len * 2; + *p = 0; + + mark_hash_blacklisted(hash); + kfree(hash); +} + +/* + * Blacklist an X509 TBS hash. + */ +static __init void uefi_blacklist_x509_tbs(const char *source, + const void *data, size_t len) +{ + uefi_blacklist_hash(source, data, len, "tbs:", 4); +} + +/* + * Blacklist the hash of an executable. + */ +static __init void uefi_blacklist_binary(const char *source, + const void *data, size_t len) +{ + uefi_blacklist_hash(source, data, len, "bin:", 4); +} + +/* + * Return the appropriate handler for particular signature list types found in + * the UEFI db and MokListRT tables. + */ +__init efi_element_handler_t get_handler_for_db(const efi_guid_t *sig_type) +{ + if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0) + return add_to_platform_keyring; + return 0; +} + +/* + * Return the appropriate handler for particular signature list types found in + * the UEFI dbx and MokListXRT tables. + */ +__init efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type) +{ + if (efi_guidcmp(*sig_type, efi_cert_x509_sha256_guid) == 0) + return uefi_blacklist_x509_tbs; + if (efi_guidcmp(*sig_type, efi_cert_sha256_guid) == 0) + return uefi_blacklist_binary; + return 0; +} diff --git a/security/integrity/platform_certs/keyring_handler.h b/security/integrity/platform_certs/keyring_handler.h new file mode 100644 index 000000000000..2462bfa08fe3 --- /dev/null +++ b/security/integrity/platform_certs/keyring_handler.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef PLATFORM_CERTS_INTERNAL_H +#define PLATFORM_CERTS_INTERNAL_H + +#include <linux/efi.h> + +void blacklist_hash(const char *source, const void *data, + size_t len, const char *type, + size_t type_len); + +/* + * Blacklist an X509 TBS hash. + */ +void blacklist_x509_tbs(const char *source, const void *data, size_t len); + +/* + * Blacklist the hash of an executable. + */ +void blacklist_binary(const char *source, const void *data, size_t len); + +/* + * Return the handler for particular signature list types found in the db. + */ +efi_element_handler_t get_handler_for_db(const efi_guid_t *sig_type); + +/* + * Return the handler for particular signature list types found in the dbx. + */ +efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type); + +#endif diff --git a/security/integrity/platform_certs/load_powerpc.c b/security/integrity/platform_certs/load_powerpc.c new file mode 100644 index 000000000000..a2900cb85357 --- /dev/null +++ b/security/integrity/platform_certs/load_powerpc.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 IBM Corporation + * Author: Nayna Jain + * + * - loads keys and hashes stored and controlled by the firmware. + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/cred.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <asm/secure_boot.h> +#include <asm/secvar.h> +#include "keyring_handler.h" + +/* + * Get a certificate list blob from the named secure variable. + */ +static __init void *get_cert_list(u8 *key, unsigned long keylen, uint64_t *size) +{ + int rc; + void *db; + + rc = secvar_ops->get(key, keylen, NULL, size); + if (rc) { + pr_err("Couldn't get size: %d\n", rc); + return NULL; + } + + db = kmalloc(*size, GFP_KERNEL); + if (!db) + return NULL; + + rc = secvar_ops->get(key, keylen, db, size); + if (rc) { + kfree(db); + pr_err("Error reading %s var: %d\n", key, rc); + return NULL; + } + + return db; +} + +/* + * Load the certs contained in the keys databases into the platform trusted + * keyring and the blacklisted X.509 cert SHA256 hashes into the blacklist + * keyring. + */ +static int __init load_powerpc_certs(void) +{ + void *db = NULL, *dbx = NULL; + uint64_t dbsize = 0, dbxsize = 0; + int rc = 0; + struct device_node *node; + + if (!secvar_ops) + return -ENODEV; + + /* The following only applies for the edk2-compat backend. */ + node = of_find_compatible_node(NULL, NULL, "ibm,edk2-compat-v1"); + if (!node) + return -ENODEV; + + /* + * Get db, and dbx. They might not exist, so it isn't an error if we + * can't get them. + */ + db = get_cert_list("db", 3, &dbsize); + if (!db) { + pr_err("Couldn't get db list from firmware\n"); + } else { + rc = parse_efi_signature_list("powerpc:db", db, dbsize, + get_handler_for_db); + if (rc) + pr_err("Couldn't parse db signatures: %d\n", rc); + kfree(db); + } + + dbx = get_cert_list("dbx", 4, &dbxsize); + if (!dbx) { + pr_info("Couldn't get dbx list from firmware\n"); + } else { + rc = parse_efi_signature_list("powerpc:dbx", dbx, dbxsize, + get_handler_for_dbx); + if (rc) + pr_err("Couldn't parse dbx signatures: %d\n", rc); + kfree(dbx); + } + + of_node_put(node); + + return rc; +} +late_initcall(load_powerpc_certs); diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c index 81b19c52832b..111898aad56e 100644 --- a/security/integrity/platform_certs/load_uefi.c +++ b/security/integrity/platform_certs/load_uefi.c @@ -9,11 +9,7 @@ #include <keys/asymmetric-type.h> #include <keys/system_keyring.h> #include "../integrity.h" - -static efi_guid_t efi_cert_x509_guid __initdata = EFI_CERT_X509_GUID; -static efi_guid_t efi_cert_x509_sha256_guid __initdata = - EFI_CERT_X509_SHA256_GUID; -static efi_guid_t efi_cert_sha256_guid __initdata = EFI_CERT_SHA256_GUID; +#include "keyring_handler.h" /* * Look to see if a UEFI variable called MokIgnoreDB exists and return true if @@ -68,72 +64,6 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, } /* - * Blacklist a hash. - */ -static __init void uefi_blacklist_hash(const char *source, const void *data, - size_t len, const char *type, - size_t type_len) -{ - char *hash, *p; - - hash = kmalloc(type_len + len * 2 + 1, GFP_KERNEL); - if (!hash) - return; - p = memcpy(hash, type, type_len); - p += type_len; - bin2hex(p, data, len); - p += len * 2; - *p = 0; - - mark_hash_blacklisted(hash); - kfree(hash); -} - -/* - * Blacklist an X509 TBS hash. - */ -static __init void uefi_blacklist_x509_tbs(const char *source, - const void *data, size_t len) -{ - uefi_blacklist_hash(source, data, len, "tbs:", 4); -} - -/* - * Blacklist the hash of an executable. - */ -static __init void uefi_blacklist_binary(const char *source, - const void *data, size_t len) -{ - uefi_blacklist_hash(source, data, len, "bin:", 4); -} - -/* - * Return the appropriate handler for particular signature list types found in - * the UEFI db and MokListRT tables. - */ -static __init efi_element_handler_t get_handler_for_db(const efi_guid_t * - sig_type) -{ - if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0) - return add_to_platform_keyring; - return 0; -} - -/* - * Return the appropriate handler for particular signature list types found in - * the UEFI dbx and MokListXRT tables. - */ -static __init efi_element_handler_t get_handler_for_dbx(const efi_guid_t * - sig_type) -{ - if (efi_guidcmp(*sig_type, efi_cert_x509_sha256_guid) == 0) - return uefi_blacklist_x509_tbs; - if (efi_guidcmp(*sig_type, efi_cert_sha256_guid) == 0) - return uefi_blacklist_binary; - return 0; -} - -/* * Load the certs contained in the UEFI databases into the platform trusted * keyring and the UEFI blacklisted X.509 cert SHA256 hashes into the blacklist * keyring. diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c index 40b790536def..b2f87015d6e9 100644 --- a/security/lockdown/lockdown.c +++ b/security/lockdown/lockdown.c @@ -32,12 +32,14 @@ static const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = { [LOCKDOWN_MODULE_PARAMETERS] = "unsafe module parameters", [LOCKDOWN_MMIOTRACE] = "unsafe mmio", [LOCKDOWN_DEBUGFS] = "debugfs access", + [LOCKDOWN_XMON_WR] = "xmon write access", [LOCKDOWN_INTEGRITY_MAX] = "integrity", [LOCKDOWN_KCORE] = "/proc/kcore access", [LOCKDOWN_KPROBES] = "use of kprobes", [LOCKDOWN_BPF_READ] = "use of bpf to read kernel RAM", [LOCKDOWN_PERF] = "unsafe use of perf", [LOCKDOWN_TRACEFS] = "use of tracefs", + [LOCKDOWN_XMON_RW] = "xmon read and write access", [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality", }; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 28eb05490d59..116b4d644f68 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2549,9 +2549,8 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm) static void selinux_bprm_committed_creds(struct linux_binprm *bprm) { const struct task_security_struct *tsec = selinux_cred(current_cred()); - struct itimerval itimer; u32 osid, sid; - int rc, i; + int rc; osid = tsec->osid; sid = tsec->sid; @@ -2569,11 +2568,8 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm) rc = avc_has_perm(&selinux_state, osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL); if (rc) { - if (IS_ENABLED(CONFIG_POSIX_TIMERS)) { - memset(&itimer, 0, sizeof itimer); - for (i = 0; i < 3; i++) - do_setitimer(i, &itimer, NULL); - } + clear_itimer(); + spin_lock_irq(¤t->sighand->siglock); if (!fatal_signal_pending(current)) { flush_sigqueue(¤t->pending); @@ -3144,6 +3140,9 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, return dentry_has_perm(current_cred(), dentry, FILE__SETATTR); } + if (!selinux_state.initialized) + return (inode_owner_or_capable(inode) ? 0 : -EPERM); + sbsec = inode->i_sb->s_security; if (!(sbsec->flags & SBLABEL_MNT)) return -EOPNOTSUPP; @@ -3227,6 +3226,15 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, return; } + if (!selinux_state.initialized) { + /* If we haven't even been initialized, then we can't validate + * against a policy, so leave the label as invalid. It may + * resolve to a valid label on the next revalidation try if + * we've since initialized. + */ + return; + } + rc = security_context_to_sid_force(&selinux_state, value, size, &newsid); if (rc) { @@ -4623,8 +4631,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in inet_get_local_port_range(sock_net(sk), &low, &high); - if (snum < max(inet_prot_sock(sock_net(sk)), low) || - snum > high) { + if (inet_port_requires_bind_service(sock_net(sk), snum) || + snum < low || snum > high) { err = sel_netport_sid(sk->sk_protocol, snum, &sid); if (err) diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 111121281c47..ae840634e3c7 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -40,10 +40,11 @@ #define POLICYDB_VERSION_CONSTRAINT_NAMES 29 #define POLICYDB_VERSION_XPERMS_IOCTL 30 #define POLICYDB_VERSION_INFINIBAND 31 +#define POLICYDB_VERSION_GLBLUB 32 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_INFINIBAND +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_GLBLUB /* Mask for just the mount related flags */ #define SE_MNTMASK 0x0f diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index e6c7643c3fc0..ee94fa469c29 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -548,10 +548,6 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, if (*ppos != 0) goto out; - length = -EFBIG; - if (count > 64 * 1024 * 1024) - goto out; - length = -ENOMEM; data = vmalloc(count); if (!data) diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h index 2260c44a568c..513e67f48878 100644 --- a/security/selinux/ss/context.h +++ b/security/selinux/ss/context.h @@ -95,6 +95,38 @@ out: return rc; } + +static inline int mls_context_glblub(struct context *dst, + struct context *c1, struct context *c2) +{ + struct mls_range *dr = &dst->range, *r1 = &c1->range, *r2 = &c2->range; + int rc = 0; + + if (r1->level[1].sens < r2->level[0].sens || + r2->level[1].sens < r1->level[0].sens) + /* These ranges have no common sensitivities */ + return -EINVAL; + + /* Take the greatest of the low */ + dr->level[0].sens = max(r1->level[0].sens, r2->level[0].sens); + + /* Take the least of the high */ + dr->level[1].sens = min(r1->level[1].sens, r2->level[1].sens); + + rc = ebitmap_and(&dr->level[0].cat, + &r1->level[0].cat, &r2->level[0].cat); + if (rc) + goto out; + + rc = ebitmap_and(&dr->level[1].cat, + &r1->level[1].cat, &r2->level[1].cat); + if (rc) + goto out; + +out: + return rc; +} + static inline int mls_context_cmp(struct context *c1, struct context *c2) { return ((c1->range.level[0].sens == c2->range.level[0].sens) && diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 09929fc5ab47..c8c3663111e2 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -77,6 +77,24 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) return 0; } +int ebitmap_and(struct ebitmap *dst, struct ebitmap *e1, struct ebitmap *e2) +{ + struct ebitmap_node *n; + int bit, rc; + + ebitmap_init(dst); + + ebitmap_for_each_positive_bit(e1, n, bit) { + if (ebitmap_get_bit(e2, bit)) { + rc = ebitmap_set_bit(dst, bit, 1); + if (rc < 0) + return rc; + } + } + return 0; +} + + #ifdef CONFIG_NETLABEL /** * ebitmap_netlbl_export - Export an ebitmap into a NetLabel category bitmap diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index 6aa7cf6a2197..9a23b81b8832 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -124,6 +124,7 @@ static inline void ebitmap_node_clr_bit(struct ebitmap_node *n, int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2); int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src); +int ebitmap_and(struct ebitmap *dst, struct ebitmap *e1, struct ebitmap *e2); int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit); int ebitmap_get_bit(struct ebitmap *e, unsigned long bit); int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value); diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 5e05f5b902d7..ec5e3d1da9ac 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -529,6 +529,9 @@ int mls_compute_sid(struct policydb *p, return mls_context_cpy_high(newcontext, tcontext); case DEFAULT_TARGET_LOW_HIGH: return mls_context_cpy(newcontext, tcontext); + case DEFAULT_GLBLUB: + return mls_context_glblub(newcontext, + scontext, tcontext); } /* Fallthrough */ diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 1260f5fb766e..e20624a68f5d 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -160,6 +160,11 @@ static struct policydb_compat_info policydb_compat[] = { .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_GLBLUB, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 162d0e79b85b..bc56b14e2216 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -69,6 +69,7 @@ struct class_datum { #define DEFAULT_TARGET_LOW 4 #define DEFAULT_TARGET_HIGH 5 #define DEFAULT_TARGET_LOW_HIGH 6 +#define DEFAULT_GLBLUB 7 char default_range; }; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index abeb09c30633..ecea41ce919b 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -28,7 +28,6 @@ #include <linux/icmpv6.h> #include <linux/slab.h> #include <linux/mutex.h> -#include <linux/pipe_fs_i.h> #include <net/cipso_ipv4.h> #include <net/ip.h> #include <net/ipv6.h> |