Commit d81f50bd authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'apparmor-pr-2018-11-01' of...

Merge tag 'apparmor-pr-2018-11-01' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor

Pull apparmor updates from John Johansen:
 "Features/Improvements:
   - replace spin_is_locked() with lockdep
   - add base support for secmark labeling and matching

  Cleanups:
   - clean an indentation issue, remove extraneous space
   - remove no-op permission check in policy_unpack
   - fix checkpatch missing spaces error in Parse secmark policy
   - fix network performance issue in aa_label_sk_perm

  Bug fixes:
   - add #ifdef checks for secmark filtering
   - fix an error code in __aa_create_ns()
   - don't try to replace stale label in ptrace checks
   - fix failure to audit context info in build_change_hat
   - check buffer bounds when mapping permissions mask
   - fully initialize aa_perms struct when answering userspace query
   - fix uninitialized value in aa_split_fqname"

* tag 'apparmor-pr-2018-11-01' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor:
  apparmor: clean an indentation issue, remove extraneous space
  apparmor: fix checkpatch error in Parse secmark policy
  apparmor: add #ifdef checks for secmark filtering
  apparmor: Fix uninitialized value in aa_split_fqname
  apparmor: don't try to replace stale label in ptraceme check
  apparmor: Replace spin_is_locked() with lockdep
  apparmor: Allow filtering based on secmark policy
  apparmor: Parse secmark policy
  apparmor: Add a wildcard secid
  apparmor: don't try to replace stale label in ptrace access check
  apparmor: Fix network performance issue in aa_label_sk_perm
