From 916cafdc95843fb9af5fd5f83ca499d75473d107 Mon Sep 17 00:00:00 2001 From: Mathias Svensson Date: Fri, 6 Jan 2017 13:32:39 -0800 Subject: samples/seccomp: fix 64-bit comparison macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There were some bugs in the JNE64 and JLT64 comparision macros. This fixes them, improves comments, and cleans up the file while we are at it. Reported-by: Stephen Röttger Signed-off-by: Mathias Svensson Signed-off-by: Kees Cook Cc: stable@vger.kernel.org Signed-off-by: James Morris --- samples/seccomp/bpf-helper.h | 125 +++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 53 deletions(-) diff --git a/samples/seccomp/bpf-helper.h b/samples/seccomp/bpf-helper.h index 38ee70f3cd5b..1d8de9edd858 100644 --- a/samples/seccomp/bpf-helper.h +++ b/samples/seccomp/bpf-helper.h @@ -138,7 +138,7 @@ union arg64 { #define ARG_32(idx) \ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)) -/* Loads hi into A and lo in X */ +/* Loads lo into M[0] and hi into M[1] and A */ #define ARG_64(idx) \ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)), \ BPF_STMT(BPF_ST, 0), /* lo -> M[0] */ \ @@ -153,88 +153,107 @@ union arg64 { BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (value), 1, 0), \ jt -/* Checks the lo, then swaps to check the hi. A=lo,X=hi */ +#define JA32(value, jt) \ + BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (value), 0, 1), \ + jt + +#define JGE32(value, jt) \ + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 0, 1), \ + jt + +#define JGT32(value, jt) \ + BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 0, 1), \ + jt + +#define JLE32(value, jt) \ + BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 1, 0), \ + jt + +#define JLT32(value, jt) \ + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 1, 0), \ + jt + +/* + * All the JXX64 checks assume lo is saved in M[0] and hi is saved in both + * A and M[1]. This invariant is kept by restoring A if necessary. + */ #define JEQ64(lo, hi, jt) \ + /* if (hi != arg.hi) goto NOMATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ + /* if (lo != arg.lo) goto NOMATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 0, 2), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ + BPF_STMT(BPF_LD+BPF_MEM, 1) #define JNE64(lo, hi, jt) \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 5, 0), \ - BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ + /* if (hi != arg.hi) goto MATCH; */ \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 3), \ + BPF_STMT(BPF_LD+BPF_MEM, 0), \ + /* if (lo != arg.lo) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 2, 0), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ - -#define JA32(value, jt) \ - BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (value), 0, 1), \ - jt + BPF_STMT(BPF_LD+BPF_MEM, 1) #define JA64(lo, hi, jt) \ + /* if (hi & arg.hi) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (hi), 3, 0), \ - BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ + BPF_STMT(BPF_LD+BPF_MEM, 0), \ + /* if (lo & arg.lo) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (lo), 0, 2), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ + BPF_STMT(BPF_LD+BPF_MEM, 1) -#define JGE32(value, jt) \ - BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 0, 1), \ - jt - -#define JLT32(value, jt) \ - BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 1, 0), \ - jt - -/* Shortcut checking if hi > arg.hi. */ #define JGE64(lo, hi, jt) \ + /* if (hi > arg.hi) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \ + /* if (hi != arg.hi) goto NOMATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ - BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ + BPF_STMT(BPF_LD+BPF_MEM, 0), \ + /* if (lo >= arg.lo) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 0, 2), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ - jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ - -#define JLT64(lo, hi, jt) \ - BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ - BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ - BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ + BPF_STMT(BPF_LD+BPF_MEM, 1) -#define JGT32(value, jt) \ - BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 0, 1), \ - jt - -#define JLE32(value, jt) \ - BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 1, 0), \ - jt - -/* Check hi > args.hi first, then do the GE checking */ #define JGT64(lo, hi, jt) \ + /* if (hi > arg.hi) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \ + /* if (hi != arg.hi) goto NOMATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ - BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ + BPF_STMT(BPF_LD+BPF_MEM, 0), \ + /* if (lo > arg.lo) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 0, 2), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ + BPF_STMT(BPF_LD+BPF_MEM, 1) #define JLE64(lo, hi, jt) \ - BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 6, 0), \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 3), \ - BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ + /* if (hi < arg.hi) goto MATCH; */ \ + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \ + /* if (hi != arg.hi) goto NOMATCH; */ \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ + BPF_STMT(BPF_LD+BPF_MEM, 0), \ + /* if (lo <= arg.lo) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ + jt, \ + BPF_STMT(BPF_LD+BPF_MEM, 1) + +#define JLT64(lo, hi, jt) \ + /* if (hi < arg.hi) goto MATCH; */ \ + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \ + /* if (hi != arg.hi) goto NOMATCH; */ \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ + BPF_STMT(BPF_LD+BPF_MEM, 0), \ + /* if (lo < arg.lo) goto MATCH; */ \ + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 2, 0), \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ + BPF_STMT(BPF_LD+BPF_MEM, 1) #define LOAD_SYSCALL_NR \ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ -- cgit v1.2.3 From da69a5306ab92e07224da54aafee8b1dccf024f6 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 9 Jan 2017 10:07:30 -0500 Subject: selinux: support distinctions among all network address families Extend SELinux to support distinctions among all network address families implemented by the kernel by defining new socket security classes and mapping to them. Otherwise, many sockets are mapped to the generic socket class and are indistinguishable in policy. This has come up previously with regard to selectively allowing access to bluetooth sockets, and more recently with regard to selectively allowing access to AF_ALG sockets. Guido Trentalancia submitted a patch that took a similar approach to add only support for distinguishing AF_ALG sockets, but this generalizes his approach to handle all address families implemented by the kernel. Socket security classes are also added for ICMP and SCTP sockets. Socket security classes were not defined for AF_* values that are reserved but unimplemented in the kernel, e.g. AF_NETBEUI, AF_SECURITY, AF_ASH, AF_ECONET, AF_SNA, AF_WANPIPE. Backward compatibility is provided by only enabling the finer-grained socket classes if a new policy capability is set in the policy; older policies will behave as before. The legacy redhat1 policy capability that was only ever used in testing within Fedora for ptrace_child is reclaimed for this purpose; as far as I can tell, this policy capability is not enabled in any supported distro policy. Add a pair of conditional compilation guards to detect when new AF_* values are added so that we can update SELinux accordingly rather than having to belatedly update it long after new address families are introduced. Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 73 +++++++++++++++++++++++++++++++++++++ security/selinux/include/classmap.h | 68 ++++++++++++++++++++++++++++++++++ security/selinux/include/security.h | 3 +- security/selinux/selinuxfs.c | 2 +- security/selinux/ss/services.c | 3 ++ 5 files changed, 147 insertions(+), 2 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c7c6619431d5..74cd3a689cf8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1268,6 +1268,8 @@ static inline int default_protocol_dgram(int protocol) static inline u16 socket_type_to_security_class(int family, int type, int protocol) { + int extsockclass = selinux_policycap_extsockclass; + switch (family) { case PF_UNIX: switch (type) { @@ -1282,13 +1284,18 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc case PF_INET6: switch (type) { case SOCK_STREAM: + case SOCK_SEQPACKET: if (default_protocol_stream(protocol)) return SECCLASS_TCP_SOCKET; + else if (extsockclass && protocol == IPPROTO_SCTP) + return SECCLASS_SCTP_SOCKET; else return SECCLASS_RAWIP_SOCKET; case SOCK_DGRAM: if (default_protocol_dgram(protocol)) return SECCLASS_UDP_SOCKET; + else if (extsockclass && protocol == IPPROTO_ICMP) + return SECCLASS_ICMP_SOCKET; else return SECCLASS_RAWIP_SOCKET; case SOCK_DCCP: @@ -1342,6 +1349,72 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc return SECCLASS_APPLETALK_SOCKET; } + if (extsockclass) { + switch (family) { + case PF_AX25: + return SECCLASS_AX25_SOCKET; + case PF_IPX: + return SECCLASS_IPX_SOCKET; + case PF_NETROM: + return SECCLASS_NETROM_SOCKET; + case PF_BRIDGE: + return SECCLASS_BRIDGE_SOCKET; + case PF_ATMPVC: + return SECCLASS_ATMPVC_SOCKET; + case PF_X25: + return SECCLASS_X25_SOCKET; + case PF_ROSE: + return SECCLASS_ROSE_SOCKET; + case PF_DECnet: + return SECCLASS_DECNET_SOCKET; + case PF_ATMSVC: + return SECCLASS_ATMSVC_SOCKET; + case PF_RDS: + return SECCLASS_RDS_SOCKET; + case PF_IRDA: + return SECCLASS_IRDA_SOCKET; + case PF_PPPOX: + return SECCLASS_PPPOX_SOCKET; + case PF_LLC: + return SECCLASS_LLC_SOCKET; + case PF_IB: + return SECCLASS_IB_SOCKET; + case PF_MPLS: + return SECCLASS_MPLS_SOCKET; + case PF_CAN: + return SECCLASS_CAN_SOCKET; + case PF_TIPC: + return SECCLASS_TIPC_SOCKET; + case PF_BLUETOOTH: + return SECCLASS_BLUETOOTH_SOCKET; + case PF_IUCV: + return SECCLASS_IUCV_SOCKET; + case PF_RXRPC: + return SECCLASS_RXRPC_SOCKET; + case PF_ISDN: + return SECCLASS_ISDN_SOCKET; + case PF_PHONET: + return SECCLASS_PHONET_SOCKET; + case PF_IEEE802154: + return SECCLASS_IEEE802154_SOCKET; + case PF_CAIF: + return SECCLASS_CAIF_SOCKET; + case PF_ALG: + return SECCLASS_ALG_SOCKET; + case PF_NFC: + return SECCLASS_NFC_SOCKET; + case PF_VSOCK: + return SECCLASS_VSOCK_SOCKET; + case PF_KCM: + return SECCLASS_KCM_SOCKET; + case PF_QIPCRTR: + return SECCLASS_QIPCRTR_SOCKET; +#if PF_MAX > 43 +#error New address family defined, please update this function. +#endif + } + } + return SECCLASS_SOCKET; } diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 13ae49b0baa0..0dfd26d0b8d8 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -171,5 +171,73 @@ struct security_class_mapping secclass_map[] = { { COMMON_CAP_PERMS, NULL } }, { "cap2_userns", { COMMON_CAP2_PERMS, NULL } }, + { "sctp_socket", + { COMMON_SOCK_PERMS, + "node_bind", NULL } }, + { "icmp_socket", + { COMMON_SOCK_PERMS, + "node_bind", NULL } }, + { "ax25_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "ipx_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "netrom_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "bridge_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "atmpvc_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "x25_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "rose_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "decnet_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "atmsvc_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "rds_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "irda_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "pppox_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "llc_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "ib_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "mpls_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "can_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "tipc_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "bluetooth_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "iucv_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "rxrpc_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "isdn_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "phonet_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "ieee802154_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "caif_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "alg_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "nfc_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "vsock_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "kcm_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "qipcrtr_socket", + { COMMON_SOCK_PERMS, NULL } }, { NULL } }; + +#if PF_MAX > 43 +#error New address family defined, please update secclass_map. +#endif diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 308a286c6cbe..beaa14b8b6cf 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -69,7 +69,7 @@ extern int selinux_enabled; enum { POLICYDB_CAPABILITY_NETPEER, POLICYDB_CAPABILITY_OPENPERM, - POLICYDB_CAPABILITY_REDHAT1, + POLICYDB_CAPABILITY_EXTSOCKCLASS, POLICYDB_CAPABILITY_ALWAYSNETWORK, __POLICYDB_CAPABILITY_MAX }; @@ -77,6 +77,7 @@ enum { extern int selinux_policycap_netpeer; extern int selinux_policycap_openperm; +extern int selinux_policycap_extsockclass; extern int selinux_policycap_alwaysnetwork; /* diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index cf9293e01fc1..0aac402a0f11 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -45,7 +45,7 @@ static char *policycap_names[] = { "network_peer_controls", "open_perms", - "redhat1", + "extended_socket_class", "always_check_network" }; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 082b20c78363..a70fcee9824b 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -72,6 +72,7 @@ int selinux_policycap_netpeer; int selinux_policycap_openperm; +int selinux_policycap_extsockclass; int selinux_policycap_alwaysnetwork; static DEFINE_RWLOCK(policy_rwlock); @@ -1988,6 +1989,8 @@ static void security_load_policycaps(void) POLICYDB_CAPABILITY_NETPEER); selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps, POLICYDB_CAPABILITY_OPENPERM); + selinux_policycap_extsockclass = ebitmap_get_bit(&policydb.policycaps, + POLICYDB_CAPABILITY_EXTSOCKCLASS); selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps, POLICYDB_CAPABILITY_ALWAYSNETWORK); } -- cgit v1.2.3 From a2c7c6fbe5ab48f6e4ed22f4649c76d1efbfe643 Mon Sep 17 00:00:00 2001 From: Yongqin Liu Date: Mon, 9 Jan 2017 10:07:30 -0500 Subject: selinux: add security in-core xattr support for tracefs Since kernel 4.1 ftrace is supported as a new separate filesystem. It gets automatically mounted by the kernel under the old path /sys/kernel/debug/tracing. Because it lives now on a separate filesystem SELinux needs to be updated to also support setting SELinux labels on tracefs inodes. This is required for compatibility in Android when moving to Linux 4.1 or newer. Signed-off-by: Yongqin Liu Signed-off-by: William Roberts Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 74cd3a689cf8..5ce633aabce6 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -492,6 +492,7 @@ static int selinux_is_sblabel_mnt(struct super_block *sb) !strcmp(sb->s_type->name, "sysfs") || !strcmp(sb->s_type->name, "pstore") || !strcmp(sb->s_type->name, "debugfs") || + !strcmp(sb->s_type->name, "tracefs") || !strcmp(sb->s_type->name, "rootfs"); } -- cgit v1.2.3 From ef37979a2cfa3905adbf0c2a681ce16c0aaea92d Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 9 Jan 2017 10:07:31 -0500 Subject: selinux: handle ICMPv6 consistently with ICMP commit 79c8b348f215 ("selinux: support distinctions among all network address families") mapped datagram ICMP sockets to the new icmp_socket security class, but left ICMPv6 sockets unchanged. This change fixes that oversight to handle both kinds of sockets consistently. Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5ce633aabce6..e4b953f760dd 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1295,7 +1295,8 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc case SOCK_DGRAM: if (default_protocol_dgram(protocol)) return SECCLASS_UDP_SOCKET; - else if (extsockclass && protocol == IPPROTO_ICMP) + else if (extsockclass && (protocol == IPPROTO_ICMP || + protocol == IPPROTO_ICMPV6)) return SECCLASS_ICMP_SOCKET; else return SECCLASS_RAWIP_SOCKET; -- cgit v1.2.3 From 01593d3299a1cfdb5e08acf95f63ec59dd674906 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 9 Jan 2017 10:07:31 -0500 Subject: selinux: allow context mounts on tmpfs, ramfs, devpts within user namespaces commit aad82892af261b9903cc11c55be3ecf5f0b0b4f8 ("selinux: Add support for unprivileged mounts from user namespaces") prohibited any use of context mount options within non-init user namespaces. However, this breaks use of context mount options for tmpfs mounts within user namespaces, which are being used by Docker/runc. There is no reason to block such usage for tmpfs, ramfs or devpts. Exempt these filesystem types from this restriction. Before: sh$ userns_child_exec -p -m -U -M '0 1000 1' -G '0 1000 1' bash sh# mount -t tmpfs -o context=system_u:object_r:user_tmp_t:s0:c13 none /tmp mount: tmpfs is write-protected, mounting read-only mount: cannot mount tmpfs read-only After: sh$ userns_child_exec -p -m -U -M '0 1000 1' -G '0 1000 1' bash sh# mount -t tmpfs -o context=system_u:object_r:user_tmp_t:s0:c13 none /tmp sh# ls -Zd /tmp unconfined_u:object_r:user_tmp_t:s0:c13 /tmp Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e4b953f760dd..e32f4b5f23a5 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -834,10 +834,14 @@ static int selinux_set_mnt_opts(struct super_block *sb, } /* - * If this is a user namespace mount, no contexts are allowed - * on the command line and security labels must be ignored. + * If this is a user namespace mount and the filesystem type is not + * explicitly whitelisted, then no contexts are allowed on the command + * line and security labels must be ignored. */ - if (sb->s_user_ns != &init_user_ns) { + if (sb->s_user_ns != &init_user_ns && + strcmp(sb->s_type->name, "tmpfs") && + strcmp(sb->s_type->name, "ramfs") && + strcmp(sb->s_type->name, "devpts")) { if (context_sid || fscontext_sid || rootcontext_sid || defcontext_sid) { rc = -EACCES; -- cgit v1.2.3 From be0554c9bf9f7cc96f5205df8f8bd3573b74320e Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 9 Jan 2017 10:07:31 -0500 Subject: selinux: clean up cred usage and simplify SELinux was sometimes using the task "objective" credentials when it could/should use the "subjective" credentials. This was sometimes hidden by the fact that we were unnecessarily passing around pointers to the current task, making it appear as if the task could be something other than current, so eliminate all such passing of current. Inline various permission checking helper functions that can be reduced to a single avc_has_perm() call. Since the credentials infrastructure only allows a task to alter its own credentials, we can always assume that current must be the same as the target task in selinux_setprocattr after the check. We likely should move this check from selinux_setprocattr() to proc_pid_attr_write() and drop the task argument to the security hook altogether; it can only serve to confuse things. Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 294 +++++++++++++++----------------------- security/selinux/include/objsec.h | 10 ++ security/selinux/selinuxfs.c | 73 +++++----- 3 files changed, 166 insertions(+), 211 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e32f4b5f23a5..262e108c36d4 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -210,16 +210,6 @@ static inline u32 task_sid(const struct task_struct *task) return sid; } -/* - * get the subjective security ID of the current task - */ -static inline u32 current_sid(void) -{ - const struct task_security_struct *tsec = current_security(); - - return tsec->sid; -} - /* Allocate and free functions for each kind of security blob. */ static int inode_alloc_security(struct inode *inode) @@ -1687,55 +1677,6 @@ static inline u32 signal_to_av(int sig) return perm; } -/* - * Check permission between a pair of credentials - * fork check, ptrace check, etc. - */ -static int cred_has_perm(const struct cred *actor, - const struct cred *target, - u32 perms) -{ - u32 asid = cred_sid(actor), tsid = cred_sid(target); - - return avc_has_perm(asid, tsid, SECCLASS_PROCESS, perms, NULL); -} - -/* - * Check permission between a pair of tasks, e.g. signal checks, - * fork check, ptrace check, etc. - * tsk1 is the actor and tsk2 is the target - * - this uses the default subjective creds of tsk1 - */ -static int task_has_perm(const struct task_struct *tsk1, - const struct task_struct *tsk2, - u32 perms) -{ - const struct task_security_struct *__tsec1, *__tsec2; - u32 sid1, sid2; - - rcu_read_lock(); - __tsec1 = __task_cred(tsk1)->security; sid1 = __tsec1->sid; - __tsec2 = __task_cred(tsk2)->security; sid2 = __tsec2->sid; - rcu_read_unlock(); - return avc_has_perm(sid1, sid2, SECCLASS_PROCESS, perms, NULL); -} - -/* - * Check permission between current and another task, e.g. signal checks, - * fork check, ptrace check, etc. - * current is the actor and tsk2 is the target - * - this uses current's subjective creds - */ -static int current_has_perm(const struct task_struct *tsk, - u32 perms) -{ - u32 sid, tsid; - - sid = current_sid(); - tsid = task_sid(tsk); - return avc_has_perm(sid, tsid, SECCLASS_PROCESS, perms, NULL); -} - #if CAP_LAST_CAP > 63 #error Fix SELinux to handle capabilities > 63. #endif @@ -1777,16 +1718,6 @@ static int cred_has_capability(const struct cred *cred, return rc; } -/* Check whether a task is allowed to use a system operation. */ -static int task_has_system(struct task_struct *tsk, - u32 perms) -{ - u32 sid = task_sid(tsk); - - return avc_has_perm(sid, SECINITSID_KERNEL, - SECCLASS_SYSTEM, perms, NULL); -} - /* Check whether a task has a particular permission to an inode. The 'adp' parameter is optional and allows other audit data to be passed (e.g. the dentry). */ @@ -1958,15 +1889,6 @@ static int may_create(struct inode *dir, FILESYSTEM__ASSOCIATE, &ad); } -/* Check whether a task can create a key. */ -static int may_create_key(u32 ksid, - struct task_struct *ctx) -{ - u32 sid = task_sid(ctx); - - return avc_has_perm(sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL); -} - #define MAY_LINK 0 #define MAY_UNLINK 1 #define MAY_RMDIR 2 @@ -2222,24 +2144,26 @@ static int selinux_binder_transfer_file(struct task_struct *from, static int selinux_ptrace_access_check(struct task_struct *child, unsigned int mode) { - if (mode & PTRACE_MODE_READ) { - u32 sid = current_sid(); - u32 csid = task_sid(child); + u32 sid = current_sid(); + u32 csid = task_sid(child); + + if (mode & PTRACE_MODE_READ) return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL); - } - return current_has_perm(child, PROCESS__PTRACE); + return avc_has_perm(sid, csid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL); } static int selinux_ptrace_traceme(struct task_struct *parent) { - return task_has_perm(parent, current, PROCESS__PTRACE); + return avc_has_perm(task_sid(parent), current_sid(), SECCLASS_PROCESS, + PROCESS__PTRACE, NULL); } static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted) { - return current_has_perm(target, PROCESS__GETCAP); + return avc_has_perm(current_sid(), task_sid(target), SECCLASS_PROCESS, + PROCESS__GETCAP, NULL); } static int selinux_capset(struct cred *new, const struct cred *old, @@ -2247,7 +2171,8 @@ static int selinux_capset(struct cred *new, const struct cred *old, const kernel_cap_t *inheritable, const kernel_cap_t *permitted) { - return cred_has_perm(old, new, PROCESS__SETCAP); + return avc_has_perm(cred_sid(old), cred_sid(new), SECCLASS_PROCESS, + PROCESS__SETCAP, NULL); } /* @@ -2303,29 +2228,22 @@ static int selinux_quota_on(struct dentry *dentry) static int selinux_syslog(int type) { - int rc; - switch (type) { case SYSLOG_ACTION_READ_ALL: /* Read last kernel messages */ case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */ - rc = task_has_system(current, SYSTEM__SYSLOG_READ); - break; + return avc_has_perm(current_sid(), SECINITSID_KERNEL, + SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, NULL); case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */ case SYSLOG_ACTION_CONSOLE_ON: /* Enable logging to console */ /* Set level of messages printed to console */ case SYSLOG_ACTION_CONSOLE_LEVEL: - rc = task_has_system(current, SYSTEM__SYSLOG_CONSOLE); - break; - case SYSLOG_ACTION_CLOSE: /* Close log */ - case SYSLOG_ACTION_OPEN: /* Open log */ - case SYSLOG_ACTION_READ: /* Read from log */ - case SYSLOG_ACTION_READ_CLEAR: /* Read/clear last kernel messages */ - case SYSLOG_ACTION_CLEAR: /* Clear ring buffer */ - default: - rc = task_has_system(current, SYSTEM__SYSLOG_MOD); - break; + return avc_has_perm(current_sid(), SECINITSID_KERNEL, + SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, + NULL); } - return rc; + /* All other syslog types */ + return avc_has_perm(current_sid(), SECINITSID_KERNEL, + SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, NULL); } /* @@ -2350,13 +2268,13 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) /* binprm security operations */ -static u32 ptrace_parent_sid(struct task_struct *task) +static u32 ptrace_parent_sid(void) { u32 sid = 0; struct task_struct *tracer; rcu_read_lock(); - tracer = ptrace_parent(task); + tracer = ptrace_parent(current); if (tracer) sid = task_sid(tracer); rcu_read_unlock(); @@ -2485,7 +2403,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) * changes its SID has the appropriate permit */ if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { - u32 ptsid = ptrace_parent_sid(current); + u32 ptsid = ptrace_parent_sid(); if (ptsid != 0) { rc = avc_has_perm(ptsid, new_tsec->sid, SECCLASS_PROCESS, @@ -3582,6 +3500,7 @@ static int default_noexec; static int file_map_prot_check(struct file *file, unsigned long prot, int shared) { const struct cred *cred = current_cred(); + u32 sid = cred_sid(cred); int rc = 0; if (default_noexec && @@ -3592,7 +3511,8 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared * private file mapping that will also be writable. * This has an additional check. */ - rc = cred_has_perm(cred, cred, PROCESS__EXECMEM); + rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, + PROCESS__EXECMEM, NULL); if (rc) goto error; } @@ -3643,6 +3563,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, unsigned long prot) { const struct cred *cred = current_cred(); + u32 sid = cred_sid(cred); if (selinux_checkreqprot) prot = reqprot; @@ -3652,12 +3573,14 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, int rc = 0; if (vma->vm_start >= vma->vm_mm->start_brk && vma->vm_end <= vma->vm_mm->brk) { - rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP); + rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, + PROCESS__EXECHEAP, NULL); } else if (!vma->vm_file && ((vma->vm_start <= vma->vm_mm->start_stack && vma->vm_end >= vma->vm_mm->start_stack) || vma_is_stack_for_current(vma))) { - rc = current_has_perm(current, PROCESS__EXECSTACK); + rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, + PROCESS__EXECSTACK, NULL); } else if (vma->vm_file && vma->anon_vma) { /* * We are making executable a file mapping that has @@ -3790,7 +3713,9 @@ static int selinux_file_open(struct file *file, const struct cred *cred) static int selinux_task_create(unsigned long clone_flags) { - return current_has_perm(current, PROCESS__FORK); + u32 sid = current_sid(); + + return avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL); } /* @@ -3900,15 +3825,12 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode) static int selinux_kernel_module_request(char *kmod_name) { - u32 sid; struct common_audit_data ad; - sid = task_sid(current); - ad.type = LSM_AUDIT_DATA_KMOD; ad.u.kmod_name = kmod_name; - return avc_has_perm(sid, SECINITSID_KERNEL, SECCLASS_SYSTEM, + return avc_has_perm(current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__MODULE_REQUEST, &ad); } @@ -3960,17 +3882,20 @@ static int selinux_kernel_read_file(struct file *file, static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) { - return current_has_perm(p, PROCESS__SETPGID); + return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + PROCESS__SETPGID, NULL); } static int selinux_task_getpgid(struct task_struct *p) { - return current_has_perm(p, PROCESS__GETPGID); + return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + PROCESS__GETPGID, NULL); } static int selinux_task_getsid(struct task_struct *p) { - return current_has_perm(p, PROCESS__GETSESSION); + return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + PROCESS__GETSESSION, NULL); } static void selinux_task_getsecid(struct task_struct *p, u32 *secid) @@ -3980,17 +3905,20 @@ static void selinux_task_getsecid(struct task_struct *p, u32 *secid) static int selinux_task_setnice(struct task_struct *p, int nice) { - return current_has_perm(p, PROCESS__SETSCHED); + return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + PROCESS__SETSCHED, NULL); } static int selinux_task_setioprio(struct task_struct *p, int ioprio) { - return current_has_perm(p, PROCESS__SETSCHED); + return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + PROCESS__SETSCHED, NULL); } static int selinux_task_getioprio(struct task_struct *p) { - return current_has_perm(p, PROCESS__GETSCHED); + return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + PROCESS__GETSCHED, NULL); } static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, @@ -4003,47 +3931,48 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, later be used as a safe reset point for the soft limit upon context transitions. See selinux_bprm_committing_creds. */ if (old_rlim->rlim_max != new_rlim->rlim_max) - return current_has_perm(p, PROCESS__SETRLIMIT); + return avc_has_perm(current_sid(), task_sid(p), + SECCLASS_PROCESS, PROCESS__SETRLIMIT, NULL); return 0; } static int selinux_task_setscheduler(struct task_struct *p) { - return current_has_perm(p, PROCESS__SETSCHED); + return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + PROCESS__SETSCHED, NULL); } static int selinux_task_getscheduler(struct task_struct *p) { - return current_has_perm(p, PROCESS__GETSCHED); + return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + PROCESS__GETSCHED, NULL); } static int selinux_task_movememory(struct task_struct *p) { - return current_has_perm(p, PROCESS__SETSCHED); + return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + PROCESS__SETSCHED, NULL); } static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig, u32 secid) { u32 perm; - int rc; if (!sig) perm = PROCESS__SIGNULL; /* null signal; existence test */ else perm = signal_to_av(sig); - if (secid) - rc = avc_has_perm(secid, task_sid(p), - SECCLASS_PROCESS, perm, NULL); - else - rc = current_has_perm(p, perm); - return rc; + if (!secid) + secid = current_sid(); + return avc_has_perm(secid, task_sid(p), SECCLASS_PROCESS, perm, NULL); } static int selinux_task_wait(struct task_struct *p) { - return task_has_perm(p, current, PROCESS__SIGCHLD); + return avc_has_perm(task_sid(p), current_sid(), SECCLASS_PROCESS, + PROCESS__SIGCHLD, NULL); } static void selinux_task_to_inode(struct task_struct *p, @@ -4333,12 +4262,11 @@ static int socket_sockcreate_sid(const struct task_security_struct *tsec, socksid); } -static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) +static int sock_has_perm(struct sock *sk, u32 perms) { struct sk_security_struct *sksec = sk->sk_security; struct common_audit_data ad; struct lsm_network_audit net = {0,}; - u32 tsid = task_sid(task); if (sksec->sid == SECINITSID_KERNEL) return 0; @@ -4347,7 +4275,8 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) ad.u.net = &net; ad.u.net->sk = sk; - return avc_has_perm(tsid, sksec->sid, sksec->sclass, perms, &ad); + return avc_has_perm(current_sid(), sksec->sid, sksec->sclass, perms, + &ad); } static int selinux_socket_create(int family, int type, @@ -4409,7 +4338,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in u16 family; int err; - err = sock_has_perm(current, sk, SOCKET__BIND); + err = sock_has_perm(sk, SOCKET__BIND); if (err) goto out; @@ -4508,7 +4437,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, struct sk_security_struct *sksec = sk->sk_security; int err; - err = sock_has_perm(current, sk, SOCKET__CONNECT); + err = sock_has_perm(sk, SOCKET__CONNECT); if (err) return err; @@ -4560,7 +4489,7 @@ out: static int selinux_socket_listen(struct socket *sock, int backlog) { - return sock_has_perm(current, sock->sk, SOCKET__LISTEN); + return sock_has_perm(sock->sk, SOCKET__LISTEN); } static int selinux_socket_accept(struct socket *sock, struct socket *newsock) @@ -4571,7 +4500,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) u16 sclass; u32 sid; - err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT); + err = sock_has_perm(sock->sk, SOCKET__ACCEPT); if (err) return err; @@ -4592,30 +4521,30 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { - return sock_has_perm(current, sock->sk, SOCKET__WRITE); + return sock_has_perm(sock->sk, SOCKET__WRITE); } static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags) { - return sock_has_perm(current, sock->sk, SOCKET__READ); + return sock_has_perm(sock->sk, SOCKET__READ); } static int selinux_socket_getsockname(struct socket *sock) { - return sock_has_perm(current, sock->sk, SOCKET__GETATTR); + return sock_has_perm(sock->sk, SOCKET__GETATTR); } static int selinux_socket_getpeername(struct socket *sock) { - return sock_has_perm(current, sock->sk, SOCKET__GETATTR); + return sock_has_perm(sock->sk, SOCKET__GETATTR); } static int selinux_socket_setsockopt(struct socket *sock, int level, int optname) { int err; - err = sock_has_perm(current, sock->sk, SOCKET__SETOPT); + err = sock_has_perm(sock->sk, SOCKET__SETOPT); if (err) return err; @@ -4625,12 +4554,12 @@ static int selinux_socket_setsockopt(struct socket *sock, int level, int optname static int selinux_socket_getsockopt(struct socket *sock, int level, int optname) { - return sock_has_perm(current, sock->sk, SOCKET__GETOPT); + return sock_has_perm(sock->sk, SOCKET__GETOPT); } static int selinux_socket_shutdown(struct socket *sock, int how) { - return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN); + return sock_has_perm(sock->sk, SOCKET__SHUTDOWN); } static int selinux_socket_unix_stream_connect(struct sock *sock, @@ -5118,7 +5047,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) goto out; } - err = sock_has_perm(current, sk, perm); + err = sock_has_perm(sk, perm); out: return err; } @@ -5449,20 +5378,17 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) return selinux_nlmsg_perm(sk, skb); } -static int ipc_alloc_security(struct task_struct *task, - struct kern_ipc_perm *perm, +static int ipc_alloc_security(struct kern_ipc_perm *perm, u16 sclass) { struct ipc_security_struct *isec; - u32 sid; isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL); if (!isec) return -ENOMEM; - sid = task_sid(task); isec->sclass = sclass; - isec->sid = sid; + isec->sid = current_sid(); perm->security = isec; return 0; @@ -5530,7 +5456,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq) u32 sid = current_sid(); int rc; - rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ); + rc = ipc_alloc_security(&msq->q_perm, SECCLASS_MSGQ); if (rc) return rc; @@ -5577,7 +5503,8 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd) case IPC_INFO: case MSG_INFO: /* No specific object, just general system-wide information. */ - return task_has_system(current, SYSTEM__IPC_INFO); + return avc_has_perm(current_sid(), SECINITSID_KERNEL, + SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); case IPC_STAT: case MSG_STAT: perms = MSGQ__GETATTR | MSGQ__ASSOCIATE; @@ -5671,7 +5598,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp) u32 sid = current_sid(); int rc; - rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM); + rc = ipc_alloc_security(&shp->shm_perm, SECCLASS_SHM); if (rc) return rc; @@ -5719,7 +5646,8 @@ static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd) case IPC_INFO: case SHM_INFO: /* No specific object, just general system-wide information. */ - return task_has_system(current, SYSTEM__IPC_INFO); + return avc_has_perm(current_sid(), SECINITSID_KERNEL, + SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); case IPC_STAT: case SHM_STAT: perms = SHM__GETATTR | SHM__ASSOCIATE; @@ -5763,7 +5691,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma) u32 sid = current_sid(); int rc; - rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM); + rc = ipc_alloc_security(&sma->sem_perm, SECCLASS_SEM); if (rc) return rc; @@ -5811,7 +5739,8 @@ static int selinux_sem_semctl(struct sem_array *sma, int cmd) case IPC_INFO: case SEM_INFO: /* No specific object, just general system-wide information. */ - return task_has_system(current, SYSTEM__IPC_INFO); + return avc_has_perm(current_sid(), SECINITSID_KERNEL, + SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); case GETPID: case GETNCNT: case GETZCNT: @@ -5892,15 +5821,16 @@ static int selinux_getprocattr(struct task_struct *p, int error; unsigned len; + rcu_read_lock(); + __tsec = __task_cred(p)->security; + if (current != p) { - error = current_has_perm(p, PROCESS__GETATTR); + error = avc_has_perm(current_sid(), __tsec->sid, + SECCLASS_PROCESS, PROCESS__GETATTR, NULL); if (error) - return error; + goto bad; } - rcu_read_lock(); - __tsec = __task_cred(p)->security; - if (!strcmp(name, "current")) sid = __tsec->sid; else if (!strcmp(name, "prev")) @@ -5913,8 +5843,10 @@ static int selinux_getprocattr(struct task_struct *p, sid = __tsec->keycreate_sid; else if (!strcmp(name, "sockcreate")) sid = __tsec->sockcreate_sid; - else - goto invalid; + else { + error = -EINVAL; + goto bad; + } rcu_read_unlock(); if (!sid) @@ -5925,9 +5857,9 @@ static int selinux_getprocattr(struct task_struct *p, return error; return len; -invalid: +bad: rcu_read_unlock(); - return -EINVAL; + return error; } static int selinux_setprocattr(struct task_struct *p, @@ -5935,31 +5867,38 @@ static int selinux_setprocattr(struct task_struct *p, { struct task_security_struct *tsec; struct cred *new; - u32 sid = 0, ptsid; + u32 mysid = current_sid(), sid = 0, ptsid; int error; char *str = value; if (current != p) { - /* SELinux only allows a process to change its own - security attributes. */ + /* + * A task may only alter its own credentials. + * SELinux has always enforced this restriction, + * and it is now mandated by the Linux credentials + * infrastructure; see Documentation/security/credentials.txt. + */ return -EACCES; } /* * Basic control over ability to set these attributes at all. - * current == p, but we'll pass them separately in case the - * above restriction is ever removed. */ if (!strcmp(name, "exec")) - error = current_has_perm(p, PROCESS__SETEXEC); + error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + PROCESS__SETEXEC, NULL); else if (!strcmp(name, "fscreate")) - error = current_has_perm(p, PROCESS__SETFSCREATE); + error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + PROCESS__SETFSCREATE, NULL); else if (!strcmp(name, "keycreate")) - error = current_has_perm(p, PROCESS__SETKEYCREATE); + error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + PROCESS__SETKEYCREATE, NULL); else if (!strcmp(name, "sockcreate")) - error = current_has_perm(p, PROCESS__SETSOCKCREATE); + error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + PROCESS__SETSOCKCREATE, NULL); else if (!strcmp(name, "current")) - error = current_has_perm(p, PROCESS__SETCURRENT); + error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + PROCESS__SETCURRENT, NULL); else error = -EINVAL; if (error) @@ -6013,7 +5952,8 @@ static int selinux_setprocattr(struct task_struct *p, } else if (!strcmp(name, "fscreate")) { tsec->create_sid = sid; } else if (!strcmp(name, "keycreate")) { - error = may_create_key(sid, p); + error = avc_has_perm(mysid, sid, SECCLASS_KEY, KEY__CREATE, + NULL); if (error) goto abort_change; tsec->keycreate_sid = sid; @@ -6040,7 +5980,7 @@ static int selinux_setprocattr(struct task_struct *p, /* Check for ptracing, and update the task SID if ok. Otherwise, leave SID unchanged and fail. */ - ptsid = ptrace_parent_sid(p); + ptsid = ptrace_parent_sid(); if (ptsid != 0) { error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL); diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index e8dab0f02c72..c03cdcd12a3b 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -37,6 +37,16 @@ struct task_security_struct { u32 sockcreate_sid; /* fscreate SID */ }; +/* + * get the subjective security ID of the current task + */ +static inline u32 current_sid(void) +{ + const struct task_security_struct *tsec = current_security(); + + return tsec->sid; +} + enum label_initialized { LABEL_INVALID, /* invalid or not initialized */ LABEL_INITIALIZED, /* initialized */ diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 0aac402a0f11..55345f84f17d 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -77,25 +77,6 @@ static char policy_opened; /* global data for policy capabilities */ static struct dentry *policycap_dir; -/* Check whether a task is allowed to use a security operation. */ -static int task_has_security(struct task_struct *tsk, - u32 perms) -{ - const struct task_security_struct *tsec; - u32 sid = 0; - - rcu_read_lock(); - tsec = __task_cred(tsk)->security; - if (tsec) - sid = tsec->sid; - rcu_read_unlock(); - if (!tsec) - return -EACCES; - - return avc_has_perm(sid, SECINITSID_SECURITY, - SECCLASS_SECURITY, perms, NULL); -} - enum sel_inos { SEL_ROOT_INO = 2, SEL_LOAD, /* load policy */ @@ -166,7 +147,9 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, new_value = !!new_value; if (new_value != selinux_enforcing) { - length = task_has_security(current, SECURITY__SETENFORCE); + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__SETENFORCE, + NULL); if (length) goto out; audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, @@ -368,7 +351,8 @@ static int sel_open_policy(struct inode *inode, struct file *filp) mutex_lock(&sel_mutex); - rc = task_has_security(current, SECURITY__READ_POLICY); + rc = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL); if (rc) goto err; @@ -429,7 +413,8 @@ static ssize_t sel_read_policy(struct file *filp, char __user *buf, mutex_lock(&sel_mutex); - ret = task_has_security(current, SECURITY__READ_POLICY); + ret = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL); if (ret) goto out; @@ -499,7 +484,8 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, mutex_lock(&sel_mutex); - length = task_has_security(current, SECURITY__LOAD_POLICY); + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__LOAD_POLICY, NULL); if (length) goto out; @@ -561,7 +547,8 @@ static ssize_t sel_write_context(struct file *file, char *buf, size_t size) u32 sid, len; ssize_t length; - length = task_has_security(current, SECURITY__CHECK_CONTEXT); + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, NULL); if (length) goto out; @@ -604,7 +591,9 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, ssize_t length; unsigned int new_value; - length = task_has_security(current, SECURITY__SETCHECKREQPROT); + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT, + NULL); if (length) return length; @@ -645,7 +634,8 @@ static ssize_t sel_write_validatetrans(struct file *file, u16 tclass; int rc; - rc = task_has_security(current, SECURITY__VALIDATE_TRANS); + rc = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__VALIDATE_TRANS, NULL); if (rc) goto out; @@ -772,7 +762,8 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) struct av_decision avd; ssize_t length; - length = task_has_security(current, SECURITY__COMPUTE_AV); + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__COMPUTE_AV, NULL); if (length) goto out; @@ -822,7 +813,9 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) u32 len; int nargs; - length = task_has_security(current, SECURITY__COMPUTE_CREATE); + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE, + NULL); if (length) goto out; @@ -919,7 +912,9 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) char *newcon = NULL; u32 len; - length = task_has_security(current, SECURITY__COMPUTE_RELABEL); + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL, + NULL); if (length) goto out; @@ -975,7 +970,9 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size) int i, rc; u32 len, nsids; - length = task_has_security(current, SECURITY__COMPUTE_USER); + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__COMPUTE_USER, + NULL); if (length) goto out; @@ -1035,7 +1032,9 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size) char *newcon = NULL; u32 len; - length = task_has_security(current, SECURITY__COMPUTE_MEMBER); + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER, + NULL); if (length) goto out; @@ -1142,7 +1141,9 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, mutex_lock(&sel_mutex); - length = task_has_security(current, SECURITY__SETBOOL); + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__SETBOOL, + NULL); if (length) goto out; @@ -1198,7 +1199,9 @@ static ssize_t sel_commit_bools_write(struct file *filep, mutex_lock(&sel_mutex); - length = task_has_security(current, SECURITY__SETBOOL); + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__SETBOOL, + NULL); if (length) goto out; @@ -1351,7 +1354,9 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file, ssize_t ret; unsigned int new_value; - ret = task_has_security(current, SECURITY__SETSECPARAM); + ret = avc_has_perm(current_sid(), SECINITSID_SECURITY, + SECCLASS_SECURITY, SECURITY__SETSECPARAM, + NULL); if (ret) return ret; -- cgit v1.2.3 From b21507e272627c434e8dd74e8d51fd8245281b59 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 9 Jan 2017 10:07:31 -0500 Subject: proc,security: move restriction on writing /proc/pid/attr nodes to proc Processes can only alter their own security attributes via /proc/pid/attr nodes. This is presently enforced by each individual security module and is also imposed by the Linux credentials implementation, which only allows a task to alter its own credentials. Move the check enforcing this restriction from the individual security modules to proc_pid_attr_write() before calling the security hook, and drop the unnecessary task argument to the security hook since it can only ever be the current task. Signed-off-by: Stephen Smalley Acked-by: Casey Schaufler Acked-by: John Johansen Signed-off-by: Paul Moore --- fs/proc/base.c | 13 +++++++++---- include/linux/lsm_hooks.h | 3 +-- include/linux/security.h | 4 ++-- security/apparmor/lsm.c | 7 ++----- security/security.c | 4 ++-- security/selinux/hooks.c | 13 +------------ security/smack/smack_lsm.c | 11 +---------- 7 files changed, 18 insertions(+), 37 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 8e7e61b28f31..988c5a77e888 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2488,6 +2488,12 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, length = -ESRCH; if (!task) goto out_no_task; + + /* A task may only write its own attributes. */ + length = -EACCES; + if (current != task) + goto out; + if (count > PAGE_SIZE) count = PAGE_SIZE; @@ -2503,14 +2509,13 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, } /* Guard against adverse ptrace interaction */ - length = mutex_lock_interruptible(&task->signal->cred_guard_mutex); + length = mutex_lock_interruptible(¤t->signal->cred_guard_mutex); if (length < 0) goto out_free; - length = security_setprocattr(task, - (char*)file->f_path.dentry->d_name.name, + length = security_setprocattr(file->f_path.dentry->d_name.name, page, count); - mutex_unlock(&task->signal->cred_guard_mutex); + mutex_unlock(¤t->signal->cred_guard_mutex); out_free: kfree(page); out: diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 558adfa5c8a8..0dde95900196 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1547,8 +1547,7 @@ union security_list_options { void (*d_instantiate)(struct dentry *dentry, struct inode *inode); int (*getprocattr)(struct task_struct *p, char *name, char **value); - int (*setprocattr)(struct task_struct *p, char *name, void *value, - size_t size); + int (*setprocattr)(const char *name, void *value, size_t size); int (*ismaclabel)(const char *name); int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen); int (*secctx_to_secid)(const char *secdata, u32 seclen, u32 *secid); diff --git a/include/linux/security.h b/include/linux/security.h index c2125e9093e8..f4ebac117fa6 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -361,7 +361,7 @@ int security_sem_semop(struct sem_array *sma, struct sembuf *sops, unsigned nsops, int alter); void security_d_instantiate(struct dentry *dentry, struct inode *inode); int security_getprocattr(struct task_struct *p, char *name, char **value); -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size); +int security_setprocattr(const char *name, void *value, size_t size); int security_netlink_send(struct sock *sk, struct sk_buff *skb); int security_ismaclabel(const char *name); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); @@ -1106,7 +1106,7 @@ static inline int security_getprocattr(struct task_struct *p, char *name, char * return -EINVAL; } -static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) +static inline int security_setprocattr(char *name, void *value, size_t size) { return -EINVAL; } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 41b8cb115801..8202e5583479 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -495,8 +495,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, return error; } -static int apparmor_setprocattr(struct task_struct *task, char *name, - void *value, size_t size) +static int apparmor_setprocattr(const char *name, void *value, + size_t size) { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; @@ -506,9 +506,6 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, if (size == 0) return -EINVAL; - /* task can only write its own attributes */ - if (current != task) - return -EACCES; /* AppArmor requires that the buffer must be null terminated atm */ if (args[size - 1] != '\0') { diff --git a/security/security.c b/security/security.c index f825304f04a7..32052f5c76b2 100644 --- a/security/security.c +++ b/security/security.c @@ -1170,9 +1170,9 @@ int security_getprocattr(struct task_struct *p, char *name, char **value) return call_int_hook(getprocattr, -EINVAL, p, name, value); } -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) +int security_setprocattr(const char *name, void *value, size_t size) { - return call_int_hook(setprocattr, -EINVAL, p, name, value, size); + return call_int_hook(setprocattr, -EINVAL, name, value, size); } int security_netlink_send(struct sock *sk, struct sk_buff *skb) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 262e108c36d4..bada3cd42b9c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5862,8 +5862,7 @@ bad: return error; } -static int selinux_setprocattr(struct task_struct *p, - char *name, void *value, size_t size) +static int selinux_setprocattr(const char *name, void *value, size_t size) { struct task_security_struct *tsec; struct cred *new; @@ -5871,16 +5870,6 @@ static int selinux_setprocattr(struct task_struct *p, int error; char *str = value; - if (current != p) { - /* - * A task may only alter its own credentials. - * SELinux has always enforced this restriction, - * and it is now mandated by the Linux credentials - * infrastructure; see Documentation/security/credentials.txt. - */ - return -EACCES; - } - /* * Basic control over ability to set these attributes at all. */ diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 94dc9d406ce3..8da4a6b9ca4d 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3620,7 +3620,6 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) /** * smack_setprocattr - Smack process attribute setting - * @p: the object task * @name: the name of the attribute in /proc/.../attr * @value: the value to set * @size: the size of the value @@ -3630,8 +3629,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) * * Returns the length of the smack label or an error code */ -static int smack_setprocattr(struct task_struct *p, char *name, - void *value, size_t size) +static int smack_setprocattr(const char *name, void *value, size_t size) { struct task_smack *tsp = current_security(); struct cred *new; @@ -3639,13 +3637,6 @@ static int smack_setprocattr(struct task_struct *p, char *name, struct smack_known_list_elem *sklep; int rc; - /* - * Changing another process' Smack value is too dangerous - * and supports no sane use case. - */ - if (p != current) - return -EPERM; - if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel)) return -EPERM; -- cgit v1.2.3 From 4262fb51c9f53e0c623663216e6a5d1872a45824 Mon Sep 17 00:00:00 2001 From: Gary Tierney Date: Mon, 9 Jan 2017 10:07:31 -0500 Subject: selinux: log errors when loading new policy Adds error logging to the code paths which can fail when loading a new policy in sel_write_load(). If the policy fails to be loaded from userspace then a warning message is printed, whereas if a failure occurs after loading policy from userspace an error message will be printed with details on where policy loading failed (recreating one of /classes/, /policy_capabilities/, /booleans/ in the SELinux fs). Also, if sel_make_bools() fails to obtain an SID for an entry in /booleans/* an error will be printed indicating the path of the boolean. Signed-off-by: Gary Tierney Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/selinuxfs.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 55345f84f17d..7672b61d6673 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -508,20 +508,28 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, goto out; length = security_load_policy(data, count); - if (length) + if (length) { + pr_warn_ratelimited("SELinux: failed to load policy\n"); goto out; + } length = sel_make_bools(); - if (length) + if (length) { + pr_err("SELinux: failed to load policy booleans\n"); goto out1; + } length = sel_make_classes(); - if (length) + if (length) { + pr_err("SELinux: failed to load policy classes\n"); goto out1; + } length = sel_make_policycap(); - if (length) + if (length) { + pr_err("SELinux: failed to load policy capabilities\n"); goto out1; + } length = count; @@ -1302,9 +1310,12 @@ static int sel_make_bools(void) isec = (struct inode_security_struct *)inode->i_security; ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid); - if (ret) + if (ret) { + pr_err("SELinux: failed to lookup sid for %s\n", page); goto out; + } + isec->sid = sid; isec->initialized = LABEL_INITIALIZED; inode->i_fop = &sel_bool_ops; -- cgit v1.2.3 From 900fde06cb9d27625fec4f5cabd7f5462adc79fb Mon Sep 17 00:00:00 2001 From: Gary Tierney Date: Mon, 9 Jan 2017 10:07:32 -0500 Subject: selinux: default to security isid in sel_make_bools() if no sid is found Use SECINITSID_SECURITY as the default SID for booleans which don't have a matching SID returned from security_genfs_sid(), also update the error message to a warning which matches this. This prevents the policy failing to load (and consequently the system failing to boot) when there is no default genfscon statement matched for the selinuxfs in the new policy. Signed-off-by: Gary Tierney Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/selinuxfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 7672b61d6673..c354807381c1 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1311,9 +1311,9 @@ static int sel_make_bools(void) isec = (struct inode_security_struct *)inode->i_security; ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid); if (ret) { - pr_err("SELinux: failed to lookup sid for %s\n", page); - goto out; - + pr_warn_ratelimited("SELinux: no sid found, defaulting to security isid for %s\n", + page); + sid = SECINITSID_SECURITY; } isec->sid = sid; -- cgit v1.2.3 From b8aa8453918ebfd93d78de56c2afd4b735e02e27 Mon Sep 17 00:00:00 2001 From: Mickaël Salaün Date: Thu, 22 Dec 2016 00:32:25 +0100 Subject: security: Fix inode_getattr documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace arguments @mnt and @dentry with @path. Signed-off-by: Mickaël Salaün Acked-by: Serge Hallyn Signed-off-by: James Morris --- include/linux/lsm_hooks.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 558adfa5c8a8..9cf50ad2fe20 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -352,8 +352,7 @@ * Return 0 if permission is granted. * @inode_getattr: * Check permission before obtaining file attributes. - * @mnt is the vfsmount where the dentry was looked up - * @dentry contains the dentry structure for the file. + * @path contains the path structure for the file. * Return 0 if permission is granted. * @inode_setxattr: * Check permission before setting the extended attributes -- cgit v1.2.3 From 3c7ce3427106f0fa2d8593f9fd8f82c494215782 Mon Sep 17 00:00:00 2001 From: Vishal Goel Date: Wed, 23 Nov 2016 10:31:08 +0530 Subject: SMACK: Add the rcu synchronization mechanism in ipv6 hooks Add the rcu synchronization mechanism for accessing smk_ipv6_port_list in smack IPv6 hooks. Access to the port list is vulnerable to a race condition issue,it does not apply proper synchronization methods while working on critical section. It is possible that when one thread is reading the list, at the same time another thread is modifying the same port list, which can cause the major problems. To ensure proper synchronization between two threads, rcu mechanism has been applied while accessing and modifying the port list. RCU will also not affect the performance, as there are more accesses than modification where RCU is most effective synchronization mechanism. Signed-off-by: Vishal Goel Signed-off-by: Himanshu Shukla Signed-off-by: Casey Schaufler --- security/smack/smack_lsm.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 94dc9d406ce3..b76696b84e5c 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -52,6 +52,7 @@ #define SMK_SENDING 2 #ifdef SMACK_IPV6_PORT_LABELING +DEFINE_MUTEX(smack_ipv6_lock); static LIST_HEAD(smk_ipv6_port_list); #endif static struct kmem_cache *smack_inode_cache; @@ -2603,17 +2604,20 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) * on the bound socket. Take the changes to the port * as well. */ - list_for_each_entry(spp, &smk_ipv6_port_list, list) { + rcu_read_lock(); + list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { if (sk != spp->smk_sock) continue; spp->smk_in = ssp->smk_in; spp->smk_out = ssp->smk_out; + rcu_read_unlock(); return; } /* * A NULL address is only used for updating existing * bound entries. If there isn't one, it's OK. */ + rcu_read_unlock(); return; } @@ -2629,16 +2633,18 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) * Look for an existing port list entry. * This is an indication that a port is getting reused. */ - list_for_each_entry(spp, &smk_ipv6_port_list, list) { + rcu_read_lock(); + list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { if (spp->smk_port != port) continue; spp->smk_port = port; spp->smk_sock = sk; spp->smk_in = ssp->smk_in; spp->smk_out = ssp->smk_out; + rcu_read_unlock(); return; } - + rcu_read_unlock(); /* * A new port entry is required. */ @@ -2651,7 +2657,9 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) spp->smk_in = ssp->smk_in; spp->smk_out = ssp->smk_out; - list_add(&spp->list, &smk_ipv6_port_list); + mutex_lock(&smack_ipv6_lock); + list_add_rcu(&spp->list, &smk_ipv6_port_list); + mutex_unlock(&smack_ipv6_lock); return; } @@ -2702,7 +2710,8 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, return 0; port = ntohs(address->sin6_port); - list_for_each_entry(spp, &smk_ipv6_port_list, list) { + rcu_read_lock(); + list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { if (spp->smk_port != port) continue; object = spp->smk_in; @@ -2710,6 +2719,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, ssp->smk_packet = spp->smk_out; break; } + rcu_read_unlock(); return smk_ipv6_check(skp, object, address, act); } -- cgit v1.2.3 From 9d44c97384fdc04585c5d5c985fe88ba7285b5ac Mon Sep 17 00:00:00 2001 From: Vishal Goel Date: Wed, 23 Nov 2016 10:31:59 +0530 Subject: Smack: Fix the issue of permission denied error in ipv6 hook Permission denied error comes when 2 IPv6 servers are running and client tries to connect one of them. Scenario is that both servers are using same IP and port but different protocols(Udp and tcp). They are using different SMACK64IPIN labels.Tcp server is using "test" and udp server is using "test-in". When we try to run tcp client with SMACK64IPOUT label as "test", then connection denied error comes. It should not happen since both tcp server and client labels are same.This happens because there is no check for protocol in smk_ipv6_port_label() function while searching for the earlier port entry. It checks whether there is an existing port entry on the basis of port only. So it updates the earlier port entry in the list. Due to which smack label gets changed for earlier entry in the "smk_ipv6_port_list" list and permission denied error comes. Now a check is added for socket type also.Now if 2 processes use same port but different protocols (tcp or udp), then 2 different port entries will be added in the list. Similarly while checking smack access in smk_ipv6_port_check() function, port entry is searched on the basis of both port and protocol. Signed-off-by: Vishal Goel Signed-off-by: Himanshu Shukla Signed-off-by: Casey Schaufler --- security/smack/smack.h | 1 + security/smack/smack_lsm.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/security/smack/smack.h b/security/smack/smack.h index 77abe2efacae..73480ee07478 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -173,6 +173,7 @@ struct smk_port_label { unsigned short smk_port; /* the port number */ struct smack_known *smk_in; /* inbound label */ struct smack_known *smk_out; /* outgoing label */ + short smk_sock_type; /* Socket type */ }; #endif /* SMACK_IPV6_PORT_LABELING */ diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index b76696b84e5c..5e4d2bdb38cb 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2635,7 +2635,7 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) */ rcu_read_lock(); list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { - if (spp->smk_port != port) + if (spp->smk_port != port || spp->smk_sock_type != sock->type) continue; spp->smk_port = port; spp->smk_sock = sk; @@ -2656,6 +2656,7 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) spp->smk_sock = sk; spp->smk_in = ssp->smk_in; spp->smk_out = ssp->smk_out; + spp->smk_sock_type = sock->type; mutex_lock(&smack_ipv6_lock); list_add_rcu(&spp->list, &smk_ipv6_port_list); @@ -2712,7 +2713,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, port = ntohs(address->sin6_port); rcu_read_lock(); list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { - if (spp->smk_port != port) + if (spp->smk_port != port || spp->smk_sock_type != sk->sk_type) continue; object = spp->smk_in; if (act == SMK_CONNECTING) -- cgit v1.2.3 From 0c96d1f5328e834048480e4696e6867992115c33 Mon Sep 17 00:00:00 2001 From: Vishal Goel Date: Wed, 23 Nov 2016 10:32:54 +0530 Subject: Smack: Fix the issue of wrong SMACK label update in socket bind fail case Fix the issue of wrong SMACK label (SMACK64IPIN) update when a second bind call is made to same IP address & port, but with different SMACK label (SMACK64IPIN) by second instance of server. In this case server returns with "Bind:Address already in use" error but before returning, SMACK label is updated in SMACK port-label mapping list inside smack_socket_bind() hook To fix this issue a new check has been added in smk_ipv6_port_label() function before updating the existing port entry. It checks whether the socket for matching port entry is closed or not. If it is closed then it means port is not bound and it is safe to update the existing port entry else return if port is still getting used. For checking whether socket is closed or not, one more field "smk_can_reuse" has been added in the "smk_port_label" structure. This field will be set to '1' in "smack_sk_free_security()" function which is called to free the socket security blob when the socket is being closed. In this function, port entry is searched in the SMACK port-label mapping list for the closing socket. If entry is found then "smk_can_reuse" field is set to '1'.Initially "smk_can_reuse" field is set to '0' in smk_ipv6_port_label() function after creating a new entry in the list which indicates that socket is in use. Signed-off-by: Vishal Goel Signed-off-by: Himanshu Shukla Signed-off-by: Casey Schaufler --- security/smack/smack.h | 1 + security/smack/smack_lsm.c | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/security/smack/smack.h b/security/smack/smack.h index 73480ee07478..2fac3b5bf44a 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -174,6 +174,7 @@ struct smk_port_label { struct smack_known *smk_in; /* inbound label */ struct smack_known *smk_out; /* outgoing label */ short smk_sock_type; /* Socket type */ + short smk_can_reuse; }; #endif /* SMACK_IPV6_PORT_LABELING */ diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 5e4d2bdb38cb..ed6885bccec4 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2354,6 +2354,20 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) */ static void smack_sk_free_security(struct sock *sk) { +#ifdef SMACK_IPV6_PORT_LABELING + struct smk_port_label *spp; + + if (sk->sk_family == PF_INET6) { + rcu_read_lock(); + list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { + if (spp->smk_sock != sk) + continue; + spp->smk_can_reuse = 1; + break; + } + rcu_read_unlock(); + } +#endif kfree(sk->sk_security); } @@ -2637,10 +2651,15 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { if (spp->smk_port != port || spp->smk_sock_type != sock->type) continue; + if (spp->smk_can_reuse != 1) { + rcu_read_unlock(); + return; + } spp->smk_port = port; spp->smk_sock = sk; spp->smk_in = ssp->smk_in; spp->smk_out = ssp->smk_out; + spp->smk_can_reuse = 0; rcu_read_unlock(); return; } @@ -2657,6 +2676,7 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) spp->smk_in = ssp->smk_in; spp->smk_out = ssp->smk_out; spp->smk_sock_type = sock->type; + spp->smk_can_reuse = 0; mutex_lock(&smack_ipv6_lock); list_add_rcu(&spp->list, &smk_ipv6_port_list); -- cgit v1.2.3 From 2e962e2fec5c35b91e3b541e2b8373504bf91e27 Mon Sep 17 00:00:00 2001 From: Vishal Goel Date: Wed, 23 Nov 2016 10:46:57 +0530 Subject: SMACK: Add new lock for adding entry in smack master list "smk_set_access()" function adds a new rule entry in subject label specific list(rule_list) and in global rule list(smack_rule_list) both. Mutex lock (rule_lock) is used to avoid simultaneous updates. But this lock is subject label specific lock. If 2 processes tries to add different rules(i.e with different subject labels) simultaneously, then both the processes can take the "rule_lock" respectively. So it will cause a problem while adding entries in master rule list. Now a new mutex lock(smack_master_list_lock) has been taken to add entry in smack_rule_list to avoid simultaneous updates of different rules. Signed-off-by: Vishal Goel Signed-off-by: Himanshu Shukla Signed-off-by: Casey Schaufler --- security/smack/smackfs.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 13743a01b35b..366b8356f75b 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -67,6 +67,7 @@ enum smk_inos { /* * List locks */ +static DEFINE_MUTEX(smack_master_list_lock); static DEFINE_MUTEX(smack_cipso_lock); static DEFINE_MUTEX(smack_ambient_lock); static DEFINE_MUTEX(smk_net4addr_lock); @@ -262,12 +263,16 @@ static int smk_set_access(struct smack_parsed_rule *srp, * it needs to get added for reporting. */ if (global) { + mutex_unlock(rule_lock); smlp = kzalloc(sizeof(*smlp), GFP_KERNEL); if (smlp != NULL) { smlp->smk_rule = sp; + mutex_lock(&smack_master_list_lock); list_add_rcu(&smlp->list, &smack_rule_list); + mutex_unlock(&smack_master_list_lock); } else rc = -ENOMEM; + return rc; } } -- cgit v1.2.3 From d54a197964e7eb636a0c64fb0dbdd67759eb71f2 Mon Sep 17 00:00:00 2001 From: Himanshu Shukla Date: Wed, 23 Nov 2016 11:58:48 +0530 Subject: SMACK: Delete list_head repeated initialization smk_copy_rules() and smk_copy_relabel() are initializing list_head though they have been initialized already in new_task_smack() function. Delete repeated initialization. Signed-off-by: Himanshu Shukla Signed-off-by: Casey Schaufler --- security/smack/smack_lsm.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index ed6885bccec4..1368f8925343 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -348,8 +348,6 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead, struct smack_rule *orp; int rc = 0; - INIT_LIST_HEAD(nhead); - list_for_each_entry_rcu(orp, ohead, list) { nrp = kzalloc(sizeof(struct smack_rule), gfp); if (nrp == NULL) { @@ -376,8 +374,6 @@ static int smk_copy_relabel(struct list_head *nhead, struct list_head *ohead, struct smack_known_list_elem *nklep; struct smack_known_list_elem *oklep; - INIT_LIST_HEAD(nhead); - list_for_each_entry(oklep, ohead, list) { nklep = kzalloc(sizeof(struct smack_known_list_elem), gfp); if (nklep == NULL) { -- cgit v1.2.3 From 3d4f673a6988f57e6f6ccd1a3b79eee171545e08 Mon Sep 17 00:00:00 2001 From: Himanshu Shukla Date: Wed, 23 Nov 2016 11:59:19 +0530 Subject: SMACK: Free the i_security blob in inode using RCU There is race condition issue while freeing the i_security blob in SMACK module. There is existing condition where i_security can be freed while inode_permission is called from path lookup on second CPU. There has been observed the page fault with such condition. VFS code and Selinux module takes care of this condition by freeing the inode and i_security field using RCU via call_rcu(). But in SMACK directly the i_secuirty blob is being freed. Use call_rcu() to fix this race condition issue. Signed-off-by: Himanshu Shukla Signed-off-by: Vishal Goel Signed-off-by: Casey Schaufler --- security/smack/smack.h | 1 + security/smack/smack_lsm.c | 32 ++++++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/security/smack/smack.h b/security/smack/smack.h index 2fac3b5bf44a..612b810fbbc6 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -114,6 +114,7 @@ struct inode_smack { struct smack_known *smk_mmap; /* label of the mmap domain */ struct mutex smk_lock; /* initialization lock */ int smk_flags; /* smack inode flags */ + struct rcu_head smk_rcu; /* for freeing inode_smack */ }; struct task_smack { diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 1368f8925343..5deda8e0fe96 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1006,15 +1006,39 @@ static int smack_inode_alloc_security(struct inode *inode) } /** - * smack_inode_free_security - free an inode blob + * smack_inode_free_rcu - Free inode_smack blob from cache + * @head: the rcu_head for getting inode_smack pointer + * + * Call back function called from call_rcu() to free + * the i_security blob pointer in inode + */ +static void smack_inode_free_rcu(struct rcu_head *head) +{ + struct inode_smack *issp; + + issp = container_of(head, struct inode_smack, smk_rcu); + kmem_cache_free(smack_inode_cache, issp); +} + +/** + * smack_inode_free_security - free an inode blob using call_rcu() * @inode: the inode with a blob * - * Clears the blob pointer in inode + * Clears the blob pointer in inode using RCU */ static void smack_inode_free_security(struct inode *inode) { - kmem_cache_free(smack_inode_cache, inode->i_security); - inode->i_security = NULL; + struct inode_smack *issp = inode->i_security; + + /* + * The inode may still be referenced in a path walk and + * a call to smack_inode_permission() can be made + * after smack_inode_free_security() is called. + * To avoid race condition free the i_security via RCU + * and leave the current inode->i_security pointer intact. + * The inode will be freed after the RCU grace period too. + */ + call_rcu(&issp->smk_rcu, smack_inode_free_rcu); } /** -- cgit v1.2.3 From 348dc288d4bf4c0272a46a80f97748f36916601b Mon Sep 17 00:00:00 2001 From: Vishal Goel Date: Wed, 23 Nov 2016 10:45:31 +0530 Subject: Smack: Traverse the smack_known_list using list_for_each_entry_rcu macro In smack_from_secattr function,"smack_known_list" is being traversed using list_for_each_entry macro, although it is a rcu protected structure. So it should be traversed using "list_for_each_entry_rcu" macro to fetch the rcu protected entry. Signed-off-by: Vishal Goel Signed-off-by: Himanshu Shukla Signed-off-by: Casey Schaufler --- security/smack/smack_lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 5deda8e0fe96..4dd458a2b1e8 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3900,7 +3900,7 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, * ambient value. */ rcu_read_lock(); - list_for_each_entry(skp, &smack_known_list, list) { + list_for_each_entry_rcu(skp, &smack_known_list, list) { if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl) continue; /* -- cgit v1.2.3 From c9d238a18baa92600ba015d6d6c2cde53f55c572 Mon Sep 17 00:00:00 2001 From: Himanshu Shukla Date: Wed, 23 Nov 2016 11:59:45 +0530 Subject: SMACK: Use smk_tskacc() instead of smk_access() for proper logging smack_file_open() is first checking the capability of calling subject, this check will skip the SMACK logging for success case. Use smk_tskacc() for proper logging and SMACK access check. Signed-off-by: Himanshu Shukla Signed-off-by: Casey Schaufler --- security/smack/smack_lsm.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 4dd458a2b1e8..681583d66c0e 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1955,12 +1955,9 @@ static int smack_file_open(struct file *file, const struct cred *cred) struct smk_audit_info ad; int rc; - if (smack_privileged(CAP_MAC_OVERRIDE)) - return 0; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); - rc = smk_access(tsp->smk_task, smk_of_inode(inode), MAY_READ, &ad); + rc = smk_tskacc(tsp, smk_of_inode(inode), MAY_READ, &ad); rc = smk_bu_credfile(cred, file, MAY_READ, rc); return rc; -- cgit v1.2.3 From 805b65a80bed029572c6462cc4be0a260e1482e9 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Fri, 9 Dec 2016 14:03:04 +0100 Subject: Smack: fix d_instantiate logic for sockfs and pipefs Since 4b936885a (v2.6.32) all inodes on sockfs and pipefs are disconnected. It caused filesystem specific code in smack_d_instantiate to be skipped, because all inodes on those pseudo filesystems were treated as root inodes. As a result all sockfs inodes had the Smack label set to floor. In most cases access checks for sockets use socket_smack data so the inode label is not important. But there are special cases that were broken. One example would be calling fcntl with F_SETOWN command on a socket fd. Now smack_d_instantiate expects all pipefs and sockfs inodes to be disconnected and has the logic in appropriate place. Signed-off-by: Rafal Krypa Signed-off-by: Casey Schaufler --- security/smack/smack_lsm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 681583d66c0e..225c4ad56444 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3486,6 +3486,13 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) case PIPEFS_MAGIC: isp->smk_inode = smk_of_current(); break; + case SOCKFS_MAGIC: + /* + * Socket access is controlled by the socket + * structures associated with the task involved. + */ + isp->smk_inode = &smack_known_star; + break; default: isp->smk_inode = sbsp->smk_root; break; @@ -3502,19 +3509,12 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) */ switch (sbp->s_magic) { case SMACK_MAGIC: - case PIPEFS_MAGIC: - case SOCKFS_MAGIC: case CGROUP_SUPER_MAGIC: /* * Casey says that it's a little embarrassing * that the smack file system doesn't do * extended attributes. * - * Casey says pipes are easy (?) - * - * Socket access is controlled by the socket - * structures associated with the task involved. - * * Cgroupfs is special */ final = &smack_known_star; -- cgit v1.2.3 From 83a1e53f392075e291a90746241dce45c6f9429a Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Mon, 12 Dec 2016 17:35:26 +0900 Subject: Smack: ignore private inode for file functions The access to fd from anon_inode is always failed because there is no set xattr operations. So this patch fixes to ignore private inode including anon_inode for file functions. It was only ignored for smack_file_receive() to share dma-buf fd, but dma-buf has other functions like ioctl and mmap. Reference: https://lkml.org/lkml/2015/4/17/16 Signed-off-by: Seung-Woo Kim Signed-off-by: Casey Schaufler --- security/smack/smack_lsm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 225c4ad56444..679455350faf 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1647,6 +1647,9 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd, struct smk_audit_info ad; struct inode *inode = file_inode(file); + if (unlikely(IS_PRIVATE(inode))) + return 0; + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); @@ -1676,6 +1679,9 @@ static int smack_file_lock(struct file *file, unsigned int cmd) int rc; struct inode *inode = file_inode(file); + if (unlikely(IS_PRIVATE(inode))) + return 0; + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); rc = smk_curacc(smk_of_inode(inode), MAY_LOCK, &ad); @@ -1702,6 +1708,9 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd, int rc = 0; struct inode *inode = file_inode(file); + if (unlikely(IS_PRIVATE(inode))) + return 0; + switch (cmd) { case F_GETLK: break; @@ -1755,6 +1764,9 @@ static int smack_mmap_file(struct file *file, if (file == NULL) return 0; + if (unlikely(IS_PRIVATE(file_inode(file)))) + return 0; + isp = file_inode(file)->i_security; if (isp->smk_mmap == NULL) return 0; -- cgit v1.2.3 From b4ba35c75a0671a06b978b6386b54148efddf39f Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Wed, 11 Jan 2017 16:33:54 -0500 Subject: selinux: drop unused socket security classes Several of the extended socket classes introduced by commit da69a5306ab92e07 ("selinux: support distinctions among all network address families") are never used because sockets can never be created with the associated address family. Remove these unused socket security classes. The removed classes are bridge_socket for PF_BRIDGE, ib_socket for PF_IB, and mpls_socket for PF_MPLS. Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 6 ------ security/selinux/include/classmap.h | 6 ------ 2 files changed, 12 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index bada3cd42b9c..55ad878f1146 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1353,8 +1353,6 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc return SECCLASS_IPX_SOCKET; case PF_NETROM: return SECCLASS_NETROM_SOCKET; - case PF_BRIDGE: - return SECCLASS_BRIDGE_SOCKET; case PF_ATMPVC: return SECCLASS_ATMPVC_SOCKET; case PF_X25: @@ -1373,10 +1371,6 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc return SECCLASS_PPPOX_SOCKET; case PF_LLC: return SECCLASS_LLC_SOCKET; - case PF_IB: - return SECCLASS_IB_SOCKET; - case PF_MPLS: - return SECCLASS_MPLS_SOCKET; case PF_CAN: return SECCLASS_CAN_SOCKET; case PF_TIPC: diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 0dfd26d0b8d8..7898ffa6d3e6 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -183,8 +183,6 @@ struct security_class_mapping secclass_map[] = { { COMMON_SOCK_PERMS, NULL } }, { "netrom_socket", { COMMON_SOCK_PERMS, NULL } }, - { "bridge_socket", - { COMMON_SOCK_PERMS, NULL } }, { "atmpvc_socket", { COMMON_SOCK_PERMS, NULL } }, { "x25_socket", @@ -203,10 +201,6 @@ struct security_class_mapping secclass_map[] = { { COMMON_SOCK_PERMS, NULL } }, { "llc_socket", { COMMON_SOCK_PERMS, NULL } }, - { "ib_socket", - { COMMON_SOCK_PERMS, NULL } }, - { "mpls_socket", - { COMMON_SOCK_PERMS, NULL } }, { "can_socket", { COMMON_SOCK_PERMS, NULL } }, { "tipc_socket", -- cgit v1.2.3 From 3a2f5a59a695a73e0cde9a61e0feae5fa730e936 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Tue, 10 Jan 2017 12:28:32 -0500 Subject: security,selinux,smack: kill security_task_wait hook As reported by yangshukui, a permission denial from security_task_wait() can lead to a soft lockup in zap_pid_ns_processes() since it only expects sys_wait4() to return 0 or -ECHILD. Further, security_task_wait() can in general lead to zombies; in the absence of some way to automatically reparent a child process upon a denial, the hook is not useful. Remove the security hook and its implementations in SELinux and Smack. Smack already removed its check from its hook. Reported-by: yangshukui Signed-off-by: Stephen Smalley Acked-by: Casey Schaufler Acked-by: Oleg Nesterov Signed-off-by: Paul Moore --- include/linux/lsm_hooks.h | 7 ------- include/linux/security.h | 6 ------ kernel/exit.c | 19 ++----------------- security/security.c | 6 ------ security/selinux/hooks.c | 7 ------- security/smack/smack_lsm.c | 20 -------------------- 6 files changed, 2 insertions(+), 63 deletions(-) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 0dde95900196..6fe7a5cb0be1 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -666,11 +666,6 @@ * @sig contains the signal value. * @secid contains the sid of the process where the signal originated * Return 0 if permission is granted. - * @task_wait: - * Check permission before allowing a process to reap a child process @p - * and collect its status information. - * @p contains the task_struct for process. - * Return 0 if permission is granted. * @task_prctl: * Check permission before performing a process control operation on the * current process. @@ -1507,7 +1502,6 @@ union security_list_options { int (*task_movememory)(struct task_struct *p); int (*task_kill)(struct task_struct *p, struct siginfo *info, int sig, u32 secid); - int (*task_wait)(struct task_struct *p); int (*task_prctl)(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); void (*task_to_inode)(struct task_struct *p, struct inode *inode); @@ -1767,7 +1761,6 @@ struct security_hook_heads { struct list_head task_getscheduler; struct list_head task_movememory; struct list_head task_kill; - struct list_head task_wait; struct list_head task_prctl; struct list_head task_to_inode; struct list_head ipc_permission; diff --git a/include/linux/security.h b/include/linux/security.h index f4ebac117fa6..d3868f2ebada 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -332,7 +332,6 @@ int security_task_getscheduler(struct task_struct *p); int security_task_movememory(struct task_struct *p); int security_task_kill(struct task_struct *p, struct siginfo *info, int sig, u32 secid); -int security_task_wait(struct task_struct *p); int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); void security_task_to_inode(struct task_struct *p, struct inode *inode); @@ -980,11 +979,6 @@ static inline int security_task_kill(struct task_struct *p, return 0; } -static inline int security_task_wait(struct task_struct *p) -{ - return 0; -} - static inline int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, diff --git a/kernel/exit.c b/kernel/exit.c index 8f14b866f9f6..60f245190571 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -1360,7 +1359,7 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p) * Returns nonzero for a final return, when we have unlocked tasklist_lock. * Returns zero if the search for a child should continue; * then ->notask_error is 0 if @p is an eligible child, - * or another error from security_task_wait(), or still -ECHILD. + * or still -ECHILD. */ static int wait_consider_task(struct wait_opts *wo, int ptrace, struct task_struct *p) @@ -1380,20 +1379,6 @@ static int wait_consider_task(struct wait_opts *wo, int ptrace, if (!ret) return ret; - ret = security_task_wait(p); - if (unlikely(ret < 0)) { - /* - * If we have not yet seen any eligible child, - * then let this error code replace -ECHILD. - * A permission error will give the user a clue - * to look for security policy problems, rather - * than for mysterious wait bugs. - */ - if (wo->notask_error) - wo->notask_error = ret; - return 0; - } - if (unlikely(exit_state == EXIT_TRACE)) { /* * ptrace == 0 means we are the natural parent. In this case @@ -1486,7 +1471,7 @@ static int wait_consider_task(struct wait_opts *wo, int ptrace, * Returns nonzero for a final return, when we have unlocked tasklist_lock. * Returns zero if the search for a child should continue; then * ->notask_error is 0 if there were any eligible children, - * or another error from security_task_wait(), or still -ECHILD. + * or still -ECHILD. */ static int do_wait_thread(struct wait_opts *wo, struct task_struct *tsk) { diff --git a/security/security.c b/security/security.c index 32052f5c76b2..8c9fee59e60a 100644 --- a/security/security.c +++ b/security/security.c @@ -1025,11 +1025,6 @@ int security_task_kill(struct task_struct *p, struct siginfo *info, return call_int_hook(task_kill, 0, p, info, sig, secid); } -int security_task_wait(struct task_struct *p) -{ - return call_int_hook(task_wait, 0, p); -} - int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { @@ -1769,7 +1764,6 @@ struct security_hook_heads security_hook_heads = { .task_movememory = LIST_HEAD_INIT(security_hook_heads.task_movememory), .task_kill = LIST_HEAD_INIT(security_hook_heads.task_kill), - .task_wait = LIST_HEAD_INIT(security_hook_heads.task_wait), .task_prctl = LIST_HEAD_INIT(security_hook_heads.task_prctl), .task_to_inode = LIST_HEAD_INIT(security_hook_heads.task_to_inode), diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 55ad878f1146..a5398fea0966 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3963,12 +3963,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info, return avc_has_perm(secid, task_sid(p), SECCLASS_PROCESS, perm, NULL); } -static int selinux_task_wait(struct task_struct *p) -{ - return avc_has_perm(task_sid(p), current_sid(), SECCLASS_PROCESS, - PROCESS__SIGCHLD, NULL); -} - static void selinux_task_to_inode(struct task_struct *p, struct inode *inode) { @@ -6211,7 +6205,6 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(task_getscheduler, selinux_task_getscheduler), LSM_HOOK_INIT(task_movememory, selinux_task_movememory), LSM_HOOK_INIT(task_kill, selinux_task_kill), - LSM_HOOK_INIT(task_wait, selinux_task_wait), LSM_HOOK_INIT(task_to_inode, selinux_task_to_inode), LSM_HOOK_INIT(ipc_permission, selinux_ipc_permission), diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 8da4a6b9ca4d..2166373ea5a4 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2271,25 +2271,6 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, return rc; } -/** - * smack_task_wait - Smack access check for waiting - * @p: task to wait for - * - * Returns 0 - */ -static int smack_task_wait(struct task_struct *p) -{ - /* - * Allow the operation to succeed. - * Zombies are bad. - * In userless environments (e.g. phones) programs - * get marked with SMACK64EXEC and even if the parent - * and child shouldn't be talking the parent still - * may expect to know when the child exits. - */ - return 0; -} - /** * smack_task_to_inode - copy task smack into the inode blob * @p: task to copy from @@ -4658,7 +4639,6 @@ static struct security_hook_list smack_hooks[] = { LSM_HOOK_INIT(task_getscheduler, smack_task_getscheduler), LSM_HOOK_INIT(task_movememory, smack_task_movememory), LSM_HOOK_INIT(task_kill, smack_task_kill), - LSM_HOOK_INIT(task_wait, smack_task_wait), LSM_HOOK_INIT(task_to_inode, smack_task_to_inode), LSM_HOOK_INIT(ipc_permission, smack_ipc_permission), -- cgit v1.2.3 From a7f6c1b63b863d29f126d9b163ad5b40008544b2 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 14 Nov 2016 20:11:52 +0900 Subject: AppArmor: Use GFP_KERNEL for __aa_kvmalloc(). Calling kmalloc(GFP_NOIO) with order == PAGE_ALLOC_COSTLY_ORDER is not recommended because it might fall into infinite retry loop without invoking the OOM killer. Since aa_dfa_unpack() is the only caller of kvzalloc() and aa_dfa_unpack() which is calling kvzalloc() via unpack_table() is doing kzalloc(GFP_KERNEL), it is safe to use GFP_KERNEL from __aa_kvmalloc(). Since aa_simple_write_to_buffer() is the only caller of kvmalloc() and aa_simple_write_to_buffer() is calling copy_from_user() which is GFP_KERNEL context (see memdup_user_nul()), it is safe to use GFP_KERNEL from __aa_kvmalloc(). Therefore, replace GFP_NOIO with GFP_KERNEL. Also, since we have vmalloc() fallback, add __GFP_NORETRY so that we don't invoke the OOM killer by kmalloc(GFP_KERNEL) with order == PAGE_ALLOC_COSTLY_ORDER. Signed-off-by: Tetsuo Handa Signed-off-by: John Johansen --- security/apparmor/lib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index c1827e068454..2ef422a25474 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -95,7 +95,8 @@ void *__aa_kvmalloc(size_t size, gfp_t flags) /* do not attempt kmalloc if we need more than 16 pages at once */ if (size <= (16*PAGE_SIZE)) - buffer = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN); + buffer = kmalloc(size, flags | GFP_KERNEL | __GFP_NORETRY | + __GFP_NOWARN); if (!buffer) { if (flags & __GFP_ZERO) buffer = vzalloc(size); -- cgit v1.2.3 From 8486adf0d755062611968ecc1632a13ebce71660 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 16 Dec 2016 17:04:13 -0800 Subject: apparmor: use designated initializers Prepare to mark sensitive kernel structures for randomization by making sure they're using designated initializers. These were identified during allyesconfig builds of x86, arm, and arm64, with most initializer fixes extracted from grsecurity. Signed-off-by: Kees Cook Signed-off-by: John Johansen --- security/apparmor/file.c | 4 ++-- security/apparmor/lsm.c | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 4d2af4b01033..608971ac6781 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -349,8 +349,8 @@ static inline bool xindex_is_subset(u32 link, u32 target) int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry) { - struct path link = { new_dir->mnt, new_dentry }; - struct path target = { new_dir->mnt, old_dentry }; + struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry }; + struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry }; struct path_cond cond = { d_backing_inode(old_dentry)->i_uid, d_backing_inode(old_dentry)->i_mode diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 41b8cb115801..f76738b1eb15 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -176,7 +176,7 @@ static int common_perm_dir_dentry(int op, const struct path *dir, struct dentry *dentry, u32 mask, struct path_cond *cond) { - struct path path = { dir->mnt, dentry }; + struct path path = { .mnt = dir->mnt, .dentry = dentry }; return common_perm(op, &path, mask, cond); } @@ -306,8 +306,10 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d profile = aa_current_profile(); if (!unconfined(profile)) { - struct path old_path = { old_dir->mnt, old_dentry }; - struct path new_path = { new_dir->mnt, new_dentry }; + struct path old_path = { .mnt = old_dir->mnt, + .dentry = old_dentry }; + struct path new_path = { .mnt = new_dir->mnt, + .dentry = new_dentry }; struct path_cond cond = { d_backing_inode(old_dentry)->i_uid, d_backing_inode(old_dentry)->i_mode }; -- cgit v1.2.3 From 12557dcba21b015f470076da6947e68bc70fff64 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:13 -0800 Subject: apparmor: move lib definitions into separate lib include Signed-off-by: John Johansen --- security/apparmor/include/apparmor.h | 82 +------------------------------ security/apparmor/include/lib.h | 94 ++++++++++++++++++++++++++++++++++++ security/apparmor/include/policy.h | 1 + security/apparmor/lib.c | 2 +- security/apparmor/match.c | 2 +- 5 files changed, 99 insertions(+), 82 deletions(-) create mode 100644 security/apparmor/include/lib.h diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 5d721e990876..1750cc0721c1 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -1,7 +1,7 @@ /* * AppArmor security module * - * This file contains AppArmor basic global and lib definitions + * This file contains AppArmor basic global * * Copyright (C) 1998-2008 Novell/SUSE * Copyright 2009-2010 Canonical Ltd. @@ -15,10 +15,7 @@ #ifndef __APPARMOR_H #define __APPARMOR_H -#include -#include - -#include "match.h" +#include /* * Class of mediation types in the AppArmor policy db @@ -43,79 +40,4 @@ extern bool aa_g_logsyscall; extern bool aa_g_paranoid_load; extern unsigned int aa_g_path_max; -/* - * DEBUG remains global (no per profile flag) since it is mostly used in sysctl - * which is not related to profile accesses. - */ - -#define AA_DEBUG(fmt, args...) \ - do { \ - if (aa_g_debug && printk_ratelimit()) \ - printk(KERN_DEBUG "AppArmor: " fmt, ##args); \ - } while (0) - -#define AA_ERROR(fmt, args...) \ - do { \ - if (printk_ratelimit()) \ - printk(KERN_ERR "AppArmor: " fmt, ##args); \ - } while (0) - -/* Flag indicating whether initialization completed */ -extern int apparmor_initialized __initdata; - -/* fn's in lib */ -char *aa_split_fqname(char *args, char **ns_name); -void aa_info_message(const char *str); -void *__aa_kvmalloc(size_t size, gfp_t flags); - -static inline void *kvmalloc(size_t size) -{ - return __aa_kvmalloc(size, 0); -} - -static inline void *kvzalloc(size_t size) -{ - return __aa_kvmalloc(size, __GFP_ZERO); -} - -/* returns 0 if kref not incremented */ -static inline int kref_get_not0(struct kref *kref) -{ - return atomic_inc_not_zero(&kref->refcount); -} - -/** - * aa_strneq - compare null terminated @str to a non null terminated substring - * @str: a null terminated string - * @sub: a substring, not necessarily null terminated - * @len: length of @sub to compare - * - * The @str string must be full consumed for this to be considered a match - */ -static inline bool aa_strneq(const char *str, const char *sub, int len) -{ - return !strncmp(str, sub, len) && !str[len]; -} - -/** - * aa_dfa_null_transition - step to next state after null character - * @dfa: the dfa to match against - * @start: the state of the dfa to start matching in - * - * aa_dfa_null_transition transitions to the next state after a null - * character which is not used in standard matching and is only - * used to separate pairs. - */ -static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, - unsigned int start) -{ - /* the null transition only needs the string's null terminator byte */ - return aa_dfa_next(dfa, start, 0); -} - -static inline bool mediated_filesystem(struct dentry *dentry) -{ - return !(dentry->d_sb->s_flags & MS_NOUSER); -} - #endif /* __APPARMOR_H */ diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h new file mode 100644 index 000000000000..71fd6d28a651 --- /dev/null +++ b/security/apparmor/include/lib.h @@ -0,0 +1,94 @@ +/* + * AppArmor security module + * + * This file contains AppArmor lib definitions + * + * 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_LIB_H +#define __AA_LIB_H + +#include +#include + +#include "match.h" + +/* + * DEBUG remains global (no per profile flag) since it is mostly used in sysctl + * which is not related to profile accesses. + */ + +#define AA_DEBUG(fmt, args...) \ + do { \ + if (aa_g_debug) \ + pr_debug_ratelimited("AppArmor: " fmt, ##args); \ + } while (0) + +#define AA_ERROR(fmt, args...) \ + pr_err_ratelimited("AppArmor: " fmt, ##args) + +/* Flag indicating whether initialization completed */ +extern int apparmor_initialized __initdata; + +/* fn's in lib */ +char *aa_split_fqname(char *args, char **ns_name); +void aa_info_message(const char *str); +void *__aa_kvmalloc(size_t size, gfp_t flags); + +static inline void *kvmalloc(size_t size) +{ + return __aa_kvmalloc(size, 0); +} + +static inline void *kvzalloc(size_t size) +{ + return __aa_kvmalloc(size, __GFP_ZERO); +} + +/* returns 0 if kref not incremented */ +static inline int kref_get_not0(struct kref *kref) +{ + return atomic_inc_not_zero(&kref->refcount); +} + +/** + * aa_strneq - compare null terminated @str to a non null terminated substring + * @str: a null terminated string + * @sub: a substring, not necessarily null terminated + * @len: length of @sub to compare + * + * The @str string must be full consumed for this to be considered a match + */ +static inline bool aa_strneq(const char *str, const char *sub, int len) +{ + return !strncmp(str, sub, len) && !str[len]; +} + +/** + * aa_dfa_null_transition - step to next state after null character + * @dfa: the dfa to match against + * @start: the state of the dfa to start matching in + * + * aa_dfa_null_transition transitions to the next state after a null + * character which is not used in standard matching and is only + * used to separate pairs. + */ +static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, + unsigned int start) +{ + /* the null transition only needs the string's null terminator byte */ + return aa_dfa_next(dfa, start, 0); +} + +static inline bool mediated_filesystem(struct dentry *dentry) +{ + return !(dentry->d_sb->s_flags & MS_NOUSER); +} + +#endif /* AA_LIB_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 52275f040a5f..190fe37ecff0 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "lib.h" #include "resource.h" extern const char *const aa_profile_mode_names[]; diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 2ef422a25474..6028ffc008ae 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -19,7 +19,7 @@ #include "include/audit.h" #include "include/apparmor.h" - +#include "include/lib.h" /** * aa_split_fqname - split a fqname into a profile and namespace name diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 3f900fcca8fb..0e04bcf91154 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -20,7 +20,7 @@ #include #include -#include "include/apparmor.h" +#include "include/lib.h" #include "include/match.h" #define base_idx(X) ((X) & 0xffffff) -- cgit v1.2.3 From fe6bb31f590c9cd9c8d3ddbdfd4301f72db91718 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:14 -0800 Subject: apparmor: split out shared policy_XXX fns to lib Signed-off-by: John Johansen --- security/apparmor/include/lib.h | 81 ++++++++++++++++++++++++ security/apparmor/include/policy.h | 13 ---- security/apparmor/lib.c | 52 ++++++++++++++++ security/apparmor/policy.c | 123 ++----------------------------------- 4 files changed, 137 insertions(+), 132 deletions(-) diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 71fd6d28a651..74cc68ea4c12 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -91,4 +91,85 @@ static inline bool mediated_filesystem(struct dentry *dentry) return !(dentry->d_sb->s_flags & MS_NOUSER); } +/* struct aa_policy - common part of both namespaces and profiles + * @name: name of the object + * @hname - The hierarchical name + * @list: list policy object is on + * @profiles: head of the profiles list contained in the object + */ +struct aa_policy { + char *name; + char *hname; + struct list_head list; + struct list_head profiles; +}; + +/** + * hname_tail - find the last component of an hname + * @name: hname to find the base profile name component of (NOT NULL) + * + * Returns: the tail (base profile name) name component of an hname + */ +static inline const char *hname_tail(const char *hname) +{ + char *split; + + hname = strim((char *)hname); + for (split = strstr(hname, "//"); split; split = strstr(hname, "//")) + hname = split + 2; + + return hname; +} + +/** + * __policy_find - find a policy by @name on a policy list + * @head: list to search (NOT NULL) + * @name: name to search for (NOT NULL) + * + * Requires: rcu_read_lock be held + * + * Returns: unrefcounted policy that match @name or NULL if not found + */ +static inline struct aa_policy *__policy_find(struct list_head *head, + const char *name) +{ + struct aa_policy *policy; + + list_for_each_entry_rcu(policy, head, list) { + if (!strcmp(policy->name, name)) + return policy; + } + return NULL; +} + +/** + * __policy_strn_find - find a policy that's name matches @len chars of @str + * @head: list to search (NOT NULL) + * @str: string to search for (NOT NULL) + * @len: length of match required + * + * Requires: rcu_read_lock be held + * + * Returns: unrefcounted policy that match @str or NULL if not found + * + * if @len == strlen(@strlen) then this is equiv to __policy_find + * other wise it allows searching for policy by a partial match of name + */ +static inline struct aa_policy *__policy_strn_find(struct list_head *head, + const char *str, int len) +{ + struct aa_policy *policy; + + list_for_each_entry_rcu(policy, head, list) { + if (aa_strneq(policy->name, str, len)) + return policy; + } + + return NULL; +} + +bool aa_policy_init(struct aa_policy *policy, const char *prefix, + const char *name); +void aa_policy_destroy(struct aa_policy *policy); + #endif /* AA_LIB_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 190fe37ecff0..9b199678a11c 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -77,19 +77,6 @@ enum profile_flags { struct aa_profile; -/* struct aa_policy - common part of both namespaces and profiles - * @name: name of the object - * @hname - The hierarchical name - * @list: list policy object is on - * @profiles: head of the profiles list contained in the object - */ -struct aa_policy { - char *name; - char *hname; - struct list_head list; - struct list_head profiles; -}; - /* struct aa_ns_acct - accounting of profiles in namespace * @max_size: maximum space allowed for all profiles in namespace * @max_count: maximum number of profiles that can be in this namespace diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 6028ffc008ae..e29ccdb0309a 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -20,6 +20,7 @@ #include "include/audit.h" #include "include/apparmor.h" #include "include/lib.h" +#include "include/policy.h" /** * aa_split_fqname - split a fqname into a profile and namespace name @@ -105,3 +106,54 @@ void *__aa_kvmalloc(size_t size, gfp_t flags) } return buffer; } + +/** + * aa_policy_init - initialize a policy structure + * @policy: policy to initialize (NOT NULL) + * @prefix: prefix name if any is required. (MAYBE NULL) + * @name: name of the policy, init will make a copy of it (NOT NULL) + * + * Note: this fn creates a copy of strings passed in + * + * Returns: true if policy init successful + */ +bool aa_policy_init(struct aa_policy *policy, const char *prefix, + const char *name) +{ + /* freed by policy_free */ + if (prefix) { + policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, + GFP_KERNEL); + if (policy->hname) + sprintf(policy->hname, "%s//%s", prefix, name); + } else + policy->hname = kstrdup(name, GFP_KERNEL); + if (!policy->hname) + return 0; + /* base.name is a substring of fqname */ + policy->name = (char *)hname_tail(policy->hname); + INIT_LIST_HEAD(&policy->list); + INIT_LIST_HEAD(&policy->profiles); + + return 1; +} + +/** + * aa_policy_destroy - free the elements referenced by @policy + * @policy: policy that is to have its elements freed (NOT NULL) + */ +void aa_policy_destroy(struct aa_policy *policy) +{ + /* still contains profiles -- invalid */ + if (on_list_rcu(&policy->profiles)) { + AA_ERROR("%s: internal error, policy '%s' contains profiles\n", + __func__, policy->name); + } + if (on_list_rcu(&policy->list)) { + AA_ERROR("%s: internal error, policy '%s' still on list\n", + __func__, policy->name); + } + + /* don't free name as its a subset of hname */ + kzfree(policy->hname); +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 179e68d7dc5f..a331149a4587 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -99,121 +99,6 @@ const char *const aa_profile_mode_names[] = { "unconfined", }; -/** - * hname_tail - find the last component of an hname - * @name: hname to find the base profile name component of (NOT NULL) - * - * Returns: the tail (base profile name) name component of an hname - */ -static const char *hname_tail(const char *hname) -{ - char *split; - hname = strim((char *)hname); - for (split = strstr(hname, "//"); split; split = strstr(hname, "//")) - hname = split + 2; - - return hname; -} - -/** - * policy_init - initialize a policy structure - * @policy: policy to initialize (NOT NULL) - * @prefix: prefix name if any is required. (MAYBE NULL) - * @name: name of the policy, init will make a copy of it (NOT NULL) - * - * Note: this fn creates a copy of strings passed in - * - * Returns: true if policy init successful - */ -static bool policy_init(struct aa_policy *policy, const char *prefix, - const char *name) -{ - /* freed by policy_free */ - if (prefix) { - policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, - GFP_KERNEL); - if (policy->hname) - sprintf(policy->hname, "%s//%s", prefix, name); - } else - policy->hname = kstrdup(name, GFP_KERNEL); - if (!policy->hname) - return 0; - /* base.name is a substring of fqname */ - policy->name = (char *)hname_tail(policy->hname); - INIT_LIST_HEAD(&policy->list); - INIT_LIST_HEAD(&policy->profiles); - - return 1; -} - -/** - * policy_destroy - free the elements referenced by @policy - * @policy: policy that is to have its elements freed (NOT NULL) - */ -static void policy_destroy(struct aa_policy *policy) -{ - /* still contains profiles -- invalid */ - if (on_list_rcu(&policy->profiles)) { - AA_ERROR("%s: internal error, " - "policy '%s' still contains profiles\n", - __func__, policy->name); - BUG(); - } - if (on_list_rcu(&policy->list)) { - AA_ERROR("%s: internal error, policy '%s' still on list\n", - __func__, policy->name); - BUG(); - } - - /* don't free name as its a subset of hname */ - kzfree(policy->hname); -} - -/** - * __policy_find - find a policy by @name on a policy list - * @head: list to search (NOT NULL) - * @name: name to search for (NOT NULL) - * - * Requires: rcu_read_lock be held - * - * Returns: unrefcounted policy that match @name or NULL if not found - */ -static struct aa_policy *__policy_find(struct list_head *head, const char *name) -{ - struct aa_policy *policy; - - list_for_each_entry_rcu(policy, head, list) { - if (!strcmp(policy->name, name)) - return policy; - } - return NULL; -} - -/** - * __policy_strn_find - find a policy that's name matches @len chars of @str - * @head: list to search (NOT NULL) - * @str: string to search for (NOT NULL) - * @len: length of match required - * - * Requires: rcu_read_lock be held - * - * Returns: unrefcounted policy that match @str or NULL if not found - * - * if @len == strlen(@strlen) then this is equiv to __policy_find - * other wise it allows searching for policy by a partial match of name - */ -static struct aa_policy *__policy_strn_find(struct list_head *head, - const char *str, int len) -{ - struct aa_policy *policy; - - list_for_each_entry_rcu(policy, head, list) { - if (aa_strneq(policy->name, str, len)) - return policy; - } - - return NULL; -} /* * Routines for AppArmor namespaces @@ -280,7 +165,7 @@ static struct aa_namespace *alloc_namespace(const char *prefix, AA_DEBUG("%s(%p)\n", __func__, ns); if (!ns) return NULL; - if (!policy_init(&ns->base, prefix, name)) + if (!aa_policy_init(&ns->base, prefix, name)) goto fail_ns; INIT_LIST_HEAD(&ns->sub_ns); @@ -321,7 +206,7 @@ static void free_namespace(struct aa_namespace *ns) if (!ns) return; - policy_destroy(&ns->base); + aa_policy_destroy(&ns->base); aa_put_namespace(ns->parent); ns->unconfined->ns = NULL; @@ -595,7 +480,7 @@ void aa_free_profile(struct aa_profile *profile) return; /* free children profiles */ - policy_destroy(&profile->base); + aa_policy_destroy(&profile->base); aa_put_profile(rcu_access_pointer(profile->parent)); aa_put_namespace(profile->ns); @@ -657,7 +542,7 @@ struct aa_profile *aa_alloc_profile(const char *hname) goto fail; kref_init(&profile->replacedby->count); - if (!policy_init(&profile->base, NULL, hname)) + if (!aa_policy_init(&profile->base, NULL, hname)) goto fail; kref_init(&profile->count); -- cgit v1.2.3 From cff281f6861e72f1416927aaa0c10a08bb7b2d3f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:15 -0800 Subject: apparmor: split apparmor policy namespaces code into its own file Policy namespaces will be diverging from profile management and expanding so put it in its own file. Signed-off-by: John Johansen --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 1 + security/apparmor/audit.c | 1 + security/apparmor/domain.c | 1 + security/apparmor/include/policy.h | 112 +------------ security/apparmor/include/policy_ns.h | 137 ++++++++++++++++ security/apparmor/lsm.c | 1 + security/apparmor/policy.c | 298 ++-------------------------------- security/apparmor/policy_ns.c | 291 +++++++++++++++++++++++++++++++++ security/apparmor/procattr.c | 1 + 10 files changed, 454 insertions(+), 391 deletions(-) create mode 100644 security/apparmor/include/policy_ns.h create mode 100644 security/apparmor/policy_ns.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index d693df874818..3485f49d9de9 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ 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 sid.o file.o + resource.o sid.o file.o policy_ns.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 5923d5665209..efac1a9565e2 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -28,6 +28,7 @@ #include "include/context.h" #include "include/crypto.h" #include "include/policy.h" +#include "include/policy_ns.h" #include "include/resource.h" /** diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3a7f1da1425e..42101c42f446 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -18,6 +18,7 @@ #include "include/apparmor.h" #include "include/audit.h" #include "include/policy.h" +#include "include/policy_ns.h" const char *const op_table[] = { "null", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index a4d90aa1045a..02d2f01e908d 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -29,6 +29,7 @@ #include "include/match.h" #include "include/path.h" #include "include/policy.h" +#include "include/policy_ns.h" /** * aa_free_domain_entries - free entries in a domain table diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 9b199678a11c..a1b1d8ab589c 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -30,6 +30,9 @@ #include "lib.h" #include "resource.h" + +struct aa_namespace; + extern const char *const aa_profile_mode_names[]; #define APPARMOR_MODE_NAMES_MAX_INDEX 4 @@ -77,57 +80,6 @@ enum profile_flags { struct aa_profile; -/* struct aa_ns_acct - accounting of profiles in namespace - * @max_size: maximum space allowed for all profiles in namespace - * @max_count: maximum number of profiles that can be in this namespace - * @size: current size of profiles - * @count: current count of profiles (includes null profiles) - */ -struct aa_ns_acct { - int max_size; - int max_count; - int size; - int count; -}; - -/* struct aa_namespace - namespace for a set of profiles - * @base: common policy - * @parent: parent of namespace - * @lock: lock for modifying the object - * @acct: accounting for the namespace - * @unconfined: special unconfined profile for the namespace - * @sub_ns: list of namespaces under the current namespace. - * @uniq_null: uniq value used for null learning profiles - * @uniq_id: a unique id count for the profiles in the namespace - * @dents: dentries for the namespaces file entries in apparmorfs - * - * An aa_namespace defines the set profiles that are searched to determine - * which profile to attach to a task. Profiles can not be shared between - * aa_namespaces and profile names within a namespace are guaranteed to be - * unique. When profiles in separate namespaces have the same name they - * are NOT considered to be equivalent. - * - * Namespaces are hierarchical and only namespaces and profiles below the - * current namespace are visible. - * - * Namespace names must be unique and can not contain the characters :/\0 - * - * FIXME TODO: add vserver support of namespaces (can it all be done in - * userspace?) - */ -struct aa_namespace { - struct aa_policy base; - struct aa_namespace *parent; - struct mutex lock; - struct aa_ns_acct acct; - struct aa_profile *unconfined; - struct list_head sub_ns; - atomic_t uniq_null; - long uniq_id; - - struct dentry *dents[AAFS_NS_SIZEOF]; -}; - /* struct aa_policydb - match engine for a policy * dfa: dfa pattern match * start: set of start states for the different classes of data @@ -212,19 +164,11 @@ struct aa_profile { struct dentry *dents[AAFS_PROF_SIZEOF]; }; -extern struct aa_namespace *root_ns; extern enum profile_mode aa_g_profile_mode; -void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); - -bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view); -const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child); -int aa_alloc_root_ns(void); -void aa_free_root_ns(void); -void aa_free_namespace_kref(struct kref *kref); +void __aa_update_replacedby(struct aa_profile *orig, struct aa_profile *new); -struct aa_namespace *aa_find_namespace(struct aa_namespace *root, - const char *name); +void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); void aa_free_replacedby_kref(struct kref *kref); @@ -238,6 +182,7 @@ struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name); ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); ssize_t aa_remove_profiles(char *name, size_t size); +void __aa_profile_list_release(struct list_head *head); #define PROF_ADD 1 #define PROF_REPLACE 0 @@ -245,12 +190,6 @@ ssize_t aa_remove_profiles(char *name, size_t size); #define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) -static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) -{ - return rcu_dereference_protected(p->parent, - mutex_is_locked(&p->ns->lock)); -} - /** * aa_get_profile - increment refcount on profile @p * @p: profile (MAYBE NULL) @@ -344,45 +283,6 @@ static inline void aa_put_replacedby(struct aa_replacedby *p) kref_put(&p->count, aa_free_replacedby_kref); } -/* requires profile list write lock held */ -static inline void __aa_update_replacedby(struct aa_profile *orig, - struct aa_profile *new) -{ - struct aa_profile *tmp; - tmp = rcu_dereference_protected(orig->replacedby->profile, - mutex_is_locked(&orig->ns->lock)); - rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new)); - orig->flags |= PFLAG_INVALID; - aa_put_profile(tmp); -} - -/** - * aa_get_namespace - increment references count on @ns - * @ns: namespace to increment reference count of (MAYBE NULL) - * - * Returns: pointer to @ns, if @ns is NULL returns NULL - * Requires: @ns must be held with valid refcount when called - */ -static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) -{ - if (ns) - aa_get_profile(ns->unconfined); - - return ns; -} - -/** - * aa_put_namespace - decrement refcount on @ns - * @ns: namespace to put reference of - * - * Decrement reference count of @ns and if no longer in use free it - */ -static inline void aa_put_namespace(struct aa_namespace *ns) -{ - if (ns) - aa_put_profile(ns->unconfined); -} - static inline int AUDIT_MODE(struct aa_profile *profile) { if (aa_g_audit != AUDIT_NORMAL) diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h new file mode 100644 index 000000000000..4b9e8c7c669a --- /dev/null +++ b/security/apparmor/include/policy_ns.h @@ -0,0 +1,137 @@ +/* + * AppArmor security module + * + * This file contains AppArmor policy 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_NAMESPACE_H +#define __AA_NAMESPACE_H + +#include + +#include "apparmor.h" +#include "apparmorfs.h" +#include "policy.h" + + +/* struct aa_ns_acct - accounting of profiles in namespace + * @max_size: maximum space allowed for all profiles in namespace + * @max_count: maximum number of profiles that can be in this namespace + * @size: current size of profiles + * @count: current count of profiles (includes null profiles) + */ +struct aa_ns_acct { + int max_size; + int max_count; + int size; + int count; +}; + +/* struct aa_namespace - namespace for a set of profiles + * @base: common policy + * @parent: parent of namespace + * @lock: lock for modifying the object + * @acct: accounting for the namespace + * @unconfined: special unconfined profile for the namespace + * @sub_ns: list of namespaces under the current namespace. + * @uniq_null: uniq value used for null learning profiles + * @uniq_id: a unique id count for the profiles in the namespace + * @dents: dentries for the namespaces file entries in apparmorfs + * + * An aa_namespace defines the set profiles that are searched to determine + * which profile to attach to a task. Profiles can not be shared between + * aa_namespaces and profile names within a namespace are guaranteed to be + * unique. When profiles in separate namespaces have the same name they + * are NOT considered to be equivalent. + * + * Namespaces are hierarchical and only namespaces and profiles below the + * current namespace are visible. + * + * Namespace names must be unique and can not contain the characters :/\0 + */ +struct aa_namespace { + struct aa_policy base; + struct aa_namespace *parent; + struct mutex lock; + struct aa_ns_acct acct; + struct aa_profile *unconfined; + struct list_head sub_ns; + atomic_t uniq_null; + long uniq_id; + + struct dentry *dents[AAFS_NS_SIZEOF]; +}; + +extern struct aa_namespace *root_ns; + +extern const char *aa_hidden_ns_name; + +bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view); +const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child); +void aa_free_namespace(struct aa_namespace *ns); +int aa_alloc_root_ns(void); +void aa_free_root_ns(void); +void aa_free_namespace_kref(struct kref *kref); + +struct aa_namespace *aa_find_namespace(struct aa_namespace *root, + const char *name); +struct aa_namespace *aa_prepare_namespace(const char *name); +void __aa_remove_namespace(struct aa_namespace *ns); + +static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) +{ + return rcu_dereference_protected(p->parent, + mutex_is_locked(&p->ns->lock)); +} + +/** + * aa_get_namespace - increment references count on @ns + * @ns: namespace to increment reference count of (MAYBE NULL) + * + * Returns: pointer to @ns, if @ns is NULL returns NULL + * Requires: @ns must be held with valid refcount when called + */ +static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) +{ + if (ns) + aa_get_profile(ns->unconfined); + + return ns; +} + +/** + * aa_put_namespace - decrement refcount on @ns + * @ns: namespace to put reference of + * + * Decrement reference count of @ns and if no longer in use free it + */ +static inline void aa_put_namespace(struct aa_namespace *ns) +{ + if (ns) + aa_put_profile(ns->unconfined); +} + +/** + * __aa_find_namespace - find a namespace on a list by @name + * @head: list to search for namespace on (NOT NULL) + * @name: name of namespace to look for (NOT NULL) + * + * Returns: unrefcounted namespace + * + * Requires: rcu_read_lock be held + */ +static inline struct aa_namespace *__aa_find_namespace(struct list_head *head, + const char *name) +{ + return (struct aa_namespace *)__policy_find(head, name); +} + +#endif /* AA_NAMESPACE_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f76738b1eb15..1dae66ba757b 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -34,6 +34,7 @@ #include "include/ipc.h" #include "include/path.h" #include "include/policy.h" +#include "include/policy_ns.h" #include "include/procattr.h" /* Flag indicating whether initialization completed */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index a331149a4587..2a861824319e 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -85,13 +85,11 @@ #include "include/match.h" #include "include/path.h" #include "include/policy.h" +#include "include/policy_ns.h" #include "include/policy_unpack.h" #include "include/resource.h" -/* root profile namespace */ -struct aa_namespace *root_ns; - const char *const aa_profile_mode_names[] = { "enforce", "complain", @@ -100,202 +98,16 @@ const char *const aa_profile_mode_names[] = { }; -/* - * Routines for AppArmor namespaces - */ - -static const char *hidden_ns_name = "---"; -/** - * aa_ns_visible - test if @view is visible from @curr - * @curr: namespace to treat as the parent (NOT NULL) - * @view: namespace to test if visible from @curr (NOT NULL) - * - * Returns: true if @view is visible from @curr else false - */ -bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) -{ - if (curr == view) - return true; - - for ( ; view; view = view->parent) { - if (view->parent == curr) - return true; - } - return false; -} - -/** - * aa_na_name - Find the ns name to display for @view from @curr - * @curr - current namespace (NOT NULL) - * @view - namespace attempting to view (NOT NULL) - * - * Returns: name of @view visible from @curr - */ -const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) -{ - /* if view == curr then the namespace name isn't displayed */ - if (curr == view) - return ""; - - if (aa_ns_visible(curr, view)) { - /* at this point if a ns is visible it is in a view ns - * thus the curr ns.hname is a prefix of its name. - * Only output the virtualized portion of the name - * Add + 2 to skip over // separating curr hname prefix - * from the visible tail of the views hname - */ - return view->base.hname + strlen(curr->base.hname) + 2; - } else - return hidden_ns_name; -} - -/** - * alloc_namespace - allocate, initialize and return a new namespace - * @prefix: parent namespace name (MAYBE NULL) - * @name: a preallocated name (NOT NULL) - * - * Returns: refcounted namespace or NULL on failure. - */ -static struct aa_namespace *alloc_namespace(const char *prefix, - const char *name) -{ - struct aa_namespace *ns; - - ns = kzalloc(sizeof(*ns), GFP_KERNEL); - AA_DEBUG("%s(%p)\n", __func__, ns); - if (!ns) - return NULL; - if (!aa_policy_init(&ns->base, prefix, name)) - goto fail_ns; - - INIT_LIST_HEAD(&ns->sub_ns); - mutex_init(&ns->lock); - - /* released by free_namespace */ - ns->unconfined = aa_alloc_profile("unconfined"); - if (!ns->unconfined) - goto fail_unconfined; - - ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR | - PFLAG_IMMUTABLE | PFLAG_NS_COUNT; - ns->unconfined->mode = APPARMOR_UNCONFINED; - - /* ns and ns->unconfined share ns->unconfined refcount */ - ns->unconfined->ns = ns; - - atomic_set(&ns->uniq_null, 0); - - return ns; - -fail_unconfined: - kzfree(ns->base.hname); -fail_ns: - kzfree(ns); - return NULL; -} - -/** - * free_namespace - free a profile namespace - * @ns: the namespace to free (MAYBE NULL) - * - * Requires: All references to the namespace must have been put, if the - * namespace was referenced by a profile confining a task, - */ -static void free_namespace(struct aa_namespace *ns) -{ - if (!ns) - return; - - aa_policy_destroy(&ns->base); - aa_put_namespace(ns->parent); - - ns->unconfined->ns = NULL; - aa_free_profile(ns->unconfined); - kzfree(ns); -} - -/** - * __aa_find_namespace - find a namespace on a list by @name - * @head: list to search for namespace on (NOT NULL) - * @name: name of namespace to look for (NOT NULL) - * - * Returns: unrefcounted namespace - * - * Requires: rcu_read_lock be held - */ -static struct aa_namespace *__aa_find_namespace(struct list_head *head, - const char *name) -{ - return (struct aa_namespace *)__policy_find(head, name); -} - -/** - * aa_find_namespace - look up a profile namespace on the namespace list - * @root: namespace to search in (NOT NULL) - * @name: name of namespace to find (NOT NULL) - * - * Returns: a refcounted namespace on the list, or NULL if no namespace - * called @name exists. - * - * refcount released by caller - */ -struct aa_namespace *aa_find_namespace(struct aa_namespace *root, - const char *name) -{ - struct aa_namespace *ns = NULL; - - rcu_read_lock(); - ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); - rcu_read_unlock(); - - return ns; -} - -/** - * aa_prepare_namespace - find an existing or create a new namespace of @name - * @name: the namespace to find or add (MAYBE NULL) - * - * Returns: refcounted namespace or NULL if failed to create one - */ -static struct aa_namespace *aa_prepare_namespace(const char *name) +/* requires profile list write lock held */ +void __aa_update_replacedby(struct aa_profile *orig, struct aa_profile *new) { - struct aa_namespace *ns, *root; - - root = aa_current_profile()->ns; - - mutex_lock(&root->lock); - - /* if name isn't specified the profile is loaded to the current ns */ - if (!name) { - /* released by caller */ - ns = aa_get_namespace(root); - goto out; - } + struct aa_profile *tmp; - /* try and find the specified ns and if it doesn't exist create it */ - /* released by caller */ - ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); - if (!ns) { - ns = alloc_namespace(root->base.hname, name); - if (!ns) - goto out; - if (__aa_fs_namespace_mkdir(ns, ns_subns_dir(root), name)) { - AA_ERROR("Failed to create interface for ns %s\n", - ns->base.name); - free_namespace(ns); - ns = NULL; - goto out; - } - ns->parent = aa_get_namespace(root); - list_add_rcu(&ns->base.list, &root->sub_ns); - /* add list ref */ - aa_get_namespace(ns); - } -out: - mutex_unlock(&root->lock); - - /* return ref */ - return ns; + tmp = rcu_dereference_protected(orig->replacedby->profile, + mutex_is_locked(&orig->ns->lock)); + rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new)); + orig->flags |= PFLAG_INVALID; + aa_put_profile(tmp); } /** @@ -333,8 +145,6 @@ static void __list_remove_profile(struct aa_profile *profile) aa_put_profile(profile); } -static void __profile_list_release(struct list_head *head); - /** * __remove_profile - remove old profile, and children * @profile: profile to be replaced (NOT NULL) @@ -344,7 +154,7 @@ static void __profile_list_release(struct list_head *head); static void __remove_profile(struct aa_profile *profile) { /* release any children lists first */ - __profile_list_release(&profile->base.profiles); + __aa_profile_list_release(&profile->base.profiles); /* released by free_profile */ __aa_update_replacedby(profile, profile->ns->unconfined); __aa_fs_profile_rmdir(profile); @@ -352,98 +162,18 @@ static void __remove_profile(struct aa_profile *profile) } /** - * __profile_list_release - remove all profiles on the list and put refs + * __aa_profile_list_release - remove all profiles on the list and put refs * @head: list of profiles (NOT NULL) * * Requires: namespace lock be held */ -static void __profile_list_release(struct list_head *head) +void __aa_profile_list_release(struct list_head *head) { struct aa_profile *profile, *tmp; list_for_each_entry_safe(profile, tmp, head, base.list) __remove_profile(profile); } -static void __ns_list_release(struct list_head *head); - -/** - * destroy_namespace - remove everything contained by @ns - * @ns: namespace to have it contents removed (NOT NULL) - */ -static void destroy_namespace(struct aa_namespace *ns) -{ - if (!ns) - return; - - mutex_lock(&ns->lock); - /* release all profiles in this namespace */ - __profile_list_release(&ns->base.profiles); - - /* release all sub namespaces */ - __ns_list_release(&ns->sub_ns); - - if (ns->parent) - __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); - __aa_fs_namespace_rmdir(ns); - mutex_unlock(&ns->lock); -} - -/** - * __remove_namespace - remove a namespace and all its children - * @ns: namespace to be removed (NOT NULL) - * - * Requires: ns->parent->lock be held and ns removed from parent. - */ -static void __remove_namespace(struct aa_namespace *ns) -{ - /* remove ns from namespace list */ - list_del_rcu(&ns->base.list); - destroy_namespace(ns); - aa_put_namespace(ns); -} - -/** - * __ns_list_release - remove all profile namespaces on the list put refs - * @head: list of profile namespaces (NOT NULL) - * - * Requires: namespace lock be held - */ -static void __ns_list_release(struct list_head *head) -{ - struct aa_namespace *ns, *tmp; - list_for_each_entry_safe(ns, tmp, head, base.list) - __remove_namespace(ns); - -} - -/** - * aa_alloc_root_ns - allocate the root profile namespace - * - * Returns: %0 on success else error - * - */ -int __init aa_alloc_root_ns(void) -{ - /* released by aa_free_root_ns - used as list ref*/ - root_ns = alloc_namespace(NULL, "root"); - if (!root_ns) - return -ENOMEM; - - return 0; -} - - /** - * aa_free_root_ns - free the root profile namespace - */ -void __init aa_free_root_ns(void) - { - struct aa_namespace *ns = root_ns; - root_ns = NULL; - - destroy_namespace(ns); - aa_put_namespace(ns); -} - static void free_replacedby(struct aa_replacedby *r) { @@ -507,7 +237,7 @@ static void aa_free_profile_rcu(struct rcu_head *head) { struct aa_profile *p = container_of(head, struct aa_profile, rcu); if (p->flags & PFLAG_NS_COUNT) - free_namespace(p->ns); + aa_free_namespace(p->ns); else aa_free_profile(p); } @@ -1181,7 +911,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) if (!name) { /* remove namespace - can only happen if fqname[0] == ':' */ mutex_lock(&ns->parent->lock); - __remove_namespace(ns); + __aa_remove_namespace(ns); mutex_unlock(&ns->parent->lock); } else { /* remove profile */ diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c new file mode 100644 index 000000000000..d4e9924a276e --- /dev/null +++ b/security/apparmor/policy_ns.c @@ -0,0 +1,291 @@ +/* + * AppArmor security module + * + * This file contains AppArmor policy manipulation functions + * + * 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. + * + * AppArmor policy namespaces, allow for different sets of policies + * to be loaded for tasks within the namespace. + */ + +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/context.h" +#include "include/policy_ns.h" +#include "include/policy.h" + +/* root profile namespace */ +struct aa_namespace *root_ns; +const char *aa_hidden_ns_name = "---"; + +/** + * aa_ns_visible - test if @view is visible from @curr + * @curr: namespace to treat as the parent (NOT NULL) + * @view: namespace to test if visible from @curr (NOT NULL) + * + * Returns: true if @view is visible from @curr else false + */ +bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) +{ + if (curr == view) + return true; + + for ( ; view; view = view->parent) { + if (view->parent == curr) + return true; + } + return false; +} + +/** + * aa_na_name - Find the ns name to display for @view from @curr + * @curr - current namespace (NOT NULL) + * @view - namespace attempting to view (NOT NULL) + * + * Returns: name of @view visible from @curr + */ +const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) +{ + /* if view == curr then the namespace name isn't displayed */ + if (curr == view) + return ""; + + if (aa_ns_visible(curr, view)) { + /* at this point if a ns is visible it is in a view ns + * thus the curr ns.hname is a prefix of its name. + * Only output the virtualized portion of the name + * Add + 2 to skip over // separating curr hname prefix + * from the visible tail of the views hname + */ + return view->base.hname + strlen(curr->base.hname) + 2; + } + + return aa_hidden_ns_name; +} + +/** + * alloc_namespace - allocate, initialize and return a new namespace + * @prefix: parent namespace name (MAYBE NULL) + * @name: a preallocated name (NOT NULL) + * + * Returns: refcounted namespace or NULL on failure. + */ +static struct aa_namespace *alloc_namespace(const char *prefix, + const char *name) +{ + struct aa_namespace *ns; + + ns = kzalloc(sizeof(*ns), GFP_KERNEL); + AA_DEBUG("%s(%p)\n", __func__, ns); + if (!ns) + return NULL; + if (!aa_policy_init(&ns->base, prefix, name)) + goto fail_ns; + + INIT_LIST_HEAD(&ns->sub_ns); + mutex_init(&ns->lock); + + /* released by free_namespace */ + ns->unconfined = aa_alloc_profile("unconfined"); + if (!ns->unconfined) + goto fail_unconfined; + + ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR | + PFLAG_IMMUTABLE | PFLAG_NS_COUNT; + ns->unconfined->mode = APPARMOR_UNCONFINED; + + /* ns and ns->unconfined share ns->unconfined refcount */ + ns->unconfined->ns = ns; + + atomic_set(&ns->uniq_null, 0); + + return ns; + +fail_unconfined: + kzfree(ns->base.hname); +fail_ns: + kzfree(ns); + return NULL; +} + +/** + * aa_free_namespace - free a profile namespace + * @ns: the namespace to free (MAYBE NULL) + * + * Requires: All references to the namespace must have been put, if the + * namespace was referenced by a profile confining a task, + */ +void aa_free_namespace(struct aa_namespace *ns) +{ + if (!ns) + return; + + aa_policy_destroy(&ns->base); + aa_put_namespace(ns->parent); + + ns->unconfined->ns = NULL; + aa_free_profile(ns->unconfined); + kzfree(ns); +} + +/** + * aa_find_namespace - look up a profile namespace on the namespace list + * @root: namespace to search in (NOT NULL) + * @name: name of namespace to find (NOT NULL) + * + * Returns: a refcounted namespace on the list, or NULL if no namespace + * called @name exists. + * + * refcount released by caller + */ +struct aa_namespace *aa_find_namespace(struct aa_namespace *root, + const char *name) +{ + struct aa_namespace *ns = NULL; + + rcu_read_lock(); + ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); + rcu_read_unlock(); + + return ns; +} + +/** + * aa_prepare_namespace - find an existing or create a new namespace of @name + * @name: the namespace to find or add (MAYBE NULL) + * + * Returns: refcounted namespace or NULL if failed to create one + */ +struct aa_namespace *aa_prepare_namespace(const char *name) +{ + struct aa_namespace *ns, *root; + + root = aa_current_profile()->ns; + + mutex_lock(&root->lock); + + /* if name isn't specified the profile is loaded to the current ns */ + if (!name) { + /* released by caller */ + ns = aa_get_namespace(root); + goto out; + } + + /* try and find the specified ns and if it doesn't exist create it */ + /* released by caller */ + ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); + if (!ns) { + ns = alloc_namespace(root->base.hname, name); + if (!ns) + goto out; + if (__aa_fs_namespace_mkdir(ns, ns_subns_dir(root), name)) { + AA_ERROR("Failed to create interface for ns %s\n", + ns->base.name); + aa_free_namespace(ns); + ns = NULL; + goto out; + } + ns->parent = aa_get_namespace(root); + list_add_rcu(&ns->base.list, &root->sub_ns); + /* add list ref */ + aa_get_namespace(ns); + } +out: + mutex_unlock(&root->lock); + + /* return ref */ + return ns; +} + +static void __ns_list_release(struct list_head *head); + +/** + * destroy_namespace - remove everything contained by @ns + * @ns: namespace to have it contents removed (NOT NULL) + */ +static void destroy_namespace(struct aa_namespace *ns) +{ + if (!ns) + return; + + mutex_lock(&ns->lock); + /* release all profiles in this namespace */ + __aa_profile_list_release(&ns->base.profiles); + + /* release all sub namespaces */ + __ns_list_release(&ns->sub_ns); + + if (ns->parent) + __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); + __aa_fs_namespace_rmdir(ns); + mutex_unlock(&ns->lock); +} + +/** + * __aa_remove_namespace - remove a namespace and all its children + * @ns: namespace to be removed (NOT NULL) + * + * Requires: ns->parent->lock be held and ns removed from parent. + */ +void __aa_remove_namespace(struct aa_namespace *ns) +{ + /* remove ns from namespace list */ + list_del_rcu(&ns->base.list); + destroy_namespace(ns); + aa_put_namespace(ns); +} + +/** + * __ns_list_release - remove all profile namespaces on the list put refs + * @head: list of profile namespaces (NOT NULL) + * + * Requires: namespace lock be held + */ +static void __ns_list_release(struct list_head *head) +{ + struct aa_namespace *ns, *tmp; + + list_for_each_entry_safe(ns, tmp, head, base.list) + __aa_remove_namespace(ns); + +} + +/** + * aa_alloc_root_ns - allocate the root profile namespace + * + * Returns: %0 on success else error + * + */ +int __init aa_alloc_root_ns(void) +{ + /* released by aa_free_root_ns - used as list ref*/ + root_ns = alloc_namespace(NULL, "root"); + if (!root_ns) + return -ENOMEM; + + return 0; +} + + /** + * aa_free_root_ns - free the root profile namespace + */ +void __init aa_free_root_ns(void) +{ + struct aa_namespace *ns = root_ns; + + root_ns = NULL; + + destroy_namespace(ns); + aa_put_namespace(ns); +} diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index b125acc9aa26..bb2600d9d826 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -15,6 +15,7 @@ #include "include/apparmor.h" #include "include/context.h" #include "include/policy.h" +#include "include/policy_ns.h" #include "include/domain.h" #include "include/procattr.h" -- cgit v1.2.3 From 98849dff90e270af3b34889b9e08252544f40b5b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:16 -0800 Subject: apparmor: rename namespace to ns to improve code line lengths Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 49 ++++++++++---------- security/apparmor/domain.c | 24 +++++----- security/apparmor/include/apparmorfs.h | 8 ++-- security/apparmor/include/policy.h | 8 ++-- security/apparmor/include/policy_ns.h | 43 +++++++++--------- security/apparmor/policy.c | 32 ++++++------- security/apparmor/policy_ns.c | 82 +++++++++++++++++----------------- security/apparmor/procattr.c | 4 +- 8 files changed, 122 insertions(+), 128 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index efac1a9565e2..4409b63f0dd7 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -478,9 +478,9 @@ fail2: return error; } -void __aa_fs_namespace_rmdir(struct aa_namespace *ns) +void __aa_fs_ns_rmdir(struct aa_ns *ns) { - struct aa_namespace *sub; + struct aa_ns *sub; struct aa_profile *child; int i; @@ -492,7 +492,7 @@ void __aa_fs_namespace_rmdir(struct aa_namespace *ns) list_for_each_entry(sub, &ns->sub_ns, base.list) { mutex_lock(&sub->lock); - __aa_fs_namespace_rmdir(sub); + __aa_fs_ns_rmdir(sub); mutex_unlock(&sub->lock); } @@ -502,10 +502,9 @@ void __aa_fs_namespace_rmdir(struct aa_namespace *ns) } } -int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, - const char *name) +int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name) { - struct aa_namespace *sub; + struct aa_ns *sub; struct aa_profile *child; struct dentry *dent, *dir; int error; @@ -536,7 +535,7 @@ int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, list_for_each_entry(sub, &ns->sub_ns, base.list) { mutex_lock(&sub->lock); - error = __aa_fs_namespace_mkdir(sub, ns_subns_dir(ns), NULL); + error = __aa_fs_ns_mkdir(sub, ns_subns_dir(ns), NULL); mutex_unlock(&sub->lock); if (error) goto fail2; @@ -548,7 +547,7 @@ fail: error = PTR_ERR(dent); fail2: - __aa_fs_namespace_rmdir(ns); + __aa_fs_ns_rmdir(ns); return error; } @@ -557,7 +556,7 @@ fail2: #define list_entry_is_head(pos, head, member) (&pos->member == (head)) /** - * __next_namespace - find the next namespace to list + * __next_ns - find the next namespace to list * @root: root namespace to stop search at (NOT NULL) * @ns: current ns position (NOT NULL) * @@ -568,10 +567,9 @@ fail2: * Requires: ns->parent->lock to be held * NOTE: will not unlock root->lock */ -static struct aa_namespace *__next_namespace(struct aa_namespace *root, - struct aa_namespace *ns) +static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns) { - struct aa_namespace *parent, *next; + struct aa_ns *parent, *next; /* is next namespace a child */ if (!list_empty(&ns->sub_ns)) { @@ -604,10 +602,10 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root, * Returns: unrefcounted profile or NULL if no profile * Requires: profile->ns.lock to be held */ -static struct aa_profile *__first_profile(struct aa_namespace *root, - struct aa_namespace *ns) +static struct aa_profile *__first_profile(struct aa_ns *root, + struct aa_ns *ns) { - for (; ns; ns = __next_namespace(root, ns)) { + for (; ns; ns = __next_ns(root, ns)) { if (!list_empty(&ns->base.profiles)) return list_first_entry(&ns->base.profiles, struct aa_profile, base.list); @@ -627,7 +625,7 @@ static struct aa_profile *__first_profile(struct aa_namespace *root, static struct aa_profile *__next_profile(struct aa_profile *p) { struct aa_profile *parent; - struct aa_namespace *ns = p->ns; + struct aa_ns *ns = p->ns; /* is next profile a child */ if (!list_empty(&p->base.profiles)) @@ -661,7 +659,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) * * Returns: next profile or NULL if there isn't one */ -static struct aa_profile *next_profile(struct aa_namespace *root, +static struct aa_profile *next_profile(struct aa_ns *root, struct aa_profile *profile) { struct aa_profile *next = __next_profile(profile); @@ -669,7 +667,7 @@ static struct aa_profile *next_profile(struct aa_namespace *root, return next; /* finished all profiles in namespace move to next namespace */ - return __first_profile(root, __next_namespace(root, profile->ns)); + return __first_profile(root, __next_ns(root, profile->ns)); } /** @@ -684,9 +682,9 @@ static struct aa_profile *next_profile(struct aa_namespace *root, static void *p_start(struct seq_file *f, loff_t *pos) { struct aa_profile *profile = NULL; - struct aa_namespace *root = aa_current_profile()->ns; + struct aa_ns *root = aa_current_profile()->ns; loff_t l = *pos; - f->private = aa_get_namespace(root); + f->private = aa_get_ns(root); /* find the first profile */ @@ -713,7 +711,7 @@ static void *p_start(struct seq_file *f, loff_t *pos) static void *p_next(struct seq_file *f, void *p, loff_t *pos) { struct aa_profile *profile = p; - struct aa_namespace *ns = f->private; + struct aa_ns *ns = f->private; (*pos)++; return next_profile(ns, profile); @@ -729,14 +727,14 @@ static void *p_next(struct seq_file *f, void *p, loff_t *pos) static void p_stop(struct seq_file *f, void *p) { struct aa_profile *profile = p; - struct aa_namespace *root = f->private, *ns; + struct aa_ns *root = f->private, *ns; if (profile) { for (ns = profile->ns; ns && ns != root; ns = ns->parent) mutex_unlock(&ns->lock); } mutex_unlock(&root->lock); - aa_put_namespace(root); + aa_put_ns(root); } /** @@ -749,7 +747,7 @@ static void p_stop(struct seq_file *f, void *p) static int seq_show_profile(struct seq_file *f, void *p) { struct aa_profile *profile = (struct aa_profile *)p; - struct aa_namespace *root = f->private; + struct aa_ns *root = f->private; if (profile->ns != root) seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); @@ -951,8 +949,7 @@ static int __init aa_create_aafs(void) if (error) goto error; - error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry.dentry, - "policy"); + error = __aa_fs_ns_mkdir(root_ns, aa_fs_entry.dentry, "policy"); if (error) goto error; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 02d2f01e908d..503cb2c54447 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -94,7 +94,7 @@ out: * Returns: permission set */ static struct file_perms change_profile_perms(struct aa_profile *profile, - struct aa_namespace *ns, + struct aa_ns *ns, const char *name, u32 request, unsigned int start) { @@ -171,7 +171,7 @@ static struct aa_profile *__attach_match(const char *name, * * Returns: profile or NULL if no match found */ -static struct aa_profile *find_attach(struct aa_namespace *ns, +static struct aa_profile *find_attach(struct aa_ns *ns, struct list_head *list, const char *name) { struct aa_profile *profile; @@ -240,7 +240,7 @@ static const char *next_name(int xtype, const char *name) static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; - struct aa_namespace *ns = profile->ns; + struct aa_ns *ns = profile->ns; u32 xtype = xindex & AA_X_TYPE_MASK; int index = xindex & AA_X_INDEX_MASK; const char *name; @@ -248,7 +248,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) /* index is guaranteed to be in range, validated at load time */ for (name = profile->file.trans.table[index]; !new_profile && name; name = next_name(xtype, name)) { - struct aa_namespace *new_ns; + struct aa_ns *new_ns; const char *xname = NULL; new_ns = NULL; @@ -268,7 +268,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) ; } /* released below */ - new_ns = aa_find_namespace(ns, ns_name); + new_ns = aa_find_ns(ns, ns_name); if (!new_ns) continue; } else if (*name == '@') { @@ -281,7 +281,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) /* released by caller */ new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname); - aa_put_namespace(new_ns); + aa_put_ns(new_ns); } /* released by caller */ @@ -302,7 +302,7 @@ static struct aa_profile *x_to_profile(struct aa_profile *profile, const char *name, u32 xindex) { struct aa_profile *new_profile = NULL; - struct aa_namespace *ns = profile->ns; + struct aa_ns *ns = profile->ns; u32 xtype = xindex & AA_X_TYPE_MASK; switch (xtype) { @@ -339,7 +339,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) { struct aa_task_cxt *cxt; struct aa_profile *profile, *new_profile = NULL; - struct aa_namespace *ns; + struct aa_ns *ns; char *buffer = NULL; unsigned int state; struct file_perms perms = {}; @@ -746,7 +746,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, { const struct cred *cred; struct aa_profile *profile, *target = NULL; - struct aa_namespace *ns = NULL; + struct aa_ns *ns = NULL; struct file_perms perms = {}; const char *name = NULL, *info = NULL; int op, error = 0; @@ -780,7 +780,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, if (ns_name) { /* released below */ - ns = aa_find_namespace(profile->ns, ns_name); + ns = aa_find_ns(profile->ns, ns_name); if (!ns) { /* we don't create new namespace in complain mode */ name = ns_name; @@ -790,7 +790,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, } } else /* released below */ - ns = aa_get_namespace(profile->ns); + ns = aa_get_ns(profile->ns); /* if the name was not specified, use the name of the current profile */ if (!hname) { @@ -843,7 +843,7 @@ audit: error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name, hname, GLOBAL_ROOT_UID, info, error); - aa_put_namespace(ns); + aa_put_ns(ns); aa_put_profile(target); put_cred(cred); diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 414e56878dd0..5626bd48d7cb 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -62,7 +62,7 @@ extern const struct file_operations aa_fs_seq_file_ops; extern void __init aa_destroy_aafs(void); struct aa_profile; -struct aa_namespace; +struct aa_ns; enum aafs_ns_type { AAFS_NS_DIR, @@ -97,8 +97,8 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile); void __aa_fs_profile_migrate_dents(struct aa_profile *old, struct aa_profile *new); int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent); -void __aa_fs_namespace_rmdir(struct aa_namespace *ns); -int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, - const char *name); +void __aa_fs_ns_rmdir(struct aa_ns *ns); +int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, + const char *name); #endif /* __AA_APPARMORFS_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index a1b1d8ab589c..415f8ab0b11e 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -31,7 +31,7 @@ #include "resource.h" -struct aa_namespace; +struct aa_ns; extern const char *const aa_profile_mode_names[]; #define APPARMOR_MODE_NAMES_MAX_INDEX 4 @@ -141,7 +141,7 @@ struct aa_profile { struct rcu_head rcu; struct aa_profile __rcu *parent; - struct aa_namespace *ns; + struct aa_ns *ns; struct aa_replacedby *replacedby; const char *rename; @@ -177,8 +177,8 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); void aa_free_profile(struct aa_profile *profile); void aa_free_profile_kref(struct kref *kref); struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); -struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name); -struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name); +struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name); +struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); ssize_t aa_remove_profiles(char *name, size_t size); diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index 4b9e8c7c669a..323752cc0c87 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -35,7 +35,7 @@ struct aa_ns_acct { int count; }; -/* struct aa_namespace - namespace for a set of profiles +/* struct aa_ns - namespace for a set of profiles * @base: common policy * @parent: parent of namespace * @lock: lock for modifying the object @@ -46,9 +46,9 @@ struct aa_ns_acct { * @uniq_id: a unique id count for the profiles in the namespace * @dents: dentries for the namespaces file entries in apparmorfs * - * An aa_namespace defines the set profiles that are searched to determine + * An aa_ns defines the set profiles that are searched to determine * which profile to attach to a task. Profiles can not be shared between - * aa_namespaces and profile names within a namespace are guaranteed to be + * aa_nss and profile names within a namespace are guaranteed to be * unique. When profiles in separate namespaces have the same name they * are NOT considered to be equivalent. * @@ -57,9 +57,9 @@ struct aa_ns_acct { * * Namespace names must be unique and can not contain the characters :/\0 */ -struct aa_namespace { +struct aa_ns { struct aa_policy base; - struct aa_namespace *parent; + struct aa_ns *parent; struct mutex lock; struct aa_ns_acct acct; struct aa_profile *unconfined; @@ -70,21 +70,20 @@ struct aa_namespace { struct dentry *dents[AAFS_NS_SIZEOF]; }; -extern struct aa_namespace *root_ns; +extern struct aa_ns *root_ns; extern const char *aa_hidden_ns_name; -bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view); -const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child); -void aa_free_namespace(struct aa_namespace *ns); +bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view); +const char *aa_ns_name(struct aa_ns *parent, struct aa_ns *child); +void aa_free_ns(struct aa_ns *ns); int aa_alloc_root_ns(void); void aa_free_root_ns(void); -void aa_free_namespace_kref(struct kref *kref); +void aa_free_ns_kref(struct kref *kref); -struct aa_namespace *aa_find_namespace(struct aa_namespace *root, - const char *name); -struct aa_namespace *aa_prepare_namespace(const char *name); -void __aa_remove_namespace(struct aa_namespace *ns); +struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name); +struct aa_ns *aa_prepare_ns(const char *name); +void __aa_remove_ns(struct aa_ns *ns); static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) { @@ -93,13 +92,13 @@ static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) } /** - * aa_get_namespace - increment references count on @ns + * aa_get_ns - increment references count on @ns * @ns: namespace to increment reference count of (MAYBE NULL) * * Returns: pointer to @ns, if @ns is NULL returns NULL * Requires: @ns must be held with valid refcount when called */ -static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) +static inline struct aa_ns *aa_get_ns(struct aa_ns *ns) { if (ns) aa_get_profile(ns->unconfined); @@ -108,19 +107,19 @@ static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) } /** - * aa_put_namespace - decrement refcount on @ns + * aa_put_ns - decrement refcount on @ns * @ns: namespace to put reference of * * Decrement reference count of @ns and if no longer in use free it */ -static inline void aa_put_namespace(struct aa_namespace *ns) +static inline void aa_put_ns(struct aa_ns *ns) { if (ns) aa_put_profile(ns->unconfined); } /** - * __aa_find_namespace - find a namespace on a list by @name + * __aa_find_ns - find a namespace on a list by @name * @head: list to search for namespace on (NOT NULL) * @name: name of namespace to look for (NOT NULL) * @@ -128,10 +127,10 @@ static inline void aa_put_namespace(struct aa_namespace *ns) * * Requires: rcu_read_lock be held */ -static inline struct aa_namespace *__aa_find_namespace(struct list_head *head, - const char *name) +static inline struct aa_ns *__aa_find_ns(struct list_head *head, + const char *name) { - return (struct aa_namespace *)__policy_find(head, name); + return (struct aa_ns *)__policy_find(head, name); } #endif /* AA_NAMESPACE_H */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 2a861824319e..2dd8717a5a89 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -213,7 +213,7 @@ void aa_free_profile(struct aa_profile *profile) aa_policy_destroy(&profile->base); aa_put_profile(rcu_access_pointer(profile->parent)); - aa_put_namespace(profile->ns); + aa_put_ns(profile->ns); kzfree(profile->rename); aa_free_file_rules(&profile->file); @@ -237,7 +237,7 @@ static void aa_free_profile_rcu(struct rcu_head *head) { struct aa_profile *p = container_of(head, struct aa_profile, rcu); if (p->flags & PFLAG_NS_COUNT) - aa_free_namespace(p->ns); + aa_free_ns(p->ns); else aa_free_profile(p); } @@ -324,7 +324,7 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) /* released on free_profile */ rcu_assign_pointer(profile->parent, aa_get_profile(parent)); - profile->ns = aa_get_namespace(parent->ns); + profile->ns = aa_get_ns(parent->ns); mutex_lock(&profile->ns->lock); __list_add_profile(&parent->base.profiles, profile); @@ -403,7 +403,7 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) * * Returns: unrefcounted policy or NULL if not found */ -static struct aa_policy *__lookup_parent(struct aa_namespace *ns, +static struct aa_policy *__lookup_parent(struct aa_ns *ns, const char *hname) { struct aa_policy *policy; @@ -466,7 +466,7 @@ static struct aa_profile *__lookup_profile(struct aa_policy *base, * * Returns: refcounted profile or NULL if not found */ -struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname) +struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname) { struct aa_profile *profile; @@ -670,7 +670,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new, * * Returns: profile to replace (no ref) on success else ptr error */ -static int __lookup_replace(struct aa_namespace *ns, const char *hname, +static int __lookup_replace(struct aa_ns *ns, const char *hname, bool noreplace, struct aa_profile **p, const char **info) { @@ -701,7 +701,7 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname, ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) { const char *ns_name, *info = NULL; - struct aa_namespace *ns = NULL; + struct aa_ns *ns = NULL; struct aa_load_ent *ent, *tmp; int op = OP_PROF_REPL; ssize_t error; @@ -713,7 +713,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) goto out; /* released below */ - ns = aa_prepare_namespace(ns_name); + ns = aa_prepare_ns(ns_name); if (!ns) { error = audit_policy(op, GFP_KERNEL, ns_name, "failed to prepare namespace", -ENOMEM); @@ -738,7 +738,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) } /* released when @new is freed */ - ent->new->ns = aa_get_namespace(ns); + ent->new->ns = aa_get_ns(ns); if (ent->old || ent->rename) continue; @@ -835,7 +835,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) mutex_unlock(&ns->lock); out: - aa_put_namespace(ns); + aa_put_ns(ns); if (error) return error; @@ -881,7 +881,7 @@ free: */ ssize_t aa_remove_profiles(char *fqname, size_t size) { - struct aa_namespace *root, *ns = NULL; + struct aa_ns *root, *ns = NULL; struct aa_profile *profile = NULL; const char *name = fqname, *info = NULL; ssize_t error = 0; @@ -898,7 +898,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) char *ns_name; name = aa_split_fqname(fqname, &ns_name); /* released below */ - ns = aa_find_namespace(root, ns_name); + ns = aa_find_ns(root, ns_name); if (!ns) { info = "namespace does not exist"; error = -ENOENT; @@ -906,12 +906,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) } } else /* released below */ - ns = aa_get_namespace(root); + ns = aa_get_ns(root); if (!name) { /* remove namespace - can only happen if fqname[0] == ':' */ mutex_lock(&ns->parent->lock); - __aa_remove_namespace(ns); + __aa_remove_ns(ns); mutex_unlock(&ns->parent->lock); } else { /* remove profile */ @@ -929,13 +929,13 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) /* don't fail removal if audit fails */ (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); - aa_put_namespace(ns); + aa_put_ns(ns); aa_put_profile(profile); return size; fail_ns_lock: mutex_unlock(&ns->lock); - aa_put_namespace(ns); + aa_put_ns(ns); fail: (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index d4e9924a276e..88b3b3c110e3 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -26,7 +26,7 @@ #include "include/policy.h" /* root profile namespace */ -struct aa_namespace *root_ns; +struct aa_ns *root_ns; const char *aa_hidden_ns_name = "---"; /** @@ -36,7 +36,7 @@ const char *aa_hidden_ns_name = "---"; * * Returns: true if @view is visible from @curr else false */ -bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) +bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view) { if (curr == view) return true; @@ -55,7 +55,7 @@ bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) * * Returns: name of @view visible from @curr */ -const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) +const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view) { /* if view == curr then the namespace name isn't displayed */ if (curr == view) @@ -75,16 +75,15 @@ const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) } /** - * alloc_namespace - allocate, initialize and return a new namespace + * alloc_ns - allocate, initialize and return a new namespace * @prefix: parent namespace name (MAYBE NULL) * @name: a preallocated name (NOT NULL) * * Returns: refcounted namespace or NULL on failure. */ -static struct aa_namespace *alloc_namespace(const char *prefix, - const char *name) +static struct aa_ns *alloc_ns(const char *prefix, const char *name) { - struct aa_namespace *ns; + struct aa_ns *ns; ns = kzalloc(sizeof(*ns), GFP_KERNEL); AA_DEBUG("%s(%p)\n", __func__, ns); @@ -96,7 +95,7 @@ static struct aa_namespace *alloc_namespace(const char *prefix, INIT_LIST_HEAD(&ns->sub_ns); mutex_init(&ns->lock); - /* released by free_namespace */ + /* released by aa_free_ns() */ ns->unconfined = aa_alloc_profile("unconfined"); if (!ns->unconfined) goto fail_unconfined; @@ -120,19 +119,19 @@ fail_ns: } /** - * aa_free_namespace - free a profile namespace + * aa_free_ns - free a profile namespace * @ns: the namespace to free (MAYBE NULL) * * Requires: All references to the namespace must have been put, if the * namespace was referenced by a profile confining a task, */ -void aa_free_namespace(struct aa_namespace *ns) +void aa_free_ns(struct aa_ns *ns) { if (!ns) return; aa_policy_destroy(&ns->base); - aa_put_namespace(ns->parent); + aa_put_ns(ns->parent); ns->unconfined->ns = NULL; aa_free_profile(ns->unconfined); @@ -140,7 +139,7 @@ void aa_free_namespace(struct aa_namespace *ns) } /** - * aa_find_namespace - look up a profile namespace on the namespace list + * aa_find_ns - look up a profile namespace on the namespace list * @root: namespace to search in (NOT NULL) * @name: name of namespace to find (NOT NULL) * @@ -149,27 +148,26 @@ void aa_free_namespace(struct aa_namespace *ns) * * refcount released by caller */ -struct aa_namespace *aa_find_namespace(struct aa_namespace *root, - const char *name) +struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name) { - struct aa_namespace *ns = NULL; + struct aa_ns *ns = NULL; rcu_read_lock(); - ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); + ns = aa_get_ns(__aa_find_ns(&root->sub_ns, name)); rcu_read_unlock(); return ns; } /** - * aa_prepare_namespace - find an existing or create a new namespace of @name + * aa_prepare_ns - find an existing or create a new namespace of @name * @name: the namespace to find or add (MAYBE NULL) * - * Returns: refcounted namespace or NULL if failed to create one + * Returns: refcounted ns or NULL if failed to create one */ -struct aa_namespace *aa_prepare_namespace(const char *name) +struct aa_ns *aa_prepare_ns(const char *name) { - struct aa_namespace *ns, *root; + struct aa_ns *ns, *root; root = aa_current_profile()->ns; @@ -178,28 +176,28 @@ struct aa_namespace *aa_prepare_namespace(const char *name) /* if name isn't specified the profile is loaded to the current ns */ if (!name) { /* released by caller */ - ns = aa_get_namespace(root); + ns = aa_get_ns(root); goto out; } /* try and find the specified ns and if it doesn't exist create it */ /* released by caller */ - ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); + ns = aa_get_ns(__aa_find_ns(&root->sub_ns, name)); if (!ns) { - ns = alloc_namespace(root->base.hname, name); + ns = alloc_ns(root->base.hname, name); if (!ns) goto out; - if (__aa_fs_namespace_mkdir(ns, ns_subns_dir(root), name)) { + if (__aa_fs_ns_mkdir(ns, ns_subns_dir(root), name)) { AA_ERROR("Failed to create interface for ns %s\n", ns->base.name); - aa_free_namespace(ns); + aa_free_ns(ns); ns = NULL; goto out; } - ns->parent = aa_get_namespace(root); + ns->parent = aa_get_ns(root); list_add_rcu(&ns->base.list, &root->sub_ns); /* add list ref */ - aa_get_namespace(ns); + aa_get_ns(ns); } out: mutex_unlock(&root->lock); @@ -211,10 +209,10 @@ out: static void __ns_list_release(struct list_head *head); /** - * destroy_namespace - remove everything contained by @ns - * @ns: namespace to have it contents removed (NOT NULL) + * destroy_ns - remove everything contained by @ns + * @ns: ns to have it contents removed (NOT NULL) */ -static void destroy_namespace(struct aa_namespace *ns) +static void destroy_ns(struct aa_ns *ns) { if (!ns) return; @@ -228,22 +226,22 @@ static void destroy_namespace(struct aa_namespace *ns) if (ns->parent) __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); - __aa_fs_namespace_rmdir(ns); + __aa_fs_ns_rmdir(ns); mutex_unlock(&ns->lock); } /** - * __aa_remove_namespace - remove a namespace and all its children + * __aa_remove_ns - remove a namespace and all its children * @ns: namespace to be removed (NOT NULL) * * Requires: ns->parent->lock be held and ns removed from parent. */ -void __aa_remove_namespace(struct aa_namespace *ns) +void __aa_remove_ns(struct aa_ns *ns) { /* remove ns from namespace list */ list_del_rcu(&ns->base.list); - destroy_namespace(ns); - aa_put_namespace(ns); + destroy_ns(ns); + aa_put_ns(ns); } /** @@ -254,15 +252,15 @@ void __aa_remove_namespace(struct aa_namespace *ns) */ static void __ns_list_release(struct list_head *head) { - struct aa_namespace *ns, *tmp; + struct aa_ns *ns, *tmp; list_for_each_entry_safe(ns, tmp, head, base.list) - __aa_remove_namespace(ns); + __aa_remove_ns(ns); } /** - * aa_alloc_root_ns - allocate the root profile namespace + * aa_alloc_root_ns - allocate the root profile namespcae * * Returns: %0 on success else error * @@ -270,7 +268,7 @@ static void __ns_list_release(struct list_head *head) int __init aa_alloc_root_ns(void) { /* released by aa_free_root_ns - used as list ref*/ - root_ns = alloc_namespace(NULL, "root"); + root_ns = alloc_ns(NULL, "root"); if (!root_ns) return -ENOMEM; @@ -282,10 +280,10 @@ int __init aa_alloc_root_ns(void) */ void __init aa_free_root_ns(void) { - struct aa_namespace *ns = root_ns; + struct aa_ns *ns = root_ns; root_ns = NULL; - destroy_namespace(ns); - aa_put_namespace(ns); + destroy_ns(ns); + aa_put_ns(ns); } diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index bb2600d9d826..15ddf74ac269 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -40,8 +40,8 @@ int aa_getprocattr(struct aa_profile *profile, char **string) int len = 0, mode_len = 0, ns_len = 0, name_len; const char *mode_str = aa_profile_mode_names[profile->mode]; const char *ns_name = NULL; - struct aa_namespace *ns = profile->ns; - struct aa_namespace *current_ns = __aa_current_profile()->ns; + struct aa_ns *ns = profile->ns; + struct aa_ns *current_ns = __aa_current_profile()->ns; char *s; if (!aa_ns_visible(current_ns, ns)) -- cgit v1.2.3 From 121d4a91e3c12ddfb167edafb9aa64cc5cc3a406 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:17 -0800 Subject: apparmor: rename sid to secid Move to common terminology with other LSMs and kernel infrastucture Signed-off-by: John Johansen --- security/apparmor/Makefile | 2 +- security/apparmor/include/secid.h | 26 ++++++++++++++++++ security/apparmor/include/sid.h | 26 ------------------ security/apparmor/secid.c | 55 +++++++++++++++++++++++++++++++++++++++ security/apparmor/sid.c | 55 --------------------------------------- 5 files changed, 82 insertions(+), 82 deletions(-) create mode 100644 security/apparmor/include/secid.h delete mode 100644 security/apparmor/include/sid.h create mode 100644 security/apparmor/secid.c delete mode 100644 security/apparmor/sid.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 3485f49d9de9..ad369a7aac24 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ 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 sid.o file.o policy_ns.o + resource.o secid.o file.o policy_ns.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h diff --git a/security/apparmor/include/secid.h b/security/apparmor/include/secid.h new file mode 100644 index 000000000000..95ed86a0f1e2 --- /dev/null +++ b/security/apparmor/include/secid.h @@ -0,0 +1,26 @@ +/* + * AppArmor security module + * + * This file contains AppArmor security identifier (secid) definitions + * + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_SECID_H +#define __AA_SECID_H + +#include + +/* secid value that will not be allocated */ +#define AA_SECID_INVALID 0 +#define AA_SECID_ALLOC AA_SECID_INVALID + +u32 aa_alloc_secid(void); +void aa_free_secid(u32 secid); + +#endif /* __AA_SECID_H */ diff --git a/security/apparmor/include/sid.h b/security/apparmor/include/sid.h deleted file mode 100644 index 513ca0e48965..000000000000 --- a/security/apparmor/include/sid.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * AppArmor security module - * - * This file contains AppArmor security identifier (sid) definitions - * - * Copyright 2009-2010 Canonical Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. - */ - -#ifndef __AA_SID_H -#define __AA_SID_H - -#include - -/* sid value that will not be allocated */ -#define AA_SID_INVALID 0 -#define AA_SID_ALLOC AA_SID_INVALID - -u32 aa_alloc_sid(void); -void aa_free_sid(u32 sid); - -#endif /* __AA_SID_H */ diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c new file mode 100644 index 000000000000..3a3edbad0b21 --- /dev/null +++ b/security/apparmor/secid.c @@ -0,0 +1,55 @@ +/* + * AppArmor security module + * + * This file contains AppArmor security identifier (secid) manipulation fns + * + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * + * AppArmor allocates a unique secid for every profile loaded. If a profile + * is replaced it receives the secid of the profile it is replacing. + * + * The secid value of 0 is invalid. + */ + +#include +#include +#include + +#include "include/secid.h" + +/* global counter from which secids are allocated */ +static u32 global_secid; +static DEFINE_SPINLOCK(secid_lock); + +/* TODO FIXME: add secid to profile mapping, and secid recycling */ + +/** + * aa_alloc_secid - allocate a new secid for a profile + */ +u32 aa_alloc_secid(void) +{ + u32 secid; + + /* + * TODO FIXME: secid recycling - part of profile mapping table + */ + spin_lock(&secid_lock); + secid = (++global_secid); + spin_unlock(&secid_lock); + return secid; +} + +/** + * aa_free_secid - free a secid + * @secid: secid to free + */ +void aa_free_secid(u32 secid) +{ + ; /* NOP ATM */ +} diff --git a/security/apparmor/sid.c b/security/apparmor/sid.c deleted file mode 100644 index f0b34f76ebef..000000000000 --- a/security/apparmor/sid.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * AppArmor security module - * - * This file contains AppArmor security identifier (sid) manipulation fns - * - * Copyright 2009-2010 Canonical Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. - * - * - * AppArmor allocates a unique sid for every profile loaded. If a profile - * is replaced it receives the sid of the profile it is replacing. - * - * The sid value of 0 is invalid. - */ - -#include -#include -#include - -#include "include/sid.h" - -/* global counter from which sids are allocated */ -static u32 global_sid; -static DEFINE_SPINLOCK(sid_lock); - -/* TODO FIXME: add sid to profile mapping, and sid recycling */ - -/** - * aa_alloc_sid - allocate a new sid for a profile - */ -u32 aa_alloc_sid(void) -{ - u32 sid; - - /* - * TODO FIXME: sid recycling - part of profile mapping table - */ - spin_lock(&sid_lock); - sid = (++global_sid); - spin_unlock(&sid_lock); - return sid; -} - -/** - * aa_free_sid - free a sid - * @sid: sid to free - */ -void aa_free_sid(u32 sid) -{ - ; /* NOP ATM */ -} -- cgit v1.2.3 From d97d51d253e08e059bba40002407ec3d188feafb Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:18 -0800 Subject: apparmor: rename PFLAG_INVALID to PFLAG_STALE Invalid does not convey the meaning of the flag anymore so rename it. Signed-off-by: John Johansen --- security/apparmor/include/context.h | 2 +- security/apparmor/include/policy.h | 6 +++--- security/apparmor/policy.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index 6bf65798e5d1..a0acc2390fae 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -152,7 +152,7 @@ static inline struct aa_profile *aa_current_profile(void) struct aa_profile *profile; BUG_ON(!cxt || !cxt->profile); - if (PROFILE_INVALID(cxt->profile)) { + if (profile_is_stale(cxt->profile)) { profile = aa_get_newest_profile(cxt->profile); aa_replace_current_profile(profile); aa_put_profile(profile); diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 415f8ab0b11e..56bef768c7eb 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -46,7 +46,7 @@ extern const char *const aa_profile_mode_names[]; #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) -#define PROFILE_INVALID(_profile) ((_profile)->flags & PFLAG_INVALID) +#define profile_is_stale(_profile) ((_profile)->flags & PFLAG_STALE) #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) @@ -71,7 +71,7 @@ enum profile_flags { PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ - PFLAG_INVALID = 0x200, /* profile replaced/removed */ + PFLAG_STALE = 0x200, /* profile replaced/removed */ PFLAG_NS_COUNT = 0x400, /* carries NS ref count */ /* These flags must correspond with PATH_flags */ @@ -253,7 +253,7 @@ static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) if (!p) return NULL; - if (PROFILE_INVALID(p)) + if (profile_is_stale(p)) return aa_get_profile_rcu(&p->replacedby->profile); return aa_get_profile(p); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 2dd8717a5a89..edc81a0d0cb4 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -106,7 +106,7 @@ void __aa_update_replacedby(struct aa_profile *orig, struct aa_profile *new) tmp = rcu_dereference_protected(orig->replacedby->profile, mutex_is_locked(&orig->ns->lock)); rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new)); - orig->flags |= PFLAG_INVALID; + orig->flags |= PFLAG_STALE; aa_put_profile(tmp); } -- cgit v1.2.3 From 8399588a7f9def9195e577f988ad06f2a0ffb1af Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:19 -0800 Subject: apparmor: rename replacedby to proxy Proxy is shorter and a better fit than replaceby, so rename it. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 36 ++++++++++---------- security/apparmor/context.c | 2 +- security/apparmor/include/policy.h | 20 +++++------ security/apparmor/policy.c | 70 +++++++++++++++++++------------------- security/apparmor/policy_ns.c | 2 +- 5 files changed, 65 insertions(+), 65 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 4409b63f0dd7..0f1a4a28e025 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -228,12 +228,12 @@ const struct file_operations aa_fs_seq_file_ops = { static int aa_fs_seq_profile_open(struct inode *inode, struct file *file, int (*show)(struct seq_file *, void *)) { - struct aa_replacedby *r = aa_get_replacedby(inode->i_private); - int error = single_open(file, show, r); + struct aa_proxy *proxy = aa_get_proxy(inode->i_private); + int error = single_open(file, show, proxy); if (error) { file->private_data = NULL; - aa_put_replacedby(r); + aa_put_proxy(proxy); } return error; @@ -243,14 +243,14 @@ static int aa_fs_seq_profile_release(struct inode *inode, struct file *file) { struct seq_file *seq = (struct seq_file *) file->private_data; if (seq) - aa_put_replacedby(seq->private); + aa_put_proxy(seq->private); return single_release(inode, file); } static int aa_fs_seq_profname_show(struct seq_file *seq, void *v) { - struct aa_replacedby *r = seq->private; - struct aa_profile *profile = aa_get_profile_rcu(&r->profile); + struct aa_proxy *proxy = seq->private; + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); seq_printf(seq, "%s\n", profile->base.name); aa_put_profile(profile); @@ -272,8 +272,8 @@ static const struct file_operations aa_fs_profname_fops = { static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v) { - struct aa_replacedby *r = seq->private; - struct aa_profile *profile = aa_get_profile_rcu(&r->profile); + struct aa_proxy *proxy = seq->private; + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]); aa_put_profile(profile); @@ -295,8 +295,8 @@ static const struct file_operations aa_fs_profmode_fops = { static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v) { - struct aa_replacedby *r = seq->private; - struct aa_profile *profile = aa_get_profile_rcu(&r->profile); + struct aa_proxy *proxy = seq->private; + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); if (profile->attach) seq_printf(seq, "%s\n", profile->attach); else if (profile->xmatch) @@ -323,8 +323,8 @@ static const struct file_operations aa_fs_profattach_fops = { static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) { - struct aa_replacedby *r = seq->private; - struct aa_profile *profile = aa_get_profile_rcu(&r->profile); + struct aa_proxy *proxy = seq->private; + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); unsigned int i, size = aa_hash_size(); if (profile->hash) { @@ -363,13 +363,13 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile) __aa_fs_profile_rmdir(child); for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) { - struct aa_replacedby *r; + struct aa_proxy *proxy; if (!profile->dents[i]) continue; - r = d_inode(profile->dents[i])->i_private; + proxy = d_inode(profile->dents[i])->i_private; securityfs_remove(profile->dents[i]); - aa_put_replacedby(r); + aa_put_proxy(proxy); profile->dents[i] = NULL; } } @@ -391,12 +391,12 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name, struct aa_profile *profile, const struct file_operations *fops) { - struct aa_replacedby *r = aa_get_replacedby(profile->replacedby); + struct aa_proxy *proxy = aa_get_proxy(profile->proxy); struct dentry *dent; - dent = securityfs_create_file(name, S_IFREG | 0444, dir, r, fops); + dent = securityfs_create_file(name, S_IFREG | 0444, dir, proxy, fops); if (IS_ERR(dent)) - aa_put_replacedby(r); + aa_put_proxy(proxy); return dent; } diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 3064c6ced87c..3c4f534ef88c 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -112,7 +112,7 @@ int aa_replace_current_profile(struct aa_profile *profile) aa_clear_task_cxt_trans(cxt); /* be careful switching cxt->profile, when racing replacement it - * is possible that cxt->profile->replacedby->profile is the reference + * is possible that cxt->profile->proxy->profile is the reference * keeping @profile valid, so make sure to get its reference before * dropping the reference on cxt->profile */ aa_get_profile(profile); diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 56bef768c7eb..f55ecb8b3777 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -91,7 +91,7 @@ struct aa_policydb { }; -struct aa_replacedby { +struct aa_proxy { struct kref count; struct aa_profile __rcu *profile; }; @@ -103,7 +103,7 @@ struct aa_replacedby { * @rcu: rcu head used when removing from @list * @parent: parent of profile * @ns: namespace the profile is in - * @replacedby: is set to the profile that replaced this profile + * @proxy: is set to the profile that replaced this profile * @rename: optional profile name that this profile renamed * @attach: human readable attachment string * @xmatch: optional extended matching for unconfined executables names @@ -126,7 +126,7 @@ struct aa_replacedby { * used to determine profile attachment against unconfined tasks. All other * attachments are determined by profile X transition rules. * - * The @replacedby struct is write protected by the profile lock. + * The @proxy struct is write protected by the profile lock. * * Profiles have a hierarchy where hats and children profiles keep * a reference to their parent. @@ -142,7 +142,7 @@ struct aa_profile { struct aa_profile __rcu *parent; struct aa_ns *ns; - struct aa_replacedby *replacedby; + struct aa_proxy *proxy; const char *rename; const char *attach; @@ -166,12 +166,12 @@ struct aa_profile { extern enum profile_mode aa_g_profile_mode; -void __aa_update_replacedby(struct aa_profile *orig, struct aa_profile *new); +void __aa_update_proxy(struct aa_profile *orig, struct aa_profile *new); void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); -void aa_free_replacedby_kref(struct kref *kref); +void aa_free_proxy_kref(struct kref *kref); struct aa_profile *aa_alloc_profile(const char *name); struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); void aa_free_profile(struct aa_profile *profile); @@ -254,7 +254,7 @@ static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) return NULL; if (profile_is_stale(p)) - return aa_get_profile_rcu(&p->replacedby->profile); + return aa_get_profile_rcu(&p->proxy->profile); return aa_get_profile(p); } @@ -269,7 +269,7 @@ static inline void aa_put_profile(struct aa_profile *p) kref_put(&p->count, aa_free_profile_kref); } -static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) +static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *p) { if (p) kref_get(&(p->count)); @@ -277,10 +277,10 @@ static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) return p; } -static inline void aa_put_replacedby(struct aa_replacedby *p) +static inline void aa_put_proxy(struct aa_proxy *p) { if (p) - kref_put(&p->count, aa_free_replacedby_kref); + kref_put(&p->count, aa_free_proxy_kref); } static inline int AUDIT_MODE(struct aa_profile *profile) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index edc81a0d0cb4..a4bf6756bc1c 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -99,13 +99,13 @@ const char *const aa_profile_mode_names[] = { /* requires profile list write lock held */ -void __aa_update_replacedby(struct aa_profile *orig, struct aa_profile *new) +void __aa_update_proxy(struct aa_profile *orig, struct aa_profile *new) { struct aa_profile *tmp; - tmp = rcu_dereference_protected(orig->replacedby->profile, + tmp = rcu_dereference_protected(orig->proxy->profile, mutex_is_locked(&orig->ns->lock)); - rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new)); + rcu_assign_pointer(orig->proxy->profile, aa_get_profile(new)); orig->flags |= PFLAG_STALE; aa_put_profile(tmp); } @@ -156,7 +156,7 @@ static void __remove_profile(struct aa_profile *profile) /* release any children lists first */ __aa_profile_list_release(&profile->base.profiles); /* released by free_profile */ - __aa_update_replacedby(profile, profile->ns->unconfined); + __aa_update_proxy(profile, profile->ns->unconfined); __aa_fs_profile_rmdir(profile); __list_remove_profile(profile); } @@ -175,21 +175,21 @@ void __aa_profile_list_release(struct list_head *head) } -static void free_replacedby(struct aa_replacedby *r) +static void free_proxy(struct aa_proxy *p) { - if (r) { + if (p) { /* r->profile will not be updated any more as r is dead */ - aa_put_profile(rcu_dereference_protected(r->profile, true)); - kzfree(r); + aa_put_profile(rcu_dereference_protected(p->profile, true)); + kzfree(p); } } -void aa_free_replacedby_kref(struct kref *kref) +void aa_free_proxy_kref(struct kref *kref) { - struct aa_replacedby *r = container_of(kref, struct aa_replacedby, - count); - free_replacedby(r); + struct aa_proxy *p = container_of(kref, struct aa_proxy, count); + + free_proxy(p); } /** @@ -223,7 +223,7 @@ void aa_free_profile(struct aa_profile *profile) kzfree(profile->dirname); aa_put_dfa(profile->xmatch); aa_put_dfa(profile->policy.dfa); - aa_put_replacedby(profile->replacedby); + aa_put_proxy(profile->proxy); kzfree(profile->hash); kzfree(profile); @@ -267,10 +267,10 @@ struct aa_profile *aa_alloc_profile(const char *hname) if (!profile) return NULL; - profile->replacedby = kzalloc(sizeof(struct aa_replacedby), GFP_KERNEL); - if (!profile->replacedby) + profile->proxy = kzalloc(sizeof(struct aa_proxy), GFP_KERNEL); + if (!profile->proxy) goto fail; - kref_init(&profile->replacedby->count); + kref_init(&profile->proxy->count); if (!aa_policy_init(&profile->base, NULL, hname)) goto fail; @@ -280,7 +280,7 @@ struct aa_profile *aa_alloc_profile(const char *hname) return profile; fail: - kzfree(profile->replacedby); + kzfree(profile->proxy); kzfree(profile); return NULL; @@ -598,7 +598,7 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh, * __replace_profile - replace @old with @new on a list * @old: profile to be replaced (NOT NULL) * @new: profile to replace @old with (NOT NULL) - * @share_replacedby: transfer @old->replacedby to @new + * @share_proxy: transfer @old->proxy to @new * * Will duplicate and refcount elements that @new inherits from @old * and will inherit @old children. @@ -608,7 +608,7 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh, * Requires: namespace list lock be held, or list not be shared */ static void __replace_profile(struct aa_profile *old, struct aa_profile *new, - bool share_replacedby) + bool share_proxy) { struct aa_profile *child, *tmp; @@ -623,7 +623,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new, p = __find_child(&new->base.profiles, child->base.name); if (p) { /* @p replaces @child */ - __replace_profile(child, p, share_replacedby); + __replace_profile(child, p, share_proxy); continue; } @@ -641,13 +641,13 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new, struct aa_profile *parent = aa_deref_parent(old); rcu_assign_pointer(new->parent, aa_get_profile(parent)); } - __aa_update_replacedby(old, new); - if (share_replacedby) { - aa_put_replacedby(new->replacedby); - new->replacedby = aa_get_replacedby(old->replacedby); - } else if (!rcu_access_pointer(new->replacedby->profile)) - /* aafs interface uses replacedby */ - rcu_assign_pointer(new->replacedby->profile, + __aa_update_proxy(old, new); + if (share_proxy) { + aa_put_proxy(new->proxy); + new->proxy = aa_get_proxy(old->proxy); + } else if (!rcu_access_pointer(new->proxy->profile)) + /* aafs interface uses proxy */ + rcu_assign_pointer(new->proxy->profile, aa_get_profile(new)); __aa_fs_profile_migrate_dents(old, new); @@ -797,15 +797,15 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) if (ent->old) { __replace_profile(ent->old, ent->new, 1); if (ent->rename) { - /* aafs interface uses replacedby */ - struct aa_replacedby *r = ent->new->replacedby; + /* aafs interface uses proxy */ + struct aa_proxy *r = ent->new->proxy; rcu_assign_pointer(r->profile, aa_get_profile(ent->new)); __replace_profile(ent->rename, ent->new, 0); } } else if (ent->rename) { - /* aafs interface uses replacedby */ - rcu_assign_pointer(ent->new->replacedby->profile, + /* aafs interface uses proxy */ + rcu_assign_pointer(ent->new->proxy->profile, aa_get_profile(ent->new)); __replace_profile(ent->rename, ent->new, 0); } else if (ent->new->parent) { @@ -819,14 +819,14 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) rcu_assign_pointer(ent->new->parent, newest); aa_put_profile(parent); } - /* aafs interface uses replacedby */ - rcu_assign_pointer(ent->new->replacedby->profile, + /* aafs interface uses proxy */ + rcu_assign_pointer(ent->new->proxy->profile, aa_get_profile(ent->new)); __list_add_profile(&newest->base.profiles, ent->new); aa_put_profile(newest); } else { - /* aafs interface uses replacedby */ - rcu_assign_pointer(ent->new->replacedby->profile, + /* aafs interface uses proxy */ + rcu_assign_pointer(ent->new->proxy->profile, aa_get_profile(ent->new)); __list_add_profile(&ns->base.profiles, ent->new); } diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 88b3b3c110e3..71fbd14e3b37 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -225,7 +225,7 @@ static void destroy_ns(struct aa_ns *ns) __ns_list_release(&ns->sub_ns); if (ns->parent) - __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); + __aa_update_proxy(ns->unconfined, ns->parent->unconfined); __aa_fs_ns_rmdir(ns); mutex_unlock(&ns->lock); } -- cgit v1.2.3 From 1741e9eb8c15de0ba6598a342c51d0688cb569d2 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:21 -0800 Subject: apparmor: add strn version of lookup_profile fn Signed-off-by: John Johansen --- security/apparmor/include/policy.h | 2 ++ security/apparmor/policy.c | 36 +++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index f55ecb8b3777..c90f4a2ffe57 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -177,6 +177,8 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); void aa_free_profile(struct aa_profile *profile); void aa_free_profile_kref(struct kref *kref); struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); +struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, + size_t n); struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name); struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index a4bf6756bc1c..291810490eaf 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -427,9 +427,10 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns, } /** - * __lookup_profile - lookup the profile matching @hname + * __lookupn_profile - lookup the profile matching @hname * @base: base list to start looking up profile name from (NOT NULL) * @hname: hierarchical profile name (NOT NULL) + * @n: length of @hname * * Requires: rcu_read_lock be held * @@ -437,53 +438,66 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns, * * Do a relative name lookup, recursing through profile tree. */ -static struct aa_profile *__lookup_profile(struct aa_policy *base, - const char *hname) +static struct aa_profile *__lookupn_profile(struct aa_policy *base, + const char *hname, size_t n) { struct aa_profile *profile = NULL; - char *split; + const char *split; - for (split = strstr(hname, "//"); split;) { + for (split = strnstr(hname, "//", n); split; + split = strnstr(hname, "//", n)) { profile = __strn_find_child(&base->profiles, hname, split - hname); if (!profile) return NULL; base = &profile->base; + n -= split + 2 - hname; hname = split + 2; - split = strstr(hname, "//"); } - profile = __find_child(&base->profiles, hname); + if (n) + return __strn_find_child(&base->profiles, hname, n); + return NULL; +} - return profile; +static struct aa_profile *__lookup_profile(struct aa_policy *base, + const char *hname) +{ + return __lookupn_profile(base, hname, strlen(hname)); } /** * aa_lookup_profile - find a profile by its full or partial name * @ns: the namespace to start from (NOT NULL) * @hname: name to do lookup on. Does not contain namespace prefix (NOT NULL) + * @n: size of @hname * * Returns: refcounted profile or NULL if not found */ -struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname) +struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, + size_t n) { struct aa_profile *profile; rcu_read_lock(); do { - profile = __lookup_profile(&ns->base, hname); + profile = __lookupn_profile(&ns->base, hname, n); } while (profile && !aa_get_profile_not0(profile)); rcu_read_unlock(); /* the unconfined profile is not in the regular profile list */ - if (!profile && strcmp(hname, "unconfined") == 0) + if (!profile && strncmp(hname, "unconfined", n) == 0) profile = aa_get_newest_profile(ns->unconfined); /* refcount released by caller */ return profile; } +struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname) +{ + return aa_lookupn_profile(ns, hname, strlen(hname)); +} /** * replacement_allowed - test to see if replacement is allowed * @profile: profile to test if it can be replaced (MAYBE NULL) -- cgit v1.2.3 From 9a2d40c12d00ead1b1a3ac8383d2d66e35674fdb Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:22 -0800 Subject: apparmor: add strn version of aa_find_ns Signed-off-by: John Johansen --- security/apparmor/include/policy_ns.h | 13 ++++++++++--- security/apparmor/policy_ns.c | 22 +++++++++++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index 323752cc0c87..381f8b078548 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -82,6 +82,7 @@ void aa_free_root_ns(void); void aa_free_ns_kref(struct kref *kref); struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name); +struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n); struct aa_ns *aa_prepare_ns(const char *name); void __aa_remove_ns(struct aa_ns *ns); @@ -119,18 +120,24 @@ static inline void aa_put_ns(struct aa_ns *ns) } /** - * __aa_find_ns - find a namespace on a list by @name + * __aa_findn_ns - find a namespace on a list by @name * @head: list to search for namespace on (NOT NULL) * @name: name of namespace to look for (NOT NULL) - * + * @n: length of @name * Returns: unrefcounted namespace * * Requires: rcu_read_lock be held */ +static inline struct aa_ns *__aa_findn_ns(struct list_head *head, + const char *name, size_t n) +{ + return (struct aa_ns *)__policy_strn_find(head, name, n); +} + static inline struct aa_ns *__aa_find_ns(struct list_head *head, const char *name) { - return (struct aa_ns *)__policy_find(head, name); + return __aa_findn_ns(head, name, strlen(name)); } #endif /* AA_NAMESPACE_H */ diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 71fbd14e3b37..9746643cbab2 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -139,26 +139,42 @@ void aa_free_ns(struct aa_ns *ns) } /** - * aa_find_ns - look up a profile namespace on the namespace list + * aa_findn_ns - look up a profile namespace on the namespace list * @root: namespace to search in (NOT NULL) * @name: name of namespace to find (NOT NULL) + * @n: length of @name * * Returns: a refcounted namespace on the list, or NULL if no namespace * called @name exists. * * refcount released by caller */ -struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name) +struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n) { struct aa_ns *ns = NULL; rcu_read_lock(); - ns = aa_get_ns(__aa_find_ns(&root->sub_ns, name)); + ns = aa_get_ns(__aa_findn_ns(&root->sub_ns, name, n)); rcu_read_unlock(); return ns; } +/** + * aa_find_ns - look up a profile namespace on the namespace list + * @root: namespace to search in (NOT NULL) + * @name: name of namespace to find (NOT NULL) + * + * Returns: a refcounted namespace on the list, or NULL if no namespace + * called @name exists. + * + * refcount released by caller + */ +struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name) +{ + return aa_findn_ns(root, name, strlen(name)); +} + /** * aa_prepare_ns - find an existing or create a new namespace of @name * @name: the namespace to find or add (MAYBE NULL) -- cgit v1.2.3 From 3b0aaf5866bf92a3e47627a02ed5e1be6d7cc110 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:23 -0800 Subject: apparmor: add lib fn to find the "split" for fqnames Signed-off-by: John Johansen --- security/apparmor/include/lib.h | 2 ++ security/apparmor/lib.c | 53 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 74cc68ea4c12..4699c2b43fa0 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -38,6 +38,8 @@ extern int apparmor_initialized __initdata; /* fn's in lib */ char *aa_split_fqname(char *args, char **ns_name); +const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, + size_t *ns_len); void aa_info_message(const char *str); void *__aa_kvmalloc(size_t size, gfp_t flags); diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index e29ccdb0309a..fec78eecce0d 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -12,6 +12,7 @@ * License. */ +#include #include #include #include @@ -60,6 +61,58 @@ char *aa_split_fqname(char *fqname, char **ns_name) return name; } +/** + * skipn_spaces - Removes leading whitespace from @str. + * @str: The string to be stripped. + * + * Returns a pointer to the first non-whitespace character in @str. + * if all whitespace will return NULL + */ + +static const char *skipn_spaces(const char *str, size_t n) +{ + for (; n && isspace(*str); --n) + ++str; + if (n) + return (char *)str; + return NULL; +} + +const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, + size_t *ns_len) +{ + const char *end = fqname + n; + const char *name = skipn_spaces(fqname, n); + + if (!name) + return NULL; + *ns_name = NULL; + *ns_len = 0; + if (name[0] == ':') { + char *split = strnchr(&name[1], end - &name[1], ':'); + *ns_name = skipn_spaces(&name[1], end - &name[1]); + if (!*ns_name) + return NULL; + if (split) { + *ns_len = split - *ns_name; + if (*ns_len == 0) + *ns_name = NULL; + split++; + if (end - split > 1 && strncmp(split, "//", 2) == 0) + split += 2; + name = skipn_spaces(split, end - split); + } else { + /* a ns name without a following profile is allowed */ + name = NULL; + *ns_len = end - *ns_name; + } + } + if (name && *name == 0) + name = NULL; + + return name; +} + /** * aa_info_message - log a none profile related status message * @str: message to log -- cgit v1.2.3 From 31617ddfdd7764a5046f076247208aa324458069 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:24 -0800 Subject: apparmor: add fn to lookup profiles by fqname Signed-off-by: John Johansen --- security/apparmor/include/policy.h | 2 ++ security/apparmor/include/policy_ns.h | 10 +++++----- security/apparmor/policy.c | 29 +++++++++++++++++++++++++++++ security/apparmor/policy_ns.c | 4 ++-- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index c90f4a2ffe57..da62d29d3992 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -180,6 +180,8 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, size_t n); struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name); +struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base, + const char *fqname, size_t n); struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index 381f8b078548..ebf9b40f84ed 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -46,11 +46,11 @@ struct aa_ns_acct { * @uniq_id: a unique id count for the profiles in the namespace * @dents: dentries for the namespaces file entries in apparmorfs * - * An aa_ns defines the set profiles that are searched to determine - * which profile to attach to a task. Profiles can not be shared between - * aa_nss and profile names within a namespace are guaranteed to be - * unique. When profiles in separate namespaces have the same name they - * are NOT considered to be equivalent. + * An aa_ns defines the set profiles that are searched to determine which + * profile to attach to a task. Profiles can not be shared between aa_ns + * and profile names within a namespace are guaranteed to be unique. When + * profiles in separate namespaces have the same name they are NOT considered + * to be equivalent. * * Namespaces are hierarchical and only namespaces and profiles below the * current namespace are visible. diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 291810490eaf..7fde5785005d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -498,6 +498,35 @@ struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname) { return aa_lookupn_profile(ns, hname, strlen(hname)); } + +struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base, + const char *fqname, size_t n) +{ + struct aa_profile *profile; + struct aa_ns *ns; + const char *name, *ns_name; + size_t ns_len; + + name = aa_splitn_fqname(fqname, n, &ns_name, &ns_len); + if (ns_name) { + ns = aa_findn_ns(base->ns, ns_name, ns_len); + if (!ns) + return NULL; + } else + ns = aa_get_ns(base->ns); + + if (name) + profile = aa_lookupn_profile(ns, name, n - (name - fqname)); + else if (ns) + /* default profile for ns, currently unconfined */ + profile = aa_get_newest_profile(ns->unconfined); + else + profile = NULL; + aa_put_ns(ns); + + return profile; +} + /** * replacement_allowed - test to see if replacement is allowed * @profile: profile to test if it can be replaced (MAYBE NULL) diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 9746643cbab2..bab23cce197c 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -226,7 +226,7 @@ static void __ns_list_release(struct list_head *head); /** * destroy_ns - remove everything contained by @ns - * @ns: ns to have it contents removed (NOT NULL) + * @ns: namespace to have it contents removed (NOT NULL) */ static void destroy_ns(struct aa_ns *ns) { @@ -276,7 +276,7 @@ static void __ns_list_release(struct list_head *head) } /** - * aa_alloc_root_ns - allocate the root profile namespcae + * aa_alloc_root_ns - allocate the root profile namespace * * Returns: %0 on success else error * -- cgit v1.2.3 From 92b6d8eff55f8dca57ade26e1dde2c3b6acdae02 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:25 -0800 Subject: apparmor: allow ns visibility question to consider subnses Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 2 +- security/apparmor/include/policy_ns.h | 4 ++-- security/apparmor/policy_ns.c | 12 +++++++++--- security/apparmor/procattr.c | 4 ++-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0f1a4a28e025..d7cfd79d9857 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -750,7 +750,7 @@ static int seq_show_profile(struct seq_file *f, void *p) struct aa_ns *root = f->private; if (profile->ns != root) - seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns, true)); seq_printf(f, "%s (%s)\n", profile->base.hname, aa_profile_mode_names[profile->mode]); diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index ebf9b40f84ed..e4c876544adc 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -74,8 +74,8 @@ extern struct aa_ns *root_ns; extern const char *aa_hidden_ns_name; -bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view); -const char *aa_ns_name(struct aa_ns *parent, struct aa_ns *child); +bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns); +const char *aa_ns_name(struct aa_ns *parent, struct aa_ns *child, bool subns); void aa_free_ns(struct aa_ns *ns); int aa_alloc_root_ns(void); void aa_free_root_ns(void); diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index bab23cce197c..e7b7a829532e 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -33,18 +33,23 @@ const char *aa_hidden_ns_name = "---"; * aa_ns_visible - test if @view is visible from @curr * @curr: namespace to treat as the parent (NOT NULL) * @view: namespace to test if visible from @curr (NOT NULL) + * @subns: whether view of a subns is allowed * * Returns: true if @view is visible from @curr else false */ -bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view) +bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns) { if (curr == view) return true; + if (!subns) + return false; + for ( ; view; view = view->parent) { if (view->parent == curr) return true; } + return false; } @@ -52,16 +57,17 @@ bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view) * aa_na_name - Find the ns name to display for @view from @curr * @curr - current namespace (NOT NULL) * @view - namespace attempting to view (NOT NULL) + * @subns - are subns visible * * Returns: name of @view visible from @curr */ -const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view) +const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view, bool subns) { /* if view == curr then the namespace name isn't displayed */ if (curr == view) return ""; - if (aa_ns_visible(curr, view)) { + if (aa_ns_visible(curr, view, subns)) { /* at this point if a ns is visible it is in a view ns * thus the curr ns.hname is a prefix of its name. * Only output the virtualized portion of the name diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index 15ddf74ac269..1babd3655520 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -44,10 +44,10 @@ int aa_getprocattr(struct aa_profile *profile, char **string) struct aa_ns *current_ns = __aa_current_profile()->ns; char *s; - if (!aa_ns_visible(current_ns, ns)) + if (!aa_ns_visible(current_ns, ns, true)) return -EACCES; - ns_name = aa_ns_name(current_ns, ns); + ns_name = aa_ns_name(current_ns, ns, true); ns_len = strlen(ns_name); /* if the visible ns_name is > 0 increase size for : :// seperator */ -- cgit v1.2.3 From 57e36bbd67bd1509f550311b162be78cadfe887b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:26 -0800 Subject: apparmor: add macro for bug asserts to check that a lock is held Signed-off-by: John Johansen --- security/apparmor/include/lib.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 4699c2b43fa0..61dedd7333df 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -19,6 +19,17 @@ #include "match.h" +/* Provide our own test for whether a write lock is held for asserts + * this is because on none SMP systems write_can_lock will always + * resolve to true, which is what you want for code making decisions + * based on it, but wrong for asserts checking that the lock is held + */ +#ifdef CONFIG_SMP +#define write_is_locked(X) !write_can_lock(X) +#else +#define write_is_locked(X) (1) +#endif /* CONFIG_SMP */ + /* * DEBUG remains global (no per profile flag) since it is mostly used in sysctl * which is not related to profile accesses. -- cgit v1.2.3 From 680cd62e910d7b7e3c1fcde6ba67c6ca770c2286 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:27 -0800 Subject: apparmor: add debug assert AA_BUG and Kconfig to control debug info Signed-off-by: John Johansen --- security/apparmor/Kconfig | 31 +++++++++++++++++++++++++++++-- security/apparmor/include/lib.h | 14 +++++++++++++- security/apparmor/lsm.c | 2 +- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index be5e9414a295..b6b68a7750ce 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -36,7 +36,6 @@ config SECURITY_APPARMOR_HASH select CRYPTO select CRYPTO_SHA1 default y - help This option selects whether introspection of loaded policy is available to userspace via the apparmor filesystem. @@ -45,7 +44,6 @@ config SECURITY_APPARMOR_HASH_DEFAULT bool "Enable policy hash introspection by default" depends on SECURITY_APPARMOR_HASH default y - help This option selects whether sha1 hashing of loaded policy is enabled by default. The generation of sha1 hashes for @@ -54,3 +52,32 @@ config SECURITY_APPARMOR_HASH_DEFAULT however it can slow down policy load on some devices. In these cases policy hashing can be disabled by default and enabled only if needed. + +config SECURITY_APPARMOR_DEBUG + bool "Build AppArmor with debug code" + depends on SECURITY_APPARMOR + default n + help + Build apparmor with debugging logic in apparmor. Not all + debugging logic will necessarily be enabled. A submenu will + provide fine grained control of the debug options that are + available. + +config SECURITY_APPARMOR_DEBUG_ASSERTS + bool "Build AppArmor with debugging asserts" + depends on SECURITY_APPARMOR_DEBUG + default y + help + Enable code assertions made with AA_BUG. These are primarily + function entry preconditions but also exist at other key + points. If the assert is triggered it will trigger a WARN + message. + +config SECURITY_APPARMOR_DEBUG_MESSAGES + bool "Debug messages enabled by default" + depends on SECURITY_APPARMOR_DEBUG + default n + help + Set the default value of the apparmor.debug kernel parameter. + When enabled, various debug messages will be logged to + the kernel message buffer. diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 61dedd7333df..d507c73ac9b8 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -35,12 +35,24 @@ * which is not related to profile accesses. */ +#define DEBUG_ON (aa_g_debug) +#define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args) #define AA_DEBUG(fmt, args...) \ do { \ - if (aa_g_debug) \ + if (DEBUG_ON) \ pr_debug_ratelimited("AppArmor: " fmt, ##args); \ } while (0) +#define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __func__, #X) + +#define AA_BUG(X, args...) AA_BUG_FMT((X), "" args) +#ifdef CONFIG_SECURITY_APPARMOR_DEBUG_ASSERTS +#define AA_BUG_FMT(X, fmt, args...) \ + WARN((X), "AppArmor WARN %s: (" #X "): " fmt, __func__, ##args) +#else +#define AA_BUG_FMT(X, fmt, args...) +#endif + #define AA_ERROR(fmt, args...) \ pr_err_ratelimited("AppArmor: " fmt, ##args) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 1dae66ba757b..99a6e5ec4ffe 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -681,7 +681,7 @@ module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR); #endif /* Debug mode */ -bool aa_g_debug; +bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_DEBUG_MESSAGES); module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); /* Audit mode */ -- cgit v1.2.3 From efeee83a7060c225fac5ac794e9c11183c267f81 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:28 -0800 Subject: apparmor: rename mediated_filesystem() to path_mediated_fs() Rename to indicate the test is only about whether path mediation is used, not whether other types of mediation might be used. Signed-off-by: John Johansen --- security/apparmor/include/lib.h | 2 +- security/apparmor/lsm.c | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index d507c73ac9b8..4ff09ed813b5 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -111,7 +111,7 @@ static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, return aa_dfa_next(dfa, start, 0); } -static inline bool mediated_filesystem(struct dentry *dentry) +static inline bool path_mediated_fs(struct dentry *dentry) { return !(dentry->d_sb->s_flags & MS_NOUSER); } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 99a6e5ec4ffe..a757c163fda6 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -195,7 +195,7 @@ static inline int common_perm_path(int op, const struct path *path, u32 mask) struct path_cond cond = { d_backing_inode(path->dentry)->i_uid, d_backing_inode(path->dentry)->i_mode }; - if (!mediated_filesystem(path->dentry)) + if (!path_mediated_fs(path->dentry)) return 0; return common_perm(op, path, mask, &cond); @@ -216,7 +216,7 @@ static int common_perm_rm(int op, const struct path *dir, struct inode *inode = d_backing_inode(dentry); struct path_cond cond = { }; - if (!inode || !mediated_filesystem(dentry)) + if (!inode || !path_mediated_fs(dentry)) return 0; cond.uid = inode->i_uid; @@ -240,7 +240,7 @@ static int common_perm_create(int op, const struct path *dir, { struct path_cond cond = { current_fsuid(), mode }; - if (!mediated_filesystem(dir->dentry)) + if (!path_mediated_fs(dir->dentry)) return 0; return common_perm_dir_dentry(op, dir, dentry, mask, &cond); @@ -287,7 +287,7 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_ struct aa_profile *profile; int error = 0; - if (!mediated_filesystem(old_dentry)) + if (!path_mediated_fs(old_dentry)) return 0; profile = aa_current_profile(); @@ -302,7 +302,7 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d struct aa_profile *profile; int error = 0; - if (!mediated_filesystem(old_dentry)) + if (!path_mediated_fs(old_dentry)) return 0; profile = aa_current_profile(); @@ -349,7 +349,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred) struct aa_profile *profile; int error = 0; - if (!mediated_filesystem(file->f_path.dentry)) + if (!path_mediated_fs(file->f_path.dentry)) return 0; /* If in exec, permission is handled by bprm hooks. @@ -402,7 +402,7 @@ static int common_file_perm(int op, struct file *file, u32 mask) BUG_ON(!fprofile); if (!file->f_path.mnt || - !mediated_filesystem(file->f_path.dentry)) + !path_mediated_fs(file->f_path.dentry)) return 0; profile = __aa_current_profile(); -- cgit v1.2.3 From 6e474e3063eae9767f219d83cf91d8360f63be0c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:29 -0800 Subject: apparmor: rename hname_tail to basename Rename to the shorter and more familiar shell cmd name Signed-off-by: John Johansen --- security/apparmor/include/lib.h | 4 ++-- security/apparmor/lib.c | 2 +- security/apparmor/policy.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 4ff09ed813b5..b5c16d3a7a18 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -130,12 +130,12 @@ struct aa_policy { }; /** - * hname_tail - find the last component of an hname + * basename - find the last component of an hname * @name: hname to find the base profile name component of (NOT NULL) * * Returns: the tail (base profile name) name component of an hname */ -static inline const char *hname_tail(const char *hname) +static inline const char *basename(const char *hname) { char *split; diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index fec78eecce0d..02203889c3c8 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -184,7 +184,7 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix, if (!policy->hname) return 0; /* base.name is a substring of fqname */ - policy->name = (char *)hname_tail(policy->hname); + policy->name = (char *)basename(policy->hname); INIT_LIST_HEAD(&policy->list); INIT_LIST_HEAD(&policy->profiles); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 7fde5785005d..3b23960b8a5d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -617,7 +617,7 @@ bool aa_may_manage_policy(int op) static struct aa_profile *__list_lookup_parent(struct list_head *lh, struct aa_profile *profile) { - const char *base = hname_tail(profile->base.hname); + const char *base = basename(profile->base.hname); long len = base - profile->base.hname; struct aa_load_ent *ent; -- cgit v1.2.3 From bbe4a7c8733c925b061dcce2d1af8926cefbe539 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:30 -0800 Subject: apparmor: constify policy name and hname Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 2 +- security/apparmor/include/lib.h | 4 ++-- security/apparmor/lib.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index d7cfd79d9857..96a02ee9c499 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -38,7 +38,7 @@ * * Returns: length of mangled name */ -static int mangle_name(char *name, char *target) +static int mangle_name(const char *name, char *target) { char *t = target; diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index b5c16d3a7a18..7e81cdab4af0 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -123,8 +123,8 @@ static inline bool path_mediated_fs(struct dentry *dentry) * @profiles: head of the profiles list contained in the object */ struct aa_policy { - char *name; - char *hname; + const char *name; + const char *hname; struct list_head list; struct list_head profiles; }; diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 02203889c3c8..91d5766d1c28 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -178,7 +178,7 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix, policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, GFP_KERNEL); if (policy->hname) - sprintf(policy->hname, "%s//%s", prefix, name); + sprintf((char *)policy->hname, "%s//%s", prefix, name); } else policy->hname = kstrdup(name, GFP_KERNEL); if (!policy->hname) -- cgit v1.2.3 From d102d895713c736fd13e21feaab38b52d8ab32ad Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:31 -0800 Subject: apparmor: pass gfp param into aa_policy_init() Signed-off-by: John Johansen --- security/apparmor/include/lib.h | 2 +- security/apparmor/lib.c | 8 ++++---- security/apparmor/policy.c | 2 +- security/apparmor/policy_ns.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 7e81cdab4af0..fa281c272970 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -194,7 +194,7 @@ static inline struct aa_policy *__policy_strn_find(struct list_head *head, } bool aa_policy_init(struct aa_policy *policy, const char *prefix, - const char *name); + const char *name, gfp_t gfp); void aa_policy_destroy(struct aa_policy *policy); #endif /* AA_LIB_H */ diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 91d5766d1c28..bcd598c7ca9d 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -171,20 +171,20 @@ void *__aa_kvmalloc(size_t size, gfp_t flags) * Returns: true if policy init successful */ bool aa_policy_init(struct aa_policy *policy, const char *prefix, - const char *name) + const char *name, gfp_t gfp) { /* freed by policy_free */ if (prefix) { policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, - GFP_KERNEL); + gfp); if (policy->hname) sprintf((char *)policy->hname, "%s//%s", prefix, name); } else - policy->hname = kstrdup(name, GFP_KERNEL); + policy->hname = kstrdup(name, gfp); if (!policy->hname) return 0; /* base.name is a substring of fqname */ - policy->name = (char *)basename(policy->hname); + policy->name = basename(policy->hname); INIT_LIST_HEAD(&policy->list); INIT_LIST_HEAD(&policy->profiles); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 3b23960b8a5d..5d99fb7ac881 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -272,7 +272,7 @@ struct aa_profile *aa_alloc_profile(const char *hname) goto fail; kref_init(&profile->proxy->count); - if (!aa_policy_init(&profile->base, NULL, hname)) + if (!aa_policy_init(&profile->base, NULL, hname, GFP_KERNEL)) goto fail; kref_init(&profile->count); diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index e7b7a829532e..8a5632f39751 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -95,7 +95,7 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name) AA_DEBUG("%s(%p)\n", __func__, ns); if (!ns) return NULL; - if (!aa_policy_init(&ns->base, prefix, name)) + if (!aa_policy_init(&ns->base, prefix, name, GFP_KERNEL)) goto fail_ns; INIT_LIST_HEAD(&ns->sub_ns); -- cgit v1.2.3 From 5fd1b95fc9b96629d185f5fe3d9342fcff78eb30 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:32 -0800 Subject: apparmor: update policy_destroy to use new debug asserts Signed-off-by: John Johansen --- security/apparmor/lib.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index bcd598c7ca9d..5d8ef31a60f1 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -197,15 +197,8 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix, */ void aa_policy_destroy(struct aa_policy *policy) { - /* still contains profiles -- invalid */ - if (on_list_rcu(&policy->profiles)) { - AA_ERROR("%s: internal error, policy '%s' contains profiles\n", - __func__, policy->name); - } - if (on_list_rcu(&policy->list)) { - AA_ERROR("%s: internal error, policy '%s' still on list\n", - __func__, policy->name); - } + AA_BUG(on_list_rcu(&policy->profiles)); + AA_BUG(on_list_rcu(&policy->list)); /* don't free name as its a subset of hname */ kzfree(policy->hname); -- cgit v1.2.3 From 73688d1ed0b8f800f312f7bc9d583463858da861 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:34 -0800 Subject: apparmor: refactor prepare_ns() and make usable from different views prepare_ns() will need to be called from alternate views, and namespaces will need to be created via different interfaces. So refactor and allow specifying the view ns. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 6 ++- security/apparmor/include/policy.h | 3 +- security/apparmor/include/policy_ns.h | 4 +- security/apparmor/policy.c | 6 ++- security/apparmor/policy_ns.c | 98 +++++++++++++++++++++++------------ 5 files changed, 79 insertions(+), 38 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 96a02ee9c499..2b48be2169cb 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -125,7 +125,8 @@ static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, error = PTR_ERR(data); if (!IS_ERR(data)) { - error = aa_replace_profiles(data, size, PROF_ADD); + error = aa_replace_profiles(__aa_current_profile()->ns, data, + size, PROF_ADD); kvfree(data); } @@ -147,7 +148,8 @@ static ssize_t profile_replace(struct file *f, const char __user *buf, data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { - error = aa_replace_profiles(data, size, PROF_REPLACE); + error = aa_replace_profiles(__aa_current_profile()->ns, data, + size, PROF_REPLACE); kvfree(data); } diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index da62d29d3992..1573cade8812 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -184,7 +184,8 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base, const char *fqname, size_t n); struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); -ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); +ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, + bool noreplace); ssize_t aa_remove_profiles(char *name, size_t size); void __aa_profile_list_release(struct list_head *head); diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index e4c876544adc..820d86d266fe 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -83,7 +83,9 @@ void aa_free_ns_kref(struct kref *kref); struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name); struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n); -struct aa_ns *aa_prepare_ns(const char *name); +struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name, + struct dentry *dir); +struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name); void __aa_remove_ns(struct aa_ns *ns); static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 5d99fb7ac881..e02ab20b0a8d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -731,6 +731,7 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, /** * aa_replace_profiles - replace profile(s) on the profile list + * @view: namespace load is viewed from * @udata: serialized data stream (NOT NULL) * @size: size of the serialized data stream * @noreplace: true if only doing addition, no replacement allowed @@ -741,7 +742,8 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, * * Returns: size of data consumed else error code on failure. */ -ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) +ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, + bool noreplace) { const char *ns_name, *info = NULL; struct aa_ns *ns = NULL; @@ -756,7 +758,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) goto out; /* released below */ - ns = aa_prepare_ns(ns_name); + ns = aa_prepare_ns(view, ns_name); if (!ns) { error = audit_policy(op, GFP_KERNEL, ns_name, "failed to prepare namespace", -ENOMEM); diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 8a5632f39751..f6cdc738ffcd 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -181,48 +181,82 @@ struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name) return aa_findn_ns(root, name, strlen(name)); } +static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name, + struct dentry *dir) +{ + struct aa_ns *ns; + int error; + + AA_BUG(!parent); + AA_BUG(!name); + AA_BUG(!mutex_is_locked(&parent->lock)); + + ns = alloc_ns(parent->base.hname, name); + if (!ns) + return NULL; + mutex_lock(&ns->lock); + error = __aa_fs_ns_mkdir(ns, ns_subns_dir(parent), name); + if (error) { + AA_ERROR("Failed to create interface for ns %s\n", + ns->base.name); + mutex_unlock(&ns->lock); + aa_free_ns(ns); + return ERR_PTR(error); + } + ns->parent = aa_get_ns(parent); + list_add_rcu(&ns->base.list, &parent->sub_ns); + /* add list ref */ + aa_get_ns(ns); + mutex_unlock(&ns->lock); + + return ns; +} + /** - * aa_prepare_ns - find an existing or create a new namespace of @name - * @name: the namespace to find or add (MAYBE NULL) + * aa_create_ns - create an ns, fail if it already exists + * @parent: the parent of the namespace being created + * @name: the name of the namespace + * @dir: if not null the dir to put the ns entries in * - * Returns: refcounted ns or NULL if failed to create one + * Returns: the a refcounted ns that has been add or an ERR_PTR */ -struct aa_ns *aa_prepare_ns(const char *name) +struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name, + struct dentry *dir) { - struct aa_ns *ns, *root; + struct aa_ns *ns; - root = aa_current_profile()->ns; + AA_BUG(!mutex_is_locked(&parent->lock)); - mutex_lock(&root->lock); + /* try and find the specified ns */ + /* released by caller */ + ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name)); + if (!ns) + ns = __aa_create_ns(parent, name, dir); + else + ns = ERR_PTR(-EEXIST); - /* if name isn't specified the profile is loaded to the current ns */ - if (!name) { - /* released by caller */ - ns = aa_get_ns(root); - goto out; - } + /* return ref */ + return ns; +} +/** + * aa_prepare_ns - find an existing or create a new namespace of @name + * @parent: ns to treat as parent + * @name: the namespace to find or add (NOT NULL) + * + * Returns: refcounted namespace or PTR_ERR if failed to create one + */ +struct aa_ns *aa_prepare_ns(struct aa_ns *parent, const char *name) +{ + struct aa_ns *ns; + + mutex_lock(&parent->lock); /* try and find the specified ns and if it doesn't exist create it */ /* released by caller */ - ns = aa_get_ns(__aa_find_ns(&root->sub_ns, name)); - if (!ns) { - ns = alloc_ns(root->base.hname, name); - if (!ns) - goto out; - if (__aa_fs_ns_mkdir(ns, ns_subns_dir(root), name)) { - AA_ERROR("Failed to create interface for ns %s\n", - ns->base.name); - aa_free_ns(ns); - ns = NULL; - goto out; - } - ns->parent = aa_get_ns(root); - list_add_rcu(&ns->base.list, &root->sub_ns); - /* add list ref */ - aa_get_ns(ns); - } -out: - mutex_unlock(&root->lock); + ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name)); + if (!ns) + ns = __aa_create_ns(parent, name, NULL); + mutex_unlock(&parent->lock); /* return ref */ return ns; -- cgit v1.2.3 From 30b026a8d16bfa15bc24f4cca1604e47ac1a2f64 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:35 -0800 Subject: apparmor: pass gfp_t parameter into profile allocation Signed-off-by: John Johansen --- security/apparmor/include/policy.h | 2 +- security/apparmor/policy.c | 11 ++++++----- security/apparmor/policy_ns.c | 2 +- security/apparmor/policy_unpack.c | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 1573cade8812..b44eaea2bd2c 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -172,7 +172,7 @@ void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); void aa_free_proxy_kref(struct kref *kref); -struct aa_profile *aa_alloc_profile(const char *name); +struct aa_profile *aa_alloc_profile(const char *name, gfp_t gfp); struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); void aa_free_profile(struct aa_profile *profile); void aa_free_profile_kref(struct kref *kref); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index e02ab20b0a8d..e310f3b63fbe 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -255,24 +255,25 @@ void aa_free_profile_kref(struct kref *kref) /** * aa_alloc_profile - allocate, initialize and return a new profile * @hname: name of the profile (NOT NULL) + * @gfp: allocation type * * Returns: refcount profile or NULL on failure */ -struct aa_profile *aa_alloc_profile(const char *hname) +struct aa_profile *aa_alloc_profile(const char *hname, gfp_t gfp) { struct aa_profile *profile; /* freed by free_profile - usually through aa_put_profile */ - profile = kzalloc(sizeof(*profile), GFP_KERNEL); + profile = kzalloc(sizeof(*profile), gfp); if (!profile) return NULL; - profile->proxy = kzalloc(sizeof(struct aa_proxy), GFP_KERNEL); + profile->proxy = kzalloc(sizeof(struct aa_proxy), gfp); if (!profile->proxy) goto fail; kref_init(&profile->proxy->count); - if (!aa_policy_init(&profile->base, NULL, hname, GFP_KERNEL)) + if (!aa_policy_init(&profile->base, NULL, hname, gfp)) goto fail; kref_init(&profile->count); @@ -312,7 +313,7 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) goto fail; sprintf(name, "%s//null-%x", parent->base.hname, uniq); - profile = aa_alloc_profile(name); + profile = aa_alloc_profile(name, GFP_KERNEL); kfree(name); if (!profile) goto fail; diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index f6cdc738ffcd..1e19bd3c7851 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -102,7 +102,7 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name) mutex_init(&ns->lock); /* released by aa_free_ns() */ - ns->unconfined = aa_alloc_profile("unconfined"); + ns->unconfined = aa_alloc_profile("unconfined", GFP_KERNEL); if (!ns->unconfined) goto fail_unconfined; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 138120698f83..9ddc6b2a7322 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -486,7 +486,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_str(e, &name, NULL)) goto fail; - profile = aa_alloc_profile(name); + profile = aa_alloc_profile(name, GFP_KERNEL); if (!profile) return ERR_PTR(-ENOMEM); -- cgit v1.2.3 From 181f7c977680dcd86eb71ad4b37239d2a385c3ad Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:36 -0800 Subject: apparmor: name null-XXX profiles after the executable When possible its better to name a learning profile after the missing profile in question. This allows for both more informative names and for profile reuse. Signed-off-by: John Johansen --- security/apparmor/domain.c | 8 +++--- security/apparmor/include/policy.h | 3 ++- security/apparmor/policy.c | 53 ++++++++++++++++++++++++++++---------- 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 503cb2c54447..1a8ffc577009 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -442,7 +442,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) } } else if (COMPLAIN_MODE(profile)) { /* no exec permission - are we in learning mode */ - new_profile = aa_new_null_profile(profile, 0); + new_profile = aa_new_null_profile(profile, false, name, + GFP_ATOMIC); if (!new_profile) { error = -ENOMEM; info = "could not create null profile"; @@ -667,7 +668,8 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) aa_put_profile(root); target = name; /* released below */ - hat = aa_new_null_profile(profile, 1); + hat = aa_new_null_profile(profile, true, hats[0], + GFP_KERNEL); if (!hat) { info = "failed null profile create"; error = -ENOMEM; @@ -815,7 +817,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, if (permtest || !COMPLAIN_MODE(profile)) goto audit; /* released below */ - target = aa_new_null_profile(profile, 0); + target = aa_new_null_profile(profile, false, hname, GFP_KERNEL); if (!target) { info = "failed null profile create"; error = -ENOMEM; diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index b44eaea2bd2c..3527e3f5a099 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -173,7 +173,8 @@ void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); void aa_free_proxy_kref(struct kref *kref); struct aa_profile *aa_alloc_profile(const char *name, gfp_t gfp); -struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); +struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, + const char *base, gfp_t gfp); void aa_free_profile(struct aa_profile *profile); void aa_free_profile_kref(struct kref *kref); struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index e310f3b63fbe..dd63ac92d28f 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -288,12 +288,16 @@ fail: } /** - * aa_new_null_profile - create a new null-X learning 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 * - * Create a null- complain mode profile used in learning mode. The name of - * the profile is unique and follows the format of parent//null-. + * 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 @@ -301,27 +305,45 @@ fail: * * Returns: new refcounted profile else NULL on failure */ -struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) +struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, + const char *base, gfp_t gfp) { - struct aa_profile *profile = NULL; + struct aa_profile *profile; char *name; - int uniq = atomic_inc_return(&parent->ns->uniq_null); - /* freed below */ - name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL); + 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) - goto fail; - sprintf(name, "%s//null-%x", parent->base.hname, uniq); + return NULL; + sprintf(name, "%s//null-%x", parent->base.hname, + atomic_inc_return(&parent->ns->uniq_null)); - profile = aa_alloc_profile(name, GFP_KERNEL); - kfree(name); +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, gfp); if (!profile) goto fail; profile->mode = APPARMOR_COMPLAIN; - profile->flags = PFLAG_NULL; + profile->flags |= PFLAG_NULL; if (hat) profile->flags |= PFLAG_HAT; + profile->path_flags = parent->path_flags; /* released on free_profile */ rcu_assign_pointer(profile->parent, aa_get_profile(parent)); @@ -332,9 +354,14 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) mutex_unlock(&profile->ns->lock); /* refcount released by caller */ +out: + kfree(name); + return profile; fail: + kfree(name); + aa_free_profile(profile); return NULL; } -- cgit v1.2.3 From abbf8734039fe57c72c999e37bd1c30d8aed1943 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:37 -0800 Subject: apparmor: remove paranoid load switch Policy should always under go a full paranoid verification. Signed-off-by: John Johansen --- security/apparmor/lsm.c | 5 +++-- security/apparmor/policy_unpack.c | 21 +++++++-------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index a757c163fda6..e40eecbbaefa 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -714,10 +714,11 @@ module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR); /* Determines how paranoid loading of policy is and how much verification * on the loaded policy is done. + * DEPRECATED: read only as strict checking of load is always done now + * that none root users (user namespaces) can load policy. */ bool aa_g_paranoid_load = 1; -module_param_named(paranoid_load, aa_g_paranoid_load, aabool, - S_IRUSR | S_IWUSR); +module_param_named(paranoid_load, aa_g_paranoid_load, aabool, S_IRUGO); /* Boot time disable flag */ static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 9ddc6b2a7322..fe73117cd940 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -340,12 +340,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) ((e->pos - e->start) & 7); size_t pad = ALIGN(sz, 8) - sz; int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | - TO_ACCEPT2_FLAG(YYTD_DATA32); - - - if (aa_g_paranoid_load) - flags |= DFA_FLAG_VERIFY_STATES; - + TO_ACCEPT2_FLAG(YYTD_DATA32) | DFA_FLAG_VERIFY_STATES; dfa = aa_dfa_unpack(blob + pad, size - pad, flags); if (IS_ERR(dfa)) @@ -705,14 +700,12 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) */ static int verify_profile(struct aa_profile *profile) { - if (aa_g_paranoid_load) { - if (profile->file.dfa && - !verify_dfa_xindex(profile->file.dfa, - profile->file.trans.size)) { - audit_iface(profile, NULL, "Invalid named transition", - NULL, -EPROTO); - return -EPROTO; - } + if (profile->file.dfa && + !verify_dfa_xindex(profile->file.dfa, + profile->file.trans.size)) { + audit_iface(profile, NULL, "Invalid named transition", + NULL, -EPROTO); + return -EPROTO; } return 0; -- cgit v1.2.3 From 5ebfb12822656beec5c56b362d44e4db81c8e1eb Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:38 -0800 Subject: apparmor: add support for force complain flag to support learning mode Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index fe73117cd940..c836a9c8b6ff 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -29,6 +29,8 @@ #include "include/policy.h" #include "include/policy_unpack.h" +#define FORCE_COMPLAIN_FLAG 0x800 + /* * The AppArmor interface treats data as a type byte followed by the * actual data. The interface has the notion of a a named entry @@ -514,7 +516,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) profile->flags |= PFLAG_HAT; if (!unpack_u32(e, &tmp, NULL)) goto fail; - if (tmp == PACKED_MODE_COMPLAIN) + if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) profile->mode = APPARMOR_COMPLAIN; else if (tmp == PACKED_MODE_KILL) profile->mode = APPARMOR_KILL; -- cgit v1.2.3 From 474d6b75106229025ab6b7bbabf2f9c246e928e1 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:39 -0800 Subject: apparmor: prepare to support newer versions of policy Newer policy encodes more than just version in the version tag, so add masking to make sure the comparison remains correct. Note: this is fully compatible with older policy as it will never set the bits being masked out. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 10 ++++++++-- security/apparmor/policy_unpack.c | 25 +++++++++++++++++-------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 2b48be2169cb..49a5122e03fe 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -799,9 +799,15 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { { } }; +static struct aa_fs_entry aa_fs_entry_versions[] = { + AA_FS_FILE_BOOLEAN("v5", 1), + { } +}; + static struct aa_fs_entry aa_fs_entry_policy[] = { - AA_FS_FILE_BOOLEAN("set_load", 1), - {} + AA_FS_DIR("versions", aa_fs_entry_versions), + AA_FS_FILE_BOOLEAN("set_load", 1), + { } }; static struct aa_fs_entry aa_fs_entry_features[] = { diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index c836a9c8b6ff..6ac292fec55f 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -29,7 +29,14 @@ #include "include/policy.h" #include "include/policy_unpack.h" +#define K_ABI_MASK 0x3ff #define FORCE_COMPLAIN_FLAG 0x800 +#define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK)) +#define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK)) + +#define v5 5 /* base version */ +#define v6 6 /* per entry policydb mediation check */ +#define v7 7 /* full network masking */ /* * The AppArmor interface treats data as a type byte followed by the @@ -646,19 +653,21 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) /* get the interface version */ if (!unpack_u32(e, &e->version, "version")) { if (required) { - audit_iface(NULL, NULL, "invalid profile format", e, - error); - return error; - } - - /* check that the interface version is currently supported */ - if (e->version != 5) { - audit_iface(NULL, NULL, "unsupported interface version", + audit_iface(NULL, NULL, "invalid profile format", e, error); return error; } } + /* Check that the interface version is currently supported. + * 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)) { + audit_iface(NULL, NULL, "unsupported interface version", + e, error); + return error; + } /* read the namespace if present */ if (unpack_str(e, &name, "namespace")) { -- cgit v1.2.3 From 293a4886f93f1d4f01ef2642b81c2509a5376ce5 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:40 -0800 Subject: apparmor: add get_dfa() fn The dfa is currently setup to be shared (has the basis of refcounting) but currently can't be because the count can't be increased. Signed-off-by: John Johansen --- security/apparmor/include/match.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index a1c04fe86790..d751c8bf72cd 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -127,6 +127,21 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, void aa_dfa_free_kref(struct kref *kref); +/** + * aa_get_dfa - increment refcount on dfa @p + * @dfa: dfa (MAYBE NULL) + * + * Returns: pointer to @dfa if @dfa is NULL will return NULL + * Requires: @dfa must be held with valid refcount when called + */ +static inline struct aa_dfa *aa_get_dfa(struct aa_dfa *dfa) +{ + if (dfa) + kref_get(&(dfa->count)); + + return dfa; +} + /** * aa_put_dfa - put a dfa refcount * @dfa: dfa to put refcount (MAYBE NULL) -- cgit v1.2.3 From 6604d4c1c1a65d3d1a6a56291d96516d1e9b7041 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:41 -0800 Subject: apparmor: allow policydb to be used as the file dfa Newer policy will combine the file and policydb dfas, allowing for better optimizations. However to support older policy we need to keep the ability to address the "file" dfa separately. So dup the policydb as if it is the file dfa and set the appropriate start state. Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 6ac292fec55f..7160addb11be 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -611,12 +611,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) error = PTR_ERR(profile->file.dfa); profile->file.dfa = NULL; goto fail; + } else if (profile->file.dfa) { + if (!unpack_u32(e, &profile->file.start, "dfa_start")) + /* default start state */ + profile->file.start = DFA_START; + } else if (profile->policy.dfa && + profile->policy.start[AA_CLASS_FILE]) { + profile->file.dfa = aa_get_dfa(profile->policy.dfa); + profile->file.start = profile->policy.start[AA_CLASS_FILE]; } - if (!unpack_u32(e, &profile->file.start, "dfa_start")) - /* default start state */ - profile->file.start = DFA_START; - if (!unpack_trans_table(e, profile)) goto fail; -- cgit v1.2.3 From 11c236b89d7c26d58c55d5613a858600a4d2ab3a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:42 -0800 Subject: apparmor: add a default null dfa Instead of testing whether a given dfa exists in every code path, have a default null dfa that is used when loaded policy doesn't provide a dfa. This will let us get rid of special casing and avoid dereference bugs when special casing is missed. Signed-off-by: John Johansen --- security/apparmor/include/match.h | 5 +++++ security/apparmor/lsm.c | 7 +++++++ security/apparmor/match.c | 27 +++++++++++++++++++++++++++ security/apparmor/nulldfa.in | 1 + security/apparmor/policy.c | 2 ++ security/apparmor/policy_unpack.c | 6 ++++-- 6 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/nulldfa.in diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index d751c8bf72cd..a85bb3b1836c 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -100,6 +100,8 @@ struct aa_dfa { struct table_header *tables[YYTD_ID_TSIZE]; }; +extern struct aa_dfa *nulldfa; + #define byte_to_byte(X) (X) #define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \ @@ -117,6 +119,9 @@ static inline size_t table_size(size_t len, size_t el_size) return ALIGN(sizeof(struct table_header) + len * el_size, 8); } +int aa_setup_dfa_engine(void); +void aa_teardown_dfa_engine(void); + struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags); unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, const char *str, int len); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index e40eecbbaefa..f852cd626f2e 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -878,6 +878,12 @@ static int __init apparmor_init(void) return 0; } + error = aa_setup_dfa_engine(); + if (error) { + AA_ERROR("Unable to setup dfa engine\n"); + goto alloc_out; + } + error = aa_alloc_root_ns(); if (error) { AA_ERROR("Unable to allocate default profile namespace\n"); @@ -905,6 +911,7 @@ static int __init apparmor_init(void) alloc_out: aa_destroy_aafs(); + aa_teardown_dfa_engine(); apparmor_enabled = 0; return error; diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 0e04bcf91154..8f0806b35a75 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -25,6 +25,33 @@ #define base_idx(X) ((X) & 0xffffff) +static char nulldfa_src[] = { + #include "nulldfa.in" +}; +struct aa_dfa *nulldfa; + +int aa_setup_dfa_engine(void) +{ + int error; + + nulldfa = aa_dfa_unpack(nulldfa_src, sizeof(nulldfa_src), + TO_ACCEPT1_FLAG(YYTD_DATA32) | + TO_ACCEPT2_FLAG(YYTD_DATA32)); + if (!IS_ERR(nulldfa)) + return 0; + + error = PTR_ERR(nulldfa); + nulldfa = NULL; + + return error; +} + +void aa_teardown_dfa_engine(void) +{ + aa_put_dfa(nulldfa); + nulldfa = NULL; +} + /** * unpack_table - unpack a dfa table (one of accept, default, base, next check) * @blob: data to unpack (NOT NULL) diff --git a/security/apparmor/nulldfa.in b/security/apparmor/nulldfa.in new file mode 100644 index 000000000000..3cb38022902e --- /dev/null +++ b/security/apparmor/nulldfa.in @@ -0,0 +1 @@ +0x1B, 0x5E, 0x78, 0x3D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x04, 0x90, 0x00, 0x00, 0x6E, 0x6F, 0x74, 0x66, 0x6C, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index dd63ac92d28f..046edecc4c8a 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -348,6 +348,8 @@ name: /* 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); __list_add_profile(&parent->base.profiles, profile); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 7160addb11be..51a7f9fc8a3e 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -603,7 +603,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) } if (!unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; - } + } else + profile->policy.dfa = aa_get_dfa(nulldfa); /* get file rules */ profile->file.dfa = unpack_dfa(e); @@ -619,7 +620,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) profile->policy.start[AA_CLASS_FILE]) { profile->file.dfa = aa_get_dfa(profile->policy.dfa); profile->file.start = profile->policy.start[AA_CLASS_FILE]; - } + } else + profile->file.dfa = aa_get_dfa(nulldfa); if (!unpack_trans_table(e, profile)) goto fail; -- cgit v1.2.3 From 34c426acb75cc21bdf84685e106db0c1a3565057 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:43 -0800 Subject: apparmor: provide userspace flag indicating binfmt_elf_mmap change Commit 9f834ec18def ("binfmt_elf: switch to new creds when switching to new mm") changed when the creds are installed by the binfmt_elf handler. This affects which creds are used to mmap the executable into the address space. Which can have an affect on apparmor policy. Add a flag to apparmor at /sys/kernel/security/apparmor/features/domain/fix_binfmt_elf_mmap to make it possible to detect this semantic change so that the userspace tools and the regression test suite can correctly deal with the change. BugLink: http://bugs.launchpad.net/bugs/1630069 Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 49a5122e03fe..5c000cb7ef8e 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -796,6 +796,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { AA_FS_FILE_BOOLEAN("change_hatv", 1), AA_FS_FILE_BOOLEAN("change_onexec", 1), AA_FS_FILE_BOOLEAN("change_profile", 1), + AA_FS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1), { } }; -- cgit v1.2.3 From a71ada305801e940ff69c2c58489778760e5148b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:45 -0800 Subject: apparmor: add special .null file used to "close" fds at exec Borrow the special null device file from selinux to "close" fds that don't have sufficient permissions at exec time. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 78 +++++++++++++++++++++++++++++++++- security/apparmor/include/apparmorfs.h | 2 + security/apparmor/include/policy_ns.h | 2 + 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 5c000cb7ef8e..2501a65fe7d3 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -18,9 +18,12 @@ #include #include #include +#include #include #include #include +#include +#include #include "include/apparmor.h" #include "include/apparmorfs.h" @@ -352,6 +355,28 @@ static const struct file_operations aa_fs_seq_hash_fops = { .release = single_release, }; +static int aa_fs_seq_show_ns_level(struct seq_file *seq, void *v) +{ + struct aa_ns *ns = aa_current_profile()->ns; + + seq_printf(seq, "%d\n", ns->level); + + return 0; +} + +static int aa_fs_seq_open_ns_level(struct inode *inode, struct file *file) +{ + return single_open(file, aa_fs_seq_show_ns_level, inode->i_private); +} + +static const struct file_operations aa_fs_ns_level = { + .owner = THIS_MODULE, + .open = aa_fs_seq_open_ns_level, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /** fns to setup dynamic per profile/namespace files **/ void __aa_fs_profile_rmdir(struct aa_profile *profile) { @@ -825,6 +850,7 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), + AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level), AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), AA_FS_DIR("features", aa_fs_entry_features), { } @@ -934,6 +960,52 @@ void __init aa_destroy_aafs(void) aafs_remove_dir(&aa_fs_entry); } + +#define NULL_FILE_NAME ".null" +struct path aa_null; + +static int aa_mk_null_file(struct dentry *parent) +{ + struct vfsmount *mount = NULL; + struct dentry *dentry; + struct inode *inode; + int count = 0; + int error = simple_pin_fs(parent->d_sb->s_type, &mount, &count); + + if (error) + return error; + + inode_lock(d_inode(parent)); + dentry = lookup_one_len(NULL_FILE_NAME, parent, strlen(NULL_FILE_NAME)); + if (IS_ERR(dentry)) { + error = PTR_ERR(dentry); + goto out; + } + inode = new_inode(parent->d_inode->i_sb); + if (!inode) { + error = -ENOMEM; + goto out1; + } + + inode->i_ino = get_next_ino(); + inode->i_mode = S_IFCHR | S_IRUGO | S_IWUGO; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, + MKDEV(MEM_MAJOR, 3)); + d_instantiate(dentry, inode); + aa_null.dentry = dget(dentry); + aa_null.mnt = mntget(mount); + + error = 0; + +out1: + dput(dentry); +out: + inode_unlock(d_inode(parent)); + simple_release_fs(&mount, &count); + return error; +} + /** * aa_create_aafs - create the apparmor security filesystem * @@ -962,7 +1034,11 @@ static int __init aa_create_aafs(void) if (error) goto error; - /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ + error = aa_mk_null_file(aa_fs_entry.dentry); + if (error) + goto error; + + /* TODO: add default profile to apparmorfs */ /* Report that AppArmor fs is enabled */ aa_info_message("AppArmor Filesystem Enabled"); diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 5626bd48d7cb..eeeae5b0cc36 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -15,6 +15,8 @@ #ifndef __AA_APPARMORFS_H #define __AA_APPARMORFS_H +extern struct path aa_null; + enum aa_fs_type { AA_FS_TYPE_BOOLEAN, AA_FS_TYPE_STRING, diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index 820d86d266fe..89cffddd7e75 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -44,6 +44,7 @@ struct aa_ns_acct { * @sub_ns: list of namespaces under the current namespace. * @uniq_null: uniq value used for null learning profiles * @uniq_id: a unique id count for the profiles in the namespace + * @level: level of ns within the tree hierarchy * @dents: dentries for the namespaces file entries in apparmorfs * * An aa_ns defines the set profiles that are searched to determine which @@ -66,6 +67,7 @@ struct aa_ns { struct list_head sub_ns; atomic_t uniq_null; long uniq_id; + int level; struct dentry *dents[AAFS_NS_SIZEOF]; }; -- cgit v1.2.3 From ee2351e4b07cb7e3609f8661effe0382fb23646b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:46 -0800 Subject: apparmor: track ns level so it can be used to help in view checks Signed-off-by: John Johansen --- security/apparmor/policy_ns.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 1e19bd3c7851..93d1826c4b09 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -204,6 +204,7 @@ static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name, return ERR_PTR(error); } ns->parent = aa_get_ns(parent); + ns->level = parent->level + 1; list_add_rcu(&ns->base.list, &parent->sub_ns); /* add list ref */ aa_get_ns(ns); -- cgit v1.2.3 From b79473f2de3eb3320e2a145da8a2ea03c7331784 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:47 -0800 Subject: apparmor: Make aa_remove_profile() callable from a different view This is prep work for fs operations being able to remove namespaces. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 3 ++- security/apparmor/include/policy.h | 2 +- security/apparmor/policy.c | 7 ++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 2501a65fe7d3..14b96a44a3f5 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -180,7 +180,8 @@ static ssize_t profile_remove(struct file *f, const char __user *buf, error = PTR_ERR(data); if (!IS_ERR(data)) { data[size] = 0; - error = aa_remove_profiles(data, size); + error = aa_remove_profiles(__aa_current_profile()->ns, data, + size); kvfree(data); } diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 3527e3f5a099..8fcfb3c78d21 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -187,7 +187,7 @@ struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, bool noreplace); -ssize_t aa_remove_profiles(char *name, size_t size); +ssize_t aa_remove_profiles(struct aa_ns *view, char *name, size_t size); void __aa_profile_list_release(struct list_head *head); #define PROF_ADD 1 diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 046edecc4c8a..0314faeacccd 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -944,6 +944,7 @@ free: /** * aa_remove_profiles - remove profile(s) from the system + * @view: namespace the remove is being done from * @fqname: name of the profile or namespace to remove (NOT NULL) * @size: size of the name * @@ -954,9 +955,9 @@ free: * * Returns: size of data consume else error code if fails */ -ssize_t aa_remove_profiles(char *fqname, size_t size) +ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) { - struct aa_ns *root, *ns = NULL; + struct aa_ns *root = NULL, *ns = NULL; struct aa_profile *profile = NULL; const char *name = fqname, *info = NULL; ssize_t error = 0; @@ -967,7 +968,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) goto fail; } - root = aa_current_profile()->ns; + root = view; if (fqname[0] == ':') { char *ns_name; -- cgit v1.2.3 From 3e3e569539864d5812ecb03792dc183ebbf81476 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:48 -0800 Subject: apparmor: allow introspecting the policy namespace name Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 14b96a44a3f5..9fd7f73a4e86 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -356,6 +356,7 @@ static const struct file_operations aa_fs_seq_hash_fops = { .release = single_release, }; + static int aa_fs_seq_show_ns_level(struct seq_file *seq, void *v) { struct aa_ns *ns = aa_current_profile()->ns; @@ -378,6 +379,28 @@ static const struct file_operations aa_fs_ns_level = { .release = single_release, }; +static int aa_fs_seq_show_ns_name(struct seq_file *seq, void *v) +{ + struct aa_ns *ns = aa_current_profile()->ns; + + seq_printf(seq, "%s\n", ns->base.name); + + return 0; +} + +static int aa_fs_seq_open_ns_name(struct inode *inode, struct file *file) +{ + return single_open(file, aa_fs_seq_show_ns_name, inode->i_private); +} + +static const struct file_operations aa_fs_ns_name = { + .owner = THIS_MODULE, + .open = aa_fs_seq_open_ns_name, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /** fns to setup dynamic per profile/namespace files **/ void __aa_fs_profile_rmdir(struct aa_profile *profile) { @@ -852,6 +875,7 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level), + AA_FS_FILE_FOPS(".ns_name", 0640, &aa_fs_ns_name), AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), AA_FS_DIR("features", aa_fs_entry_features), { } -- cgit v1.2.3 From a6f233003b1af70132619bca386dfae1862a45e8 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:49 -0800 Subject: apparmor: allow specifying the profile doing the management Signed-off-by: John Johansen --- security/apparmor/policy.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 0314faeacccd..0125de6061fd 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -582,6 +582,7 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace, /** * aa_audit_policy - Do auditing of policy changes + * @profile: profile to check if it can manage policy * @op: policy operation being performed * @gfp: memory allocation flags * @name: name of profile being manipulated (NOT NULL) @@ -590,8 +591,8 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace, * * Returns: the error to be returned after audit is done */ -static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, - int error) +static int audit_policy(struct aa_profile *profile, int op, gfp_t gfp, + const char *name, const char *info, int error) { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; @@ -602,7 +603,7 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, aad.info = info; aad.error = error; - return aa_audit(AUDIT_APPARMOR_STATUS, __aa_current_profile(), gfp, + return aa_audit(AUDIT_APPARMOR_STATUS, profile, gfp, &sa, NULL); } @@ -632,12 +633,14 @@ bool aa_may_manage_policy(int op) { /* check if loading policy is locked out */ if (aa_g_lock_policy) { - audit_policy(op, GFP_KERNEL, NULL, "policy_locked", -EACCES); + audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, + "policy_locked", -EACCES); return 0; } if (!policy_admin_capable()) { - audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); + audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, + "not policy admin", -EACCES); return 0; } @@ -762,6 +765,7 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, /** * aa_replace_profiles - replace profile(s) on the profile list * @view: namespace load is viewed from + * @profile: profile that is attempting to load/replace policy * @udata: serialized data stream (NOT NULL) * @size: size of the serialized data stream * @noreplace: true if only doing addition, no replacement allowed @@ -790,7 +794,8 @@ ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, /* released below */ ns = aa_prepare_ns(view, ns_name); if (!ns) { - error = audit_policy(op, GFP_KERNEL, ns_name, + error = audit_policy(__aa_current_profile(), op, GFP_KERNEL, + ns_name, "failed to prepare namespace", -ENOMEM); goto free; } @@ -867,7 +872,8 @@ ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error); + audit_policy(__aa_current_profile(), op, GFP_ATOMIC, + ent->new->base.hname, NULL, error); if (ent->old) { __replace_profile(ent->old, ent->new, 1); @@ -921,7 +927,8 @@ fail_lock: /* audit cause of failure */ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(op, GFP_KERNEL, ent->new->base.hname, info, error); + audit_policy(__aa_current_profile(), op, GFP_KERNEL, + ent->new->base.hname, info, error); /* audit status that rest of profiles in the atomic set failed too */ info = "valid profile in failed atomic policy load"; list_for_each_entry(tmp, &lh, list) { @@ -931,7 +938,8 @@ fail_lock: continue; } op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(op, GFP_KERNEL, tmp->new->base.hname, info, error); + audit_policy(__aa_current_profile(), op, GFP_KERNEL, + tmp->new->base.hname, info, error); } free: list_for_each_entry_safe(ent, tmp, &lh, list) { @@ -1004,7 +1012,8 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) } /* don't fail removal if audit fails */ - (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); + (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, + name, info, error); aa_put_ns(ns); aa_put_profile(profile); return size; @@ -1014,6 +1023,7 @@ fail_ns_lock: aa_put_ns(ns); fail: - (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); + (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, + name, info, error); return error; } -- cgit v1.2.3 From 2bd8dbbf22fe9eb2a99273436f815d49ceb23a8f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:50 -0800 Subject: apparmor: add ns being viewed as a param to policy_view_capable() Prepare for a tighter pairing of user namespaces and apparmor policy namespaces, by making the ns to be viewed available and checking that the user namespace level is the same as the policy ns level. This strict pairing will be relaxed once true support of user namespaces lands. Signed-off-by: John Johansen --- security/apparmor/include/context.h | 6 ++++++ security/apparmor/include/policy.h | 4 +++- security/apparmor/lsm.c | 8 ++++---- security/apparmor/policy.c | 25 ++++++++++++++++++++++--- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index a0acc2390fae..d378bff47ccd 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -20,6 +20,7 @@ #include #include "policy.h" +#include "policy_ns.h" #define cred_cxt(X) (X)->security #define current_cxt() cred_cxt(current_cred()) @@ -162,6 +163,11 @@ static inline struct aa_profile *aa_current_profile(void) return cxt->profile; } +static inline struct aa_ns *aa_get_current_ns(void) +{ + return aa_get_ns(__aa_current_profile()->ns); +} + /** * aa_clear_task_cxt_trans - clear transition tracking info from the cxt * @cxt: task context to clear (NOT NULL) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 8fcfb3c78d21..b0b65c525bcc 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -33,6 +33,8 @@ struct aa_ns; +extern int unprivileged_userns_apparmor_policy; + extern const char *const aa_profile_mode_names[]; #define APPARMOR_MODE_NAMES_MAX_INDEX 4 @@ -297,7 +299,7 @@ static inline int AUDIT_MODE(struct aa_profile *profile) return profile->audit; } -bool policy_view_capable(void); +bool policy_view_capable(struct aa_ns *ns); bool policy_admin_capable(void); bool aa_may_manage_policy(int op); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f852cd626f2e..f83ba33651a0 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -745,7 +745,7 @@ 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) { - if (!policy_view_capable()) + if (!policy_view_capable(NULL)) return -EPERM; return param_get_bool(buffer, kp); } @@ -759,7 +759,7 @@ static int param_set_aabool(const char *val, const struct kernel_param *kp) static int param_get_aabool(char *buffer, const struct kernel_param *kp) { - if (!policy_view_capable()) + if (!policy_view_capable(NULL)) return -EPERM; return param_get_bool(buffer, kp); } @@ -773,14 +773,14 @@ static int param_set_aauint(const char *val, const struct kernel_param *kp) static int param_get_aauint(char *buffer, const struct kernel_param *kp) { - if (!policy_view_capable()) + if (!policy_view_capable(NULL)) return -EPERM; return param_get_uint(buffer, kp); } static int param_get_audit(char *buffer, struct kernel_param *kp) { - if (!policy_view_capable()) + if (!policy_view_capable(NULL)) return -EPERM; if (!apparmor_enabled) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 0125de6061fd..f092c04c7c4e 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -76,6 +76,7 @@ #include #include #include +#include #include "include/apparmor.h" #include "include/capability.h" @@ -89,6 +90,7 @@ #include "include/policy_unpack.h" #include "include/resource.h" +int unprivileged_userns_apparmor_policy = 1; const char *const aa_profile_mode_names[] = { "enforce", @@ -607,20 +609,37 @@ static int audit_policy(struct aa_profile *profile, int op, gfp_t gfp, &sa, NULL); } -bool policy_view_capable(void) +/** + * policy_view_capable - check if viewing policy in at @ns is allowed + * ns: namespace being viewed by current task (may be NULL) + * Returns: true if viewing policy is allowed + * + * If @ns is NULL then the namespace being viewed is assumed to be the + * tasks current namespace. + */ +bool policy_view_capable(struct aa_ns *ns) { struct user_namespace *user_ns = current_user_ns(); + struct aa_ns *view_ns = aa_get_current_ns(); + bool root_in_user_ns = uid_eq(current_euid(), make_kuid(user_ns, 0)) || + in_egroup_p(make_kgid(user_ns, 0)); bool response = false; + if (!ns) + ns = view_ns; - if (ns_capable(user_ns, CAP_MAC_ADMIN)) + if (root_in_user_ns && aa_ns_visible(view_ns, ns, true) && + (user_ns == &init_user_ns || + (unprivileged_userns_apparmor_policy != 0 && + user_ns->level == view_ns->level))) response = true; + aa_put_ns(view_ns); return response; } bool policy_admin_capable(void) { - return policy_view_capable() && !aa_g_lock_policy; + return policy_view_capable(NULL) && !aa_g_lock_policy; } /** -- cgit v1.2.3 From fd2a80438d736012129977bec779db093979057e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:51 -0800 Subject: apparmor: add ns being viewed as a param to policy_admin_capable() Prepare for a tighter pairing of user namespaces and apparmor policy namespaces, by making the ns to be viewed available. Signed-off-by: John Johansen --- security/apparmor/include/policy.h | 2 +- security/apparmor/lsm.c | 12 ++++++------ security/apparmor/policy.c | 12 +++++++++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index b0b65c525bcc..27f9171fa31f 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -300,7 +300,7 @@ static inline int AUDIT_MODE(struct aa_profile *profile) } bool policy_view_capable(struct aa_ns *ns); -bool policy_admin_capable(void); +bool policy_admin_capable(struct aa_ns *ns); bool aa_may_manage_policy(int op); #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f83ba33651a0..09f1c407c4d8 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -738,7 +738,7 @@ __setup("apparmor=", apparmor_enabled_setup); /* set global flag turning off the ability to load policy */ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp) { - if (!policy_admin_capable()) + if (!policy_admin_capable(NULL)) return -EPERM; return param_set_bool(val, kp); } @@ -752,7 +752,7 @@ static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) static int param_set_aabool(const char *val, const struct kernel_param *kp) { - if (!policy_admin_capable()) + if (!policy_admin_capable(NULL)) return -EPERM; return param_set_bool(val, kp); } @@ -766,7 +766,7 @@ static int param_get_aabool(char *buffer, const struct kernel_param *kp) static int param_set_aauint(const char *val, const struct kernel_param *kp) { - if (!policy_admin_capable()) + if (!policy_admin_capable(NULL)) return -EPERM; return param_set_uint(val, kp); } @@ -792,7 +792,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp) static int param_set_audit(const char *val, struct kernel_param *kp) { int i; - if (!policy_admin_capable()) + if (!policy_admin_capable(NULL)) return -EPERM; if (!apparmor_enabled) @@ -813,7 +813,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp) static int param_get_mode(char *buffer, struct kernel_param *kp) { - if (!policy_admin_capable()) + if (!policy_view_capable(NULL)) return -EPERM; if (!apparmor_enabled) @@ -825,7 +825,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) static int param_set_mode(const char *val, struct kernel_param *kp) { int i; - if (!policy_admin_capable()) + if (!policy_admin_capable(NULL)) return -EPERM; if (!apparmor_enabled) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index f092c04c7c4e..ef64c25b2a45 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -637,9 +637,15 @@ bool policy_view_capable(struct aa_ns *ns) return response; } -bool policy_admin_capable(void) +bool policy_admin_capable(struct aa_ns *ns) { - return policy_view_capable(NULL) && !aa_g_lock_policy; + struct user_namespace *user_ns = current_user_ns(); + bool capable = ns_capable(user_ns, CAP_MAC_ADMIN); + + AA_DEBUG("cap_mac_admin? %d\n", capable); + AA_DEBUG("policy locked? %d\n", aa_g_lock_policy); + + return policy_view_capable(ns) && capable && !aa_g_lock_policy; } /** @@ -657,7 +663,7 @@ bool aa_may_manage_policy(int op) return 0; } - if (!policy_admin_capable()) { + if (!policy_admin_capable(NULL)) { audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, "not policy admin", -EACCES); return 0; -- cgit v1.2.3 From 078c73c63fb2878689da334f112507639c72c14f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:52 -0800 Subject: apparmor: add profile and ns params to aa_may_manage_policy() Policy management will be expanded beyond traditional unconfined root. This will require knowning the profile of the task doing the management and the ns view. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 2 +- security/apparmor/include/policy.h | 2 +- security/apparmor/policy.c | 22 ++++++++++------------ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 9fd7f73a4e86..cc6ee1ee2b42 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -100,7 +100,7 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, * Don't allow profile load/replace/remove from profiles that don't * have CAP_MAC_ADMIN */ - if (!aa_may_manage_policy(op)) + if (!aa_may_manage_policy(__aa_current_profile(), NULL, op)) return ERR_PTR(-EACCES); /* freed by caller to simple_write_to_buffer */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 27f9171fa31f..95641e235d47 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -301,6 +301,6 @@ static inline int AUDIT_MODE(struct aa_profile *profile) bool policy_view_capable(struct aa_ns *ns); bool policy_admin_capable(struct aa_ns *ns); -bool aa_may_manage_policy(int op); +int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, int op); #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index ef64c25b2a45..27d93aa58016 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -650,26 +650,24 @@ bool policy_admin_capable(struct aa_ns *ns) /** * aa_may_manage_policy - can the current task manage policy + * @profile: profile to check if it can manage policy * @op: the policy manipulation operation being done * - * Returns: true if the task is allowed to manipulate policy + * Returns: 0 if the task is allowed to manipulate policy else error */ -bool aa_may_manage_policy(int op) +int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, int op) { /* check if loading policy is locked out */ - if (aa_g_lock_policy) { - audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, + if (aa_g_lock_policy) + return audit_policy(profile, op, GFP_KERNEL, NULL, "policy_locked", -EACCES); - return 0; - } - if (!policy_admin_capable(NULL)) { - audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, - "not policy admin", -EACCES); - return 0; - } + if (!policy_admin_capable(ns)) + return audit_policy(profile, op, GFP_KERNEL, NULL, + "not policy admin", -EACCES); - return 1; + /* TODO: add fine grained mediation of policy loads */ + return 0; } static struct aa_profile *__list_lookup_parent(struct list_head *lh, -- cgit v1.2.3 From fc1c9fd10a53a17abb3348adb2ec5d29813a0397 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:54 -0800 Subject: apparmor: add ns name to the audit data for policy loads Signed-off-by: John Johansen --- security/apparmor/include/audit.h | 1 + security/apparmor/policy.c | 34 ++++++++++++++++++++++++---------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index ba3dfd17f23f..dbfb4a6d72b6 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -113,6 +113,7 @@ struct apparmor_audit_data { void *target; struct { long pos; + const char *ns; void *target; } iface; struct { diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 27d93aa58016..3c5c0b28eac5 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -582,11 +582,23 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace, return 0; } +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->iface.ns) { + audit_log_format(ab, " ns="); + audit_log_untrustedstring(ab, sa->aad->iface.ns); + } +} + /** * aa_audit_policy - Do auditing of policy changes * @profile: profile to check if it can manage policy * @op: policy operation being performed * @gfp: memory allocation flags + * @nsname: name of the ns being manipulated (MAY BE NULL) * @name: name of profile being manipulated (NOT NULL) * @info: any extra information to be audited (MAYBE NULL) * @error: error code @@ -594,19 +606,21 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace, * Returns: the error to be returned after audit is done */ static int audit_policy(struct aa_profile *profile, int op, gfp_t gfp, - const char *name, const char *info, int error) + const char *nsname, const char *name, + const char *info, int error) { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.op = op; + aad.iface.ns = nsname; aad.name = name; aad.info = info; aad.error = error; return aa_audit(AUDIT_APPARMOR_STATUS, profile, gfp, - &sa, NULL); + &sa, audit_cb); } /** @@ -659,11 +673,11 @@ int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, int op) { /* check if loading policy is locked out */ if (aa_g_lock_policy) - return audit_policy(profile, op, GFP_KERNEL, NULL, + return audit_policy(profile, op, GFP_KERNEL, NULL, NULL, "policy_locked", -EACCES); if (!policy_admin_capable(ns)) - return audit_policy(profile, op, GFP_KERNEL, NULL, + return audit_policy(profile, op, GFP_KERNEL, NULL, NULL, "not policy admin", -EACCES); /* TODO: add fine grained mediation of policy loads */ @@ -818,7 +832,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, ns = aa_prepare_ns(view, ns_name); if (!ns) { error = audit_policy(__aa_current_profile(), op, GFP_KERNEL, - ns_name, + NULL, ns_name, "failed to prepare namespace", -ENOMEM); goto free; } @@ -895,7 +909,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_ATOMIC, + audit_policy(__aa_current_profile(), op, GFP_ATOMIC, NULL, ent->new->base.hname, NULL, error); if (ent->old) { @@ -950,7 +964,7 @@ fail_lock: /* audit cause of failure */ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_KERNEL, + audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, ent->new->base.hname, info, error); /* audit status that rest of profiles in the atomic set failed too */ info = "valid profile in failed atomic policy load"; @@ -961,7 +975,7 @@ fail_lock: continue; } op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_KERNEL, + audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, tmp->new->base.hname, info, error); } free: @@ -1036,7 +1050,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) /* don't fail removal if audit fails */ (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, - name, info, error); + NULL, name, info, error); aa_put_ns(ns); aa_put_profile(profile); return size; @@ -1047,6 +1061,6 @@ fail_ns_lock: fail: (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, - name, info, error); + NULL, name, info, error); return error; } -- cgit v1.2.3 From 5ac8c355ae0013d82b3a07b49aebeadfce9b6e52 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:55 -0800 Subject: apparmor: allow introspecting the loaded policy pre internal transform Store loaded policy and allow introspecting it through apparmorfs. This has several uses from debugging, policy validation, and policy checkpoint and restore for containers. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 213 ++++++++++++++++++++++++------ security/apparmor/crypto.c | 39 +++++- security/apparmor/include/apparmorfs.h | 5 + security/apparmor/include/crypto.h | 5 + security/apparmor/include/policy.h | 5 +- security/apparmor/include/policy_unpack.h | 27 +++- security/apparmor/policy.c | 14 +- security/apparmor/policy_unpack.c | 28 +++- 8 files changed, 278 insertions(+), 58 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index cc6ee1ee2b42..2e6790cf54da 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -33,6 +33,7 @@ #include "include/policy.h" #include "include/policy_ns.h" #include "include/resource.h" +#include "include/policy_unpack.h" /** * aa_mangle_name - mangle a profile name to std profile layout form @@ -84,11 +85,13 @@ static int mangle_name(const char *name, char *target) * Returns: kernel buffer containing copy of user buffer data or an * ERR_PTR on failure. */ -static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, - size_t alloc_size, size_t copy_size, - loff_t *pos) +static struct aa_loaddata *aa_simple_write_to_buffer(int op, + const char __user *userbuf, + size_t alloc_size, + size_t copy_size, + loff_t *pos) { - char *data; + struct aa_loaddata *data; BUG_ON(copy_size > alloc_size); @@ -96,19 +99,16 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, /* only writes from pos 0, that is complete writes */ return ERR_PTR(-ESPIPE); - /* - * Don't allow profile load/replace/remove from profiles that don't - * have CAP_MAC_ADMIN - */ - if (!aa_may_manage_policy(__aa_current_profile(), NULL, op)) - return ERR_PTR(-EACCES); - /* freed by caller to simple_write_to_buffer */ - data = kvmalloc(alloc_size); + data = kvmalloc(sizeof(*data) + alloc_size); if (data == NULL) return ERR_PTR(-ENOMEM); + kref_init(&data->count); + data->size = copy_size; + data->hash = NULL; + data->abi = 0; - if (copy_from_user(data, userbuf, copy_size)) { + if (copy_from_user(data->data, userbuf, copy_size)) { kvfree(data); return ERR_PTR(-EFAULT); } @@ -116,26 +116,38 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, return data; } - -/* .load file hook fn to load policy */ -static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, - loff_t *pos) +static ssize_t policy_update(int binop, const char __user *buf, size_t size, + loff_t *pos) { - char *data; ssize_t error; + struct aa_loaddata *data; + struct aa_profile *profile = aa_current_profile(); + int op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL; + /* high level check about policy management - fine grained in + * below after unpack + */ + error = aa_may_manage_policy(profile, profile->ns, op); + if (error) + return error; - data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos); - + data = aa_simple_write_to_buffer(op, buf, size, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { - error = aa_replace_profiles(__aa_current_profile()->ns, data, - size, PROF_ADD); - kvfree(data); + error = aa_replace_profiles(profile->ns, binop, data); + aa_put_loaddata(data); } return error; } +static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, + loff_t *pos) +{ + int error = policy_update(PROF_ADD, buf, size, pos); + + return error; +} + static const struct file_operations aa_fs_profile_load = { .write = profile_load, .llseek = default_llseek, @@ -145,16 +157,7 @@ static const struct file_operations aa_fs_profile_load = { static ssize_t profile_replace(struct file *f, const char __user *buf, size_t size, loff_t *pos) { - char *data; - ssize_t error; - - data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos); - error = PTR_ERR(data); - if (!IS_ERR(data)) { - error = aa_replace_profiles(__aa_current_profile()->ns, data, - size, PROF_REPLACE); - kvfree(data); - } + int error = policy_update(PROF_REPLACE, buf, size, pos); return error; } @@ -164,27 +167,35 @@ static const struct file_operations aa_fs_profile_replace = { .llseek = default_llseek, }; -/* .remove file hook fn to remove loaded policy */ static ssize_t profile_remove(struct file *f, const char __user *buf, size_t size, loff_t *pos) { - char *data; + struct aa_loaddata *data; + struct aa_profile *profile; ssize_t error; + profile = aa_current_profile(); + /* high level check about policy management - fine grained in + * below after unpack + */ + error = aa_may_manage_policy(profile, profile->ns, OP_PROF_RM); + if (error) + goto out; + /* * aa_remove_profile needs a null terminated string so 1 extra * byte is allocated and the copied data is null terminated. */ - data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos); + data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, + pos); error = PTR_ERR(data); if (!IS_ERR(data)) { - data[size] = 0; - error = aa_remove_profiles(__aa_current_profile()->ns, data, - size); - kvfree(data); + data->data[size] = 0; + error = aa_remove_profiles(profile->ns, data->data, size); + aa_put_loaddata(data); } - + out: return error; } @@ -401,6 +412,100 @@ static const struct file_operations aa_fs_ns_name = { .release = single_release, }; +static int rawdata_release(struct inode *inode, struct file *file) +{ + /* TODO: switch to loaddata when profile switched to symlink */ + aa_put_loaddata(file->private_data); + + return 0; +} + +static int aa_fs_seq_raw_abi_show(struct seq_file *seq, void *v) +{ + struct aa_proxy *proxy = seq->private; + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); + + if (profile->rawdata->abi) { + seq_printf(seq, "v%d", profile->rawdata->abi); + seq_puts(seq, "\n"); + } + aa_put_profile(profile); + + return 0; +} + +static int aa_fs_seq_raw_abi_open(struct inode *inode, struct file *file) +{ + return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_abi_show); +} + +static const struct file_operations aa_fs_seq_raw_abi_fops = { + .owner = THIS_MODULE, + .open = aa_fs_seq_raw_abi_open, + .read = seq_read, + .llseek = seq_lseek, + .release = aa_fs_seq_profile_release, +}; + +static int aa_fs_seq_raw_hash_show(struct seq_file *seq, void *v) +{ + struct aa_proxy *proxy = seq->private; + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); + unsigned int i, size = aa_hash_size(); + + if (profile->rawdata->hash) { + for (i = 0; i < size; i++) + seq_printf(seq, "%.2x", profile->rawdata->hash[i]); + seq_puts(seq, "\n"); + } + aa_put_profile(profile); + + return 0; +} + +static int aa_fs_seq_raw_hash_open(struct inode *inode, struct file *file) +{ + return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_hash_show); +} + +static const struct file_operations aa_fs_seq_raw_hash_fops = { + .owner = THIS_MODULE, + .open = aa_fs_seq_raw_hash_open, + .read = seq_read, + .llseek = seq_lseek, + .release = aa_fs_seq_profile_release, +}; + +static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size, + loff_t *ppos) +{ + struct aa_loaddata *rawdata = file->private_data; + + return simple_read_from_buffer(buf, size, ppos, rawdata->data, + rawdata->size); +} + +static int rawdata_open(struct inode *inode, struct file *file) +{ + struct aa_proxy *proxy = inode->i_private; + struct aa_profile *profile; + + if (!policy_view_capable(NULL)) + return -EACCES; + profile = aa_get_profile_rcu(&proxy->profile); + file->private_data = aa_get_loaddata(profile->rawdata); + aa_put_profile(profile); + + return 0; +} + +static const struct file_operations aa_fs_rawdata_fops = { + .open = rawdata_open, + .read = rawdata_read, + .llseek = generic_file_llseek, + .release = rawdata_release, +}; + /** fns to setup dynamic per profile/namespace files **/ void __aa_fs_profile_rmdir(struct aa_profile *profile) { @@ -512,6 +617,29 @@ int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) profile->dents[AAFS_PROF_HASH] = dent; } + if (profile->rawdata) { + dent = create_profile_file(dir, "raw_sha1", profile, + &aa_fs_seq_raw_hash_fops); + if (IS_ERR(dent)) + goto fail; + profile->dents[AAFS_PROF_RAW_HASH] = dent; + + dent = create_profile_file(dir, "raw_abi", profile, + &aa_fs_seq_raw_abi_fops); + if (IS_ERR(dent)) + goto fail; + profile->dents[AAFS_PROF_RAW_ABI] = dent; + + dent = securityfs_create_file("raw_data", S_IFREG | 0444, dir, + profile->proxy, + &aa_fs_rawdata_fops); + if (IS_ERR(dent)) + goto fail; + profile->dents[AAFS_PROF_RAW_DATA] = dent; + d_inode(dent)->i_size = profile->rawdata->size; + aa_get_proxy(profile->proxy); + } + list_for_each_entry(child, &profile->base.profiles, base.list) { error = __aa_fs_profile_mkdir(child, prof_child_dir(profile)); if (error) @@ -817,6 +945,9 @@ static const struct seq_operations aa_fs_profiles_op = { static int profiles_open(struct inode *inode, struct file *file) { + if (!policy_view_capable(NULL)) + return -EACCES; + return seq_open(file, &aa_fs_profiles_op); } diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c index b75dab0df1cb..de8dc78b6144 100644 --- a/security/apparmor/crypto.c +++ b/security/apparmor/crypto.c @@ -29,6 +29,43 @@ unsigned int aa_hash_size(void) return apparmor_hash_size; } +char *aa_calc_hash(void *data, size_t len) +{ + struct { + struct shash_desc shash; + char ctx[crypto_shash_descsize(apparmor_tfm)]; + } desc; + char *hash = NULL; + int error = -ENOMEM; + + if (!apparmor_tfm) + return NULL; + + hash = kzalloc(apparmor_hash_size, GFP_KERNEL); + if (!hash) + goto fail; + + desc.shash.tfm = apparmor_tfm; + desc.shash.flags = 0; + + error = crypto_shash_init(&desc.shash); + if (error) + goto fail; + error = crypto_shash_update(&desc.shash, (u8 *) data, len); + if (error) + goto fail; + error = crypto_shash_final(&desc.shash, hash); + if (error) + goto fail; + + return hash; + +fail: + kfree(hash); + + return ERR_PTR(error); +} + int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, size_t len) { @@ -37,7 +74,7 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, char ctx[crypto_shash_descsize(apparmor_tfm)]; } desc; int error = -ENOMEM; - u32 le32_version = cpu_to_le32(version); + __le32 le32_version = cpu_to_le32(version); if (!aa_g_hash_policy) return 0; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index eeeae5b0cc36..a593e75b3b03 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -70,6 +70,7 @@ enum aafs_ns_type { AAFS_NS_DIR, AAFS_NS_PROFS, AAFS_NS_NS, + AAFS_NS_RAW_DATA, AAFS_NS_COUNT, AAFS_NS_MAX_COUNT, AAFS_NS_SIZE, @@ -85,12 +86,16 @@ enum aafs_prof_type { AAFS_PROF_MODE, AAFS_PROF_ATTACH, AAFS_PROF_HASH, + AAFS_PROF_RAW_DATA, + AAFS_PROF_RAW_HASH, + AAFS_PROF_RAW_ABI, AAFS_PROF_SIZEOF, }; #define ns_dir(X) ((X)->dents[AAFS_NS_DIR]) #define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS]) #define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS]) +#define ns_subdata_dir(X) ((X)->dents[AAFS_NS_RAW_DATA]) #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) diff --git a/security/apparmor/include/crypto.h b/security/apparmor/include/crypto.h index dc418e5024d9..c1469f8db174 100644 --- a/security/apparmor/include/crypto.h +++ b/security/apparmor/include/crypto.h @@ -18,9 +18,14 @@ #ifdef CONFIG_SECURITY_APPARMOR_HASH unsigned int aa_hash_size(void); +char *aa_calc_hash(void *data, size_t len); int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, size_t len); #else +static inline char *aa_calc_hash(void *data, size_t len) +{ + return NULL; +} static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, size_t len) { diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 95641e235d47..fbbc8677f527 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -161,6 +161,7 @@ struct aa_profile { struct aa_caps caps; struct aa_rlimit rlimits; + struct aa_loaddata *rawdata; unsigned char *hash; char *dirname; struct dentry *dents[AAFS_PROF_SIZEOF]; @@ -187,8 +188,8 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base, const char *fqname, size_t n); struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); -ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, - bool noreplace); +ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, + struct aa_loaddata *udata); ssize_t aa_remove_profiles(struct aa_ns *view, char *name, size_t size); void __aa_profile_list_release(struct list_head *head); diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index c214fb88b1bc..7b675b6f7f02 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -16,6 +16,7 @@ #define __POLICY_INTERFACE_H #include +#include struct aa_load_ent { struct list_head list; @@ -34,6 +35,30 @@ struct aa_load_ent *aa_load_ent_alloc(void); #define PACKED_MODE_KILL 2 #define PACKED_MODE_UNCONFINED 3 -int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns); +/* struct aa_loaddata - buffer of policy load data set */ +struct aa_loaddata { + struct kref count; + size_t size; + int abi; + unsigned char *hash; + char data[]; +}; + +int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns); + +static inline struct aa_loaddata * +aa_get_loaddata(struct aa_loaddata *data) +{ + if (data) + kref_get(&(data->count)); + return data; +} + +void aa_loaddata_kref(struct kref *kref); +static inline void aa_put_loaddata(struct aa_loaddata *data) +{ + if (data) + kref_put(&data->count, aa_loaddata_kref); +} #endif /* __POLICY_INTERFACE_H */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 3c5c0b28eac5..ff29b606f2b3 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -228,6 +228,7 @@ void aa_free_profile(struct aa_profile *profile) aa_put_proxy(profile->proxy); kzfree(profile->hash); + aa_put_loaddata(profile->rawdata); kzfree(profile); } @@ -802,10 +803,8 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, /** * aa_replace_profiles - replace profile(s) on the profile list * @view: namespace load is viewed from - * @profile: profile that is attempting to load/replace policy - * @udata: serialized data stream (NOT NULL) - * @size: size of the serialized data stream * @noreplace: true if only doing addition, no replacement allowed + * @udata: serialized data stream (NOT NULL) * * unpack and replace a profile on the profile list and uses of that profile * by any aa_task_cxt. If the profile does not exist on the profile list @@ -813,8 +812,8 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, * * Returns: size of data consumed else error code on failure. */ -ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, - bool noreplace) +ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, + struct aa_loaddata *udata) { const char *ns_name, *info = NULL; struct aa_ns *ns = NULL; @@ -824,7 +823,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, LIST_HEAD(lh); /* released below */ - error = aa_unpack(udata, size, &lh, &ns_name); + error = aa_unpack(udata, &lh, &ns_name); if (error) goto out; @@ -841,6 +840,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, /* setup parent and ns info */ list_for_each_entry(ent, &lh, list) { struct aa_policy *policy; + ent->new->rawdata = aa_get_loaddata(udata); error = __lookup_replace(ns, ent->new->base.hname, noreplace, &ent->old, &info); if (error) @@ -957,7 +957,7 @@ out: if (error) return error; - return size; + return udata->size; fail_lock: mutex_unlock(&ns->lock); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 51a7f9fc8a3e..fb4ef84b88e1 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -117,6 +117,16 @@ static int audit_iface(struct aa_profile *new, const char *name, audit_cb); } +void aa_loaddata_kref(struct kref *kref) +{ + struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count); + + if (d) { + kzfree(d->hash); + kvfree(d); + } +} + /* test if read will be in packed data bounds */ static bool inbounds(struct aa_ext *e, size_t size) { @@ -749,7 +759,6 @@ struct aa_load_ent *aa_load_ent_alloc(void) /** * aa_unpack - unpack packed binary profile(s) data loaded from user space * @udata: user data copied to kmem (NOT NULL) - * @size: the size of the user data * @lh: list to place unpacked profiles in a aa_repl_ws * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) * @@ -759,15 +768,16 @@ struct aa_load_ent *aa_load_ent_alloc(void) * * Returns: profile(s) on @lh else error pointer if fails to unpack */ -int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) +int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, + const char **ns) { struct aa_load_ent *tmp, *ent; struct aa_profile *profile = NULL; int error; struct aa_ext e = { - .start = udata, - .end = udata + size, - .pos = udata, + .start = udata->data, + .end = udata->data + udata->size, + .pos = udata->data, }; *ns = NULL; @@ -802,7 +812,13 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) ent->new = profile; list_add_tail(&ent->list, lh); } - + udata->abi = e.version & K_ABI_MASK; + udata->hash = aa_calc_hash(udata->data, udata->size); + if (IS_ERR(udata->hash)) { + error = PTR_ERR(udata->hash); + udata->hash = NULL; + goto fail; + } return 0; fail_profile: -- cgit v1.2.3 From 04dc715e24d0820bf8740e1a1135ed61fe162bc8 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:56 -0800 Subject: apparmor: audit policy ns specified in policy load Verify that profiles in a load set specify the same policy ns and audit the name of the policy ns that policy is being loaded for. Signed-off-by: John Johansen --- security/apparmor/include/policy_unpack.h | 1 + security/apparmor/policy.c | 54 +++++++++++++++++++++++-------- security/apparmor/policy_unpack.c | 46 ++++++++++++++++++++------ 3 files changed, 77 insertions(+), 24 deletions(-) diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index 7b675b6f7f02..4c1319eebc42 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -23,6 +23,7 @@ struct aa_load_ent { struct aa_profile *new; struct aa_profile *old; struct aa_profile *rename; + const char *ns_name; }; void aa_load_ent_free(struct aa_load_ent *ent); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index ff29b606f2b3..eb1ccd171789 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -819,7 +819,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, struct aa_ns *ns = NULL; struct aa_load_ent *ent, *tmp; int op = OP_PROF_REPL; - ssize_t error; + ssize_t count, error; LIST_HEAD(lh); /* released below */ @@ -827,14 +827,40 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, if (error) goto out; - /* released below */ - ns = aa_prepare_ns(view, ns_name); - if (!ns) { - error = audit_policy(__aa_current_profile(), op, GFP_KERNEL, - NULL, ns_name, - "failed to prepare namespace", -ENOMEM); - goto free; + /* ensure that profiles are all for the same ns + * TODO: update locking to remove this constaint. All profiles in + * the load set must succeed as a set or the load will + * fail. Sort ent list and take ns locks in hierarchy order + */ + count = 0; + list_for_each_entry(ent, &lh, list) { + if (ns_name) { + if (ent->ns_name && + strcmp(ent->ns_name, ns_name) != 0) { + info = "policy load has mixed namespaces"; + error = -EACCES; + goto fail; + } + } else if (ent->ns_name) { + if (count) { + info = "policy load has mixed namespaces"; + error = -EACCES; + goto fail; + } + ns_name = ent->ns_name; + } else + count++; } + if (ns_name) { + ns = aa_prepare_ns(view, ns_name); + if (IS_ERR(ns)) { + info = "failed to prepare namespace"; + error = PTR_ERR(ns); + ns = NULL; + goto fail; + } + } else + ns = aa_get_ns(view); mutex_lock(&ns->lock); /* setup parent and ns info */ @@ -964,7 +990,8 @@ fail_lock: /* audit cause of failure */ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, +fail: + audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name, ent->new->base.hname, info, error); /* audit status that rest of profiles in the atomic set failed too */ info = "valid profile in failed atomic policy load"; @@ -975,10 +1002,9 @@ fail_lock: continue; } op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, + audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name, tmp->new->base.hname, info, error); } -free: list_for_each_entry_safe(ent, tmp, &lh, list) { list_del_init(&ent->list); aa_load_ent_free(ent); @@ -1005,6 +1031,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) struct aa_ns *root = NULL, *ns = NULL; struct aa_profile *profile = NULL; const char *name = fqname, *info = NULL; + char *ns_name = NULL; ssize_t error = 0; if (*fqname == 0) { @@ -1016,7 +1043,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) root = view; if (fqname[0] == ':') { - char *ns_name; name = aa_split_fqname(fqname, &ns_name); /* released below */ ns = aa_find_ns(root, ns_name); @@ -1050,7 +1076,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) /* don't fail removal if audit fails */ (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, - NULL, name, info, error); + ns_name, name, info, error); aa_put_ns(ns); aa_put_profile(profile); return size; @@ -1061,6 +1087,6 @@ fail_ns_lock: fail: (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, - NULL, name, info, error); + ns_name, name, info, error); return error; } diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index fb4ef84b88e1..38c148f33fa4 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -91,6 +91,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) /** * audit_iface - do audit message for policy unpacking/load/replace/remove * @new: profile if it has been allocated (MAYBE NULL) + * @ns_name: name of the ns the profile is to be loaded to (MAY BE NULL) * @name: name of the profile being manipulated (MAYBE NULL) * @info: any extra info about the failure (MAYBE NULL) * @e: buffer position info @@ -98,14 +99,16 @@ static void audit_cb(struct audit_buffer *ab, void *va) * * Returns: %0 or error */ -static int audit_iface(struct aa_profile *new, const char *name, - const char *info, struct aa_ext *e, int error) +static int audit_iface(struct aa_profile *new, const char *ns_name, + const char *name, const char *info, struct aa_ext *e, + int error) { struct aa_profile *profile = __aa_current_profile(); struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; + aad.iface.ns = ns_name; if (e) aad.iface.pos = e->pos - e->start; aad.iface.target = new; @@ -486,19 +489,32 @@ fail: * * NOTE: unpack profile sets audit struct if there is a failure */ -static struct aa_profile *unpack_profile(struct aa_ext *e) +static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) { struct aa_profile *profile = NULL; - const char *name = NULL; + const char *tmpname, *tmpns = NULL, *name = NULL; + size_t ns_len; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; + *ns_name = NULL; + /* check that we have the right struct being passed */ if (!unpack_nameX(e, AA_STRUCT, "profile")) goto fail; if (!unpack_str(e, &name, NULL)) goto fail; + if (*name == '\0') + goto fail; + + tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len); + if (tmpns) { + *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL); + if (!*ns_name) + goto fail; + name = tmpname; + } profile = aa_alloc_profile(name, GFP_KERNEL); if (!profile) @@ -646,7 +662,8 @@ fail: name = NULL; else if (!name) name = "unknown"; - audit_iface(profile, name, "failed to unpack profile", e, error); + audit_iface(profile, NULL, name, "failed to unpack profile", e, + error); aa_free_profile(profile); return ERR_PTR(error); @@ -669,7 +686,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) /* get the interface version */ if (!unpack_u32(e, &e->version, "version")) { if (required) { - audit_iface(NULL, NULL, "invalid profile format", + audit_iface(NULL, NULL, NULL, "invalid profile format", e, error); return error; } @@ -680,15 +697,21 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) * Mask off everything that is not kernel abi version */ if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) { - audit_iface(NULL, NULL, "unsupported interface version", + audit_iface(NULL, NULL, NULL, "unsupported interface version", e, error); return error; } /* read the namespace if present */ if (unpack_str(e, &name, "namespace")) { + if (*name == '\0') { + audit_iface(NULL, NULL, NULL, "invalid namespace name", + e, error); + return error; + } if (*ns && strcmp(*ns, name)) - audit_iface(NULL, NULL, "invalid ns change", e, error); + audit_iface(NULL, NULL, NULL, "invalid ns change", e, + error); else if (!*ns) *ns = name; } @@ -730,7 +753,7 @@ static int verify_profile(struct aa_profile *profile) if (profile->file.dfa && !verify_dfa_xindex(profile->file.dfa, profile->file.trans.size)) { - audit_iface(profile, NULL, "Invalid named transition", + audit_iface(profile, NULL, NULL, "Invalid named transition", NULL, -EPROTO); return -EPROTO; } @@ -744,6 +767,7 @@ void aa_load_ent_free(struct aa_load_ent *ent) aa_put_profile(ent->rename); aa_put_profile(ent->old); aa_put_profile(ent->new); + kfree(ent->ns_name); kzfree(ent); } } @@ -782,13 +806,14 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, *ns = NULL; while (e.pos < e.end) { + char *ns_name = NULL; void *start; error = verify_header(&e, e.pos == e.start, ns); if (error) goto fail; start = e.pos; - profile = unpack_profile(&e); + profile = unpack_profile(&e, &ns_name); if (IS_ERR(profile)) { error = PTR_ERR(profile); goto fail; @@ -810,6 +835,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, } ent->new = profile; + ent->ns_name = ns_name; list_add_tail(&ent->list, lh); } udata->abi = e.version & K_ABI_MASK; -- cgit v1.2.3 From 12dd7171d645a6658326ba234e6d4fc57a73bf98 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:57 -0800 Subject: apparmor: pass the subject profile into profile replace/remove This is just setup for new ns specific .load, .replace, .remove interface files. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 5 +++-- security/apparmor/include/policy.h | 7 ++++--- security/apparmor/policy.c | 25 ++++++++++++++----------- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 2e6790cf54da..d654aacd7db4 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -133,7 +133,7 @@ static ssize_t policy_update(int binop, const char __user *buf, size_t size, data = aa_simple_write_to_buffer(op, buf, size, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { - error = aa_replace_profiles(profile->ns, binop, data); + error = aa_replace_profiles(profile->ns, profile, binop, data); aa_put_loaddata(data); } @@ -192,7 +192,8 @@ static ssize_t profile_remove(struct file *f, const char __user *buf, error = PTR_ERR(data); if (!IS_ERR(data)) { data->data[size] = 0; - error = aa_remove_profiles(profile->ns, data->data, size); + error = aa_remove_profiles(profile->ns, profile, data->data, + size); aa_put_loaddata(data); } out: diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index fbbc8677f527..b315ad5bdd85 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -188,9 +188,10 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base, const char *fqname, size_t n); struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); -ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, - struct aa_loaddata *udata); -ssize_t aa_remove_profiles(struct aa_ns *view, char *name, size_t size); +ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile, + bool noreplace, struct aa_loaddata *udata); +ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *profile, + char *name, size_t size); void __aa_profile_list_release(struct list_head *head); #define PROF_ADD 1 diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index eb1ccd171789..912cdbed7977 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -803,6 +803,7 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, /** * aa_replace_profiles - replace profile(s) on the profile list * @view: namespace load is viewed from + * @label: label that is attempting to load/replace policy * @noreplace: true if only doing addition, no replacement allowed * @udata: serialized data stream (NOT NULL) * @@ -812,8 +813,8 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, * * Returns: size of data consumed else error code on failure. */ -ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, - struct aa_loaddata *udata) +ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile, + bool noreplace, struct aa_loaddata *udata) { const char *ns_name, *info = NULL; struct aa_ns *ns = NULL; @@ -935,7 +936,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_ATOMIC, NULL, + audit_policy(profile, op, GFP_ATOMIC, NULL, ent->new->base.hname, NULL, error); if (ent->old) { @@ -991,8 +992,8 @@ fail_lock: /* audit cause of failure */ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; fail: - audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name, - ent->new->base.hname, info, error); + audit_policy(profile, op, GFP_KERNEL, ns_name, ent->new->base.hname, + info, error); /* audit status that rest of profiles in the atomic set failed too */ info = "valid profile in failed atomic policy load"; list_for_each_entry(tmp, &lh, list) { @@ -1002,7 +1003,7 @@ fail: continue; } op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name, + audit_policy(profile, op, GFP_KERNEL, ns_name, tmp->new->base.hname, info, error); } list_for_each_entry_safe(ent, tmp, &lh, list) { @@ -1016,6 +1017,7 @@ fail: /** * aa_remove_profiles - remove profile(s) from the system * @view: namespace the remove is being done from + * @subj: profile attempting to remove policy * @fqname: name of the profile or namespace to remove (NOT NULL) * @size: size of the name * @@ -1026,7 +1028,8 @@ fail: * * Returns: size of data consume else error code if fails */ -ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) +ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *subj, + char *fqname, size_t size) { struct aa_ns *root = NULL, *ns = NULL; struct aa_profile *profile = NULL; @@ -1075,8 +1078,8 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) } /* don't fail removal if audit fails */ - (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, - ns_name, name, info, error); + (void) audit_policy(subj, OP_PROF_RM, GFP_KERNEL, ns_name, name, info, + error); aa_put_ns(ns); aa_put_profile(profile); return size; @@ -1086,7 +1089,7 @@ fail_ns_lock: aa_put_ns(ns); fail: - (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, - ns_name, name, info, error); + (void) audit_policy(subj, OP_PROF_RM, GFP_KERNEL, ns_name, name, info, + error); return error; } -- cgit v1.2.3 From b7fd2c0340eacbee892425e9007647568b7f2a3c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:58 -0800 Subject: apparmor: add per policy ns .load, .replace, .remove interface files Having per policy ns interface files helps with containers restoring policy. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 146 ++++++++++++++++++++++++++++----- security/apparmor/include/apparmorfs.h | 6 ++ 2 files changed, 130 insertions(+), 22 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index d654aacd7db4..af36af88c4ed 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -117,7 +117,7 @@ static struct aa_loaddata *aa_simple_write_to_buffer(int op, } static ssize_t policy_update(int binop, const char __user *buf, size_t size, - loff_t *pos) + loff_t *pos, struct aa_ns *ns) { ssize_t error; struct aa_loaddata *data; @@ -126,24 +126,29 @@ static ssize_t policy_update(int binop, const char __user *buf, size_t size, /* high level check about policy management - fine grained in * below after unpack */ - error = aa_may_manage_policy(profile, profile->ns, op); + error = aa_may_manage_policy(profile, ns, op); if (error) return error; data = aa_simple_write_to_buffer(op, buf, size, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { - error = aa_replace_profiles(profile->ns, profile, binop, data); + error = aa_replace_profiles(ns ? ns : profile->ns, profile, + binop, data); aa_put_loaddata(data); } return error; } +/* .load file hook fn to load policy */ static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, loff_t *pos) { - int error = policy_update(PROF_ADD, buf, size, pos); + struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); + int error = policy_update(PROF_ADD, buf, size, pos, ns); + + aa_put_ns(ns); return error; } @@ -157,7 +162,10 @@ static const struct file_operations aa_fs_profile_load = { static ssize_t profile_replace(struct file *f, const char __user *buf, size_t size, loff_t *pos) { - int error = policy_update(PROF_REPLACE, buf, size, pos); + struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); + int error = policy_update(PROF_REPLACE, buf, size, pos, ns); + + aa_put_ns(ns); return error; } @@ -167,18 +175,20 @@ static const struct file_operations aa_fs_profile_replace = { .llseek = default_llseek, }; +/* .remove file hook fn to remove loaded policy */ static ssize_t profile_remove(struct file *f, const char __user *buf, size_t size, loff_t *pos) { struct aa_loaddata *data; struct aa_profile *profile; ssize_t error; + struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); profile = aa_current_profile(); /* high level check about policy management - fine grained in * below after unpack */ - error = aa_may_manage_policy(profile, profile->ns, OP_PROF_RM); + error = aa_may_manage_policy(profile, ns, OP_PROF_RM); if (error) goto out; @@ -192,11 +202,12 @@ static ssize_t profile_remove(struct file *f, const char __user *buf, error = PTR_ERR(data); if (!IS_ERR(data)) { data->data[size] = 0; - error = aa_remove_profiles(profile->ns, profile, data->data, - size); + error = aa_remove_profiles(ns ? ns : profile->ns, profile, + data->data, size); aa_put_loaddata(data); } out: + aa_put_ns(ns); return error; } @@ -676,12 +687,77 @@ void __aa_fs_ns_rmdir(struct aa_ns *ns) mutex_unlock(&sub->lock); } + if (ns_subns_dir(ns)) { + sub = d_inode(ns_subns_dir(ns))->i_private; + aa_put_ns(sub); + } + if (ns_subload(ns)) { + sub = d_inode(ns_subload(ns))->i_private; + aa_put_ns(sub); + } + if (ns_subreplace(ns)) { + sub = d_inode(ns_subreplace(ns))->i_private; + aa_put_ns(sub); + } + if (ns_subremove(ns)) { + sub = d_inode(ns_subremove(ns))->i_private; + aa_put_ns(sub); + } + for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) { securityfs_remove(ns->dents[i]); ns->dents[i] = NULL; } } +/* assumes cleanup in caller */ +static int __aa_fs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir) +{ + struct dentry *dent; + + AA_BUG(!ns); + AA_BUG(!dir); + + dent = securityfs_create_dir("profiles", dir); + if (IS_ERR(dent)) + return PTR_ERR(dent); + ns_subprofs_dir(ns) = dent; + + dent = securityfs_create_dir("raw_data", dir); + if (IS_ERR(dent)) + return PTR_ERR(dent); + ns_subdata_dir(ns) = dent; + + dent = securityfs_create_file(".load", 0640, dir, ns, + &aa_fs_profile_load); + if (IS_ERR(dent)) + return PTR_ERR(dent); + aa_get_ns(ns); + ns_subload(ns) = dent; + + dent = securityfs_create_file(".replace", 0640, dir, ns, + &aa_fs_profile_replace); + if (IS_ERR(dent)) + return PTR_ERR(dent); + aa_get_ns(ns); + ns_subreplace(ns) = dent; + + dent = securityfs_create_file(".remove", 0640, dir, ns, + &aa_fs_profile_remove); + if (IS_ERR(dent)) + return PTR_ERR(dent); + aa_get_ns(ns); + ns_subremove(ns) = dent; + + dent = securityfs_create_dir("namespaces", dir); + if (IS_ERR(dent)) + return PTR_ERR(dent); + aa_get_ns(ns); + ns_subns_dir(ns) = dent; + + return 0; +} + int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name) { struct aa_ns *sub; @@ -689,30 +765,31 @@ int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name) struct dentry *dent, *dir; int error; + AA_BUG(!ns); + AA_BUG(!parent); + AA_BUG(!mutex_is_locked(&ns->lock)); + if (!name) name = ns->base.name; + /* create ns dir if it doesn't already exist */ dent = securityfs_create_dir(name, parent); if (IS_ERR(dent)) goto fail; - ns_dir(ns) = dir = dent; - - dent = securityfs_create_dir("profiles", dir); - if (IS_ERR(dent)) - goto fail; - ns_subprofs_dir(ns) = dent; - dent = securityfs_create_dir("namespaces", dir); - if (IS_ERR(dent)) - goto fail; - ns_subns_dir(ns) = dent; + ns_dir(ns) = dir = dent; + error = __aa_fs_ns_mkdir_entries(ns, dir); + if (error) + goto fail2; + /* profiles */ list_for_each_entry(child, &ns->base.profiles, base.list) { error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns)); if (error) goto fail2; } + /* subnamespaces */ list_for_each_entry(sub, &ns->sub_ns, base.list) { mutex_lock(&sub->lock); error = __aa_fs_ns_mkdir(sub, ns_subns_dir(ns), NULL); @@ -1003,12 +1080,9 @@ static struct aa_fs_entry aa_fs_entry_features[] = { }; static struct aa_fs_entry aa_fs_entry_apparmor[] = { - AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), - AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), - AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level), AA_FS_FILE_FOPS(".ns_name", 0640, &aa_fs_ns_name), - AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), + AA_FS_FILE_FOPS("profiles", 0440, &aa_fs_profiles_fops), AA_FS_DIR("features", aa_fs_entry_features), { } }; @@ -1172,6 +1246,7 @@ out: */ static int __init aa_create_aafs(void) { + struct dentry *dent; int error; if (!apparmor_initialized) @@ -1187,7 +1262,34 @@ static int __init aa_create_aafs(void) if (error) goto error; + dent = securityfs_create_file(".load", 0666, aa_fs_entry.dentry, + NULL, &aa_fs_profile_load); + if (IS_ERR(dent)) { + error = PTR_ERR(dent); + goto error; + } + ns_subload(root_ns) = dent; + + dent = securityfs_create_file(".replace", 0666, aa_fs_entry.dentry, + NULL, &aa_fs_profile_replace); + if (IS_ERR(dent)) { + error = PTR_ERR(dent); + goto error; + } + ns_subreplace(root_ns) = dent; + + dent = securityfs_create_file(".remove", 0666, aa_fs_entry.dentry, + NULL, &aa_fs_profile_remove); + if (IS_ERR(dent)) { + error = PTR_ERR(dent); + goto error; + } + ns_subremove(root_ns) = dent; + + mutex_lock(&root_ns->lock); error = __aa_fs_ns_mkdir(root_ns, aa_fs_entry.dentry, "policy"); + mutex_unlock(&root_ns->lock); + if (error) goto error; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index a593e75b3b03..120a798b5bb0 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -71,6 +71,9 @@ enum aafs_ns_type { AAFS_NS_PROFS, AAFS_NS_NS, AAFS_NS_RAW_DATA, + AAFS_NS_LOAD, + AAFS_NS_REPLACE, + AAFS_NS_REMOVE, AAFS_NS_COUNT, AAFS_NS_MAX_COUNT, AAFS_NS_SIZE, @@ -96,6 +99,9 @@ enum aafs_prof_type { #define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS]) #define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS]) #define ns_subdata_dir(X) ((X)->dents[AAFS_NS_RAW_DATA]) +#define ns_subload(X) ((X)->dents[AAFS_NS_LOAD]) +#define ns_subreplace(X) ((X)->dents[AAFS_NS_REPLACE]) +#define ns_subremove(X) ((X)->dents[AAFS_NS_REMOVE]) #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) -- cgit v1.2.3 From a20aa95fbe1abb4c6f333a1f55e9fd15b01c7f12 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:59 -0800 Subject: apparmor: fail task profile update if current_cred isn't real_cred Trying to update the task cred while the task current cred is not the real cred will result in an error at the cred layer. Avoid this by failing early and delaying the update. Signed-off-by: John Johansen --- security/apparmor/context.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 3c4f534ef88c..3f32f594c999 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -100,6 +100,9 @@ int aa_replace_current_profile(struct aa_profile *profile) if (cxt->profile == profile) return 0; + if (current_cred() != current_real_cred()) + return -EBUSY; + new = prepare_creds(); if (!new) return -ENOMEM; -- cgit v1.2.3 From 55a26ebf630b6bf1cb7ddf8882fdc81d58afeaa2 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:00 -0800 Subject: apparmor: rename context abreviation cxt to the more standard ctx Signed-off-by: John Johansen --- security/apparmor/context.c | 100 ++++++++++++++++++------------------ security/apparmor/domain.c | 42 +++++++-------- security/apparmor/include/context.h | 78 ++++++++++++++-------------- security/apparmor/lsm.c | 72 +++++++++++++------------- security/apparmor/policy.c | 2 +- 5 files changed, 150 insertions(+), 144 deletions(-) diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 3f32f594c999..71e9910cca7b 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -13,11 +13,11 @@ * License. * * - * AppArmor sets confinement on every task, via the the aa_task_cxt and - * the aa_task_cxt.profile, both of which are required and are not allowed - * to be NULL. The aa_task_cxt is not reference counted and is unique + * AppArmor sets confinement on every task, via the the aa_task_ctx and + * the aa_task_ctx.profile, both of which are required and are not allowed + * to be NULL. The aa_task_ctx is not reference counted and is unique * to each cred (which is reference count). The profile pointed to by - * the task_cxt is reference counted. + * the task_ctx is reference counted. * * TODO * If a task uses change_hat it currently does not return to the old @@ -30,28 +30,28 @@ #include "include/policy.h" /** - * aa_alloc_task_context - allocate a new task_cxt + * aa_alloc_task_context - allocate a new task_ctx * @flags: gfp flags for allocation * * Returns: allocated buffer or NULL on failure */ -struct aa_task_cxt *aa_alloc_task_context(gfp_t flags) +struct aa_task_ctx *aa_alloc_task_context(gfp_t flags) { - return kzalloc(sizeof(struct aa_task_cxt), flags); + return kzalloc(sizeof(struct aa_task_ctx), flags); } /** - * aa_free_task_context - free a task_cxt - * @cxt: task_cxt to free (MAYBE NULL) + * aa_free_task_context - free a task_ctx + * @ctx: task_ctx to free (MAYBE NULL) */ -void aa_free_task_context(struct aa_task_cxt *cxt) +void aa_free_task_context(struct aa_task_ctx *ctx) { - if (cxt) { - aa_put_profile(cxt->profile); - aa_put_profile(cxt->previous); - aa_put_profile(cxt->onexec); + if (ctx) { + aa_put_profile(ctx->profile); + aa_put_profile(ctx->previous); + aa_put_profile(ctx->onexec); - kzfree(cxt); + kzfree(ctx); } } @@ -60,7 +60,7 @@ void aa_free_task_context(struct aa_task_cxt *cxt) * @new: a blank task context (NOT NULL) * @old: the task context to copy (NOT NULL) */ -void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old) +void aa_dup_task_context(struct aa_task_ctx *new, const struct aa_task_ctx *old) { *new = *old; aa_get_profile(new->profile); @@ -93,11 +93,11 @@ struct aa_profile *aa_get_task_profile(struct task_struct *task) */ int aa_replace_current_profile(struct aa_profile *profile) { - struct aa_task_cxt *cxt = current_cxt(); + struct aa_task_ctx *ctx = current_ctx(); struct cred *new; BUG_ON(!profile); - if (cxt->profile == profile) + if (ctx->profile == profile) return 0; if (current_cred() != current_real_cred()) @@ -107,20 +107,22 @@ int aa_replace_current_profile(struct aa_profile *profile) if (!new) return -ENOMEM; - cxt = cred_cxt(new); - if (unconfined(profile) || (cxt->profile->ns != profile->ns)) + ctx = cred_ctx(new); + if (unconfined(profile) || (ctx->profile->ns != profile->ns)) /* if switching to unconfined or a different profile namespace * clear out context state */ - aa_clear_task_cxt_trans(cxt); + aa_clear_task_ctx_trans(ctx); - /* be careful switching cxt->profile, when racing replacement it - * is possible that cxt->profile->proxy->profile is the reference + /* + * be careful switching ctx->profile, when racing replacement it + * is possible that ctx->profile->proxy->profile is the reference * keeping @profile valid, so make sure to get its reference before - * dropping the reference on cxt->profile */ + * dropping the reference on ctx->profile + */ aa_get_profile(profile); - aa_put_profile(cxt->profile); - cxt->profile = profile; + aa_put_profile(ctx->profile); + ctx->profile = profile; commit_creds(new); return 0; @@ -134,15 +136,15 @@ int aa_replace_current_profile(struct aa_profile *profile) */ int aa_set_current_onexec(struct aa_profile *profile) { - struct aa_task_cxt *cxt; + struct aa_task_ctx *ctx; struct cred *new = prepare_creds(); if (!new) return -ENOMEM; - cxt = cred_cxt(new); + ctx = cred_ctx(new); aa_get_profile(profile); - aa_put_profile(cxt->onexec); - cxt->onexec = profile; + aa_put_profile(ctx->onexec); + ctx->onexec = profile; commit_creds(new); return 0; @@ -160,28 +162,28 @@ int aa_set_current_onexec(struct aa_profile *profile) */ int aa_set_current_hat(struct aa_profile *profile, u64 token) { - struct aa_task_cxt *cxt; + struct aa_task_ctx *ctx; struct cred *new = prepare_creds(); if (!new) return -ENOMEM; BUG_ON(!profile); - cxt = cred_cxt(new); - if (!cxt->previous) { + ctx = cred_ctx(new); + if (!ctx->previous) { /* transfer refcount */ - cxt->previous = cxt->profile; - cxt->token = token; - } else if (cxt->token == token) { - aa_put_profile(cxt->profile); + ctx->previous = ctx->profile; + ctx->token = token; + } else if (ctx->token == token) { + aa_put_profile(ctx->profile); } else { - /* previous_profile && cxt->token != token */ + /* previous_profile && ctx->token != token */ abort_creds(new); return -EACCES; } - cxt->profile = aa_get_newest_profile(profile); + ctx->profile = aa_get_newest_profile(profile); /* clear exec on switching context */ - aa_put_profile(cxt->onexec); - cxt->onexec = NULL; + aa_put_profile(ctx->onexec); + ctx->onexec = NULL; commit_creds(new); return 0; @@ -198,27 +200,27 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token) */ int aa_restore_previous_profile(u64 token) { - struct aa_task_cxt *cxt; + struct aa_task_ctx *ctx; struct cred *new = prepare_creds(); if (!new) return -ENOMEM; - cxt = cred_cxt(new); - if (cxt->token != token) { + ctx = cred_ctx(new); + if (ctx->token != token) { abort_creds(new); return -EACCES; } /* ignore restores when there is no saved profile */ - if (!cxt->previous) { + if (!ctx->previous) { abort_creds(new); return 0; } - aa_put_profile(cxt->profile); - cxt->profile = aa_get_newest_profile(cxt->previous); - BUG_ON(!cxt->profile); + aa_put_profile(ctx->profile); + ctx->profile = aa_get_newest_profile(ctx->previous); + AA_BUG(!ctx->profile); /* clear exec && prev information when restoring to previous context */ - aa_clear_task_cxt_trans(cxt); + aa_clear_task_ctx_trans(ctx); commit_creds(new); return 0; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 1a8ffc577009..84856b7bbee1 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -337,7 +337,7 @@ static struct aa_profile *x_to_profile(struct aa_profile *profile, */ int apparmor_bprm_set_creds(struct linux_binprm *bprm) { - struct aa_task_cxt *cxt; + struct aa_task_ctx *ctx; struct aa_profile *profile, *new_profile = NULL; struct aa_ns *ns; char *buffer = NULL; @@ -353,10 +353,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->cred_prepared) return 0; - cxt = cred_cxt(bprm->cred); - BUG_ON(!cxt); + ctx = cred_ctx(bprm->cred); + AA_BUG(!ctx); - profile = aa_get_newest_profile(cxt->profile); + profile = aa_get_newest_profile(ctx->profile); /* * get the namespace from the replacement profile as replacement * can change the namespace @@ -380,9 +380,9 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) */ if (unconfined(profile)) { /* unconfined task */ - if (cxt->onexec) + if (ctx->onexec) /* change_profile on exec already been granted */ - new_profile = aa_get_profile(cxt->onexec); + new_profile = aa_get_profile(ctx->onexec); else new_profile = find_attach(ns, &ns->base.profiles, name); if (!new_profile) @@ -397,10 +397,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) /* find exec permissions for name */ state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms); - if (cxt->onexec) { + if (ctx->onexec) { struct file_perms cp; info = "change_profile onexec"; - new_profile = aa_get_newest_profile(cxt->onexec); + new_profile = aa_get_newest_profile(ctx->onexec); if (!(perms.allow & AA_MAY_ONEXEC)) goto audit; @@ -409,8 +409,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) * exec\0change_profile */ state = aa_dfa_null_transition(profile->file.dfa, state); - cp = change_profile_perms(profile, cxt->onexec->ns, - cxt->onexec->base.name, + cp = change_profile_perms(profile, ctx->onexec->ns, + ctx->onexec->base.name, AA_MAY_ONEXEC, state); if (!(cp.allow & AA_MAY_ONEXEC)) @@ -499,13 +499,13 @@ apply: bprm->per_clear |= PER_CLEAR_ON_SETID; x_clear: - aa_put_profile(cxt->profile); - /* transfer new profile reference will be released when cxt is freed */ - cxt->profile = new_profile; + aa_put_profile(ctx->profile); + /* transfer new profile reference will be released when ctx is freed */ + ctx->profile = new_profile; new_profile = NULL; /* clear out all temporary/transitional state from the context */ - aa_clear_task_cxt_trans(cxt); + aa_clear_task_ctx_trans(ctx); audit: error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, @@ -545,17 +545,17 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm) void apparmor_bprm_committing_creds(struct linux_binprm *bprm) { struct aa_profile *profile = __aa_current_profile(); - struct aa_task_cxt *new_cxt = cred_cxt(bprm->cred); + struct aa_task_ctx *new_ctx = cred_ctx(bprm->cred); /* bail out if unconfined or not changing profile */ - if ((new_cxt->profile == profile) || - (unconfined(new_cxt->profile))) + if ((new_ctx->profile == profile) || + (unconfined(new_ctx->profile))) return; current->pdeath_signal = 0; /* reset soft limits and set hard limits for the new profile */ - __aa_transition_rlimits(profile, new_cxt->profile); + __aa_transition_rlimits(profile, new_ctx->profile); } /** @@ -604,7 +604,7 @@ static char *new_compound_name(const char *n1, const char *n2) int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) { const struct cred *cred; - struct aa_task_cxt *cxt; + struct aa_task_ctx *ctx; struct aa_profile *profile, *previous_profile, *hat = NULL; char *name = NULL; int i; @@ -622,9 +622,9 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) /* released below */ cred = get_current_cred(); - cxt = cred_cxt(cred); + ctx = cred_ctx(cred); profile = aa_get_newest_profile(aa_cred_profile(cred)); - previous_profile = aa_get_newest_profile(cxt->previous); + previous_profile = aa_get_newest_profile(ctx->previous); if (unconfined(profile)) { info = "unconfined"; diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index d378bff47ccd..5b18fedab4c8 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -22,43 +22,43 @@ #include "policy.h" #include "policy_ns.h" -#define cred_cxt(X) (X)->security -#define current_cxt() cred_cxt(current_cred()) +#define cred_ctx(X) ((X)->security) +#define current_ctx() cred_ctx(current_cred()) -/* struct aa_file_cxt - the AppArmor context the file was opened in +/* struct aa_file_ctx - the AppArmor context the file was opened in * @perms: the permission the file was opened with * - * The file_cxt could currently be directly stored in file->f_security + * The file_ctx could currently be directly stored in file->f_security * as the profile reference is now stored in the f_cred. However the - * cxt struct will expand in the future so we keep the struct. + * ctx struct will expand in the future so we keep the struct. */ -struct aa_file_cxt { +struct aa_file_ctx { u16 allow; }; /** - * aa_alloc_file_context - allocate file_cxt + * aa_alloc_file_context - allocate file_ctx * @gfp: gfp flags for allocation * - * Returns: file_cxt or NULL on failure + * Returns: file_ctx or NULL on failure */ -static inline struct aa_file_cxt *aa_alloc_file_context(gfp_t gfp) +static inline struct aa_file_ctx *aa_alloc_file_context(gfp_t gfp) { - return kzalloc(sizeof(struct aa_file_cxt), gfp); + return kzalloc(sizeof(struct aa_file_ctx), gfp); } /** - * aa_free_file_context - free a file_cxt - * @cxt: file_cxt to free (MAYBE_NULL) + * aa_free_file_context - free a file_ctx + * @ctx: file_ctx to free (MAYBE_NULL) */ -static inline void aa_free_file_context(struct aa_file_cxt *cxt) +static inline void aa_free_file_context(struct aa_file_ctx *ctx) { - if (cxt) - kzfree(cxt); + if (ctx) + kzfree(ctx); } /** - * struct aa_task_cxt - primary label for confined tasks + * struct aa_task_ctx - primary label for confined tasks * @profile: the current profile (NOT NULL) * @exec: profile to transition to on next exec (MAYBE NULL) * @previous: profile the task may return to (MAYBE NULL) @@ -69,17 +69,17 @@ static inline void aa_free_file_context(struct aa_file_cxt *cxt) * * TODO: make so a task can be confined by a stack of contexts */ -struct aa_task_cxt { +struct aa_task_ctx { struct aa_profile *profile; struct aa_profile *onexec; struct aa_profile *previous; u64 token; }; -struct aa_task_cxt *aa_alloc_task_context(gfp_t flags); -void aa_free_task_context(struct aa_task_cxt *cxt); -void aa_dup_task_context(struct aa_task_cxt *new, - const struct aa_task_cxt *old); +struct aa_task_ctx *aa_alloc_task_context(gfp_t flags); +void aa_free_task_context(struct aa_task_ctx *ctx); +void aa_dup_task_context(struct aa_task_ctx *new, + const struct aa_task_ctx *old); int aa_replace_current_profile(struct aa_profile *profile); int aa_set_current_onexec(struct aa_profile *profile); int aa_set_current_hat(struct aa_profile *profile, u64 token); @@ -97,9 +97,10 @@ struct aa_profile *aa_get_task_profile(struct task_struct *task); */ static inline struct aa_profile *aa_cred_profile(const struct cred *cred) { - struct aa_task_cxt *cxt = cred_cxt(cred); - BUG_ON(!cxt || !cxt->profile); - return cxt->profile; + struct aa_task_ctx *ctx = cred_ctx(cred); + + AA_BUG(!ctx || !ctx->profile); + return ctx->profile; } /** @@ -149,18 +150,19 @@ static inline struct aa_profile *__aa_current_profile(void) */ static inline struct aa_profile *aa_current_profile(void) { - const struct aa_task_cxt *cxt = current_cxt(); + const struct aa_task_ctx *ctx = current_ctx(); struct aa_profile *profile; - BUG_ON(!cxt || !cxt->profile); - if (profile_is_stale(cxt->profile)) { - profile = aa_get_newest_profile(cxt->profile); + AA_BUG(!ctx || !ctx->profile); + + if (profile_is_stale(ctx->profile)) { + profile = aa_get_newest_profile(ctx->profile); aa_replace_current_profile(profile); aa_put_profile(profile); - cxt = current_cxt(); + ctx = current_ctx(); } - return cxt->profile; + return ctx->profile; } static inline struct aa_ns *aa_get_current_ns(void) @@ -169,16 +171,16 @@ static inline struct aa_ns *aa_get_current_ns(void) } /** - * aa_clear_task_cxt_trans - clear transition tracking info from the cxt - * @cxt: task context to clear (NOT NULL) + * aa_clear_task_ctx_trans - clear transition tracking info from the ctx + * @ctx: task context to clear (NOT NULL) */ -static inline void aa_clear_task_cxt_trans(struct aa_task_cxt *cxt) +static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx) { - aa_put_profile(cxt->previous); - aa_put_profile(cxt->onexec); - cxt->previous = NULL; - cxt->onexec = NULL; - cxt->token = 0; + aa_put_profile(ctx->previous); + aa_put_profile(ctx->onexec); + ctx->previous = NULL; + ctx->onexec = NULL; + ctx->token = 0; } #endif /* __AA_CONTEXT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 09f1c407c4d8..84666114e9f5 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -45,12 +45,12 @@ int apparmor_initialized __initdata; */ /* - * free the associated aa_task_cxt and put its profiles + * free the associated aa_task_ctx and put its profiles */ static void apparmor_cred_free(struct cred *cred) { - aa_free_task_context(cred_cxt(cred)); - cred_cxt(cred) = NULL; + aa_free_task_context(cred_ctx(cred)); + cred_ctx(cred) = NULL; } /* @@ -59,27 +59,29 @@ static void apparmor_cred_free(struct cred *cred) static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) { /* freed by apparmor_cred_free */ - struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); - if (!cxt) + struct aa_task_ctx *ctx = aa_alloc_task_context(gfp); + + if (!ctx) return -ENOMEM; - cred_cxt(cred) = cxt; + cred_ctx(cred) = ctx; return 0; } /* - * prepare new aa_task_cxt for modification by prepare_cred block + * prepare new aa_task_ctx for modification by prepare_cred block */ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp) { /* freed by apparmor_cred_free */ - struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); - if (!cxt) + struct aa_task_ctx *ctx = aa_alloc_task_context(gfp); + + if (!ctx) return -ENOMEM; - aa_dup_task_context(cxt, cred_cxt(old)); - cred_cxt(new) = cxt; + aa_dup_task_context(ctx, cred_ctx(old)); + cred_ctx(new) = ctx; return 0; } @@ -88,10 +90,10 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, */ static void apparmor_cred_transfer(struct cred *new, const struct cred *old) { - const struct aa_task_cxt *old_cxt = cred_cxt(old); - struct aa_task_cxt *new_cxt = cred_cxt(new); + const struct aa_task_ctx *old_ctx = cred_ctx(old); + struct aa_task_ctx *new_ctx = cred_ctx(new); - aa_dup_task_context(new_cxt, old_cxt); + aa_dup_task_context(new_ctx, old_ctx); } static int apparmor_ptrace_access_check(struct task_struct *child, @@ -345,7 +347,7 @@ static int apparmor_inode_getattr(const struct path *path) static int apparmor_file_open(struct file *file, const struct cred *cred) { - struct aa_file_cxt *fcxt = file->f_security; + struct aa_file_ctx *fctx = file->f_security; struct aa_profile *profile; int error = 0; @@ -358,7 +360,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred) * actually execute the image. */ if (current->in_execve) { - fcxt->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP; + fctx->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP; return 0; } @@ -370,7 +372,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred) error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0, aa_map_file_to_perms(file), &cond); /* todo cache full allowed permissions set and state */ - fcxt->allow = aa_map_file_to_perms(file); + fctx->allow = aa_map_file_to_perms(file); } return error; @@ -388,14 +390,14 @@ static int apparmor_file_alloc_security(struct file *file) static void apparmor_file_free_security(struct file *file) { - struct aa_file_cxt *cxt = file->f_security; + struct aa_file_ctx *ctx = file->f_security; - aa_free_file_context(cxt); + aa_free_file_context(ctx); } static int common_file_perm(int op, struct file *file, u32 mask) { - struct aa_file_cxt *fcxt = file->f_security; + struct aa_file_ctx *fctx = file->f_security; struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred); int error = 0; @@ -415,7 +417,7 @@ static int common_file_perm(int op, struct file *file, u32 mask) * delegation from unconfined tasks */ if (!unconfined(profile) && !unconfined(fprofile) && - ((fprofile != profile) || (mask & ~fcxt->allow))) + ((fprofile != profile) || (mask & ~fctx->allow))) error = aa_file_perm(op, profile, file, mask); return error; @@ -477,15 +479,15 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, int error = -ENOENT; /* released below */ const struct cred *cred = get_task_cred(task); - struct aa_task_cxt *cxt = cred_cxt(cred); + struct aa_task_ctx *ctx = cred_ctx(cred); struct aa_profile *profile = NULL; if (strcmp(name, "current") == 0) - profile = aa_get_newest_profile(cxt->profile); - else if (strcmp(name, "prev") == 0 && cxt->previous) - profile = aa_get_newest_profile(cxt->previous); - else if (strcmp(name, "exec") == 0 && cxt->onexec) - profile = aa_get_newest_profile(cxt->onexec); + profile = aa_get_newest_profile(ctx->profile); + else if (strcmp(name, "prev") == 0 && ctx->previous) + profile = aa_get_newest_profile(ctx->previous); + else if (strcmp(name, "exec") == 0 && ctx->onexec) + profile = aa_get_newest_profile(ctx->onexec); else error = -EINVAL; @@ -849,21 +851,21 @@ static int param_set_mode(const char *val, struct kernel_param *kp) */ /** - * set_init_cxt - set a task context and profile on the first task. + * set_init_ctx - set a task context and profile on the first task. * * TODO: allow setting an alternate profile than unconfined */ -static int __init set_init_cxt(void) +static int __init set_init_ctx(void) { struct cred *cred = (struct cred *)current->real_cred; - struct aa_task_cxt *cxt; + struct aa_task_ctx *ctx; - cxt = aa_alloc_task_context(GFP_KERNEL); - if (!cxt) + ctx = aa_alloc_task_context(GFP_KERNEL); + if (!ctx) return -ENOMEM; - cxt->profile = aa_get_profile(root_ns->unconfined); - cred_cxt(cred) = cxt; + ctx->profile = aa_get_profile(root_ns->unconfined); + cred_ctx(cred) = ctx; return 0; } @@ -890,7 +892,7 @@ static int __init apparmor_init(void) goto alloc_out; } - error = set_init_cxt(); + error = set_init_ctx(); if (error) { AA_ERROR("Failed to set context on init task\n"); aa_free_root_ns(); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 912cdbed7977..4ec24474bd1a 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -808,7 +808,7 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, * @udata: serialized data stream (NOT NULL) * * unpack and replace a profile on the profile list and uses of that profile - * by any aa_task_cxt. If the profile does not exist on the profile list + * by any aa_task_ctx. If the profile does not exist on the profile list * it is added. * * Returns: size of data consumed else error code on failure. -- cgit v1.2.3 From 47f6e5cc7355e4ff2fd7ace919aa9e291077c26b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:01 -0800 Subject: apparmor: change op from int to const char * Having ops be an integer that is an index into an op name table is awkward and brittle. Every op change requires an edit for both the op constant and a string in the table. Instead switch to using const strings directly, eliminating the need for the table that needs to be kept in sync. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 4 +- security/apparmor/audit.c | 55 +------------------ security/apparmor/domain.c | 4 +- security/apparmor/file.c | 9 ++-- security/apparmor/include/audit.h | 108 ++++++++++++++++++------------------- security/apparmor/include/file.h | 9 ++-- security/apparmor/include/policy.h | 3 +- security/apparmor/lsm.c | 15 +++--- security/apparmor/policy.c | 7 +-- security/apparmor/procattr.c | 4 +- 10 files changed, 84 insertions(+), 134 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index af36af88c4ed..999a43e598f0 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -85,7 +85,7 @@ static int mangle_name(const char *name, char *target) * Returns: kernel buffer containing copy of user buffer data or an * ERR_PTR on failure. */ -static struct aa_loaddata *aa_simple_write_to_buffer(int op, +static struct aa_loaddata *aa_simple_write_to_buffer(const char *op, const char __user *userbuf, size_t alloc_size, size_t copy_size, @@ -122,7 +122,7 @@ static ssize_t policy_update(int binop, const char __user *buf, size_t size, ssize_t error; struct aa_loaddata *data; struct aa_profile *profile = aa_current_profile(); - int op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL; + const char *op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL; /* high level check about policy management - fine grained in * below after unpack */ diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 42101c42f446..bcd28d88df7b 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -20,59 +20,6 @@ #include "include/policy.h" #include "include/policy_ns.h" -const char *const op_table[] = { - "null", - - "sysctl", - "capable", - - "unlink", - "mkdir", - "rmdir", - "mknod", - "truncate", - "link", - "symlink", - "rename_src", - "rename_dest", - "chmod", - "chown", - "getattr", - "open", - - "file_perm", - "file_lock", - "file_mmap", - "file_mprotect", - - "create", - "post_create", - "bind", - "connect", - "listen", - "accept", - "sendmsg", - "recvmsg", - "getsockname", - "getpeername", - "getsockopt", - "setsockopt", - "socket_shutdown", - - "ptrace", - - "exec", - "change_hat", - "change_profile", - "change_onexec", - - "setprocattr", - "setrlimit", - - "profile_replace", - "profile_load", - "profile_remove" -}; const char *const audit_mode_names[] = { "normal", @@ -120,7 +67,7 @@ static void audit_pre(struct audit_buffer *ab, void *ca) if (sa->aad->op) { audit_log_format(ab, " operation="); - audit_log_string(ab, op_table[sa->aad->op]); + audit_log_string(ab, sa->aad->op); } if (sa->aad->info) { diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 84856b7bbee1..c2f1d651db23 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -750,8 +750,8 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, struct aa_profile *profile, *target = NULL; struct aa_ns *ns = NULL; struct file_perms perms = {}; - const char *name = NULL, *info = NULL; - int op, error = 0; + const char *name = NULL, *info = NULL, *op; + int error = 0; u32 request; if (!hname && !ns_name) diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 608971ac6781..e04f044340ba 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -104,7 +104,7 @@ static void file_audit_cb(struct audit_buffer *ab, void *va) * Returns: %0 or error on failure */ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, - gfp_t gfp, int op, u32 request, const char *name, + gfp_t gfp, const char *op, u32 request, const char *name, const char *target, kuid_t ouid, const char *info, int error) { int type = AUDIT_APPARMOR_AUTO; @@ -276,8 +276,9 @@ static inline bool is_deleted(struct dentry *dentry) * * Returns: %0 else error if access denied or other error */ -int aa_path_perm(int op, struct aa_profile *profile, const struct path *path, - int flags, u32 request, struct path_cond *cond) +int aa_path_perm(const char *op, struct aa_profile *profile, + const struct path *path, int flags, u32 request, + struct path_cond *cond) { char *buffer = NULL; struct file_perms perms = {}; @@ -446,7 +447,7 @@ audit: * * Returns: %0 if access allowed else error */ -int aa_file_perm(int op, struct aa_profile *profile, struct file *file, +int aa_file_perm(const char *op, struct aa_profile *profile, struct file *file, u32 request) { struct path_cond cond = { diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index dbfb4a6d72b6..956c0b16a30f 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -46,65 +46,63 @@ enum audit_type { AUDIT_APPARMOR_AUTO }; -extern const char *const op_table[]; -enum aa_ops { - OP_NULL, - - OP_SYSCTL, - OP_CAPABLE, - - OP_UNLINK, - OP_MKDIR, - OP_RMDIR, - OP_MKNOD, - OP_TRUNC, - OP_LINK, - OP_SYMLINK, - OP_RENAME_SRC, - OP_RENAME_DEST, - OP_CHMOD, - OP_CHOWN, - OP_GETATTR, - OP_OPEN, - - OP_FPERM, - OP_FLOCK, - OP_FMMAP, - OP_FMPROT, - - OP_CREATE, - OP_POST_CREATE, - OP_BIND, - OP_CONNECT, - OP_LISTEN, - OP_ACCEPT, - OP_SENDMSG, - OP_RECVMSG, - OP_GETSOCKNAME, - OP_GETPEERNAME, - OP_GETSOCKOPT, - OP_SETSOCKOPT, - OP_SOCK_SHUTDOWN, - - OP_PTRACE, - - OP_EXEC, - OP_CHANGE_HAT, - OP_CHANGE_PROFILE, - OP_CHANGE_ONEXEC, - - OP_SETPROCATTR, - OP_SETRLIMIT, - - OP_PROF_REPL, - OP_PROF_LOAD, - OP_PROF_RM, -}; +#define OP_NULL NULL + +#define OP_SYSCTL "sysctl" +#define OP_CAPABLE "capable" + +#define OP_UNLINK "unlink" +#define OP_MKDIR "mkdir" +#define OP_RMDIR "rmdir" +#define OP_MKNOD "mknod" +#define OP_TRUNC "truncate" +#define OP_LINK "link" +#define OP_SYMLINK "symlink" +#define OP_RENAME_SRC "rename_src" +#define OP_RENAME_DEST "rename_dest" +#define OP_CHMOD "chmod" +#define OP_CHOWN "chown" +#define OP_GETATTR "getattr" +#define OP_OPEN "open" + +#define OP_FPERM "file_perm" +#define OP_FLOCK "file_lock" +#define OP_FMMAP "file_mmap" +#define OP_FMPROT "file_mprotect" + +#define OP_CREATE "create" +#define OP_POST_CREATE "post_create" +#define OP_BIND "bind" +#define OP_CONNECT "connect" +#define OP_LISTEN "listen" +#define OP_ACCEPT "accept" +#define OP_SENDMSG "sendmsg" +#define OP_RECVMSG "recvmsg" +#define OP_GETSOCKNAME "getsockname" +#define OP_GETPEERNAME "getpeername" +#define OP_GETSOCKOPT "getsockopt" +#define OP_SETSOCKOPT "setsockopt" +#define OP_SHUTDOWN "socket_shutdown" + +#define OP_PTRACE "ptrace" + +#define OP_EXEC "exec" + +#define OP_CHANGE_HAT "change_hat" +#define OP_CHANGE_PROFILE "change_profile" +#define OP_CHANGE_ONEXEC "change_onexec" + +#define OP_SETPROCATTR "setprocattr" +#define OP_SETRLIMIT "setrlimit" + +#define OP_PROF_REPL "profile_replace" +#define OP_PROF_LOAD "profile_load" +#define OP_PROF_RM "profile_remove" struct apparmor_audit_data { int error; - int op; + const char *op; int type; void *profile; const char *name; diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 4803c97d1992..0eb54363e033 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -145,7 +145,7 @@ static inline u16 dfa_map_xindex(u16 mask) dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, - gfp_t gfp, int op, u32 request, const char *name, + gfp_t gfp, const char *op, u32 request, const char *name, const char *target, kuid_t ouid, const char *info, int error); /** @@ -171,13 +171,14 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, const char *name, struct path_cond *cond, struct file_perms *perms); -int aa_path_perm(int op, struct aa_profile *profile, const struct path *path, - int flags, u32 request, struct path_cond *cond); +int aa_path_perm(const char *op, struct aa_profile *profile, + const struct path *path, int flags, u32 request, + struct path_cond *cond); int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry); -int aa_file_perm(int op, struct aa_profile *profile, struct file *file, +int aa_file_perm(const char *op, struct aa_profile *profile, struct file *file, u32 request); static inline void aa_free_file_rules(struct aa_file_rules *rules) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index b315ad5bdd85..a5a997896836 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -303,6 +303,7 @@ static inline int AUDIT_MODE(struct aa_profile *profile) bool policy_view_capable(struct aa_ns *ns); bool policy_admin_capable(struct aa_ns *ns); -int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, int op); +int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, + const char *op); #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 84666114e9f5..c751b033420c 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -152,7 +152,7 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns, * * Returns: %0 else error code if error or permission denied */ -static int common_perm(int op, const struct path *path, u32 mask, +static int common_perm(const char *op, const struct path *path, u32 mask, struct path_cond *cond) { struct aa_profile *profile; @@ -175,7 +175,7 @@ static int common_perm(int op, const struct path *path, u32 mask, * * Returns: %0 else error code if error or permission denied */ -static int common_perm_dir_dentry(int op, const struct path *dir, +static int common_perm_dir_dentry(const char *op, const struct path *dir, struct dentry *dentry, u32 mask, struct path_cond *cond) { @@ -192,7 +192,8 @@ static int common_perm_dir_dentry(int op, const struct path *dir, * * Returns: %0 else error code if error or permission denied */ -static inline int common_perm_path(int op, const struct path *path, u32 mask) +static inline int common_perm_path(const char *op, const struct path *path, + u32 mask) { struct path_cond cond = { d_backing_inode(path->dentry)->i_uid, d_backing_inode(path->dentry)->i_mode @@ -212,7 +213,7 @@ static inline int common_perm_path(int op, const struct path *path, u32 mask) * * Returns: %0 else error code if error or permission denied */ -static int common_perm_rm(int op, const struct path *dir, +static int common_perm_rm(const char *op, const struct path *dir, struct dentry *dentry, u32 mask) { struct inode *inode = d_backing_inode(dentry); @@ -237,7 +238,7 @@ static int common_perm_rm(int op, const struct path *dir, * * Returns: %0 else error code if error or permission denied */ -static int common_perm_create(int op, const struct path *dir, +static int common_perm_create(const char *op, const struct path *dir, struct dentry *dentry, u32 mask, umode_t mode) { struct path_cond cond = { current_fsuid(), mode }; @@ -395,7 +396,7 @@ static void apparmor_file_free_security(struct file *file) aa_free_file_context(ctx); } -static int common_file_perm(int op, struct file *file, u32 mask) +static int common_file_perm(const char *op, struct file *file, u32 mask) { struct aa_file_ctx *fctx = file->f_security; struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred); @@ -438,7 +439,7 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd) return common_file_perm(OP_FLOCK, file, mask); } -static int common_mmap(int op, struct file *file, unsigned long prot, +static int common_mmap(const char *op, struct file *file, unsigned long prot, unsigned long flags) { int mask = 0; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 4ec24474bd1a..17754ee58ff1 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -606,7 +606,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) * * Returns: the error to be returned after audit is done */ -static int audit_policy(struct aa_profile *profile, int op, gfp_t gfp, +static int audit_policy(struct aa_profile *profile, const char *op, gfp_t gfp, const char *nsname, const char *name, const char *info, int error) { @@ -670,7 +670,8 @@ bool policy_admin_capable(struct aa_ns *ns) * * Returns: 0 if the task is allowed to manipulate policy else error */ -int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, int op) +int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, + const char *op) { /* check if loading policy is locked out */ if (aa_g_lock_policy) @@ -819,7 +820,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile, const char *ns_name, *info = NULL; struct aa_ns *ns = NULL; struct aa_load_ent *ent, *tmp; - int op = OP_PROF_REPL; + const char *op = OP_PROF_REPL; ssize_t count, error; LIST_HEAD(lh); diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index 1babd3655520..4cb5f3dc906d 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -88,13 +88,13 @@ int aa_getprocattr(struct aa_profile *profile, char **string) * * Returns: start position of name after token else NULL on failure */ -static char *split_token_from_name(int op, char *args, u64 * token) +static char *split_token_from_name(const char *op, char *args, u64 *token) { char *name; *token = simple_strtoull(args, &name, 16); if ((name == args) || *name != '^') { - AA_ERROR("%s: Invalid input '%s'", op_table[op], args); + AA_ERROR("%s: Invalid input '%s'", op, args); return ERR_PTR(-EINVAL); } -- cgit v1.2.3 From ef88a7ac55fdd3bf6ac3942b83aa29311b45339b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:02 -0800 Subject: apparmor: change aad apparmor_audit_data macro to a fn macro The aad macro can replace aad strings when it is not intended to. Switch to a fn macro so it is only applied when intended. Also at the same time cleanup audit_data initialization by putting common boiler plate behind a macro, and dropping the gfp_t parameter which will become useless. Signed-off-by: John Johansen --- security/apparmor/audit.c | 42 ++++++++++++------------ security/apparmor/capability.c | 10 ++---- security/apparmor/domain.c | 13 ++++---- security/apparmor/file.c | 69 +++++++++++++++++++-------------------- security/apparmor/include/audit.h | 43 +++++++++++++++++------- security/apparmor/include/file.h | 2 +- security/apparmor/ipc.c | 18 ++++------ security/apparmor/lib.c | 8 ++--- security/apparmor/lsm.c | 12 +++---- security/apparmor/policy.c | 42 +++++++++++------------- security/apparmor/policy_unpack.c | 38 +++++++++++---------- security/apparmor/resource.c | 19 ++++------- 12 files changed, 155 insertions(+), 161 deletions(-) diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index bcd28d88df7b..0c81ff64993b 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -62,23 +62,23 @@ static void audit_pre(struct audit_buffer *ab, void *ca) if (aa_g_audit_header) { audit_log_format(ab, "apparmor="); - audit_log_string(ab, aa_audit_type[sa->aad->type]); + audit_log_string(ab, aa_audit_type[aad(sa)->type]); } - if (sa->aad->op) { + if (aad(sa)->op) { audit_log_format(ab, " operation="); - audit_log_string(ab, sa->aad->op); + audit_log_string(ab, aad(sa)->op); } - if (sa->aad->info) { + if (aad(sa)->info) { audit_log_format(ab, " info="); - audit_log_string(ab, sa->aad->info); - if (sa->aad->error) - audit_log_format(ab, " error=%d", sa->aad->error); + audit_log_string(ab, aad(sa)->info); + if (aad(sa)->error) + audit_log_format(ab, " error=%d", aad(sa)->error); } - if (sa->aad->profile) { - struct aa_profile *profile = sa->aad->profile; + if (aad(sa)->profile) { + struct aa_profile *profile = aad(sa)->profile; if (profile->ns != root_ns) { audit_log_format(ab, " namespace="); audit_log_untrustedstring(ab, profile->ns->base.hname); @@ -87,9 +87,9 @@ static void audit_pre(struct audit_buffer *ab, void *ca) audit_log_untrustedstring(ab, profile->base.hname); } - if (sa->aad->name) { + if (aad(sa)->name) { audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, sa->aad->name); + audit_log_untrustedstring(ab, aad(sa)->name); } } @@ -101,7 +101,7 @@ static void audit_pre(struct audit_buffer *ab, void *ca) void aa_audit_msg(int type, struct common_audit_data *sa, void (*cb) (struct audit_buffer *, void *)) { - sa->aad->type = type; + aad(sa)->type = type; common_lsm_audit(sa, audit_pre, cb); } @@ -109,7 +109,6 @@ void aa_audit_msg(int type, struct common_audit_data *sa, * aa_audit - Log a profile based audit event to the audit subsystem * @type: audit type for the message * @profile: profile to check against (NOT NULL) - * @gfp: allocation flags to use * @sa: audit event (NOT NULL) * @cb: optional callback fn for type specific fields (MAYBE NULL) * @@ -117,14 +116,13 @@ void aa_audit_msg(int type, struct common_audit_data *sa, * * Returns: error on failure */ -int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, - struct common_audit_data *sa, +int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa, void (*cb) (struct audit_buffer *, void *)) { BUG_ON(!profile); if (type == AUDIT_APPARMOR_AUTO) { - if (likely(!sa->aad->error)) { + if (likely(!aad(sa)->error)) { if (AUDIT_MODE(profile) != AUDIT_ALL) return 0; type = AUDIT_APPARMOR_AUDIT; @@ -136,23 +134,23 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, if (AUDIT_MODE(profile) == AUDIT_QUIET || (type == AUDIT_APPARMOR_DENIED && AUDIT_MODE(profile) == AUDIT_QUIET)) - return sa->aad->error; + return aad(sa)->error; if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) type = AUDIT_APPARMOR_KILL; if (!unconfined(profile)) - sa->aad->profile = profile; + aad(sa)->profile = profile; aa_audit_msg(type, sa, cb); - if (sa->aad->type == AUDIT_APPARMOR_KILL) + if (aad(sa)->type == AUDIT_APPARMOR_KILL) (void)send_sig_info(SIGKILL, NULL, sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? sa->u.tsk : current); - if (sa->aad->type == AUDIT_APPARMOR_ALLOWED) - return complain_error(sa->aad->error); + if (aad(sa)->type == AUDIT_APPARMOR_ALLOWED) + return complain_error(aad(sa)->error); - return sa->aad->error; + return aad(sa)->error; } diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 1101c6f64bb7..1d2e2de5515f 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -66,13 +66,9 @@ static int audit_caps(struct aa_profile *profile, int cap, int error) { struct audit_cache *ent; int type = AUDIT_APPARMOR_AUTO; - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_CAP; - sa.aad = &aad; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE); sa.u.cap = cap; - sa.aad->op = OP_CAPABLE; - sa.aad->error = error; + aad(&sa)->error = error; if (likely(!error)) { /* test if auditing is being forced */ @@ -104,7 +100,7 @@ static int audit_caps(struct aa_profile *profile, int cap, int error) } put_cpu_var(audit_cache); - return aa_audit(type, profile, GFP_ATOMIC, &sa, audit_cb); + return aa_audit(type, profile, &sa, audit_cb); } /** diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index c2f1d651db23..d18b3f0e5534 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -508,8 +508,7 @@ x_clear: aa_clear_task_ctx_trans(ctx); audit: - error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, - name, + error = aa_audit_file(profile, &perms, OP_EXEC, MAY_EXEC, name, new_profile ? new_profile->base.hname : NULL, cond.uid, info, error); @@ -714,9 +713,9 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) audit: if (!permtest) - error = aa_audit_file(profile, &perms, GFP_KERNEL, - OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL, - target, GLOBAL_ROOT_UID, info, error); + error = aa_audit_file(profile, &perms, OP_CHANGE_HAT, + AA_MAY_CHANGEHAT, NULL, target, + GLOBAL_ROOT_UID, info, error); out: aa_put_profile(hat); @@ -842,8 +841,8 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, audit: if (!permtest) - error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, - name, hname, GLOBAL_ROOT_UID, info, error); + error = aa_audit_file(profile, &perms, op, request, name, + hname, GLOBAL_ROOT_UID, info, error); aa_put_ns(ns); aa_put_profile(target); diff --git a/security/apparmor/file.c b/security/apparmor/file.c index e04f044340ba..750564c3ab71 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -67,24 +67,24 @@ static void file_audit_cb(struct audit_buffer *ab, void *va) struct common_audit_data *sa = va; kuid_t fsuid = current_fsuid(); - if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { + if (aad(sa)->fs.request & AA_AUDIT_FILE_MASK) { audit_log_format(ab, " requested_mask="); - audit_file_mask(ab, sa->aad->fs.request); + audit_file_mask(ab, aad(sa)->fs.request); } - if (sa->aad->fs.denied & AA_AUDIT_FILE_MASK) { + if (aad(sa)->fs.denied & AA_AUDIT_FILE_MASK) { audit_log_format(ab, " denied_mask="); - audit_file_mask(ab, sa->aad->fs.denied); + audit_file_mask(ab, aad(sa)->fs.denied); } - if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { + if (aad(sa)->fs.request & AA_AUDIT_FILE_MASK) { audit_log_format(ab, " fsuid=%d", from_kuid(&init_user_ns, fsuid)); audit_log_format(ab, " ouid=%d", - from_kuid(&init_user_ns, sa->aad->fs.ouid)); + from_kuid(&init_user_ns, aad(sa)->fs.ouid)); } - if (sa->aad->fs.target) { + if (aad(sa)->fs.target) { audit_log_format(ab, " target="); - audit_log_untrustedstring(ab, sa->aad->fs.target); + audit_log_untrustedstring(ab, aad(sa)->fs.target); } } @@ -104,54 +104,53 @@ static void file_audit_cb(struct audit_buffer *ab, void *va) * Returns: %0 or error on failure */ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, - gfp_t gfp, const char *op, u32 request, const char *name, + const char *op, u32 request, const char *name, const char *target, kuid_t ouid, const char *info, int error) { int type = AUDIT_APPARMOR_AUTO; - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_TASK; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, op); + + sa.u.tsk = NULL; + aad(&sa)->fs.request = request; + aad(&sa)->name = name; + aad(&sa)->fs.target = target; + aad(&sa)->fs.ouid = ouid; + aad(&sa)->info = info; + aad(&sa)->error = error; sa.u.tsk = NULL; - sa.aad = &aad; - aad.op = op, - aad.fs.request = request; - aad.name = name; - aad.fs.target = target; - aad.fs.ouid = ouid; - aad.info = info; - aad.error = error; - - if (likely(!sa.aad->error)) { + + if (likely(!aad(&sa)->error)) { u32 mask = perms->audit; if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) mask = 0xffff; /* mask off perms that are not being force audited */ - sa.aad->fs.request &= mask; + aad(&sa)->fs.request &= mask; - if (likely(!sa.aad->fs.request)) + if (likely(!aad(&sa)->fs.request)) return 0; type = AUDIT_APPARMOR_AUDIT; } else { /* only report permissions that were denied */ - sa.aad->fs.request = sa.aad->fs.request & ~perms->allow; + aad(&sa)->fs.request = aad(&sa)->fs.request & ~perms->allow; + AA_BUG(!aad(&sa)->fs.request); - if (sa.aad->fs.request & perms->kill) + if (aad(&sa)->fs.request & perms->kill) type = AUDIT_APPARMOR_KILL; /* quiet known rejects, assumes quiet and kill do not overlap */ - if ((sa.aad->fs.request & perms->quiet) && + if ((aad(&sa)->fs.request & perms->quiet) && AUDIT_MODE(profile) != AUDIT_NOQUIET && AUDIT_MODE(profile) != AUDIT_ALL) - sa.aad->fs.request &= ~perms->quiet; + aad(&sa)->fs.request &= ~perms->quiet; - if (!sa.aad->fs.request) - return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + if (!aad(&sa)->fs.request) + return COMPLAIN_MODE(profile) ? 0 : aad(&sa)->error; } - sa.aad->fs.denied = sa.aad->fs.request & ~perms->allow; - return aa_audit(type, profile, gfp, &sa, file_audit_cb); + aad(&sa)->fs.denied = aad(&sa)->fs.request & ~perms->allow; + return aa_audit(type, profile, &sa, file_audit_cb); } /** @@ -302,8 +301,8 @@ int aa_path_perm(const char *op, struct aa_profile *profile, if (request & ~perms.allow) error = -EACCES; } - error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name, - NULL, cond->uid, info, error); + error = aa_audit_file(profile, &perms, op, request, name, NULL, + cond->uid, info, error); kfree(buffer); return error; @@ -430,7 +429,7 @@ done_tests: error = 0; audit: - error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request, + error = aa_audit_file(profile, &lperms, OP_LINK, request, lname, tname, cond.uid, info, error); kfree(buffer); kfree(buffer2); diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 956c0b16a30f..fdc4774318ba 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -108,34 +108,53 @@ struct apparmor_audit_data { const char *name; const char *info; union { - void *target; + /* these entries require a custom callback fn */ struct { + struct aa_profile *peer; + struct { + const char *target; + u32 request; + u32 denied; + kuid_t ouid; + } fs; + }; + struct { + const char *name; long pos; const char *ns; - void *target; } iface; struct { int rlim; unsigned long max; } rlim; - struct { - const char *target; - u32 request; - u32 denied; - kuid_t ouid; - } fs; }; }; -/* define a short hand for apparmor_audit_data structure */ -#define aad apparmor_audit_data +/* macros for dealing with apparmor_audit_data structure */ +#define aad(SA) ((SA)->apparmor_audit_data) +#define DEFINE_AUDIT_DATA(NAME, T, X) \ + /* TODO: cleanup audit init so we don't need _aad = {0,} */ \ + struct apparmor_audit_data NAME ## _aad = { .op = (X), }; \ + struct common_audit_data NAME = \ + { \ + .type = (T), \ + .u.tsk = NULL, \ + }; \ + NAME.apparmor_audit_data = &(NAME ## _aad) void aa_audit_msg(int type, struct common_audit_data *sa, void (*cb) (struct audit_buffer *, void *)); -int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, - struct common_audit_data *sa, +int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa, void (*cb) (struct audit_buffer *, void *)); +#define aa_audit_error(ERROR, SA, CB) \ +({ \ + aad((SA))->error = (ERROR); \ + aa_audit_msg(AUDIT_APPARMOR_ERROR, (SA), (CB)); \ + aad((SA))->error; \ +}) + + static inline int complain_error(int error) { if (error == -EPERM || error == -EACCES) diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 0eb54363e033..38f821bf49b6 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -145,7 +145,7 @@ static inline u16 dfa_map_xindex(u16 mask) dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, - gfp_t gfp, const char *op, u32 request, const char *name, + const char *op, u32 request, const char *name, const char *target, kuid_t ouid, const char *info, int error); /** diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 777ac1c47253..edac790923c3 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -25,8 +25,8 @@ static void audit_cb(struct audit_buffer *ab, void *va) { struct common_audit_data *sa = va; - audit_log_format(ab, " target="); - audit_log_untrustedstring(ab, sa->aad->target); + audit_log_format(ab, " peer="); + audit_log_untrustedstring(ab, aad(sa)->peer->base.hname); } /** @@ -40,16 +40,12 @@ static void audit_cb(struct audit_buffer *ab, void *va) static int aa_audit_ptrace(struct aa_profile *profile, struct aa_profile *target, int error) { - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.op = OP_PTRACE; - aad.target = target; - aad.error = error; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE); - return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_ATOMIC, &sa, - audit_cb); + aad(&sa)->peer = target; + aad(&sa)->error = error; + + return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb); } /** diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 5d8ef31a60f1..66475bda6f72 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -120,11 +120,9 @@ const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, void aa_info_message(const char *str) { if (audit_enabled) { - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.info = str; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL); + + aad(&sa)->info = str; aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); } printk(KERN_INFO "AppArmor: %s\n", str); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index c751b033420c..c4bae8ae538f 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -504,11 +504,10 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, static int apparmor_setprocattr(struct task_struct *task, char *name, void *value, size_t size) { - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; char *command, *largs = NULL, *args = value; size_t arg_size; int error; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETPROCATTR); if (size == 0) return -EINVAL; @@ -568,12 +567,9 @@ out: return error; fail: - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.profile = aa_current_profile(); - aad.op = OP_SETPROCATTR; - aad.info = name; - aad.error = error = -EINVAL; + aad(&sa)->profile = aa_current_profile(); + aad(&sa)->info = name; + aad(&sa)->error = error = -EINVAL; aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL); goto out; } diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 17754ee58ff1..bc63cf7b606a 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -588,9 +588,9 @@ static void audit_cb(struct audit_buffer *ab, void *va) { struct common_audit_data *sa = va; - if (sa->aad->iface.ns) { + if (aad(sa)->iface.ns) { audit_log_format(ab, " ns="); - audit_log_untrustedstring(ab, sa->aad->iface.ns); + audit_log_untrustedstring(ab, aad(sa)->iface.ns); } } @@ -606,22 +606,18 @@ static void audit_cb(struct audit_buffer *ab, void *va) * * Returns: the error to be returned after audit is done */ -static int audit_policy(struct aa_profile *profile, const char *op, gfp_t gfp, +static int audit_policy(struct aa_profile *profile, const char *op, const char *nsname, const char *name, const char *info, int error) { - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.op = op; - aad.iface.ns = nsname; - aad.name = name; - aad.info = info; - aad.error = error; - - return aa_audit(AUDIT_APPARMOR_STATUS, profile, gfp, - &sa, audit_cb); + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op); + + aad(&sa)->iface.ns = nsname; + aad(&sa)->name = name; + aad(&sa)->info = info; + aad(&sa)->error = error; + + return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb); } /** @@ -675,11 +671,11 @@ int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, { /* check if loading policy is locked out */ if (aa_g_lock_policy) - return audit_policy(profile, op, GFP_KERNEL, NULL, NULL, + return audit_policy(profile, op, NULL, NULL, "policy_locked", -EACCES); if (!policy_admin_capable(ns)) - return audit_policy(profile, op, GFP_KERNEL, NULL, NULL, + return audit_policy(profile, op, NULL, NULL, "not policy admin", -EACCES); /* TODO: add fine grained mediation of policy loads */ @@ -937,8 +933,8 @@ ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile, list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(profile, op, GFP_ATOMIC, NULL, - ent->new->base.hname, NULL, error); + audit_policy(profile, op, NULL, ent->new->base.hname, + NULL, error); if (ent->old) { __replace_profile(ent->old, ent->new, 1); @@ -993,7 +989,7 @@ fail_lock: /* audit cause of failure */ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; fail: - audit_policy(profile, op, GFP_KERNEL, ns_name, ent->new->base.hname, + audit_policy(profile, op, ns_name, ent->new->base.hname, info, error); /* audit status that rest of profiles in the atomic set failed too */ info = "valid profile in failed atomic policy load"; @@ -1004,7 +1000,7 @@ fail: continue; } op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(profile, op, GFP_KERNEL, ns_name, + audit_policy(profile, op, ns_name, tmp->new->base.hname, info, error); } list_for_each_entry_safe(ent, tmp, &lh, list) { @@ -1079,7 +1075,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *subj, } /* don't fail removal if audit fails */ - (void) audit_policy(subj, OP_PROF_RM, GFP_KERNEL, ns_name, name, info, + (void) audit_policy(subj, OP_PROF_RM, ns_name, name, info, error); aa_put_ns(ns); aa_put_profile(profile); @@ -1090,7 +1086,7 @@ fail_ns_lock: aa_put_ns(ns); fail: - (void) audit_policy(subj, OP_PROF_RM, GFP_KERNEL, ns_name, name, info, + (void) audit_policy(subj, OP_PROF_RM, ns_name, name, info, error); return error; } diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 38c148f33fa4..441efc965f2b 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -79,13 +79,17 @@ struct aa_ext { static void audit_cb(struct audit_buffer *ab, void *va) { struct common_audit_data *sa = va; - if (sa->aad->iface.target) { - struct aa_profile *name = sa->aad->iface.target; + + if (aad(sa)->iface.ns) { + audit_log_format(ab, " ns="); + audit_log_untrustedstring(ab, aad(sa)->iface.ns); + } + if (aad(sa)->iface.name) { audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, name->base.hname); + audit_log_untrustedstring(ab, aad(sa)->iface.name); } - if (sa->aad->iface.pos) - audit_log_format(ab, " offset=%ld", sa->aad->iface.pos); + if (aad(sa)->iface.pos) + audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos); } /** @@ -104,20 +108,18 @@ static int audit_iface(struct aa_profile *new, const char *ns_name, int error) { struct aa_profile *profile = __aa_current_profile(); - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.iface.ns = ns_name; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL); if (e) - aad.iface.pos = e->pos - e->start; - aad.iface.target = new; - aad.name = name; - aad.info = info; - aad.error = error; - - return aa_audit(AUDIT_APPARMOR_STATUS, profile, GFP_KERNEL, &sa, - audit_cb); + aad(&sa)->iface.pos = e->pos - e->start; + aad(&sa)->iface.ns = ns_name; + if (new) + aad(&sa)->iface.name = new->base.hname; + else + aad(&sa)->iface.name = name; + aad(&sa)->info = info; + aad(&sa)->error = error; + + return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb); } void aa_loaddata_kref(struct kref *kref) diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 67a6072ead4b..86a941afd956 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -35,7 +35,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) struct common_audit_data *sa = va; audit_log_format(ab, " rlimit=%s value=%lu", - rlim_names[sa->aad->rlim.rlim], sa->aad->rlim.max); + rlim_names[aad(sa)->rlim.rlim], aad(sa)->rlim.max); } /** @@ -50,17 +50,12 @@ static void audit_cb(struct audit_buffer *ab, void *va) static int audit_resource(struct aa_profile *profile, unsigned int resource, unsigned long value, int error) { - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.op = OP_SETRLIMIT, - aad.rlim.rlim = resource; - aad.rlim.max = value; - aad.error = error; - return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_KERNEL, &sa, - audit_cb); + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETRLIMIT); + + aad(&sa)->rlim.rlim = resource; + aad(&sa)->rlim.max = value; + aad(&sa)->error = error; + return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb); } /** -- cgit v1.2.3 From 5ef50d014c59240c2cac38594377583c4e9ea4fa Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:03 -0800 Subject: apparmor: remove unused op parameter from simple_write_to_buffer() Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 999a43e598f0..fd0d9e38b6c6 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -76,7 +76,6 @@ static int mangle_name(const char *name, char *target) /** * aa_simple_write_to_buffer - common routine for getting policy from user - * @op: operation doing the user buffer copy * @userbuf: user buffer to copy data from (NOT NULL) * @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size) * @copy_size: size of data to copy from user buffer @@ -85,8 +84,7 @@ static int mangle_name(const char *name, char *target) * Returns: kernel buffer containing copy of user buffer data or an * ERR_PTR on failure. */ -static struct aa_loaddata *aa_simple_write_to_buffer(const char *op, - const char __user *userbuf, +static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf, size_t alloc_size, size_t copy_size, loff_t *pos) @@ -130,7 +128,7 @@ static ssize_t policy_update(int binop, const char __user *buf, size_t size, if (error) return error; - data = aa_simple_write_to_buffer(op, buf, size, size, pos); + data = aa_simple_write_to_buffer(buf, size, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { error = aa_replace_profiles(ns ? ns : profile->ns, profile, @@ -196,8 +194,7 @@ static ssize_t profile_remove(struct file *f, const char __user *buf, * aa_remove_profile needs a null terminated string so 1 extra * byte is allocated and the copied data is null terminated. */ - data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, - pos); + data = aa_simple_write_to_buffer(buf, size + 1, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { -- cgit v1.2.3 From c3e1e584ad3853b3f13ea4bd0aabd43e6c9b9fda Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:05 -0800 Subject: apparmor: fix change_hat debug output Signed-off-by: John Johansen --- security/apparmor/procattr.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index 4cb5f3dc906d..a9a9ee6659ae 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -139,12 +139,13 @@ int aa_setprocattr_changehat(char *args, size_t size, int test) for (count = 0; (hat < end) && count < 16; ++count) { char *next = hat + strlen(hat) + 1; hats[count] = hat; + AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d hat '%s'\n" + , __func__, current->pid, token, count, hat); hat = next; } - } - - AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", - __func__, token, hat ? hat : NULL); + } else + AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d Hat '%s'\n", + __func__, current->pid, token, count, ""); return aa_change_hat(hats, count, token, test); } -- cgit v1.2.3 From aa9a39ad8f60cc73e1bd2f18f0693bba6be8b067 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:06 -0800 Subject: apparmor: convert change_profile to use fqname later to give better control Moving the use of fqname to later allows learning profiles to be based on the fqname request instead of just the hname. It also allows cleaning up some of the name parsing and lookup by allowing the use of the fqlookupn_profile() lib fn. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 1 + security/apparmor/domain.c | 61 ++++++++++++-------------------------- security/apparmor/include/domain.h | 4 +-- security/apparmor/lsm.c | 12 ++++---- security/apparmor/procattr.c | 16 ---------- 5 files changed, 28 insertions(+), 66 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index fd0d9e38b6c6..7613a28f157e 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1052,6 +1052,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { AA_FS_FILE_BOOLEAN("change_onexec", 1), AA_FS_FILE_BOOLEAN("change_profile", 1), AA_FS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1), + AA_FS_FILE_STRING("version", "1.2"), { } }; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index d18b3f0e5534..ef4beef06e9d 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -729,8 +729,7 @@ out: /** * aa_change_profile - perform a one-way profile transition - * @ns_name: name of the profile namespace to change to (MAYBE NULL) - * @hname: name of profile to change to (MAYBE NULL) + * @fqname: name of profile may include namespace (NOT NULL) * @onexec: whether this transition is to take place immediately or at exec * @permtest: true if this is just a permission test * @@ -742,19 +741,20 @@ out: * * Returns %0 on success, error otherwise. */ -int aa_change_profile(const char *ns_name, const char *hname, bool onexec, - bool permtest) +int aa_change_profile(const char *fqname, bool onexec, + bool permtest, bool stack) { const struct cred *cred; struct aa_profile *profile, *target = NULL; - struct aa_ns *ns = NULL; struct file_perms perms = {}; - const char *name = NULL, *info = NULL, *op; + const char *info = NULL, *op; int error = 0; u32 request; - if (!hname && !ns_name) + if (!fqname || !*fqname) { + AA_DEBUG("no profile name"); return -EINVAL; + } if (onexec) { request = AA_MAY_ONEXEC; @@ -779,44 +779,15 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, return -EPERM; } - if (ns_name) { - /* released below */ - ns = aa_find_ns(profile->ns, ns_name); - if (!ns) { - /* we don't create new namespace in complain mode */ - name = ns_name; - info = "namespace not found"; - error = -ENOENT; - goto audit; - } - } else - /* released below */ - ns = aa_get_ns(profile->ns); - - /* if the name was not specified, use the name of the current profile */ - if (!hname) { - if (unconfined(profile)) - hname = ns->unconfined->base.hname; - else - hname = profile->base.hname; - } - - perms = change_profile_perms(profile, ns, hname, request, - profile->file.start); - if (!(perms.allow & request)) { - error = -EACCES; - goto audit; - } - - /* released below */ - target = aa_lookup_profile(ns, hname); + target = aa_fqlookupn_profile(profile, fqname, strlen(fqname)); if (!target) { info = "profile not found"; error = -ENOENT; if (permtest || !COMPLAIN_MODE(profile)) goto audit; /* released below */ - target = aa_new_null_profile(profile, false, hname, GFP_KERNEL); + target = aa_new_null_profile(profile, false, fqname, + GFP_KERNEL); if (!target) { info = "failed null profile create"; error = -ENOMEM; @@ -824,6 +795,13 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, } } + perms = change_profile_perms(profile, target->ns, target->base.hname, + request, profile->file.start); + if (!(perms.allow & request)) { + error = -EACCES; + goto audit; + } + /* check if tracing task is allowed to trace target domain */ error = may_change_ptraced_domain(target); if (error) { @@ -841,10 +819,9 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, audit: if (!permtest) - error = aa_audit_file(profile, &perms, op, request, name, - hname, GLOBAL_ROOT_UID, info, error); + error = aa_audit_file(profile, &perms, op, request, NULL, + fqname, GLOBAL_ROOT_UID, info, error); - aa_put_ns(ns); aa_put_profile(target); put_cred(cred); diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464f0a3f..30544729878a 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -30,7 +30,7 @@ void apparmor_bprm_committed_creds(struct linux_binprm *bprm); void aa_free_domain_entries(struct aa_domain *domain); int aa_change_hat(const char *hats[], int count, u64 token, bool permtest); -int aa_change_profile(const char *ns_name, const char *name, bool onexec, - bool permtest); +int aa_change_profile(const char *fqname, bool onexec, bool permtest, + bool stack); #endif /* __AA_DOMAIN_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index c4bae8ae538f..264aa192032e 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -543,17 +543,17 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, error = aa_setprocattr_changehat(args, arg_size, AA_DO_TEST); } else if (strcmp(command, "changeprofile") == 0) { - error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, - !AA_DO_TEST); + error = aa_change_profile(args, !AA_ONEXEC, + !AA_DO_TEST, false); } else if (strcmp(command, "permprofile") == 0) { - error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, - AA_DO_TEST); + error = aa_change_profile(args, !AA_ONEXEC, AA_DO_TEST, + false); } else goto fail; } else if (strcmp(name, "exec") == 0) { if (strcmp(command, "exec") == 0) - error = aa_setprocattr_changeprofile(args, AA_ONEXEC, - !AA_DO_TEST); + error = aa_change_profile(args, AA_ONEXEC, !AA_DO_TEST, + false); else goto fail; } else diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index a9a9ee6659ae..3466a27bca09 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -149,19 +149,3 @@ int aa_setprocattr_changehat(char *args, size_t size, int test) return aa_change_hat(hats, count, token, test); } - -/** - * aa_setprocattr_changeprofile - handle procattr interface to changeprofile - * @fqname: args received from writting to /proc//attr/current (NOT NULL) - * @onexec: true if change_profile should be delayed until exec - * @test: true if this is a test of change_profile permissions - * - * Returns: %0 or error code if change_profile fails - */ -int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test) -{ - char *name, *ns_name; - - name = aa_split_fqname(fqname, &ns_name); - return aa_change_profile(ns_name, name, onexec, test); -} -- cgit v1.2.3 From 31f75bfecd9cef7d485b1cda3c6c38cc0b4a5c6c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:07 -0800 Subject: apparmor: make computing policy hashes conditional on kernel parameter Allow turning off the computation of the policy hashes via the apparmor.hash_policy kernel parameter. Signed-off-by: John Johansen --- security/apparmor/lsm.c | 46 +++++++++++++++++++-------------------- security/apparmor/policy_unpack.c | 15 ++++++++----- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 264aa192032e..6a5cf54cfa72 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -166,42 +166,42 @@ static int common_perm(const char *op, const struct path *path, u32 mask, } /** - * common_perm_dir_dentry - common permission wrapper when path is dir, dentry + * common_perm_cond - common permission wrapper around inode cond * @op: operation being checked - * @dir: directory of the dentry (NOT NULL) - * @dentry: dentry to check (NOT NULL) + * @path: location to check (NOT NULL) * @mask: requested permissions mask - * @cond: conditional info for the permission request (NOT NULL) * * Returns: %0 else error code if error or permission denied */ -static int common_perm_dir_dentry(const char *op, const struct path *dir, - struct dentry *dentry, u32 mask, - struct path_cond *cond) +static int common_perm_cond(const char *op, const struct path *path, u32 mask) { - struct path path = { .mnt = dir->mnt, .dentry = dentry }; + struct path_cond cond = { d_backing_inode(path->dentry)->i_uid, + d_backing_inode(path->dentry)->i_mode + }; - return common_perm(op, &path, mask, cond); + if (!path_mediated_fs(path->dentry)) + return 0; + + return common_perm(op, path, mask, &cond); } /** - * common_perm_path - common permission wrapper when mnt, dentry + * common_perm_dir_dentry - common permission wrapper when path is dir, dentry * @op: operation being checked - * @path: location to check (NOT NULL) + * @dir: directory of the dentry (NOT NULL) + * @dentry: dentry to check (NOT NULL) * @mask: requested permissions mask + * @cond: conditional info for the permission request (NOT NULL) * * Returns: %0 else error code if error or permission denied */ -static inline int common_perm_path(const char *op, const struct path *path, - u32 mask) +static int common_perm_dir_dentry(const char *op, const struct path *dir, + struct dentry *dentry, u32 mask, + struct path_cond *cond) { - struct path_cond cond = { d_backing_inode(path->dentry)->i_uid, - d_backing_inode(path->dentry)->i_mode - }; - if (!path_mediated_fs(path->dentry)) - return 0; + struct path path = { .mnt = dir->mnt, .dentry = dentry }; - return common_perm(op, path, mask, &cond); + return common_perm(op, &path, mask, cond); } /** @@ -274,7 +274,7 @@ static int apparmor_path_mknod(const struct path *dir, struct dentry *dentry, static int apparmor_path_truncate(const struct path *path) { - return common_perm_path(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE); + return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE); } static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry, @@ -333,17 +333,17 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d static int apparmor_path_chmod(const struct path *path, umode_t mode) { - return common_perm_path(OP_CHMOD, path, AA_MAY_CHMOD); + return common_perm_cond(OP_CHMOD, path, AA_MAY_CHMOD); } static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid) { - return common_perm_path(OP_CHOWN, path, AA_MAY_CHOWN); + return common_perm_cond(OP_CHOWN, path, AA_MAY_CHOWN); } static int apparmor_inode_getattr(const struct path *path) { - return common_perm_path(OP_GETATTR, path, AA_MAY_META_READ); + return common_perm_cond(OP_GETATTR, path, AA_MAY_META_READ); } static int apparmor_file_open(struct file *file, const struct cred *cred) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 441efc965f2b..59c891ad1270 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -825,7 +825,8 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, if (error) goto fail_profile; - error = aa_calc_profile_hash(profile, e.version, start, + if (aa_g_hash_policy) + error = aa_calc_profile_hash(profile, e.version, start, e.pos - start); if (error) goto fail_profile; @@ -841,11 +842,13 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, list_add_tail(&ent->list, lh); } udata->abi = e.version & K_ABI_MASK; - udata->hash = aa_calc_hash(udata->data, udata->size); - if (IS_ERR(udata->hash)) { - error = PTR_ERR(udata->hash); - udata->hash = NULL; - goto fail; + if (aa_g_hash_policy) { + udata->hash = aa_calc_hash(udata->data, udata->size); + if (IS_ERR(udata->hash)) { + error = PTR_ERR(udata->hash); + udata->hash = NULL; + goto fail; + } } return 0; -- cgit v1.2.3 From 12eb87d50bfe234c3f964e9fb47bbd0135010c13 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:08 -0800 Subject: apparmor: update cap audit to check SECURITY_CAP_NOAUDIT apparmor should be checking the SECURITY_CAP_NOAUDIT constant. Also in complain mode make it so apparmor can elect to log a message, informing of the check. Signed-off-by: John Johansen --- security/apparmor/capability.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 1d2e2de5515f..ed0a3e6b8022 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "include/apparmor.h" #include "include/capability.h" @@ -55,6 +56,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) * audit_caps - audit a capability * @profile: profile being tested for confinement (NOT NULL) * @cap: capability tested + @audit: whether an audit record should be generated * @error: error code returned by test * * Do auditing of capability and handle, audit/complain/kill modes switching @@ -62,13 +64,16 @@ static void audit_cb(struct audit_buffer *ab, void *va) * * Returns: 0 or sa->error on success, error code on failure */ -static int audit_caps(struct aa_profile *profile, int cap, int error) +static int audit_caps(struct aa_profile *profile, int cap, int audit, + int error) { struct audit_cache *ent; int type = AUDIT_APPARMOR_AUTO; DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE); sa.u.cap = cap; aad(&sa)->error = error; + if (audit == SECURITY_CAP_NOAUDIT) + aad(&sa)->info = "optional: no audit"; if (likely(!error)) { /* test if auditing is being forced */ @@ -129,11 +134,10 @@ int aa_capable(struct aa_profile *profile, int cap, int audit) { int error = profile_capable(profile, cap); - if (!audit) { - if (COMPLAIN_MODE(profile)) - return complain_error(error); - return error; + if (audit == SECURITY_CAP_NOAUDIT) { + if (!COMPLAIN_MODE(profile)) + return error; } - return audit_caps(profile, cap, error); + return audit_caps(profile, cap, audit, error); } -- cgit v1.2.3 From e025be0f26d5597b0a2bdfa65145a0171e77b614 Mon Sep 17 00:00:00 2001 From: William Hua Date: Sun, 15 Jan 2017 16:49:28 -0800 Subject: apparmor: support querying extended trusted helper extra data Allow a profile to carry extra data that can be queried via userspace. This provides a means to store extra data in a profile that a trusted helper can extract and use from live policy. Signed-off-by: William Hua Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 139 +++++++++++++++++++++++++++++++++++++ security/apparmor/include/policy.h | 16 +++++ security/apparmor/lsm.c | 1 + security/apparmor/policy.c | 23 ++++++ security/apparmor/policy_unpack.c | 66 ++++++++++++++++++ 5 files changed, 245 insertions(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 7613a28f157e..6834000640d7 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -213,6 +213,144 @@ static const struct file_operations aa_fs_profile_remove = { .llseek = default_llseek, }; +/** + * query_data - queries a policy and writes its data to buf + * @buf: the resulting data is stored here (NOT NULL) + * @buf_len: size of buf + * @query: query string used to retrieve data + * @query_len: size of query including second NUL byte + * + * The buffers pointed to by buf and query may overlap. The query buffer is + * parsed before buf is written to. + * + * The query should look like "