mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 16:35:02 +01:00
1123 lines
32 KiB
Diff
1123 lines
32 KiB
Diff
![]() |
From 3de9004cf346b3f79065f51b9ecbd03bae92cb42 Mon Sep 17 00:00:00 2001
|
||
|
From: John Johansen <john.johansen@canonical.com>
|
||
|
Date: Tue, 18 Jul 2017 23:18:33 -0700
|
||
|
Subject: [PATCH 1/2] apparmor: add base infastructure for socket mediation
|
||
|
|
||
|
Provide a basic mediation of sockets. This is not a full net mediation
|
||
|
but just whether a spcific family of socket can be used by an
|
||
|
application, along with setting up some basic infrastructure for
|
||
|
network mediation to follow.
|
||
|
|
||
|
the user space rule hav the basic form of
|
||
|
NETWORK RULE = [ QUALIFIERS ] 'network' [ DOMAIN ]
|
||
|
[ TYPE | PROTOCOL ]
|
||
|
|
||
|
DOMAIN = ( 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' |
|
||
|
'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' |
|
||
|
'netbeui' | 'security' | 'key' | 'packet' | 'ash' |
|
||
|
'econet' | 'atmsvc' | 'sna' | 'irda' | 'pppox' |
|
||
|
'wanpipe' | 'bluetooth' | 'netlink' | 'unix' | 'rds' |
|
||
|
'llc' | 'can' | 'tipc' | 'iucv' | 'rxrpc' | 'isdn' |
|
||
|
'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' |
|
||
|
'vsock' | 'mpls' | 'ib' | 'kcm' ) ','
|
||
|
|
||
|
TYPE = ( 'stream' | 'dgram' | 'seqpacket' | 'rdm' | 'raw' |
|
||
|
'packet' )
|
||
|
|
||
|
PROTOCOL = ( 'tcp' | 'udp' | 'icmp' )
|
||
|
|
||
|
eg.
|
||
|
network,
|
||
|
network inet,
|
||
|
|
||
|
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||
|
Acked-by: Seth Arnold <seth.arnold@canonical.com>
|
||
|
---
|
||
|
security/apparmor/.gitignore | 1 +
|
||
|
security/apparmor/Makefile | 43 ++++-
|
||
|
security/apparmor/apparmorfs.c | 1 +
|
||
|
security/apparmor/file.c | 30 +++
|
||
|
security/apparmor/include/audit.h | 26 ++-
|
||
|
security/apparmor/include/net.h | 114 +++++++++++
|
||
|
security/apparmor/include/perms.h | 5 +-
|
||
|
security/apparmor/include/policy.h | 13 ++
|
||
|
security/apparmor/lib.c | 5 +-
|
||
|
security/apparmor/lsm.c | 387 +++++++++++++++++++++++++++++++++++++
|
||
|
security/apparmor/net.c | 184 ++++++++++++++++++
|
||
|
security/apparmor/policy_unpack.c | 47 ++++-
|
||
|
12 files changed, 840 insertions(+), 16 deletions(-)
|
||
|
create mode 100644 security/apparmor/include/net.h
|
||
|
create mode 100644 security/apparmor/net.c
|
||
|
|
||
|
diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore
|
||
|
index 9cdec70d72b8..d5b291e94264 100644
|
||
|
--- a/security/apparmor/.gitignore
|
||
|
+++ b/security/apparmor/.gitignore
|
||
|
@@ -1,5 +1,6 @@
|
||
|
#
|
||
|
# Generated include files
|
||
|
#
|
||
|
+net_names.h
|
||
|
capability_names.h
|
||
|
rlim_names.h
|
||
|
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
|
||
|
index 9a6b4033d52b..e7ff2183532a 100644
|
||
|
--- a/security/apparmor/Makefile
|
||
|
+++ b/security/apparmor/Makefile
|
||
|
@@ -5,11 +5,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 mount.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
|
||
|
@@ -62,6 +95,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
|
||
|
@@ -69,3 +103,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)
|
||
|
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
||
|
index caaf51dda648..518d5928661b 100644
|
||
|
--- a/security/apparmor/apparmorfs.c
|
||
|
+++ b/security/apparmor/apparmorfs.c
|
||
|
@@ -2202,6 +2202,7 @@ 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),
|
||
|
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
|
||
|
index 3382518b87fa..db80221891c6 100644
|
||
|
--- a/security/apparmor/file.c
|
||
|
+++ b/security/apparmor/file.c
|
||
|
@@ -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();
|
||
|
|
||
|
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
|
||
|
index 620e81169659..ff4316e1068d 100644
|
||
|
--- a/security/apparmor/include/audit.h
|
||
|
+++ b/security/apparmor/include/audit.h
|
||
|
@@ -121,21 +121,29 @@ 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 {
|
||
|
struct aa_profile *profile;
|
||
|
const char *ns;
|
||
|
long pos;
|
||
|
} iface;
|
||
|
- int signal;
|
||
|
- struct {
|
||
|
- int rlim;
|
||
|
- unsigned long max;
|
||
|
- } rlim;
|
||
|
struct {
|
||
|
const char *src_name;
|
||
|
const char *type;
|
||
|
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
|
||
|
new file mode 100644
|
||
|
index 000000000000..140c8efcf364
|
||
|
--- /dev/null
|
||
|
+++ b/security/apparmor/include/net.h
|
||
|
@@ -0,0 +1,114 @@
|
||
|
+/*
|
||
|
+ * 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 */
|
||
|
diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h
|
||
|
index 2b27bb79aec4..af04d5a7d73d 100644
|
||
|
--- a/security/apparmor/include/perms.h
|
||
|
+++ b/security/apparmor/include/perms.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,
|
||
|
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
|
||
|
index 17fe41a9cac3..4364088a0b9e 100644
|
||
|
--- a/security/apparmor/include/policy.h
|
||
|
+++ b/security/apparmor/include/policy.h
|
||
|
@@ -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)
|
||
|
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
|
||
|
index 08ca26bcca77..8818621b5d95 100644
|
||
|
--- a/security/apparmor/lib.c
|
||
|
+++ b/security/apparmor/lib.c
|
||
|
@@ -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];
|
||
|
|
||
|
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
|
||
|
index 1346ee5be04f..72b915dfcaf7 100644
|
||
|
--- a/security/apparmor/lsm.c
|
||
|
+++ b/security/apparmor/lsm.c
|
||
|
@@ -33,6 +33,7 @@
|
||
|
#include "include/context.h"
|
||
|
#include "include/file.h"
|
||
|
#include "include/ipc.h"
|
||
|
+#include "include/net.h"
|
||
|
#include "include/path.h"
|
||
|
#include "include/label.h"
|
||
|
#include "include/policy.h"
|
||
|
@@ -736,6 +737,368 @@ static int apparmor_task_kill(struct task_struct *target, struct siginfo *info,
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
+/**
|
||
|
+ * apparmor_sk_alloc_security - allocate and attach the sk_security field
|
||
|
+ */
|
||
|
+static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t flags)
|
||
|
+{
|
||
|
+ struct aa_sk_ctx *ctx;
|
||
|
+
|
||
|
+ ctx = kzalloc(sizeof(*ctx), flags);
|
||
|
+ if (!ctx)
|
||
|
+ return -ENOMEM;
|
||
|
+
|
||
|
+ SK_CTX(sk) = ctx;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_sk_free_security - free the sk_security field
|
||
|
+ */
|
||
|
+static void apparmor_sk_free_security(struct sock *sk)
|
||
|
+{
|
||
|
+ struct aa_sk_ctx *ctx = SK_CTX(sk);
|
||
|
+
|
||
|
+ SK_CTX(sk) = NULL;
|
||
|
+ aa_put_label(ctx->label);
|
||
|
+ aa_put_label(ctx->peer);
|
||
|
+ path_put(&ctx->path);
|
||
|
+ kfree(ctx);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_clone_security - clone the sk_security field
|
||
|
+ */
|
||
|
+static void apparmor_sk_clone_security(const struct sock *sk,
|
||
|
+ struct sock *newsk)
|
||
|
+{
|
||
|
+ struct aa_sk_ctx *ctx = SK_CTX(sk);
|
||
|
+ struct aa_sk_ctx *new = SK_CTX(newsk);
|
||
|
+
|
||
|
+ new->label = aa_get_label(ctx->label);
|
||
|
+ new->peer = aa_get_label(ctx->peer);
|
||
|
+ new->path = ctx->path;
|
||
|
+ path_get(&new->path);
|
||
|
+}
|
||
|
+
|
||
|
+static int aa_sock_create_perm(struct aa_label *label, int family, int type,
|
||
|
+ int protocol)
|
||
|
+{
|
||
|
+ AA_BUG(!label);
|
||
|
+ AA_BUG(in_interrupt());
|
||
|
+
|
||
|
+ return aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type,
|
||
|
+ protocol);
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_create - check perms before creating a new socket
|
||
|
+ */
|
||
|
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
|
||
|
+{
|
||
|
+ struct aa_label *label;
|
||
|
+ int error = 0;
|
||
|
+
|
||
|
+ label = begin_current_label_crit_section();
|
||
|
+ if (!(kern || unconfined(label)))
|
||
|
+ error = aa_sock_create_perm(label, family, type, protocol);
|
||
|
+ end_current_label_crit_section(label);
|
||
|
+
|
||
|
+ return error;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_post_create - setup the per-socket security struct
|
||
|
+ *
|
||
|
+ * Note:
|
||
|
+ * - kernel sockets currently labeled unconfined but we may want to
|
||
|
+ * move to a special kernel label
|
||
|
+ * - socket may not have sk here if created with sock_create_lite or
|
||
|
+ * sock_alloc. These should be accept cases which will be handled in
|
||
|
+ * sock_graft.
|
||
|
+ */
|
||
|
+static int apparmor_socket_post_create(struct socket *sock, int family,
|
||
|
+ int type, int protocol, int kern)
|
||
|
+{
|
||
|
+ struct aa_label *label;
|
||
|
+
|
||
|
+ if (kern) {
|
||
|
+ struct aa_ns *ns = aa_get_current_ns();
|
||
|
+
|
||
|
+ label = aa_get_label(ns_unconfined(ns));
|
||
|
+ aa_put_ns(ns);
|
||
|
+ } else
|
||
|
+ label = aa_get_current_label();
|
||
|
+
|
||
|
+ if (sock->sk) {
|
||
|
+ struct aa_sk_ctx *ctx = SK_CTX(sock->sk);
|
||
|
+
|
||
|
+ aa_put_label(ctx->label);
|
||
|
+ ctx->label = aa_get_label(label);
|
||
|
+ }
|
||
|
+ aa_put_label(label);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_bind - check perms before bind addr to socket
|
||
|
+ */
|
||
|
+static int apparmor_socket_bind(struct socket *sock,
|
||
|
+ struct sockaddr *address, int addrlen)
|
||
|
+{
|
||
|
+ AA_BUG(!sock);
|
||
|
+ AA_BUG(!sock->sk);
|
||
|
+ AA_BUG(!address);
|
||
|
+ AA_BUG(in_interrupt());
|
||
|
+
|
||
|
+ return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_connect - check perms before connecting @sock to @address
|
||
|
+ */
|
||
|
+static int apparmor_socket_connect(struct socket *sock,
|
||
|
+ struct sockaddr *address, int addrlen)
|
||
|
+{
|
||
|
+ AA_BUG(!sock);
|
||
|
+ AA_BUG(!sock->sk);
|
||
|
+ AA_BUG(!address);
|
||
|
+ AA_BUG(in_interrupt());
|
||
|
+
|
||
|
+ return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_list - check perms before allowing listen
|
||
|
+ */
|
||
|
+static int apparmor_socket_listen(struct socket *sock, int backlog)
|
||
|
+{
|
||
|
+ AA_BUG(!sock);
|
||
|
+ AA_BUG(!sock->sk);
|
||
|
+ AA_BUG(in_interrupt());
|
||
|
+
|
||
|
+ return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_accept - check perms before accepting a new connection.
|
||
|
+ *
|
||
|
+ * Note: while @newsock is created and has some information, the accept
|
||
|
+ * has not been done.
|
||
|
+ */
|
||
|
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
|
||
|
+{
|
||
|
+ AA_BUG(!sock);
|
||
|
+ AA_BUG(!sock->sk);
|
||
|
+ AA_BUG(!newsock);
|
||
|
+ AA_BUG(in_interrupt());
|
||
|
+
|
||
|
+ return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk);
|
||
|
+}
|
||
|
+
|
||
|
+static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock,
|
||
|
+ struct msghdr *msg, int size)
|
||
|
+{
|
||
|
+ AA_BUG(!sock);
|
||
|
+ AA_BUG(!sock->sk);
|
||
|
+ AA_BUG(!msg);
|
||
|
+ AA_BUG(in_interrupt());
|
||
|
+
|
||
|
+ return aa_sk_perm(op, request, sock->sk);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_sendmsg - check perms before sending msg to another socket
|
||
|
+ */
|
||
|
+static int apparmor_socket_sendmsg(struct socket *sock,
|
||
|
+ struct msghdr *msg, int size)
|
||
|
+{
|
||
|
+ return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_recvmsg - check perms before receiving a message
|
||
|
+ */
|
||
|
+static int apparmor_socket_recvmsg(struct socket *sock,
|
||
|
+ struct msghdr *msg, int size, int flags)
|
||
|
+{
|
||
|
+ return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size);
|
||
|
+}
|
||
|
+
|
||
|
+/* revaliation, get/set attr, shutdown */
|
||
|
+static int aa_sock_perm(const char *op, u32 request, struct socket *sock)
|
||
|
+{
|
||
|
+ AA_BUG(!sock);
|
||
|
+ AA_BUG(!sock->sk);
|
||
|
+ AA_BUG(in_interrupt());
|
||
|
+
|
||
|
+ return aa_sk_perm(op, request, sock->sk);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_getsockname - check perms before getting the local address
|
||
|
+ */
|
||
|
+static int apparmor_socket_getsockname(struct socket *sock)
|
||
|
+{
|
||
|
+ return aa_sock_perm(OP_GETSOCKNAME, AA_MAY_GETATTR, sock);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_getpeername - check perms before getting remote address
|
||
|
+ */
|
||
|
+static int apparmor_socket_getpeername(struct socket *sock)
|
||
|
+{
|
||
|
+ return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock);
|
||
|
+}
|
||
|
+
|
||
|
+/* revaliation, get/set attr, opt */
|
||
|
+static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock,
|
||
|
+ int level, int optname)
|
||
|
+{
|
||
|
+ AA_BUG(!sock);
|
||
|
+ AA_BUG(!sock->sk);
|
||
|
+ AA_BUG(in_interrupt());
|
||
|
+
|
||
|
+ return aa_sk_perm(op, request, sock->sk);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_getsockopt - check perms before getting socket options
|
||
|
+ */
|
||
|
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
|
||
|
+ int optname)
|
||
|
+{
|
||
|
+ return aa_sock_opt_perm(OP_GETSOCKOPT, AA_MAY_GETOPT, sock,
|
||
|
+ level, optname);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_setsockopt - check perms before setting socket options
|
||
|
+ */
|
||
|
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
|
||
|
+ int optname)
|
||
|
+{
|
||
|
+ return aa_sock_opt_perm(OP_SETSOCKOPT, AA_MAY_SETOPT, sock,
|
||
|
+ level, optname);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_shutdown - check perms before shutting down @sock conn
|
||
|
+ */
|
||
|
+static int apparmor_socket_shutdown(struct socket *sock, int how)
|
||
|
+{
|
||
|
+ return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_sock_recv_skb - check perms before associating skb to sk
|
||
|
+ *
|
||
|
+ * Note: can not sleep may be called with locks held
|
||
|
+ *
|
||
|
+ * dont want protocol specific in __skb_recv_datagram()
|
||
|
+ * to deny an incoming connection socket_sock_rcv_skb()
|
||
|
+ */
|
||
|
+static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
||
|
+{
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+static struct aa_label *sk_peer_label(struct sock *sk)
|
||
|
+{
|
||
|
+ struct aa_sk_ctx *ctx = SK_CTX(sk);
|
||
|
+
|
||
|
+ if (ctx->peer)
|
||
|
+ return ctx->peer;
|
||
|
+
|
||
|
+ return ERR_PTR(-ENOPROTOOPT);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_getpeersec_stream - get security context of peer
|
||
|
+ *
|
||
|
+ * Note: for tcp only valid if using ipsec or cipso on lan
|
||
|
+ */
|
||
|
+static int apparmor_socket_getpeersec_stream(struct socket *sock,
|
||
|
+ char __user *optval,
|
||
|
+ int __user *optlen,
|
||
|
+ unsigned int len)
|
||
|
+{
|
||
|
+ char *name;
|
||
|
+ int slen, error = 0;
|
||
|
+ struct aa_label *label;
|
||
|
+ struct aa_label *peer;
|
||
|
+
|
||
|
+ label = begin_current_label_crit_section();
|
||
|
+ peer = sk_peer_label(sock->sk);
|
||
|
+ if (IS_ERR(peer)) {
|
||
|
+ error = PTR_ERR(peer);
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+ slen = aa_label_asxprint(&name, labels_ns(label), peer,
|
||
|
+ FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
|
||
|
+ FLAG_HIDDEN_UNCONFINED, GFP_KERNEL);
|
||
|
+ /* don't include terminating \0 in slen, it breaks some apps */
|
||
|
+ if (slen < 0) {
|
||
|
+ error = -ENOMEM;
|
||
|
+ } else {
|
||
|
+ if (slen > len) {
|
||
|
+ error = -ERANGE;
|
||
|
+ } else if (copy_to_user(optval, name, slen)) {
|
||
|
+ error = -EFAULT;
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ if (put_user(slen, optlen))
|
||
|
+ error = -EFAULT;
|
||
|
+out:
|
||
|
+ kfree(name);
|
||
|
+
|
||
|
+ }
|
||
|
+
|
||
|
+done:
|
||
|
+ end_current_label_crit_section(label);
|
||
|
+
|
||
|
+ return error;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_socket_getpeersec_dgram - get security label of packet
|
||
|
+ * @sock: the peer socket
|
||
|
+ * @skb: packet data
|
||
|
+ * @secid: pointer to where to put the secid of the packet
|
||
|
+ *
|
||
|
+ * Sets the netlabel socket state on sk from parent
|
||
|
+ */
|
||
|
+static int apparmor_socket_getpeersec_dgram(struct socket *sock,
|
||
|
+ struct sk_buff *skb, u32 *secid)
|
||
|
+
|
||
|
+{
|
||
|
+ /* TODO: requires secid support */
|
||
|
+ return -ENOPROTOOPT;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * apparmor_sock_graft - Initialize newly created socket
|
||
|
+ * @sk: child sock
|
||
|
+ * @parent: parent socket
|
||
|
+ *
|
||
|
+ * Note: could set off of SOCK_CTX(parent) but need to track inode and we can
|
||
|
+ * just set sk security information off of current creating process label
|
||
|
+ * Labeling of sk for accept case - probably should be sock based
|
||
|
+ * instead of task, because of the case where an implicitly labeled
|
||
|
+ * socket is shared by different tasks.
|
||
|
+ */
|
||
|
+static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
|
||
|
+{
|
||
|
+ struct aa_sk_ctx *ctx = SK_CTX(sk);
|
||
|
+
|
||
|
+ if (!ctx->label)
|
||
|
+ ctx->label = aa_get_current_label();
|
||
|
+}
|
||
|
+
|
||
|
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
|
||
|
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
|
||
|
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
|
||
|
@@ -770,6 +1133,30 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
|
||
|
LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
|
||
|
LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
|
||
|
|
||
|
+ LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security),
|
||
|
+ LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security),
|
||
|
+ LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security),
|
||
|
+
|
||
|
+ LSM_HOOK_INIT(socket_create, apparmor_socket_create),
|
||
|
+ LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create),
|
||
|
+ LSM_HOOK_INIT(socket_bind, apparmor_socket_bind),
|
||
|
+ LSM_HOOK_INIT(socket_connect, apparmor_socket_connect),
|
||
|
+ LSM_HOOK_INIT(socket_listen, apparmor_socket_listen),
|
||
|
+ LSM_HOOK_INIT(socket_accept, apparmor_socket_accept),
|
||
|
+ LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg),
|
||
|
+ LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg),
|
||
|
+ LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname),
|
||
|
+ LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername),
|
||
|
+ LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt),
|
||
|
+ LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt),
|
||
|
+ LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown),
|
||
|
+ LSM_HOOK_INIT(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb),
|
||
|
+ LSM_HOOK_INIT(socket_getpeersec_stream,
|
||
|
+ apparmor_socket_getpeersec_stream),
|
||
|
+ LSM_HOOK_INIT(socket_getpeersec_dgram,
|
||
|
+ apparmor_socket_getpeersec_dgram),
|
||
|
+ LSM_HOOK_INIT(sock_graft, apparmor_sock_graft),
|
||
|
+
|
||
|
LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank),
|
||
|
LSM_HOOK_INIT(cred_free, apparmor_cred_free),
|
||
|
LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare),
|
||
|
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
|
||
|
new file mode 100644
|
||
|
index 000000000000..33d54435f8d6
|
||
|
--- /dev/null
|
||
|
+++ b/security/apparmor/net.c
|
||
|
@@ -0,0 +1,184 @@
|
||
|
+/*
|
||
|
+ * 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);
|
||
|
+}
|
||
|
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
|
||
|
index 4ede87c30f8b..5a2aec358322 100644
|
||
|
--- a/security/apparmor/policy_unpack.c
|
||
|
+++ b/security/apparmor/policy_unpack.c
|
||
|
@@ -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)) {
|
||
|
@@ -584,7 +597,7 @@ 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;
|
||
|
const char *info = "failed to unpack profile";
|
||
|
- size_t ns_len;
|
||
|
+ size_t size = 0, ns_len;
|
||
|
struct rhashtable_params params = { 0 };
|
||
|
char *key = NULL;
|
||
|
struct aa_data *data;
|
||
|
@@ -717,6 +730,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||
|
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";
|
||
|
--
|
||
|
2.14.1
|
||
|
|