Signed-off-by: John Johansen Signed-off-by: Jesse Michael --- security/apparmor/Makefile | 7 + security/apparmor/apparmor.h | 7 + security/apparmor/lsm.c | 129 ++++++++++++++++++++++++++++++++++- security/apparmor/main.c | 96 ++++++++++++++++++++++++++ security/apparmor/module_interface.c | 20 +++++ 5 files changed, 255 insertions(+), 4 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 #include #include +#include +#include /* * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags @@ -169,6 +171,7 @@ struct aa_profile { struct list_head task_contexts; spinlock_t lock; unsigned long int_flags; + u16 network_families[AF_MAX]; }; extern struct list_head profile_ns_list; @@ -215,6 +218,7 @@ struct aa_audit { int request_mask, denied_mask; struct iattr *iattr; pid_t task, parent; + int family, type, protocol; int error_code; }; @@ -276,6 +280,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); /* list.c */ extern struct aa_namespace *__aa_find_namespace(const char *name, --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "apparmor.h" #include "inline.h" @@ -616,6 +617,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) { @@ -716,9 +828,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, @@ -754,6 +863,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, }; static void info_message(const char *str) --- a/security/apparmor/main.c +++ b/security/apparmor/main.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #include "apparmor.h" @@ -341,6 +344,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 @@ -406,6 +427,24 @@ static int aa_audit_base(struct aa_profi audit_log_untrustedstring(ab, sa->name2); } + 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) { @@ -725,6 +764,63 @@ 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; + + 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]; + + 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; + + 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 @@ -283,6 +283,8 @@ static struct aa_profile *aa_unpack_prof { struct aa_profile *profile = NULL; struct aa_audit sa; + size_t size = 0; + int i; int error = -EPROTO; @@ -311,6 +313,24 @@ static struct aa_profile *aa_unpack_prof if (!aa_is_u32(e, &(profile->capabilities), 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_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)) { -- -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/