Commit 79444df4 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'apparmor-pr-2017-09-22' of...

Merge tag 'apparmor-pr-2017-09-22' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor

Pull apparmor updates from John Johansen:
 "This is the apparmor pull request, similar to SELinux and seccomp.

  It's the same series that I was sent to James' security tree + one
  regression fix that was found after the series was sent to James and
  would have been sent for v4.14-rc2.

  Features:
  - in preparation for secid mapping add support for absolute root view
    based labels
  - add base infastructure for socket mediation
  - add mount mediation
  - add signal mediation

  minor cleanups and changes:
  - be defensive, ensure unconfined profiles have dfas initialized
  - add more debug asserts to apparmorfs
  - enable policy unpacking to audit different reasons for failure
  - cleanup conditional check for label in label_print
  - Redundant condition: prev_ns. in [label.c:1498]

  Bug Fixes:
  - fix regression in apparmorfs DAC access permissions
  - fix build failure on sparc caused by undeclared signals
  - fix sparse report of incorrect type assignment when freeing label proxies
  - fix race condition in null profile creation
  - Fix an error code in aafs_create()
  - Fix logical error in verify_header()
  - Fix shadowed local variable in unpack_trans_table()"

* tag 'apparmor-pr-2017-09-22' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor:
  apparmor: fix apparmorfs DAC access permissions
  apparmor: fix build failure on sparc caused by undeclared signals
  apparmor: fix incorrect type assignment when freeing proxies
  apparmor: ensure unconfined profiles have dfas initialized
  apparmor: fix race condition in null profile creation
  apparmor: move new_null_profile to after profile lookup fns()
  apparmor: add base infastructure for socket mediation
  apparmor: add more debug asserts to apparmorfs
  apparmor: make policy_unpack able to audit different info messages
  apparmor: add support for absolute root view based labels
  apparmor: cleanup conditional check for label in label_print
  apparmor: add mount mediation
  apparmor: add the ability to mediate signals
  apparmor: Redundant condition: prev_ns. in [label.c:1498]
  apparmor: Fix an error code in aafs_create()
  apparmor: Fix logical error in verify_header()
  apparmor: Fix shadowed local variable in unpack_trans_table()
