Add an LSM function security_reconcile_netlbl() which uses the new LSM hook socket_netlbl_secattr() to decide if the active security modules are in agreement regarding the labeling of a network packet. Signed-off-by: Casey Schaufler <casey@xxxxxxxxxxxxxxxx> --- include/linux/lsm_hooks.h | 15 +++++++++ include/linux/security.h | 9 ++++++ security/security.c | 50 +++++++++++++++++++++++++++++ security/selinux/hooks.c | 3 ++ security/selinux/include/netlabel.h | 7 ++++ security/selinux/netlabel.c | 9 ++++++ security/smack/smack_lsm.c | 9 ++++++ 7 files changed, 102 insertions(+) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 67797c67093b..4bf88fa5b55d 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -29,6 +29,9 @@ #include <linux/init.h> #include <linux/rculist.h> +#ifdef CONFIG_NETLABEL +struct netlbl_lsm_secattr; +#endif /** * union security_list_options - Linux Security Module hook function list * @@ -1432,6 +1435,10 @@ * @bpf_prog_free_security: * Clean up the security information stored inside bpf prog. * + * Security hooks for network labeling (Netlabel) operations. + * + * @socket_netlbl_secattr: + * Report the netlabel attributes this module wants for this socket. */ union security_list_options { int (*binder_set_context_mgr)(struct task_struct *mgr); @@ -1788,6 +1795,11 @@ union security_list_options { int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux); void (*bpf_prog_free_security)(struct bpf_prog_aux *aux); #endif /* CONFIG_BPF_SYSCALL */ +#ifdef CONFIG_NETLABEL + void (*socket_netlbl_secattr)(struct sock *sk, + struct netlbl_lsm_secattr **secattr, + int *set); +#endif }; struct security_hook_heads { @@ -2025,6 +2037,9 @@ struct security_hook_heads { struct hlist_head bpf_prog_alloc_security; struct hlist_head bpf_prog_free_security; #endif /* CONFIG_BPF_SYSCALL */ +#ifdef CONFIG_NETLABEL + struct hlist_head socket_netlbl_secattr; +#endif } __randomize_layout; /* diff --git a/include/linux/security.h b/include/linux/security.h index 0e699d4ed13a..c234d881c206 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1934,5 +1934,14 @@ static inline void security_bpf_prog_free(struct bpf_prog_aux *aux) #endif /* CONFIG_SECURITY */ #endif /* CONFIG_BPF_SYSCALL */ +#ifdef CONFIG_NETLABEL +extern int security_reconcile_netlbl(struct sock *sk); +#else +static inline int security_reconcile_netlbl(struct sock *sk) +{ + return 0; +} +#endif + #endif /* ! __LINUX_SECURITY_H */ diff --git a/security/security.c b/security/security.c index e726fc7c6712..bfe40c11f5bf 100644 --- a/security/security.c +++ b/security/security.c @@ -34,6 +34,9 @@ #include <linux/binfmts.h> #include <net/flow.h> #include <net/sock.h> +#ifdef CONFIG_NETLABEL +#include <net/netlabel.h> +#endif #define MAX_LSM_EVM_XATTR 2 @@ -3003,3 +3006,50 @@ void security_bpf_prog_free(struct bpf_prog_aux *aux) call_void_hook(bpf_prog_free_security, aux); } #endif /* CONFIG_BPF_SYSCALL */ + +#ifdef CONFIG_NETLABEL +int security_reconcile_netlbl(struct sock *sk) +{ + struct netlbl_lsm_secattr *prev = NULL; + struct netlbl_lsm_secattr *this = NULL; + int prev_set = 0; + int this_set = 0; + struct security_hook_list *hp; + + hlist_for_each_entry(hp, &security_hook_heads.socket_netlbl_secattr, + list) { + hp->hook.socket_netlbl_secattr(sk, &this, &this_set); + if (this_set == 0 || this == NULL) + continue; + if (prev != NULL) { + /* + * Both unlabeled is easily acceptable. + */ + if (prev_set == NETLBL_NLTYPE_UNLABELED && + this_set == NETLBL_NLTYPE_UNLABELED) + continue; + /* + * The nltype being different means that + * the secattrs aren't comparible. Except + * that ADDRSELECT means that couldn't know + * when the socket was created. + */ + if (prev_set != this_set && + prev_set != NETLBL_NLTYPE_ADDRSELECT && + this_set != NETLBL_NLTYPE_ADDRSELECT) + return -EACCES; + /* + * Count on the Netlabel system's judgement. + */ + if (!netlbl_secattr_equal(prev, this)) + return -EACCES; + } + prev = this; + prev_set = this_set; + } + /* + * No conflicts have been found. + */ + return 0; +} +#endif /* CONFIG_NETLABEL */ diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 91ef2ae77abb..48468a4b478c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6887,6 +6887,9 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free), LSM_HOOK_INIT(bpf_prog_free_security, selinux_bpf_prog_free), #endif +#ifdef CONFIG_NETLABEL + LSM_HOOK_INIT(socket_netlbl_secattr, selinux_socket_netlbl_secattr), +#endif }; static __init int selinux_init(void) diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h index 8671de09c363..b316c62e7bcc 100644 --- a/security/selinux/include/netlabel.h +++ b/security/selinux/include/netlabel.h @@ -69,6 +69,9 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr); int selinux_netlbl_socket_connect_locked(struct sock *sk, struct sockaddr *addr); +void selinux_socket_netlbl_secattr(struct sock *sk, + struct netlbl_lsm_secattr **secattr, + int *set); #else static inline void selinux_netlbl_cache_invalidate(void) @@ -165,6 +168,10 @@ static inline int selinux_netlbl_socket_connect_locked(struct sock *sk, { return 0; } +static inline void selinux_socket_netlbl_secattr(struct sock *sk, + struct netlbl_lsm_secattr **secattr) +{ +} #endif /* CONFIG_NETLABEL */ #endif diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 56e780340114..0f50a646c8cd 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -642,3 +642,12 @@ int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr) return rc; } + +void selinux_socket_netlbl_secattr(struct sock *sk, + struct netlbl_lsm_secattr **secattr, + int *set) +{ + struct sk_security_struct *sksec = selinux_sock(sk); + *secattr = sksec->nlbl_secattr; + *set = sksec->nlbl_set; +} diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 87c81cbc8c67..122c13604d28 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -4572,6 +4572,14 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode, } return 0; } +void smack_socket_netlbl_secattr(struct sock *sk, + struct netlbl_lsm_secattr **secattr, + int *set) +{ + struct socket_smack *ssp = smack_sock(sk); + *secattr = &ssp->smk_out->smk_netlabel; + *set = ssp->smk_set; +} struct lsm_blob_sizes smack_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct task_smack), @@ -4733,6 +4741,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(inode_copy_up, smack_inode_copy_up), LSM_HOOK_INIT(inode_copy_up_xattr, smack_inode_copy_up_xattr), LSM_HOOK_INIT(dentry_create_files_as, smack_dentry_create_files_as), + LSM_HOOK_INIT(socket_netlbl_secattr, smack_socket_netlbl_secattr), }; -- 2.20.1