parents c2aa1a44 566f52ec
...@@ -496,7 +496,7 @@ static void update_file_ctx(struct aa_file_ctx *fctx, struct aa_label *label, ...@@ -496,7 +496,7 @@ static void update_file_ctx(struct aa_file_ctx *fctx, struct aa_label *label,
/* update caching of label on file_ctx */ /* update caching of label on file_ctx */
spin_lock(&fctx->lock); spin_lock(&fctx->lock);
old = rcu_dereference_protected(fctx->label, old = rcu_dereference_protected(fctx->label,
spin_is_locked(&fctx->lock)); lockdep_is_held(&fctx->lock));
l = aa_label_merge(old, label, GFP_ATOMIC); l = aa_label_merge(old, label, GFP_ATOMIC);
if (l) { if (l) {
if (l != old) { if (l != old) {
......
...@@ -151,6 +151,8 @@ static inline struct aa_label *begin_current_label_crit_section(void) ...@@ -151,6 +151,8 @@ static inline struct aa_label *begin_current_label_crit_section(void)
{ {
struct aa_label *label = aa_current_raw_label(); struct aa_label *label = aa_current_raw_label();
might_sleep();
if (label_is_stale(label)) { if (label_is_stale(label)) {
label = aa_get_newest_label(label); label = aa_get_newest_label(label);
if (aa_replace_current_label(label) == 0) if (aa_replace_current_label(label) == 0)
......
...@@ -83,6 +83,13 @@ struct aa_sk_ctx { ...@@ -83,6 +83,13 @@ struct aa_sk_ctx {
__e; \ __e; \
}) })
struct aa_secmark {
u8 audit;
u8 deny;
u32 secid;
char *label;
};
extern struct aa_sfs_entry aa_sfs_entry_network[]; extern struct aa_sfs_entry aa_sfs_entry_network[];
void audit_net_cb(struct audit_buffer *ab, void *va); void audit_net_cb(struct audit_buffer *ab, void *va);
...@@ -103,4 +110,7 @@ int aa_sk_perm(const char *op, u32 request, struct sock *sk); ...@@ -103,4 +110,7 @@ int aa_sk_perm(const char *op, u32 request, struct sock *sk);
int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request,
struct socket *sock); struct socket *sock);
int apparmor_secmark_check(struct aa_label *label, char *op, u32 request,
u32 secid, struct sock *sk);
#endif /* __AA_NET_H */ #endif /* __AA_NET_H */
...@@ -155,6 +155,9 @@ struct aa_profile { ...@@ -155,6 +155,9 @@ struct aa_profile {
struct aa_rlimit rlimits; struct aa_rlimit rlimits;
int secmark_count;
struct aa_secmark *secmark;
struct aa_loaddata *rawdata; struct aa_loaddata *rawdata;
unsigned char *hash; unsigned char *hash;
char *dirname; char *dirname;
......
...@@ -22,6 +22,9 @@ struct aa_label; ...@@ -22,6 +22,9 @@ struct aa_label;
/* secid value that will not be allocated */ /* secid value that will not be allocated */
#define AA_SECID_INVALID 0 #define AA_SECID_INVALID 0
/* secid value that matches any other secid */
#define AA_SECID_WILDCARD 1
struct aa_label *aa_secid_to_label(u32 secid); struct aa_label *aa_secid_to_label(u32 secid);
int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid); int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
......
...@@ -90,10 +90,12 @@ const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, ...@@ -90,10 +90,12 @@ const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
const char *end = fqname + n; const char *end = fqname + n;
const char *name = skipn_spaces(fqname, n); const char *name = skipn_spaces(fqname, n);
if (!name)
return NULL;
*ns_name = NULL; *ns_name = NULL;
*ns_len = 0; *ns_len = 0;
if (!name)
return NULL;
if (name[0] == ':') { if (name[0] == ':') {
char *split = strnchr(&name[1], end - &name[1], ':'); char *split = strnchr(&name[1], end - &name[1], ':');
*ns_name = skipn_spaces(&name[1], end - &name[1]); *ns_name = skipn_spaces(&name[1], end - &name[1]);
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/audit.h> #include <linux/audit.h>
#include <linux/user_namespace.h> #include <linux/user_namespace.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <net/sock.h> #include <net/sock.h>
#include "include/apparmor.h" #include "include/apparmor.h"
...@@ -114,13 +116,13 @@ static int apparmor_ptrace_access_check(struct task_struct *child, ...@@ -114,13 +116,13 @@ static int apparmor_ptrace_access_check(struct task_struct *child,
struct aa_label *tracer, *tracee; struct aa_label *tracer, *tracee;
int error; int error;
tracer = begin_current_label_crit_section(); tracer = __begin_current_label_crit_section();
tracee = aa_get_task_label(child); tracee = aa_get_task_label(child);
error = aa_may_ptrace(tracer, tracee, error = aa_may_ptrace(tracer, tracee,
(mode & PTRACE_MODE_READ) ? AA_PTRACE_READ (mode & PTRACE_MODE_READ) ? AA_PTRACE_READ
: AA_PTRACE_TRACE); : AA_PTRACE_TRACE);
aa_put_label(tracee); aa_put_label(tracee);
end_current_label_crit_section(tracer); __end_current_label_crit_section(tracer);
return error; return error;
} }
...@@ -130,11 +132,11 @@ static int apparmor_ptrace_traceme(struct task_struct *parent) ...@@ -130,11 +132,11 @@ static int apparmor_ptrace_traceme(struct task_struct *parent)
struct aa_label *tracer, *tracee; struct aa_label *tracer, *tracee;
int error; int error;
tracee = begin_current_label_crit_section(); tracee = __begin_current_label_crit_section();
tracer = aa_get_task_label(parent); tracer = aa_get_task_label(parent);
error = aa_may_ptrace(tracer, tracee, AA_PTRACE_TRACE); error = aa_may_ptrace(tracer, tracee, AA_PTRACE_TRACE);
aa_put_label(tracer); aa_put_label(tracer);
end_current_label_crit_section(tracee); __end_current_label_crit_section(tracee);
return error; return error;
} }
...@@ -1020,6 +1022,7 @@ static int apparmor_socket_shutdown(struct socket *sock, int how) ...@@ -1020,6 +1022,7 @@ static int apparmor_socket_shutdown(struct socket *sock, int how)
return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock); return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock);
} }
#ifdef CONFIG_NETWORK_SECMARK
/** /**
* apparmor_socket_sock_recv_skb - check perms before associating skb to sk * apparmor_socket_sock_recv_skb - check perms before associating skb to sk
* *
...@@ -1030,8 +1033,15 @@ static int apparmor_socket_shutdown(struct socket *sock, int how) ...@@ -1030,8 +1033,15 @@ static int apparmor_socket_shutdown(struct socket *sock, int how)
*/ */
static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{ {
struct aa_sk_ctx *ctx = SK_CTX(sk);
if (!skb->secmark)
return 0; return 0;
return apparmor_secmark_check(ctx->label, OP_RECVMSG, AA_MAY_RECEIVE,
skb->secmark, sk);
} }
#endif
static struct aa_label *sk_peer_label(struct sock *sk) static struct aa_label *sk_peer_label(struct sock *sk)
...@@ -1126,6 +1136,20 @@ static void apparmor_sock_graft(struct sock *sk, struct socket *parent) ...@@ -1126,6 +1136,20 @@ static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
ctx->label = aa_get_current_label(); ctx->label = aa_get_current_label();
} }
#ifdef CONFIG_NETWORK_SECMARK
static int apparmor_inet_conn_request(struct sock *sk, struct sk_buff *skb,
struct request_sock *req)
{
struct aa_sk_ctx *ctx = SK_CTX(sk);
if (!skb->secmark)
return 0;
return apparmor_secmark_check(ctx->label, OP_CONNECT, AA_MAY_CONNECT,
skb->secmark, sk);
}
#endif
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
...@@ -1177,12 +1201,17 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { ...@@ -1177,12 +1201,17 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt),
LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt),
LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown),
#ifdef CONFIG_NETWORK_SECMARK
LSM_HOOK_INIT(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb), LSM_HOOK_INIT(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb),
#endif
LSM_HOOK_INIT(socket_getpeersec_stream, LSM_HOOK_INIT(socket_getpeersec_stream,
apparmor_socket_getpeersec_stream), apparmor_socket_getpeersec_stream),
LSM_HOOK_INIT(socket_getpeersec_dgram, LSM_HOOK_INIT(socket_getpeersec_dgram,
apparmor_socket_getpeersec_dgram), apparmor_socket_getpeersec_dgram),
LSM_HOOK_INIT(sock_graft, apparmor_sock_graft), LSM_HOOK_INIT(sock_graft, apparmor_sock_graft),
#ifdef CONFIG_NETWORK_SECMARK
LSM_HOOK_INIT(inet_conn_request, apparmor_inet_conn_request),
#endif
LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank),
LSM_HOOK_INIT(cred_free, apparmor_cred_free), LSM_HOOK_INIT(cred_free, apparmor_cred_free),
...@@ -1538,6 +1567,97 @@ static inline int apparmor_init_sysctl(void) ...@@ -1538,6 +1567,97 @@ static inline int apparmor_init_sysctl(void)
} }
#endif /* CONFIG_SYSCTL */ #endif /* CONFIG_SYSCTL */
#if defined(CONFIG_NETFILTER) && defined(CONFIG_NETWORK_SECMARK)
static unsigned int apparmor_ip_postroute(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct aa_sk_ctx *ctx;
struct sock *sk;
if (!skb->secmark)
return NF_ACCEPT;
sk = skb_to_full_sk(skb);
if (sk == NULL)
return NF_ACCEPT;
ctx = SK_CTX(sk);
if (!apparmor_secmark_check(ctx->label, OP_SENDMSG, AA_MAY_SEND,
skb->secmark, sk))
return NF_ACCEPT;
return NF_DROP_ERR(-ECONNREFUSED);
}
static unsigned int apparmor_ipv4_postroute(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
return apparmor_ip_postroute(priv, skb, state);
}
static unsigned int apparmor_ipv6_postroute(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
return apparmor_ip_postroute(priv, skb, state);
}
static const struct nf_hook_ops apparmor_nf_ops[] = {
{
.hook = apparmor_ipv4_postroute,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_SELINUX_FIRST,
},
#if IS_ENABLED(CONFIG_IPV6)
{
.hook = apparmor_ipv6_postroute,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP6_PRI_SELINUX_FIRST,
},
#endif
};
static int __net_init apparmor_nf_register(struct net *net)
{
int ret;
ret = nf_register_net_hooks(net, apparmor_nf_ops,
ARRAY_SIZE(apparmor_nf_ops));
return ret;
}
static void __net_exit apparmor_nf_unregister(struct net *net)
{
nf_unregister_net_hooks(net, apparmor_nf_ops,
ARRAY_SIZE(apparmor_nf_ops));
}
static struct pernet_operations apparmor_net_ops = {
.init = apparmor_nf_register,
.exit = apparmor_nf_unregister,
};
static int __init apparmor_nf_ip_init(void)
{
int err;
if (!apparmor_enabled)
return 0;
err = register_pernet_subsys(&apparmor_net_ops);
if (err)
panic("Apparmor: register_pernet_subsys: error %d\n", err);
return 0;
}
__initcall(apparmor_nf_ip_init);
#endif
static int __init apparmor_init(void) static int __init apparmor_init(void)
{ {
int error; int error;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "include/label.h" #include "include/label.h"
#include "include/net.h" #include "include/net.h"
#include "include/policy.h" #include "include/policy.h"
#include "include/secid.h"
#include "net_names.h" #include "net_names.h"
...@@ -146,17 +147,20 @@ int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, ...@@ -146,17 +147,20 @@ int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family,
static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request, static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request,
struct sock *sk) struct sock *sk)
{ {
struct aa_profile *profile; int error = 0;
DEFINE_AUDIT_SK(sa, op, sk);
AA_BUG(!label); AA_BUG(!label);
AA_BUG(!sk); AA_BUG(!sk);
if (unconfined(label)) if (!unconfined(label)) {
return 0; struct aa_profile *profile;
DEFINE_AUDIT_SK(sa, op, sk);
return fn_for_each_confined(label, profile, error = fn_for_each_confined(label, profile,
aa_profile_af_sk_perm(profile, &sa, request, sk)); aa_profile_af_sk_perm(profile, &sa, request, sk));
}
return error;
} }
int aa_sk_perm(const char *op, u32 request, struct sock *sk) int aa_sk_perm(const char *op, u32 request, struct sock *sk)
...@@ -185,3 +189,70 @@ int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, ...@@ -185,3 +189,70 @@ int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request,
return aa_label_sk_perm(label, op, request, sock->sk); return aa_label_sk_perm(label, op, request, sock->sk);
} }
#ifdef CONFIG_NETWORK_SECMARK
static int apparmor_secmark_init(struct aa_secmark *secmark)
{
struct aa_label *label;
if (secmark->label[0] == '*') {
secmark->secid = AA_SECID_WILDCARD;
return 0;
}
label = aa_label_strn_parse(&root_ns->unconfined->label,
secmark->label, strlen(secmark->label),
GFP_ATOMIC, false, false);
if (IS_ERR(label))
return PTR_ERR(label);
secmark->secid = label->secid;
return 0;
}
static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
struct common_audit_data *sa, struct sock *sk)
{
int i, ret;
struct aa_perms perms = { };
if (profile->secmark_count == 0)
return 0;
for (i = 0; i < profile->secmark_count; i++) {
if (!profile->secmark[i].secid) {
ret = apparmor_secmark_init(&profile->secmark[i]);
if (ret)
return ret;
}
if (profile->secmark[i].secid == secid ||
profile->secmark[i].secid == AA_SECID_WILDCARD) {
if (profile->secmark[i].deny)
perms.deny = ALL_PERMS_MASK;
else
perms.allow = ALL_PERMS_MASK;
if (profile->secmark[i].audit)
perms.audit = ALL_PERMS_MASK;
}
}
aa_apply_modes_to_perms(profile, &perms);
return aa_check_perms(profile, &perms, request, sa, audit_net_cb);
}
int apparmor_secmark_check(struct aa_label *label, char *op, u32 request,
u32 secid, struct sock *sk)
{
struct aa_profile *profile;
DEFINE_AUDIT_SK(sa, op, sk);
return fn_for_each_confined(label, profile,
aa_secmark_perm(profile, request, secid,
&sa, sk));
}
#endif
...@@ -231,6 +231,9 @@ void aa_free_profile(struct aa_profile *profile) ...@@ -231,6 +231,9 @@ void aa_free_profile(struct aa_profile *profile)
for (i = 0; i < profile->xattr_count; i++) for (i = 0; i < profile->xattr_count; i++)
kzfree(profile->xattrs[i]); kzfree(profile->xattrs[i]);
kzfree(profile->xattrs); kzfree(profile->xattrs);
for (i = 0; i < profile->secmark_count; i++)
kzfree(profile->secmark[i].label);
kzfree(profile->secmark);
kzfree(profile->dirname); kzfree(profile->dirname);
aa_put_dfa(profile->xmatch); aa_put_dfa(profile->xmatch);
aa_put_dfa(profile->policy.dfa); aa_put_dfa(profile->policy.dfa);
......
...@@ -292,6 +292,19 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) ...@@ -292,6 +292,19 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name)
return 0; return 0;
} }
static bool unpack_u8(struct aa_ext *e, u8 *data, const char *name)
{
if (unpack_nameX(e, AA_U8, name)) {
if (!inbounds(e, sizeof(u8)))
return 0;
if (data)
*data = get_unaligned((u8 *)e->pos);
e->pos += sizeof(u8);
return 1;
}
return 0;
}
static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
{ {
if (unpack_nameX(e, AA_U32, name)) { if (unpack_nameX(e, AA_U32, name)) {
...@@ -529,6 +542,49 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) ...@@ -529,6 +542,49 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile)
return 0; return 0;
} }
static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile)
{
void *pos = e->pos;
int i, size;
if (unpack_nameX(e, AA_STRUCT, "secmark")) {
size = unpack_array(e, NULL);
profile->secmark = kcalloc(size, sizeof(struct aa_secmark),
GFP_KERNEL);
if (!profile->secmark)
goto fail;
profile->secmark_count = size;
for (i = 0; i < size; i++) {
if (!unpack_u8(e, &profile->secmark[i].audit, NULL))
goto fail;
if (!unpack_u8(e, &profile->secmark[i].deny, NULL))
goto fail;
if (!unpack_strdup(e, &profile->secmark[i].label, NULL))
goto fail;
}
if (!unpack_nameX(e, AA_ARRAYEND, NULL))
goto fail;
if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail;
}
return 1;
fail:
if (profile->secmark) {
for (i = 0; i < size; i++)
kfree(profile->secmark[i].label);
kfree(profile->secmark);
profile->secmark_count = 0;
}
e->pos = pos;
return 0;
}
static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
{ {
void *pos = e->pos; void *pos = e->pos;
...@@ -727,6 +783,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -727,6 +783,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
goto fail; goto fail;
} }
if (!unpack_secmark(e, profile)) {
info = "failed to unpack profile secmark rules";
goto fail;
}
if (unpack_nameX(e, AA_STRUCT, "policydb")) { if (unpack_nameX(e, AA_STRUCT, "policydb")) {
/* generic policy dfa - optional and may be NULL */ /* generic policy dfa - optional and may be NULL */
info = "failed to unpack policydb"; info = "failed to unpack policydb";
......
...@@ -32,8 +32,7 @@ ...@@ -32,8 +32,7 @@
* secids - do not pin labels with a refcount. They rely on the label * secids - do not pin labels with a refcount. They rely on the label
* properly updating/freeing them * properly updating/freeing them
*/ */
#define AA_FIRST_SECID 2
#define AA_FIRST_SECID 1
static DEFINE_IDR(aa_secids); static DEFINE_IDR(aa_secids);
static DEFINE_SPINLOCK(secid_lock); static DEFINE_SPINLOCK(secid_lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment