mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-06 09:21:00 +01:00
400 lines
11 KiB
Diff
400 lines
11 KiB
Diff
---
|
|
security/apparmor/Makefile | 7 +
|
|
security/apparmor/apparmor.h | 9 ++
|
|
security/apparmor/lsm.c | 129 ++++++++++++++++++++++++++++++++++-
|
|
security/apparmor/main.c | 107 ++++++++++++++++++++++++++++-
|
|
security/apparmor/module_interface.c | 26 ++++++-
|
|
5 files changed, 271 insertions(+), 7 deletions(-)
|
|
|
|
--- a/security/apparmor/Makefile
|
|
+++ b/security/apparmor/Makefile
|
|
@@ -8,6 +8,11 @@ apparmor-y := main.o list.o procattr.o l
|
|
quiet_cmd_make-caps = GEN $@
|
|
cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@
|
|
|
|
-$(obj)/main.o : $(obj)/capability_names.h
|
|
+quiet_cmd_make-af = GEN $@
|
|
+cmd_make-af = sed -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "s/^\#define[ \\t]\\+AF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@
|
|
+
|
|
+$(obj)/main.o : $(obj)/capability_names.h $(obj)/af_names.h
|
|
$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
|
|
$(call cmd,make-caps)
|
|
+$(obj)/af_names.h : $(srctree)/include/linux/socket.h
|
|
+ $(call cmd,make-af)
|
|
--- a/security/apparmor/apparmor.h
|
|
+++ b/security/apparmor/apparmor.h
|
|
@@ -16,6 +16,8 @@
|
|
#include <linux/fs.h>
|
|
#include <linux/binfmts.h>
|
|
#include <linux/rcupdate.h>
|
|
+#include <linux/socket.h>
|
|
+#include <net/sock.h>
|
|
|
|
/*
|
|
* We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags
|
|
@@ -208,6 +210,9 @@ struct aa_profile {
|
|
struct list_head task_contexts;
|
|
spinlock_t lock;
|
|
unsigned long int_flags;
|
|
+ u16 network_families[AF_MAX];
|
|
+ u16 audit_network[AF_MAX];
|
|
+ u16 quiet_network[AF_MAX];
|
|
};
|
|
|
|
extern struct list_head profile_ns_list;
|
|
@@ -254,6 +259,7 @@ struct aa_audit {
|
|
int request_mask, denied_mask, audit_mask;
|
|
struct iattr *iattr;
|
|
pid_t task, parent;
|
|
+ int family, type, protocol;
|
|
int error_code;
|
|
};
|
|
|
|
@@ -315,6 +321,9 @@ extern void aa_change_task_context(struc
|
|
struct aa_profile *previous_profile);
|
|
extern int aa_may_ptrace(struct aa_task_context *cxt,
|
|
struct aa_profile *tracee);
|
|
+extern int aa_net_perm(struct aa_profile *profile, char *operation,
|
|
+ int family, int type, int protocol);
|
|
+extern int aa_revalidate_sk(struct sock *sk, char *operation);
|
|
|
|
/* lsm.c */
|
|
extern int apparmor_initialized;
|
|
--- a/security/apparmor/lsm.c
|
|
+++ b/security/apparmor/lsm.c
|
|
@@ -18,6 +18,7 @@
|
|
#include <linux/ctype.h>
|
|
#include <linux/sysctl.h>
|
|
#include <linux/audit.h>
|
|
+#include <net/sock.h>
|
|
|
|
#include "apparmor.h"
|
|
#include "inline.h"
|
|
@@ -663,6 +664,117 @@ static void apparmor_task_free_security(
|
|
aa_release(task);
|
|
}
|
|
|
|
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
|
|
+{
|
|
+ struct aa_profile *profile;
|
|
+ int error = 0;
|
|
+
|
|
+ if (kern)
|
|
+ return 0;
|
|
+
|
|
+ profile = aa_get_profile(current);
|
|
+ if (profile)
|
|
+ error = aa_net_perm(profile, "socket_create", family,
|
|
+ type, protocol);
|
|
+ aa_put_profile(profile);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+static int apparmor_socket_post_create(struct socket *sock, int family,
|
|
+ int type, int protocol, int kern)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+
|
|
+ if (kern)
|
|
+ return 0;
|
|
+
|
|
+ return aa_revalidate_sk(sk, "socket_post_create");
|
|
+}
|
|
+
|
|
+static int apparmor_socket_bind(struct socket *sock,
|
|
+ struct sockaddr *address, int addrlen)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+
|
|
+ return aa_revalidate_sk(sk, "socket_bind");
|
|
+}
|
|
+
|
|
+static int apparmor_socket_connect(struct socket *sock,
|
|
+ struct sockaddr *address, int addrlen)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+
|
|
+ return aa_revalidate_sk(sk, "socket_connect");
|
|
+}
|
|
+
|
|
+static int apparmor_socket_listen(struct socket *sock, int backlog)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+
|
|
+ return aa_revalidate_sk(sk, "socket_listen");
|
|
+}
|
|
+
|
|
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+
|
|
+ return aa_revalidate_sk(sk, "socket_accept");
|
|
+}
|
|
+
|
|
+static int apparmor_socket_sendmsg(struct socket *sock,
|
|
+ struct msghdr *msg, int size)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+
|
|
+ return aa_revalidate_sk(sk, "socket_sendmsg");
|
|
+}
|
|
+
|
|
+static int apparmor_socket_recvmsg(struct socket *sock,
|
|
+ struct msghdr *msg, int size, int flags)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+
|
|
+ return aa_revalidate_sk(sk, "socket_recvmsg");
|
|
+}
|
|
+
|
|
+static int apparmor_socket_getsockname(struct socket *sock)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+
|
|
+ return aa_revalidate_sk(sk, "socket_getsockname");
|
|
+}
|
|
+
|
|
+static int apparmor_socket_getpeername(struct socket *sock)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+
|
|
+ return aa_revalidate_sk(sk, "socket_getpeername");
|
|
+}
|
|
+
|
|
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
|
|
+ int optname)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+
|
|
+ return aa_revalidate_sk(sk, "socket_getsockopt");
|
|
+}
|
|
+
|
|
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
|
|
+ int optname)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+
|
|
+ return aa_revalidate_sk(sk, "socket_setsockopt");
|
|
+}
|
|
+
|
|
+static int apparmor_socket_shutdown(struct socket *sock, int how)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+
|
|
+ return aa_revalidate_sk(sk, "socket_shutdown");
|
|
+}
|
|
+
|
|
static int apparmor_getprocattr(struct task_struct *task, char *name,
|
|
char **value)
|
|
{
|
|
@@ -763,9 +875,6 @@ struct security_operations apparmor_ops
|
|
.capable = apparmor_capable,
|
|
.syslog = cap_syslog,
|
|
|
|
- .netlink_send = cap_netlink_send,
|
|
- .netlink_recv = cap_netlink_recv,
|
|
-
|
|
.bprm_apply_creds = cap_bprm_apply_creds,
|
|
.bprm_set_security = apparmor_bprm_set_security,
|
|
.bprm_secureexec = apparmor_bprm_secureexec,
|
|
@@ -801,6 +910,20 @@ struct security_operations apparmor_ops
|
|
|
|
.getprocattr = apparmor_getprocattr,
|
|
.setprocattr = apparmor_setprocattr,
|
|
+
|
|
+ .socket_create = apparmor_socket_create,
|
|
+ .socket_post_create = apparmor_socket_post_create,
|
|
+ .socket_bind = apparmor_socket_bind,
|
|
+ .socket_connect = apparmor_socket_connect,
|
|
+ .socket_listen = apparmor_socket_listen,
|
|
+ .socket_accept = apparmor_socket_accept,
|
|
+ .socket_sendmsg = apparmor_socket_sendmsg,
|
|
+ .socket_recvmsg = apparmor_socket_recvmsg,
|
|
+ .socket_getsockname = apparmor_socket_getsockname,
|
|
+ .socket_getpeername = apparmor_socket_getpeername,
|
|
+ .socket_getsockopt = apparmor_socket_getsockopt,
|
|
+ .socket_setsockopt = apparmor_socket_setsockopt,
|
|
+ .socket_shutdown = apparmor_socket_shutdown,
|
|
};
|
|
|
|
void info_message(const char *str)
|
|
--- a/security/apparmor/main.c
|
|
+++ b/security/apparmor/main.c
|
|
@@ -14,6 +14,9 @@
|
|
#include <linux/audit.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/ptrace.h>
|
|
+#include <linux/socket.h>
|
|
+#include <linux/net.h>
|
|
+#include <net/sock.h>
|
|
|
|
#include "apparmor.h"
|
|
|
|
@@ -116,6 +119,24 @@ static void aa_audit_file_mask(struct au
|
|
audit_log_format(ab, " %s=\"%s::%s\"", name, user, other);
|
|
}
|
|
|
|
+static const char *address_families[] = {
|
|
+#include "af_names.h"
|
|
+};
|
|
+
|
|
+static const char *sock_types[] = {
|
|
+ "unknown(0)",
|
|
+ "stream",
|
|
+ "dgram",
|
|
+ "raw",
|
|
+ "rdm",
|
|
+ "seqpacket",
|
|
+ "dccp",
|
|
+ "unknown(7)",
|
|
+ "unknown(8)",
|
|
+ "unknown(9)",
|
|
+ "packet",
|
|
+};
|
|
+
|
|
/**
|
|
* aa_audit - Log an audit event to the audit subsystem
|
|
* @profile: profile to check against
|
|
@@ -187,7 +208,25 @@ static int aa_audit_base(struct aa_profi
|
|
audit_log_untrustedstring(ab, sa->name2);
|
|
}
|
|
|
|
- audit_log_format(ab, " pid=%d", current->pid);
|
|
+ if (sa->family || sa->type) {
|
|
+ if (address_families[sa->family])
|
|
+ audit_log_format(ab, " family=\"%s\"",
|
|
+ address_families[sa->family]);
|
|
+ else
|
|
+ audit_log_format(ab, " family=\"unknown(%d)\"",
|
|
+ sa->family);
|
|
+
|
|
+ if (sock_types[sa->type])
|
|
+ audit_log_format(ab, " sock_type=\"%s\"",
|
|
+ sock_types[sa->type]);
|
|
+ else
|
|
+ audit_log_format(ab, " sock_type=\"unknown(%d)\"",
|
|
+ sa->type);
|
|
+
|
|
+ audit_log_format(ab, " protocol=%d", sa->protocol);
|
|
+ }
|
|
+
|
|
+ audit_log_format(ab, " pid=%d", current->pid);
|
|
|
|
if (profile) {
|
|
audit_log_format(ab, " profile=");
|
|
@@ -768,6 +807,72 @@ int aa_link(struct aa_profile *profile,
|
|
return error;
|
|
}
|
|
|
|
+int aa_net_perm(struct aa_profile *profile, char *operation,
|
|
+ int family, int type, int protocol)
|
|
+{
|
|
+ struct aa_audit sa;
|
|
+ int error = 0;
|
|
+ u16 family_mask, audit_mask, quiet_mask;
|
|
+
|
|
+ if ((family < 0) || (family >= AF_MAX))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if ((type < 0) || (type >= SOCK_MAX))
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* unix domain and netlink sockets are handled by ipc */
|
|
+ if (family == AF_UNIX || family == AF_NETLINK)
|
|
+ return 0;
|
|
+
|
|
+ family_mask = profile->network_families[family];
|
|
+ audit_mask = profile->audit_network[family];
|
|
+ quiet_mask = profile->quiet_network[family];
|
|
+
|
|
+ error = (family_mask & (1 << type)) ? 0 : -EACCES;
|
|
+
|
|
+ memset(&sa, 0, sizeof(sa));
|
|
+ sa.operation = operation;
|
|
+ sa.gfp_mask = GFP_KERNEL;
|
|
+ sa.family = family;
|
|
+ sa.type = type;
|
|
+ sa.protocol = protocol;
|
|
+ sa.error_code = error;
|
|
+
|
|
+ if (likely(!error)) {
|
|
+ if (!PROFILE_AUDIT(profile) && !(family_mask & audit_mask))
|
|
+ return 0;
|
|
+ } else if (!((1 << type) & ~quiet_mask)) {
|
|
+ return error;
|
|
+ }
|
|
+
|
|
+ error = aa_audit(profile, &sa);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+int aa_revalidate_sk(struct sock *sk, char *operation)
|
|
+{
|
|
+ struct aa_profile *profile;
|
|
+ int error = 0;
|
|
+
|
|
+ /* this is some debugging code to flush out the network hooks that
|
|
+ that are called in interrupt context */
|
|
+ if (in_interrupt()) {
|
|
+ printk("AppArmor Debug: Hook being called from interrupt context\n");
|
|
+ dump_stack();
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ profile = aa_get_profile(current);
|
|
+ if (profile)
|
|
+ error = aa_net_perm(profile, operation,
|
|
+ sk->sk_family, sk->sk_type,
|
|
+ sk->sk_protocol);
|
|
+ aa_put_profile(profile);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
/*******************************
|
|
* Global task related functions
|
|
*******************************/
|
|
--- a/security/apparmor/module_interface.c
|
|
+++ b/security/apparmor/module_interface.c
|
|
@@ -320,8 +320,8 @@ static struct aa_profile *aa_unpack_prof
|
|
struct aa_audit *sa)
|
|
{
|
|
struct aa_profile *profile = NULL;
|
|
-
|
|
- int error = -EPROTO;
|
|
+ size_t size = 0;
|
|
+ int i, error = -EPROTO;
|
|
|
|
profile = alloc_aa_profile();
|
|
if (!profile)
|
|
@@ -354,6 +354,28 @@ static struct aa_profile *aa_unpack_prof
|
|
if (!aa_is_u32(e, &(profile->set_caps), NULL))
|
|
goto fail;
|
|
|
|
+ size = aa_is_array(e, "net_allowed_af");
|
|
+ if (size) {
|
|
+ if (size > AF_MAX)
|
|
+ goto fail;
|
|
+
|
|
+ for (i = 0; i < size; i++) {
|
|
+ if (!aa_is_u16(e, &profile->network_families[i], NULL))
|
|
+ goto fail;
|
|
+ if (!aa_is_u16(e, &profile->audit_network[i], NULL))
|
|
+ goto fail;
|
|
+ if (!aa_is_u16(e, &profile->quiet_network[i], NULL))
|
|
+ goto fail;
|
|
+ }
|
|
+ if (!aa_is_nameX(e, AA_ARRAYEND, NULL))
|
|
+ goto fail;
|
|
+ /* allow unix domain and netlink sockets they are handled
|
|
+ * by IPC
|
|
+ */
|
|
+ }
|
|
+ profile->network_families[AF_UNIX] = 0xffff;
|
|
+ profile->network_families[AF_NETLINK] = 0xffff;
|
|
+
|
|
/* get file rules */
|
|
profile->file_rules = aa_unpack_dfa(e);
|
|
if (IS_ERR(profile->file_rules)) {
|