parents c65da8e2 bf81100f
#
# Generated include files
#
net_names.h
capability_names.h
rlim_names.h
......@@ -4,11 +4,44 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
resource.o secid.o file.o policy_ns.o label.o
resource.o secid.o file.o policy_ns.o label.o mount.o net.o
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
clean-files := capability_names.h rlim_names.h
clean-files := capability_names.h rlim_names.h net_names.h
# Build a lower case string table of address family names
# Transform lines from
# #define AF_LOCAL 1 /* POSIX name for AF_UNIX */
# #define AF_INET 2 /* Internet IP Protocol */
# to
# [1] = "local",
# [2] = "inet",
#
# and build the securityfs entries for the mapping.
# Transforms lines from
# #define AF_INET 2 /* Internet IP Protocol */
# to
# #define AA_SFS_AF_MASK "local inet"
quiet_cmd_make-af = GEN $@
cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \
's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
echo "};" >> $@ ;\
printf '%s' '\#define AA_SFS_AF_MASK "' >> $@ ;\
sed -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \
's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\
$< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
# Build a lower case string table of sock type names
# Transform lines from
# SOCK_STREAM = 1,
# to
# [1] = "stream",
quiet_cmd_make-sock = GEN $@
cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\
sed $^ >>$@ -r -n \
-e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
echo "};" >> $@
# Build a lower case string table of capability names
# Transforms lines from
......@@ -61,6 +94,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \
tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
$(obj)/capability.o : $(obj)/capability_names.h
$(obj)/net.o : $(obj)/net_names.h
$(obj)/resource.o : $(obj)/rlim_names.h
$(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \
$(src)/Makefile
......@@ -68,3 +102,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \
$(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \
$(src)/Makefile
$(call cmd,make-rlim)
$(obj)/net_names.h : $(srctree)/include/linux/socket.h \
$(srctree)/include/linux/net.h \
$(src)/Makefile
$(call cmd,make-af)
$(call cmd,make-sock)
......@@ -32,6 +32,7 @@
#include "include/audit.h"
#include "include/context.h"
#include "include/crypto.h"
#include "include/ipc.h"
#include "include/policy_ns.h"
#include "include/label.h"
#include "include/policy.h"
......@@ -248,8 +249,10 @@ static struct dentry *aafs_create(const char *name, umode_t mode,
inode_lock(dir);
dentry = lookup_one_len(name, parent, strlen(name));
if (IS_ERR(dentry))
if (IS_ERR(dentry)) {
error = PTR_ERR(dentry);
goto fail_lock;
}
if (d_really_is_positive(dentry)) {
error = -EEXIST;
......@@ -1443,6 +1446,10 @@ void __aafs_profile_migrate_dents(struct aa_profile *old,
{
int i;
AA_BUG(!old);
AA_BUG(!new);
AA_BUG(!mutex_is_locked(&profiles_ns(old)->lock));
for (i = 0; i < AAFS_PROF_SIZEOF; i++) {
new->dents[i] = old->dents[i];
if (new->dents[i])
......@@ -1506,6 +1513,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
struct dentry *dent = NULL, *dir;
int error;
AA_BUG(!profile);
AA_BUG(!mutex_is_locked(&profiles_ns(profile)->lock));
if (!parent) {
struct aa_profile *p;
p = aa_deref_parent(profile);
......@@ -1731,6 +1741,7 @@ void __aafs_ns_rmdir(struct aa_ns *ns)
if (!ns)
return;
AA_BUG(!mutex_is_locked(&ns->lock));
list_for_each_entry(child, &ns->base.profiles, base.list)
__aafs_profile_rmdir(child);
......@@ -1903,6 +1914,10 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
{
struct aa_ns *parent, *next;
AA_BUG(!root);
AA_BUG(!ns);
AA_BUG(ns != root && !mutex_is_locked(&ns->parent->lock));
/* is next namespace a child */
if (!list_empty(&ns->sub_ns)) {
next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
......@@ -1937,6 +1952,9 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
static struct aa_profile *__first_profile(struct aa_ns *root,
struct aa_ns *ns)
{
AA_BUG(!root);
AA_BUG(ns && !mutex_is_locked(&ns->lock));
for (; ns; ns = __next_ns(root, ns)) {
if (!list_empty(&ns->base.profiles))
return list_first_entry(&ns->base.profiles,
......@@ -1959,6 +1977,8 @@ static struct aa_profile *__next_profile(struct aa_profile *p)
struct aa_profile *parent;
struct aa_ns *ns = p->ns;
AA_BUG(!mutex_is_locked(&profiles_ns(p)->lock));
/* is next profile a child */
if (!list_empty(&p->base.profiles))
return list_first_entry(&p->base.profiles, typeof(*p),
......@@ -2127,6 +2147,11 @@ static struct aa_sfs_entry aa_sfs_entry_ptrace[] = {
{ }
};
static struct aa_sfs_entry aa_sfs_entry_signal[] = {
AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK),
{ }
};
static struct aa_sfs_entry aa_sfs_entry_domain[] = {
AA_SFS_FILE_BOOLEAN("change_hat", 1),
AA_SFS_FILE_BOOLEAN("change_hatv", 1),
......@@ -2151,9 +2176,14 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = {
{ }
};
static struct aa_sfs_entry aa_sfs_entry_mount[] = {
AA_SFS_FILE_STRING("mask", "mount umount pivot_root"),
{ }
};
static struct aa_sfs_entry aa_sfs_entry_ns[] = {
AA_SFS_FILE_BOOLEAN("profile", 1),
AA_SFS_FILE_BOOLEAN("pivot_root", 1),
AA_SFS_FILE_BOOLEAN("pivot_root", 0),
{ }
};
......@@ -2172,22 +2202,25 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
AA_SFS_DIR("policy", aa_sfs_entry_policy),
AA_SFS_DIR("domain", aa_sfs_entry_domain),
AA_SFS_DIR("file", aa_sfs_entry_file),
AA_SFS_DIR("network", aa_sfs_entry_network),
AA_SFS_DIR("mount", aa_sfs_entry_mount),
AA_SFS_DIR("namespaces", aa_sfs_entry_ns),
AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit),
AA_SFS_DIR("caps", aa_sfs_entry_caps),
AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace),
AA_SFS_DIR("signal", aa_sfs_entry_signal),
AA_SFS_DIR("query", aa_sfs_entry_query),
{ }
};
static struct aa_sfs_entry aa_sfs_entry_apparmor[] = {
AA_SFS_FILE_FOPS(".access", 0640, &aa_sfs_access),
AA_SFS_FILE_FOPS(".access", 0666, &aa_sfs_access),
AA_SFS_FILE_FOPS(".stacked", 0444, &seq_ns_stacked_fops),
AA_SFS_FILE_FOPS(".ns_stacked", 0444, &seq_ns_nsstacked_fops),
AA_SFS_FILE_FOPS(".ns_level", 0666, &seq_ns_level_fops),
AA_SFS_FILE_FOPS(".ns_name", 0640, &seq_ns_name_fops),
AA_SFS_FILE_FOPS("profiles", 0440, &aa_sfs_profiles_fops),
AA_SFS_FILE_FOPS(".ns_level", 0444, &seq_ns_level_fops),
AA_SFS_FILE_FOPS(".ns_name", 0444, &seq_ns_name_fops),
AA_SFS_FILE_FOPS("profiles", 0444, &aa_sfs_profiles_fops),
AA_SFS_DIR("features", aa_sfs_entry_features),
{ }
};
......
......@@ -374,8 +374,8 @@ static const char *next_name(int xtype, const char *name)
*
* Returns: refcounted label, or NULL on failure (MAYBE NULL)
*/
static struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
const char **name)
struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
const char **name)
{
struct aa_label *label = NULL;
u32 xtype = xindex & AA_X_TYPE_MASK;
......
......@@ -21,6 +21,7 @@
#include "include/context.h"
#include "include/file.h"
#include "include/match.h"
#include "include/net.h"
#include "include/path.h"
#include "include/policy.h"
#include "include/label.h"
......@@ -566,6 +567,32 @@ static int __file_path_perm(const char *op, struct aa_label *label,
return error;
}
static int __file_sock_perm(const char *op, struct aa_label *label,
struct aa_label *flabel, struct file *file,
u32 request, u32 denied)
{
struct socket *sock = (struct socket *) file->private_data;
int error;
AA_BUG(!sock);
/* revalidation due to label out of date. No revocation at this time */
if (!denied && aa_label_is_subset(flabel, label))
return 0;
/* TODO: improve to skip profiles cached in flabel */
error = aa_sock_file_perm(label, op, request, sock);
if (denied) {
/* TODO: improve to skip profiles checked above */
/* check every profile in file label to is cached */
last_error(error, aa_sock_file_perm(flabel, op, request, sock));
}
if (!error)
update_file_ctx(file_ctx(file), label, request);
return error;
}
/**
* aa_file_perm - do permission revalidation check & audit for @file
* @op: operation being checked
......@@ -610,6 +637,9 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
error = __file_path_perm(op, label, flabel, file, request,
denied);
else if (S_ISSOCK(file_inode(file)->i_mode))
error = __file_sock_perm(op, label, flabel, file, request,
denied);
done:
rcu_read_unlock();
......
......@@ -27,7 +27,9 @@
#define AA_CLASS_NET 4
#define AA_CLASS_RLIMITS 5
#define AA_CLASS_DOMAIN 6
#define AA_CLASS_MOUNT 7
#define AA_CLASS_PTRACE 9
#define AA_CLASS_SIGNAL 10
#define AA_CLASS_LABEL 16
#define AA_CLASS_LAST AA_CLASS_LABEL
......
......@@ -71,6 +71,10 @@ enum audit_type {
#define OP_FMPROT "file_mprotect"
#define OP_INHERIT "file_inherit"
#define OP_PIVOTROOT "pivotroot"
#define OP_MOUNT "mount"
#define OP_UMOUNT "umount"
#define OP_CREATE "create"
#define OP_POST_CREATE "post_create"
#define OP_BIND "bind"
......@@ -86,6 +90,7 @@ enum audit_type {
#define OP_SHUTDOWN "socket_shutdown"
#define OP_PTRACE "ptrace"
#define OP_SIGNAL "signal"
#define OP_EXEC "exec"
......@@ -116,20 +121,36 @@ struct apparmor_audit_data {
/* these entries require a custom callback fn */
struct {
struct aa_label *peer;
struct {
const char *target;
kuid_t ouid;
} fs;
union {
struct {
kuid_t ouid;
const char *target;
} fs;
struct {
int type, protocol;
struct sock *peer_sk;
void *addr;
int addrlen;
} net;
int signal;
struct {
int rlim;
unsigned long max;
} rlim;
};
};
struct {
const char *name;
long pos;
struct aa_profile *profile;
const char *ns;
long pos;
} iface;
struct {
int rlim;
unsigned long max;
} rlim;
const char *src_name;
const char *type;
const char *trans;
const char *data;
unsigned long flags;
} mnt;
};
};
......
......@@ -15,6 +15,8 @@
#include <linux/binfmts.h>
#include <linux/types.h>
#include "label.h"
#ifndef __AA_DOMAIN_H
#define __AA_DOMAIN_H
......@@ -29,6 +31,9 @@ struct aa_domain {
#define AA_CHANGE_ONEXEC 4
#define AA_CHANGE_STACK 8
struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
const char **name);
int apparmor_bprm_set_creds(struct linux_binprm *bprm);
void aa_free_domain_entries(struct aa_domain *domain);
......
......@@ -27,8 +27,14 @@ struct aa_profile;
#define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \
AA_MAY_BE_READ | AA_MAY_BE_TRACED)
#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE)
#define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \
"segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \
"xcpu xfsz vtalrm prof winch io pwr sys emt lost"
int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
u32 request);
int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig);
#endif /* __AA_IPC_H */
......@@ -310,6 +310,7 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp);
#define FLAG_SHOW_MODE 1
#define FLAG_VIEW_SUBNS 2
#define FLAG_HIDDEN_UNCONFINED 4
#define FLAG_ABS_ROOT 8
int aa_label_snxprint(char *str, size_t size, struct aa_ns *view,
struct aa_label *label, int flags);
int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label,
......
/*
* AppArmor security module
*
* This file contains AppArmor file mediation function definitions.
*
* Copyright 2017 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.
*/
#ifndef __AA_MOUNT_H
#define __AA_MOUNT_H
#include <linux/fs.h>
#include <linux/path.h>
#include "domain.h"
#include "policy.h"
/* mount perms */
#define AA_MAY_PIVOTROOT 0x01
#define AA_MAY_MOUNT 0x02
#define AA_MAY_UMOUNT 0x04
#define AA_AUDIT_DATA 0x40
#define AA_MNT_CONT_MATCH 0x40
#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN)
int aa_remount(struct aa_label *label, const struct path *path,
unsigned long flags, void *data);
int aa_bind_mount(struct aa_label *label, const struct path *path,
const char *old_name, unsigned long flags);
int aa_mount_change_type(struct aa_label *label, const struct path *path,
unsigned long flags);
int aa_move_mount(struct aa_label *label, const struct path *path,
const char *old_name);
int aa_new_mount(struct aa_label *label, const char *dev_name,
const struct path *path, const char *type, unsigned long flags,
void *data);
int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags);
int aa_pivotroot(struct aa_label *label, const struct path *old_path,
const struct path *new_path);
#endif /* __AA_MOUNT_H */
/*
* AppArmor security module
*
* This file contains AppArmor network mediation definitions.
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2017 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.
*/
#ifndef __AA_NET_H
#define __AA_NET_H
#include <net/sock.h>
#include <linux/path.h>
#include "apparmorfs.h"
#include "label.h"
#include "perms.h"
#include "policy.h"
#define AA_MAY_SEND AA_MAY_WRITE
#define AA_MAY_RECEIVE AA_MAY_READ
#define AA_MAY_SHUTDOWN AA_MAY_DELETE
#define AA_MAY_CONNECT AA_MAY_OPEN
#define AA_MAY_ACCEPT 0x00100000
#define AA_MAY_BIND 0x00200000
#define AA_MAY_LISTEN 0x00400000
#define AA_MAY_SETOPT 0x01000000
#define AA_MAY_GETOPT 0x02000000
#define NET_PERMS_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \
AA_MAY_SHUTDOWN | AA_MAY_BIND | AA_MAY_LISTEN | \
AA_MAY_CONNECT | AA_MAY_ACCEPT | AA_MAY_SETATTR | \
AA_MAY_GETATTR | AA_MAY_SETOPT | AA_MAY_GETOPT)
#define NET_FS_PERMS (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \
AA_MAY_SHUTDOWN | AA_MAY_CONNECT | AA_MAY_RENAME |\
AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_CHMOD | \
AA_MAY_CHOWN | AA_MAY_CHGRP | AA_MAY_LOCK | \
AA_MAY_MPROT)
#define NET_PEER_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CONNECT | \
AA_MAY_ACCEPT)
struct aa_sk_ctx {
struct aa_label *label;
struct aa_label *peer;
struct path path;
};
#define SK_CTX(X) ((X)->sk_security)
#define SOCK_ctx(X) SOCK_INODE(X)->i_security
#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \
struct lsm_network_audit NAME ## _net = { .sk = (SK), \
.family = (F)}; \
DEFINE_AUDIT_DATA(NAME, \
((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \
LSM_AUDIT_DATA_NONE, \
OP); \
NAME.u.net = &(NAME ## _net); \
aad(&NAME)->net.type = (T); \
aad(&NAME)->net.protocol = (P)
#define DEFINE_AUDIT_SK(NAME, OP, SK) \
DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \
(SK)->sk_protocol)
/* struct aa_net - network confinement data
* @allow: basic network families permissions
* @audit: which network permissions to force audit
* @quiet: which network permissions to quiet rejects
*/
struct aa_net {
u16 allow[AF_MAX];
u16 audit[AF_MAX];
u16 quiet[AF_MAX];
};
extern struct aa_sfs_entry aa_sfs_entry_network[];
void audit_net_cb(struct audit_buffer *ab, void *va);
int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
u32 request, u16 family, int type);
int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family,
int type, int protocol);
static inline int aa_profile_af_sk_perm(struct aa_profile *profile,
struct common_audit_data *sa,
u32 request,
struct sock *sk)
{
return aa_profile_af_perm(profile, sa, request, sk->sk_family,
sk->sk_type);
}
int aa_sk_perm(const char *op, u32 request, struct sock *sk);
int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request,
struct socket *sock);
static inline void aa_free_net_rules(struct aa_net *new)
{
/* NOP */
}
#endif /* __AA_NET_H */
......@@ -135,9 +135,10 @@ extern struct aa_perms allperms;
void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask);
void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask);
void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
u32 mask);
void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
u32 chrsmask, const char **names, u32 namesmask);
u32 chrsmask, const char * const *names, u32 namesmask);
void aa_apply_modes_to_perms(struct aa_profile *profile,
struct aa_perms *perms);
void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
......
......@@ -30,6 +30,7 @@
#include "file.h"
#include "lib.h"
#include "label.h"
#include "net.h"
#include "perms.h"
#include "resource.h"
......@@ -111,6 +112,7 @@ struct aa_data {
* @policy: general match rules governing policy
* @file: The set of rules governing basic file access and domain transitions
* @caps: capabilities for the profile
* @net: network controls for the profile
* @rlimits: rlimits for the profile
*
* @dents: dentries for the profiles file entries in apparmorfs
......@@ -148,6 +150,7 @@ struct aa_profile {
struct aa_policydb policy;
struct aa_file_rules file;
struct aa_caps caps;
struct aa_net net;
struct aa_rlimit rlimits;
struct aa_loaddata *rawdata;
......@@ -220,6 +223,16 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile,
return 0;
}
static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile,
u16 AF) {
unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET);
u16 be_af = cpu_to_be16(AF);
if (!state)
return 0;
return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2);
}
/**
* aa_get_profile - increment refcount on profile @p
* @p: profile (MAYBE NULL)
......
#include <linux/signal.h>
#define SIGUNKNOWN 0
#define MAXMAPPED_SIG 35
/* provide a mapping of arch signal to internal signal # for mediation
* those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO
* map to the same entry those that may/or may not get a separate entry
*/
static const int sig_map[MAXMAPPED_SIG] = {
[0] = MAXMAPPED_SIG, /* existence test */
[SIGHUP] = 1,
[SIGINT] = 2,
[SIGQUIT] = 3,
[SIGILL] = 4,
[SIGTRAP] = 5, /* -, 5, - */
[SIGABRT] = 6, /* SIGIOT: -, 6, - */
[SIGBUS] = 7, /* 10, 7, 10 */
[SIGFPE] = 8,
[SIGKILL] = 9,
[SIGUSR1] = 10, /* 30, 10, 16 */
[SIGSEGV] = 11,
[SIGUSR2] = 12, /* 31, 12, 17 */
[SIGPIPE] = 13,
[SIGALRM] = 14,
[SIGTERM] = 15,
#ifdef SIGSTKFLT
[SIGSTKFLT] = 16, /* -, 16, - */
#endif
[SIGCHLD] = 17, /* 20, 17, 18. SIGCHLD -, -, 18 */
[SIGCONT] = 18, /* 19, 18, 25 */
[SIGSTOP] = 19, /* 17, 19, 23 */
[SIGTSTP] = 20, /* 18, 20, 24 */
[SIGTTIN] = 21, /* 21, 21, 26 */
[SIGTTOU] = 22, /* 22, 22, 27 */
[SIGURG] = 23, /* 16, 23, 21 */
[SIGXCPU] = 24, /* 24, 24, 30 */
[SIGXFSZ] = 25, /* 25, 25, 31 */
[SIGVTALRM] = 26, /* 26, 26, 28 */
[SIGPROF] = 27, /* 27, 27, 29 */
[SIGWINCH] = 28, /* 28, 28, 20 */
[SIGIO] = 29, /* SIGPOLL: 23, 29, 22 */
[SIGPWR] = 30, /* 29, 30, 19. SIGINFO 29, -, - */
#ifdef SIGSYS
[SIGSYS] = 31, /* 12, 31, 12. often SIG LOST/UNUSED */
#endif
#ifdef SIGEMT
[SIGEMT] = 32, /* 7, - , 7 */
#endif
#if defined(SIGLOST) && SIGPWR != SIGLOST /* sparc */
[SIGLOST] = 33, /* unused on Linux */
#endif
#if defined(SIGUNUSED) && \
defined(SIGLOST) && defined(SIGSYS) && SIGLOST != SIGSYS
[SIGUNUSED] = 34, /* -, 31, - */
#endif
};
/* this table is ordered post sig_map[sig] mapping */
static const char *const sig_names[MAXMAPPED_SIG + 1] = {
"unknown",
"hup",
"int",
"quit",
"ill",
"trap",
"abrt",
"bus",
"fpe",
"kill",
"usr1",
"segv",
"usr2",
"pipe",
"alrm",
"term",
"stkflt",
"chld",
"cont",
"stop",
"stp",
"ttin",
"ttou",
"urg",
"xcpu",
"xfsz",
"vtalrm",
"prof",
"winch",
"io",
"pwr",
"sys",
"emt",
"lost",
"unused",
"exists", /* always last existence test mapped to MAXMAPPED_SIG */
};
......@@ -20,6 +20,7 @@
#include "include/context.h"
#include "include/policy.h"
#include "include/ipc.h"
#include "include/sig_names.h"
/**
* audit_ptrace_mask - convert mask to permission string
......@@ -121,3 +122,101 @@ int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
}
static inline int map_signal_num(int sig)
{
if (sig > SIGRTMAX)
return SIGUNKNOWN;
else if (sig >= SIGRTMIN)
return sig - SIGRTMIN + 128; /* rt sigs mapped to 128 */
else if (sig <= MAXMAPPED_SIG)
return sig_map[sig];
return SIGUNKNOWN;
}
/**
* audit_file_mask - convert mask to permission string
* @buffer: buffer to write string to (NOT NULL)
* @mask: permission mask to convert
*/
static void audit_signal_mask(struct audit_buffer *ab, u32 mask)
{
if (mask & MAY_READ)
audit_log_string(ab, "receive");
if (mask & MAY_WRITE)
audit_log_string(ab, "send");
}
/**
* audit_cb - call back for signal specific audit fields
* @ab: audit_buffer (NOT NULL)
* @va: audit struct to audit values of (NOT NULL)
*/
static void audit_signal_cb(struct audit_buffer *ab, void *va)
{
struct common_audit_data *sa = va;
if (aad(sa)->request & AA_SIGNAL_PERM_MASK) {
audit_log_format(ab, " requested_mask=");
audit_signal_mask(ab, aad(sa)->request);
if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) {
audit_log_format(ab, " denied_mask=");
audit_signal_mask(ab, aad(sa)->denied);
}
}
if (aad(sa)->signal <= MAXMAPPED_SIG)
audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]);
else
audit_log_format(ab, " signal=rtmin+%d",
aad(sa)->signal - 128);
audit_log_format(ab, " peer=");
aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
FLAGS_NONE, GFP_ATOMIC);
}
/* TODO: update to handle compound name&name2, conditionals */
static void profile_match_signal(struct aa_profile *profile, const char *label,
int signal, struct aa_perms *perms)
{
unsigned int state;
/* TODO: secondary cache check <profile, profile, perm> */
state = aa_dfa_next(profile->policy.dfa,
profile->policy.start[AA_CLASS_SIGNAL],
signal);
state = aa_dfa_match(profile->policy.dfa, state, label);
aa_compute_perms(profile->policy.dfa, state, perms);
}
static int profile_signal_perm(struct aa_profile *profile,
struct aa_profile *peer, u32 request,
struct common_audit_data *sa)
{
struct aa_perms perms;
if (profile_unconfined(profile) ||
!PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
return 0;
aad(sa)->peer = &peer->label;
profile_match_signal(profile, peer->base.hname, aad(sa)->signal,
&perms);
aa_apply_modes_to_perms(profile, &perms);
return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
}
static int aa_signal_cross_perm(struct aa_profile *sender,
struct aa_profile *target,
struct common_audit_data *sa)
{
return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa),
profile_signal_perm(target, sender, MAY_READ, sa));
}
int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig)
{
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL);
aad(&sa)->signal = map_signal_num(sig);
return xcheck_labels_profiles(sender, target, aa_signal_cross_perm,
&sa);
}
......@@ -49,7 +49,7 @@ static void free_proxy(struct aa_proxy *proxy)
/* p->label will not updated any more as p is dead */
aa_put_label(rcu_dereference_protected(proxy->label, true));
memset(proxy, 0, sizeof(*proxy));
proxy->label = (struct aa_label *) PROXY_POISON;
RCU_INIT_POINTER(proxy->label, (struct aa_label *)PROXY_POISON);
kfree(proxy);
}
}
......@@ -1450,9 +1450,11 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp)
* cached label name is present and visible
* @label->hname only exists if label is namespace hierachical
*/
static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label)
static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label,
int flags)
{
if (label->hname && labels_ns(label) == ns)
if (label->hname && (!ns || labels_ns(label) == ns) &&
!(flags & ~FLAG_SHOW_MODE))
return true;
return false;
......@@ -1495,7 +1497,7 @@ static int aa_profile_snxprint(char *str, size_t size, struct aa_ns *view,
view = profiles_ns(profile);
if (view != profile->ns &&
(!prev_ns || (prev_ns && *prev_ns != profile->ns))) {
(!prev_ns || (*prev_ns != profile->ns))) {
if (prev_ns)
*prev_ns = profile->ns;
ns_name = aa_ns_name(view, profile->ns,
......@@ -1605,8 +1607,13 @@ int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns,
AA_BUG(!str && size != 0);
AA_BUG(!label);
if (!ns)
if (flags & FLAG_ABS_ROOT) {
ns = root_ns;
len = snprintf(str, size, "=");
update_for_len(total, len, size, str);
} else if (!ns) {
ns = labels_ns(label);
}
label_for_each(i, label, profile) {
if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) {
......@@ -1710,10 +1717,8 @@ void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns,
AA_BUG(!ab);
AA_BUG(!label);
if (!ns)
ns = labels_ns(label);
if (!use_label_hname(ns, label) || display_mode(ns, label, flags)) {
if (!use_label_hname(ns, label, flags) ||
display_mode(ns, label, flags)) {
len = aa_label_asxprint(&name, ns, label, flags, gfp);
if (len == -1) {
AA_DEBUG("label print error");
......@@ -1738,10 +1743,7 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
AA_BUG(!f);
AA_BUG(!label);
if (!ns)
ns = labels_ns(label);
if (!use_label_hname(ns, label)) {
if (!use_label_hname(ns, label, flags)) {
char *str;
int len;
......@@ -1764,10 +1766,7 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
{
AA_BUG(!label);
if (!ns)
ns = labels_ns(label);
if (!use_label_hname(ns, label)) {
if (!use_label_hname(ns, label, flags)) {
char *str;
int len;
......@@ -1874,6 +1873,9 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
if (*str == '&')
str++;
}
if (*str == '=')
base = &root_ns->unconfined->label;
error = vec_setup(profile, vec, len, gfp);
if (error)
return ERR_PTR(error);
......
......@@ -211,7 +211,8 @@ void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask)
*str = '\0';
}
void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask)
void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
u32 mask)
{
const char *fmt = "%s";
unsigned int i, perm = 1;
......@@ -229,7 +230,7 @@ void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask)
}
void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
u32 chrsmask, const char **names, u32 namesmask)
u32 chrsmask, const char * const *names, u32 namesmask)
{
char str[33];
......
This diff is collapsed.
This diff is collapsed.
/*
* AppArmor security module
*
* This file contains AppArmor network mediation
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2017 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.
*/
#include "include/apparmor.h"
#include "include/audit.h"
#include "include/context.h"
#include "include/label.h"
#include "include/net.h"
#include "include/policy.h"
#include "net_names.h"
struct aa_sfs_entry aa_sfs_entry_network[] = {
AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK),
{ }
};
static const char * const net_mask_names[] = {
"unknown",
"send",
"receive",
"unknown",
"create",
"shutdown",
"connect",
"unknown",
"setattr",
"getattr",
"setcred",
"getcred",
"chmod",
"chown",
"chgrp",
"lock",
"mmap",
"mprot",
"unknown",
"unknown",
"accept",
"bind",
"listen",
"unknown",
"setopt",
"getopt",
"unknown",
"unknown",
"unknown",
"unknown",
"unknown",
"unknown",
};
/* audit callback for net specific fields */
void audit_net_cb(struct audit_buffer *ab, void *va)
{
struct common_audit_data *sa = va;
audit_log_format(ab, " family=");
if (address_family_names[sa->u.net->family])
audit_log_string(ab, address_family_names[sa->u.net->family]);
else
audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family);
audit_log_format(ab, " sock_type=");
if (sock_type_names[aad(sa)->net.type])
audit_log_string(ab, sock_type_names[aad(sa)->net.type]);
else
audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type);
audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol);
if (aad(sa)->request & NET_PERMS_MASK) {
audit_log_format(ab, " requested_mask=");
aa_audit_perm_mask(ab, aad(sa)->request, NULL, 0,
net_mask_names, NET_PERMS_MASK);
if (aad(sa)->denied & NET_PERMS_MASK) {
audit_log_format(ab, " denied_mask=");
aa_audit_perm_mask(ab, aad(sa)->denied, NULL, 0,
net_mask_names, NET_PERMS_MASK);
}
}
if (aad(sa)->peer) {
audit_log_format(ab, " peer=");
aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
FLAGS_NONE, GFP_ATOMIC);
}
}
/* Generic af perm */
int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
u32 request, u16 family, int type)
{
struct aa_perms perms = { };
AA_BUG(family >= AF_MAX);
AA_BUG(type < 0 || type >= SOCK_MAX);
if (profile_unconfined(profile))
return 0;
perms.allow = (profile->net.allow[family] & (1 << type)) ?
ALL_PERMS_MASK : 0;
perms.audit = (profile->net.audit[family] & (1 << type)) ?
ALL_PERMS_MASK : 0;
perms.quiet = (profile->net.quiet[family] & (1 << type)) ?
ALL_PERMS_MASK : 0;
aa_apply_modes_to_perms(profile, &perms);
return aa_check_perms(profile, &perms, request, sa, audit_net_cb);
}
int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family,
int type, int protocol)
{
struct aa_profile *profile;
DEFINE_AUDIT_NET(sa, op, NULL, family, type, protocol);
return fn_for_each_confined(label, profile,
aa_profile_af_perm(profile, &sa, request, family,
type));
}
static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request,
struct sock *sk)
{
struct aa_profile *profile;
DEFINE_AUDIT_SK(sa, op, sk);
AA_BUG(!label);
AA_BUG(!sk);
if (unconfined(label))
return 0;
return fn_for_each_confined(label, profile,
aa_profile_af_sk_perm(profile, &sa, request, sk));
}
int aa_sk_perm(const char *op, u32 request, struct sock *sk)
{
struct aa_label *label;
int error;
AA_BUG(!sk);
AA_BUG(in_interrupt());
/* TODO: switch to begin_current_label ???? */
label = begin_current_label_crit_section();
error = aa_label_sk_perm(label, op, request, sk);
end_current_label_crit_section(label);
return error;
}
int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request,
struct socket *sock)
{
AA_BUG(!label);
AA_BUG(!sock);
AA_BUG(!sock->sk);
return aa_label_sk_perm(label, op, request, sock->sk);
}
......@@ -289,85 +289,6 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
return NULL;
}
/**
* aa_new_null_profile - create or find a null-X learning profile
* @parent: profile that caused this profile to be created (NOT NULL)
* @hat: true if the null- learning profile is a hat
* @base: name to base the null profile off of
* @gfp: type of allocation
*
* Find/Create a null- complain mode profile used in learning mode. The
* name of the profile is unique and follows the format of parent//null-XXX.
* where XXX is based on the @name or if that fails or is not supplied
* a unique number
*
* null profiles are added to the profile list but the list does not
* hold a count on them so that they are automatically released when
* not in use.
*
* Returns: new refcounted profile else NULL on failure
*/
struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
const char *base, gfp_t gfp)
{
struct aa_profile *profile;
char *name;
AA_BUG(!parent);
if (base) {
name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base),
gfp);
if (name) {
sprintf(name, "%s//null-%s", parent->base.hname, base);
goto name;
}
/* fall through to try shorter uniq */
}
name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp);
if (!name)
return NULL;
sprintf(name, "%s//null-%x", parent->base.hname,
atomic_inc_return(&parent->ns->uniq_null));
name:
/* lookup to see if this is a dup creation */
profile = aa_find_child(parent, basename(name));
if (profile)
goto out;
profile = aa_alloc_profile(name, NULL, gfp);
if (!profile)
goto fail;
profile->mode = APPARMOR_COMPLAIN;
profile->label.flags |= FLAG_NULL;
if (hat)
profile->label.flags |= FLAG_HAT;
profile->path_flags = parent->path_flags;
/* released on free_profile */
rcu_assign_pointer(profile->parent, aa_get_profile(parent));
profile->ns = aa_get_ns(parent->ns);
profile->file.dfa = aa_get_dfa(nulldfa);
profile->policy.dfa = aa_get_dfa(nulldfa);
mutex_lock(&profile->ns->lock);
__add_profile(&parent->base.profiles, profile);
mutex_unlock(&profile->ns->lock);
/* refcount released by caller */
out:
kfree(name);
return profile;
fail:
aa_free_profile(profile);
return NULL;
}
/* TODO: profile accounting - setup in remove */
/**
......@@ -558,6 +479,93 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
return profile;
}
/**
* aa_new_null_profile - create or find a null-X learning profile
* @parent: profile that caused this profile to be created (NOT NULL)
* @hat: true if the null- learning profile is a hat
* @base: name to base the null profile off of
* @gfp: type of allocation
*
* Find/Create a null- complain mode profile used in learning mode. The
* name of the profile is unique and follows the format of parent//null-XXX.
* where XXX is based on the @name or if that fails or is not supplied
* a unique number
*
* null profiles are added to the profile list but the list does not
* hold a count on them so that they are automatically released when
* not in use.
*
* Returns: new refcounted profile else NULL on failure
*/
struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
const char *base, gfp_t gfp)
{
struct aa_profile *p, *profile;
const char *bname;
char *name;
AA_BUG(!parent);
if (base) {
name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base),
gfp);
if (name) {
sprintf(name, "%s//null-%s", parent->base.hname, base);
goto name;
}
/* fall through to try shorter uniq */
}
name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp);
if (!name)
return NULL;
sprintf(name, "%s//null-%x", parent->base.hname,
atomic_inc_return(&parent->ns->uniq_null));
name:
/* lookup to see if this is a dup creation */
bname = basename(name);
profile = aa_find_child(parent, bname);
if (profile)
goto out;
profile = aa_alloc_profile(name, NULL, gfp);
if (!profile)
goto fail;
profile->mode = APPARMOR_COMPLAIN;
profile->label.flags |= FLAG_NULL;
if (hat)
profile->label.flags |= FLAG_HAT;
profile->path_flags = parent->path_flags;
/* released on free_profile */
rcu_assign_pointer(profile->parent, aa_get_profile(parent));
profile->ns = aa_get_ns(parent->ns);
profile->file.dfa = aa_get_dfa(nulldfa);
profile->policy.dfa = aa_get_dfa(nulldfa);
mutex_lock(&profile->ns->lock);
p = __find_child(&parent->base.profiles, bname);
if (p) {
aa_free_profile(profile);
profile = aa_get_profile(p);
} else {
__add_profile(&parent->base.profiles, profile);
}
mutex_unlock(&profile->ns->lock);
/* refcount released by caller */
out:
kfree(name);
return profile;
fail:
aa_free_profile(profile);
return NULL;
}
/**
* replacement_allowed - test to see if replacement is allowed
* @profile: profile to test if it can be replaced (MAYBE NULL)
......
......@@ -112,6 +112,8 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name)
ns->unconfined->label.flags |= FLAG_IX_ON_NAME_ERROR |
FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED;
ns->unconfined->mode = APPARMOR_UNCONFINED;
ns->unconfined->file.dfa = aa_get_dfa(nulldfa);
ns->unconfined->policy.dfa = aa_get_dfa(nulldfa);
/* ns and ns->unconfined share ns->unconfined refcount */
ns->unconfined->ns = ns;
......
......@@ -85,9 +85,9 @@ static void audit_cb(struct audit_buffer *ab, void *va)
audit_log_format(ab, " ns=");
audit_log_untrustedstring(ab, aad(sa)->iface.ns);
}
if (aad(sa)->iface.name) {
if (aad(sa)->name) {
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, aad(sa)->iface.name);
audit_log_untrustedstring(ab, aad(sa)->name);
}
if (aad(sa)->iface.pos)
audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos);
......@@ -114,9 +114,9 @@ static int audit_iface(struct aa_profile *new, const char *ns_name,
aad(&sa)->iface.pos = e->pos - e->start;
aad(&sa)->iface.ns = ns_name;
if (new)
aad(&sa)->iface.name = new->base.hname;
aad(&sa)->name = new->base.hname;
else
aad(&sa)->iface.name = name;
aad(&sa)->name = name;
aad(&sa)->info = info;
aad(&sa)->error = error;
......@@ -275,6 +275,19 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name)
return 0;
}
static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name)
{
if (unpack_nameX(e, AA_U16, name)) {
if (!inbounds(e, sizeof(u16)))
return 0;
if (data)
*data = le16_to_cpu(get_unaligned((__le16 *) e->pos));
e->pos += sizeof(u16);
return 1;
}
return 0;
}
static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
{
if (unpack_nameX(e, AA_U32, name)) {
......@@ -448,7 +461,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
*/
static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
{
void *pos = e->pos;
void *saved_pos = e->pos;
/* exec table is optional */
if (unpack_nameX(e, AA_STRUCT, "xtable")) {
......@@ -511,7 +524,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
fail:
aa_free_domain_entries(&profile->file.trans);
e->pos = pos;
e->pos = saved_pos;
return 0;
}
......@@ -583,7 +596,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
{
struct aa_profile *profile = NULL;
const char *tmpname, *tmpns = NULL, *name = NULL;
size_t ns_len;
const char *info = "failed to unpack profile";
size_t size = 0, ns_len;
struct rhashtable_params params = { 0 };
char *key = NULL;
struct aa_data *data;
......@@ -604,8 +618,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len);
if (tmpns) {
*ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL);
if (!*ns_name)
if (!*ns_name) {
info = "out of memory";
goto fail;
}
name = tmpname;
}
......@@ -624,12 +640,15 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
if (IS_ERR(profile->xmatch)) {
error = PTR_ERR(profile->xmatch);
profile->xmatch = NULL;
info = "bad xmatch";
goto fail;
}
/* xmatch_len is not optional if xmatch is set */
if (profile->xmatch) {
if (!unpack_u32(e, &tmp, NULL))
if (!unpack_u32(e, &tmp, NULL)) {
info = "missing xmatch len";
goto fail;
}
profile->xmatch_len = tmp;
}
......@@ -637,8 +656,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
(void) unpack_str(e, &profile->disconnected, "disconnected");
/* per profile debug flags (complain, audit) */
if (!unpack_nameX(e, AA_STRUCT, "flags"))
if (!unpack_nameX(e, AA_STRUCT, "flags")) {
info = "profile missing flags";
goto fail;
}
info = "failed to unpack profile flags";
if (!unpack_u32(e, &tmp, NULL))
goto fail;
if (tmp & PACKED_FLAG_HAT)
......@@ -667,6 +689,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
/* set a default value if path_flags field is not present */
profile->path_flags = PATH_MEDIATE_DELETED;
info = "failed to unpack profile capabilities";
if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL))
goto fail;
if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL))
......@@ -676,6 +699,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
if (!unpack_u32(e, &tmpcap.cap[0], NULL))
goto fail;
info = "failed to unpack upper profile capabilities";
if (unpack_nameX(e, AA_STRUCT, "caps64")) {
/* optional upper half of 64 bit caps */
if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL))
......@@ -690,6 +714,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
goto fail;
}
info = "failed to unpack extended profile capabilities";
if (unpack_nameX(e, AA_STRUCT, "capsx")) {
/* optional extended caps mediation mask */
if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL))
......@@ -700,11 +725,46 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
goto fail;
}
if (!unpack_rlimits(e, profile))
if (!unpack_rlimits(e, profile)) {
info = "failed to unpack profile rlimits";
goto fail;
}
size = unpack_array(e, "net_allowed_af");
if (size) {
for (i = 0; i < size; i++) {
/* discard extraneous rules that this kernel will
* never request
*/
if (i >= AF_MAX) {
u16 tmp;
if (!unpack_u16(e, &tmp, NULL) ||
!unpack_u16(e, &tmp, NULL) ||
!unpack_u16(e, &tmp, NULL))
goto fail;
continue;
}
if (!unpack_u16(e, &profile->net.allow[i], NULL))
goto fail;
if (!unpack_u16(e, &profile->net.audit[i], NULL))
goto fail;
if (!unpack_u16(e, &profile->net.quiet[i], NULL))
goto fail;
}
if (!unpack_nameX(e, AA_ARRAYEND, NULL))
goto fail;
}
if (VERSION_LT(e->version, v7)) {
/* pre v7 policy always allowed these */
profile->net.allow[AF_UNIX] = 0xffff;
profile->net.allow[AF_NETLINK] = 0xffff;
}
if (unpack_nameX(e, AA_STRUCT, "policydb")) {
/* generic policy dfa - optional and may be NULL */
info = "failed to unpack policydb";
profile->policy.dfa = unpack_dfa(e);
if (IS_ERR(profile->policy.dfa)) {
error = PTR_ERR(profile->policy.dfa);
......@@ -734,6 +794,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
if (IS_ERR(profile->file.dfa)) {
error = PTR_ERR(profile->file.dfa);
profile->file.dfa = NULL;
info = "failed to unpack profile file rules";
goto fail;
} else if (profile->file.dfa) {
if (!unpack_u32(e, &profile->file.start, "dfa_start"))
......@@ -746,10 +807,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
} else
profile->file.dfa = aa_get_dfa(nulldfa);
if (!unpack_trans_table(e, profile))
if (!unpack_trans_table(e, profile)) {
info = "failed to unpack profile transition table";
goto fail;
}
if (unpack_nameX(e, AA_STRUCT, "data")) {
info = "out of memory";
profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
if (!profile->data)
goto fail;
......@@ -761,8 +825,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
params.hashfn = strhash;
params.obj_cmpfn = datacmp;
if (rhashtable_init(profile->data, &params))
if (rhashtable_init(profile->data, &params)) {
info = "failed to init key, value hash table";
goto fail;
}
while (unpack_strdup(e, &key, NULL)) {
data = kzalloc(sizeof(*data), GFP_KERNEL);
......@@ -784,12 +850,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
profile->data->p);
}
if (!unpack_nameX(e, AA_STRUCTEND, NULL))
if (!unpack_nameX(e, AA_STRUCTEND, NULL)) {
info = "failed to unpack end of key, value data table";
goto fail;
}
}
if (!unpack_nameX(e, AA_STRUCTEND, NULL))
if (!unpack_nameX(e, AA_STRUCTEND, NULL)) {
info = "failed to unpack end of profile";
goto fail;
}
return profile;
......@@ -798,8 +868,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
name = NULL;
else if (!name)
name = "unknown";
audit_iface(profile, NULL, name, "failed to unpack profile", e,
error);
audit_iface(profile, NULL, name, info, e, error);
aa_free_profile(profile);
return ERR_PTR(error);
......@@ -832,7 +901,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
* if not specified use previous version
* Mask off everything that is not kernel abi version
*/
if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) {
if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v7)) {
audit_iface(NULL, NULL, NULL, "unsupported interface version",
e, error);
return error;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment