mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	apparmor: Allow filtering based on secmark policy
Add support for dropping or accepting packets based on their secmark tags. Signed-off-by: Matthew Garrett <mjg59@google.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
		
							parent
							
								
									9caafbe2b4
								
							
						
					
					
						commit
						ab9f211508
					
				
					 2 changed files with 177 additions and 1 deletions
				
			
		| 
						 | 
					@ -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"
 | 
				
			||||||
| 
						 | 
					@ -1030,7 +1032,13 @@ 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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1126,6 +1134,18 @@ static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
 | 
				
			||||||
		ctx->label = aa_get_current_label();
 | 
							ctx->label = aa_get_current_label();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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),
 | 
				
			||||||
| 
						 | 
					@ -1183,6 +1203,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
 | 
				
			||||||
	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),
 | 
				
			||||||
 | 
						LSM_HOOK_INIT(inet_conn_request, apparmor_inet_conn_request),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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 +1559,95 @@ static inline int apparmor_init_sysctl(void)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif /* CONFIG_SYSCTL */
 | 
					#endif /* CONFIG_SYSCTL */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __init apparmor_init(void)
 | 
					static int __init apparmor_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int error;
 | 
						int error;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -188,3 +189,68 @@ 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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue