| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168 |
- From c59a2789cc146827c225f9f9035b9fc23a82fc45 Mon Sep 17 00:00:00 2001
- From: John Johansen <[email protected]>
- Date: Tue, 18 Jul 2017 23:27:23 -0700
- Subject: UBUNTU: SAUCE: apparmor: af_unix mediation
- af_socket mediation did not make it into 4.17 so add remaining out
- of tree patch
- Signed-off-by: John Johansen <[email protected]>
- Signed-off-by: Seth Forshee <[email protected]>
- ---
- security/apparmor/Makefile | 3 +-
- security/apparmor/af_unix.c | 652 ++++++++++++++++++++++++++++++++++++
- security/apparmor/apparmorfs.c | 6 +
- security/apparmor/file.c | 4 +-
- security/apparmor/include/af_unix.h | 114 +++++++
- security/apparmor/include/net.h | 4 +
- security/apparmor/include/path.h | 1 +
- security/apparmor/include/policy.h | 10 +-
- security/apparmor/lsm.c | 112 +++++++
- security/apparmor/net.c | 53 ++-
- 10 files changed, 953 insertions(+), 6 deletions(-)
- create mode 100644 security/apparmor/af_unix.c
- create mode 100644 security/apparmor/include/af_unix.h
- (limited to 'security/apparmor')
- diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
- index ff23fcf..fad407f 100644
- --- a/security/apparmor/Makefile
- +++ b/security/apparmor/Makefile
- @@ -5,7 +5,8 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
-
- apparmor-y := apparmorfs.o audit.o capability.o task.o ipc.o lib.o match.o \
- path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
- - resource.o secid.o file.o policy_ns.o label.o mount.o net.o
- + resource.o secid.o file.o policy_ns.o label.o mount.o net.o \
- + af_unix.o
- apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
-
- clean-files := capability_names.h rlim_names.h net_names.h
- diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c
- new file mode 100644
- index 0000000..54b3796
- --- /dev/null
- +++ b/security/apparmor/af_unix.c
- @@ -0,0 +1,652 @@
- +/*
- + * AppArmor security module
- + *
- + * This file contains AppArmor af_unix fine grained mediation
- + *
- + * Copyright 2018 Canonical Ltd.
- + *
- + * This program is free software; you can redistribute it and/or
- + * modify it under the terms of the GNU General Public License as
- + * published by the Free Software Foundation, version 2 of the
- + * License.
- + */
- +
- +#include <net/tcp_states.h>
- +
- +#include "include/audit.h"
- +#include "include/af_unix.h"
- +#include "include/apparmor.h"
- +#include "include/file.h"
- +#include "include/label.h"
- +#include "include/path.h"
- +#include "include/policy.h"
- +#include "include/cred.h"
- +
- +static inline struct sock *aa_sock(struct unix_sock *u)
- +{
- + return &u->sk;
- +}
- +
- +static inline int unix_fs_perm(const char *op, u32 mask, struct aa_label *label,
- + struct unix_sock *u, int flags)
- +{
- + AA_BUG(!label);
- + AA_BUG(!u);
- + AA_BUG(!UNIX_FS(aa_sock(u)));
- +
- + if (unconfined(label) || !LABEL_MEDIATES(label, AA_CLASS_FILE))
- + return 0;
- +
- + mask &= NET_FS_PERMS;
- + if (!u->path.dentry) {
- + struct path_cond cond = { };
- + struct aa_perms perms = { };
- + struct aa_profile *profile;
- +
- + /* socket path has been cleared because it is being shutdown
- + * can only fall back to original sun_path request
- + */
- + struct aa_sk_ctx *ctx = SK_CTX(&u->sk);
- + if (ctx->path.dentry)
- + return aa_path_perm(op, label, &ctx->path, flags, mask,
- + &cond);
- + return fn_for_each_confined(label, profile,
- + ((flags | profile->path_flags) & PATH_MEDIATE_DELETED) ?
- + __aa_path_perm(op, profile,
- + u->addr->name->sun_path, mask,
- + &cond, flags, &perms) :
- + aa_audit_file(profile, &nullperms, op, mask,
- + u->addr->name->sun_path, NULL,
- + NULL, cond.uid,
- + "Failed name lookup - "
- + "deleted entry", -EACCES));
- + } else {
- + /* the sunpath may not be valid for this ns so use the path */
- + struct path_cond cond = { u->path.dentry->d_inode->i_uid,
- + u->path.dentry->d_inode->i_mode
- + };
- +
- + return aa_path_perm(op, label, &u->path, flags, mask, &cond);
- + }
- +
- + return 0;
- +}
- +
- +/* passing in state returned by PROFILE_MEDIATES_AF */
- +static unsigned int match_to_prot(struct aa_profile *profile,
- + unsigned int state, int type, int protocol,
- + const char **info)
- +{
- + __be16 buffer[2];
- + buffer[0] = cpu_to_be16(type);
- + buffer[1] = cpu_to_be16(protocol);
- + state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer,
- + 4);
- + if (!state)
- + *info = "failed type and protocol match";
- + return state;
- +}
- +
- +static unsigned int match_addr(struct aa_profile *profile, unsigned int state,
- + struct sockaddr_un *addr, int addrlen)
- +{
- + if (addr)
- + /* include leading \0 */
- + state = aa_dfa_match_len(profile->policy.dfa, state,
- + addr->sun_path,
- + unix_addr_len(addrlen));
- + else
- + /* anonymous end point */
- + state = aa_dfa_match_len(profile->policy.dfa, state, "\x01",
- + 1);
- + /* todo change to out of band */
- + state = aa_dfa_null_transition(profile->policy.dfa, state);
- + return state;
- +}
- +
- +static unsigned int match_to_local(struct aa_profile *profile,
- + unsigned int state, int type, int protocol,
- + struct sockaddr_un *addr, int addrlen,
- + const char **info)
- +{
- + state = match_to_prot(profile, state, type, protocol, info);
- + if (state) {
- + state = match_addr(profile, state, addr, addrlen);
- + if (state) {
- + /* todo: local label matching */
- + state = aa_dfa_null_transition(profile->policy.dfa,
- + state);
- + if (!state)
- + *info = "failed local label match";
- + } else
- + *info = "failed local address match";
- + }
- +
- + return state;
- +}
- +
- +static unsigned int match_to_sk(struct aa_profile *profile,
- + unsigned int state, struct unix_sock *u,
- + const char **info)
- +{
- + struct sockaddr_un *addr = NULL;
- + int addrlen = 0;
- +
- + if (u->addr) {
- + addr = u->addr->name;
- + addrlen = u->addr->len;
- + }
- +
- + return match_to_local(profile, state, u->sk.sk_type, u->sk.sk_protocol,
- + addr, addrlen, info);
- +}
- +
- +#define CMD_ADDR 1
- +#define CMD_LISTEN 2
- +#define CMD_OPT 4
- +
- +static inline unsigned int match_to_cmd(struct aa_profile *profile,
- + unsigned int state, struct unix_sock *u,
- + char cmd, const char **info)
- +{
- + state = match_to_sk(profile, state, u, info);
- + if (state) {
- + state = aa_dfa_match_len(profile->policy.dfa, state, &cmd, 1);
- + if (!state)
- + *info = "failed cmd selection match";
- + }
- +
- + return state;
- +}
- +
- +static inline unsigned int match_to_peer(struct aa_profile *profile,
- + unsigned int state,
- + struct unix_sock *u,
- + struct sockaddr_un *peer_addr,
- + int peer_addrlen,
- + const char **info)
- +{
- + state = match_to_cmd(profile, state, u, CMD_ADDR, info);
- + if (state) {
- + state = match_addr(profile, state, peer_addr, peer_addrlen);
- + if (!state)
- + *info = "failed peer address match";
- + }
- + return state;
- +}
- +
- +static int do_perms(struct aa_profile *profile, unsigned int state, u32 request,
- + struct common_audit_data *sa)
- +{
- + struct aa_perms perms;
- +
- + AA_BUG(!profile);
- +
- + aa_compute_perms(profile->policy.dfa, state, &perms);
- + aa_apply_modes_to_perms(profile, &perms);
- + return aa_check_perms(profile, &perms, request, sa,
- + audit_net_cb);
- +}
- +
- +static int match_label(struct aa_profile *profile, struct aa_profile *peer,
- + unsigned int state, u32 request,
- + struct common_audit_data *sa)
- +{
- + AA_BUG(!profile);
- + AA_BUG(!peer);
- +
- + aad(sa)->peer = &peer->label;
- +
- + if (state) {
- + state = aa_dfa_match(profile->policy.dfa, state,
- + peer->base.hname);
- + if (!state)
- + aad(sa)->info = "failed peer label match";
- + }
- + return do_perms(profile, state, request, sa);
- +}
- +
- +
- +/* unix sock creation comes before we know if the socket will be an fs
- + * socket
- + * v6 - semantics are handled by mapping in profile load
- + * v7 - semantics require sock create for tasks creating an fs socket.
- + */
- +static int profile_create_perm(struct aa_profile *profile, int family,
- + int type, int protocol)
- +{
- + unsigned int state;
- + DEFINE_AUDIT_NET(sa, OP_CREATE, NULL, family, type, protocol);
- +
- + AA_BUG(!profile);
- + AA_BUG(profile_unconfined(profile));
- +
- + if ((state = PROFILE_MEDIATES_AF(profile, AF_UNIX))) {
- + state = match_to_prot(profile, state, type, protocol,
- + &aad(&sa)->info);
- + return do_perms(profile, state, AA_MAY_CREATE, &sa);
- + }
- +
- + return aa_profile_af_perm(profile, &sa, AA_MAY_CREATE, family, type);
- +}
- +
- +int aa_unix_create_perm(struct aa_label *label, int family, int type,
- + int protocol)
- +{
- + struct aa_profile *profile;
- +
- + if (unconfined(label))
- + return 0;
- +
- + return fn_for_each_confined(label, profile,
- + profile_create_perm(profile, family, type, protocol));
- +}
- +
- +
- +static inline int profile_sk_perm(struct aa_profile *profile, const char *op,
- + u32 request, struct sock *sk)
- +{
- + unsigned int state;
- + DEFINE_AUDIT_SK(sa, op, sk);
- +
- + AA_BUG(!profile);
- + AA_BUG(!sk);
- + AA_BUG(UNIX_FS(sk));
- + AA_BUG(profile_unconfined(profile));
- +
- + state = PROFILE_MEDIATES_AF(profile, AF_UNIX);
- + if (state) {
- + state = match_to_sk(profile, state, unix_sk(sk),
- + &aad(&sa)->info);
- + return do_perms(profile, state, request, &sa);
- + }
- +
- + return aa_profile_af_sk_perm(profile, &sa, request, sk);
- +}
- +
- +int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request,
- + struct sock *sk)
- +{
- + struct aa_profile *profile;
- +
- + return fn_for_each_confined(label, profile,
- + profile_sk_perm(profile, op, request, sk));
- +}
- +
- +static int unix_label_sock_perm(struct aa_label *label, const char *op, u32 request,
- + struct socket *sock)
- +{
- + if (unconfined(label))
- + return 0;
- + if (UNIX_FS(sock->sk))
- + return unix_fs_perm(op, request, label, unix_sk(sock->sk), 0);
- +
- + return aa_unix_label_sk_perm(label, op, request, sock->sk);
- +}
- +
- +/* revaliation, get/set attr */
- +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock)
- +{
- + struct aa_label *label;
- + int error;
- +
- + label = begin_current_label_crit_section();
- + error = unix_label_sock_perm(label, op, request, sock);
- + end_current_label_crit_section(label);
- +
- + return error;
- +}
- +
- +static int profile_bind_perm(struct aa_profile *profile, struct sock *sk,
- + struct sockaddr *addr, int addrlen)
- +{
- + unsigned int state;
- + DEFINE_AUDIT_SK(sa, OP_BIND, sk);
- +
- + AA_BUG(!profile);
- + AA_BUG(!sk);
- + AA_BUG(addr->sa_family != AF_UNIX);
- + AA_BUG(profile_unconfined(profile));
- + AA_BUG(unix_addr_fs(addr, addrlen));
- +
- + state = PROFILE_MEDIATES_AF(profile, AF_UNIX);
- + if (state) {
- + /* bind for abstract socket */
- + aad(&sa)->net.addr = unix_addr(addr);
- + aad(&sa)->net.addrlen = addrlen;
- +
- + state = match_to_local(profile, state,
- + sk->sk_type, sk->sk_protocol,
- + unix_addr(addr), addrlen,
- + &aad(&sa)->info);
- + return do_perms(profile, state, AA_MAY_BIND, &sa);
- + }
- +
- + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_BIND, sk);
- +}
- +
- +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address,
- + int addrlen)
- +{
- + struct aa_profile *profile;
- + struct aa_label *label;
- + int error = 0;
- +
- + label = begin_current_label_crit_section();
- + /* fs bind is handled by mknod */
- + if (!(unconfined(label) || unix_addr_fs(address, addrlen)))
- + error = fn_for_each_confined(label, profile,
- + profile_bind_perm(profile, sock->sk, address,
- + addrlen));
- + end_current_label_crit_section(label);
- +
- + return error;
- +}
- +
- +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address,
- + int addrlen)
- +{
- + /* unix connections are covered by the
- + * - unix_stream_connect (stream) and unix_may_send hooks (dgram)
- + * - fs connect is handled by open
- + */
- + return 0;
- +}
- +
- +static int profile_listen_perm(struct aa_profile *profile, struct sock *sk,
- + int backlog)
- +{
- + unsigned int state;
- + DEFINE_AUDIT_SK(sa, OP_LISTEN, sk);
- +
- + AA_BUG(!profile);
- + AA_BUG(!sk);
- + AA_BUG(UNIX_FS(sk));
- + AA_BUG(profile_unconfined(profile));
- +
- + state = PROFILE_MEDIATES_AF(profile, AF_UNIX);
- + if (state) {
- + __be16 b = cpu_to_be16(backlog);
- +
- + state = match_to_cmd(profile, state, unix_sk(sk), CMD_LISTEN,
- + &aad(&sa)->info);
- + if (state) {
- + state = aa_dfa_match_len(profile->policy.dfa, state,
- + (char *) &b, 2);
- + if (!state)
- + aad(&sa)->info = "failed listen backlog match";
- + }
- + return do_perms(profile, state, AA_MAY_LISTEN, &sa);
- + }
- +
- + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_LISTEN, sk);
- +}
- +
- +int aa_unix_listen_perm(struct socket *sock, int backlog)
- +{
- + struct aa_profile *profile;
- + struct aa_label *label;
- + int error = 0;
- +
- + label = begin_current_label_crit_section();
- + if (!(unconfined(label) || UNIX_FS(sock->sk)))
- + error = fn_for_each_confined(label, profile,
- + profile_listen_perm(profile, sock->sk,
- + backlog));
- + end_current_label_crit_section(label);
- +
- + return error;
- +}
- +
- +
- +static inline int profile_accept_perm(struct aa_profile *profile,
- + struct sock *sk,
- + struct sock *newsk)
- +{
- + unsigned int state;
- + DEFINE_AUDIT_SK(sa, OP_ACCEPT, sk);
- +
- + AA_BUG(!profile);
- + AA_BUG(!sk);
- + AA_BUG(UNIX_FS(sk));
- + AA_BUG(profile_unconfined(profile));
- +
- + state = PROFILE_MEDIATES_AF(profile, AF_UNIX);
- + if (state) {
- + state = match_to_sk(profile, state, unix_sk(sk),
- + &aad(&sa)->info);
- + return do_perms(profile, state, AA_MAY_ACCEPT, &sa);
- + }
- +
- + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_ACCEPT, sk);
- +}
- +
- +/* ability of sock to connect, not peer address binding */
- +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock)
- +{
- + struct aa_profile *profile;
- + struct aa_label *label;
- + int error = 0;
- +
- + label = begin_current_label_crit_section();
- + if (!(unconfined(label) || UNIX_FS(sock->sk)))
- + error = fn_for_each_confined(label, profile,
- + profile_accept_perm(profile, sock->sk,
- + newsock->sk));
- + end_current_label_crit_section(label);
- +
- + return error;
- +}
- +
- +
- +/* dgram handled by unix_may_sendmsg, right to send on stream done at connect
- + * could do per msg unix_stream here
- + */
- +/* sendmsg, recvmsg */
- +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock,
- + struct msghdr *msg, int size)
- +{
- + return 0;
- +}
- +
- +
- +static int profile_opt_perm(struct aa_profile *profile, const char *op, u32 request,
- + struct sock *sk, int level, int optname)
- +{
- + unsigned int state;
- + DEFINE_AUDIT_SK(sa, op, sk);
- +
- + AA_BUG(!profile);
- + AA_BUG(!sk);
- + AA_BUG(UNIX_FS(sk));
- + AA_BUG(profile_unconfined(profile));
- +
- + state = PROFILE_MEDIATES_AF(profile, AF_UNIX);
- + if (state) {
- + __be16 b = cpu_to_be16(optname);
- +
- + state = match_to_cmd(profile, state, unix_sk(sk), CMD_OPT,
- + &aad(&sa)->info);
- + if (state) {
- + state = aa_dfa_match_len(profile->policy.dfa, state,
- + (char *) &b, 2);
- + if (!state)
- + aad(&sa)->info = "failed sockopt match";
- + }
- + return do_perms(profile, state, request, &sa);
- + }
- +
- + return aa_profile_af_sk_perm(profile, &sa, request, sk);
- +}
- +
- +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level,
- + int optname)
- +{
- + struct aa_profile *profile;
- + struct aa_label *label;
- + int error = 0;
- +
- + label = begin_current_label_crit_section();
- + if (!(unconfined(label) || UNIX_FS(sock->sk)))
- + error = fn_for_each_confined(label, profile,
- + profile_opt_perm(profile, op, request,
- + sock->sk, level, optname));
- + end_current_label_crit_section(label);
- +
- + return error;
- +}
- +
- +/* null peer_label is allowed, in which case the peer_sk label is used */
- +static int profile_peer_perm(struct aa_profile *profile, const char *op, u32 request,
- + struct sock *sk, struct sock *peer_sk,
- + struct aa_label *peer_label,
- + struct common_audit_data *sa)
- +{
- + unsigned int state;
- +
- + AA_BUG(!profile);
- + AA_BUG(profile_unconfined(profile));
- + AA_BUG(!sk);
- + AA_BUG(!peer_sk);
- + AA_BUG(UNIX_FS(peer_sk));
- +
- + state = PROFILE_MEDIATES_AF(profile, AF_UNIX);
- + if (state) {
- + struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk);
- + struct aa_profile *peerp;
- + struct sockaddr_un *addr = NULL;
- + int len = 0;
- + if (unix_sk(peer_sk)->addr) {
- + addr = unix_sk(peer_sk)->addr->name;
- + len = unix_sk(peer_sk)->addr->len;
- + }
- + state = match_to_peer(profile, state, unix_sk(sk),
- + addr, len, &aad(sa)->info);
- + if (!peer_label)
- + peer_label = peer_ctx->label;
- + return fn_for_each_in_ns(peer_label, peerp,
- + match_label(profile, peerp, state, request,
- + sa));
- + }
- +
- + return aa_profile_af_sk_perm(profile, sa, request, sk);
- +}
- +
- +/**
- + *
- + * Requires: lock held on both @sk and @peer_sk
- + */
- +int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request,
- + struct sock *sk, struct sock *peer_sk,
- + struct aa_label *peer_label)
- +{
- + struct unix_sock *peeru = unix_sk(peer_sk);
- + struct unix_sock *u = unix_sk(sk);
- +
- + AA_BUG(!label);
- + AA_BUG(!sk);
- + AA_BUG(!peer_sk);
- +
- + if (UNIX_FS(aa_sock(peeru)))
- + return unix_fs_perm(op, request, label, peeru, 0);
- + else if (UNIX_FS(aa_sock(u)))
- + return unix_fs_perm(op, request, label, u, 0);
- + else {
- + struct aa_profile *profile;
- + DEFINE_AUDIT_SK(sa, op, sk);
- + aad(&sa)->net.peer_sk = peer_sk;
- +
- + /* TODO: ns!!! */
- + if (!net_eq(sock_net(sk), sock_net(peer_sk))) {
- + ;
- + }
- +
- + if (unconfined(label))
- + return 0;
- +
- + return fn_for_each_confined(label, profile,
- + profile_peer_perm(profile, op, request, sk,
- + peer_sk, peer_label, &sa));
- + }
- +}
- +
- +
- +/* from net/unix/af_unix.c */
- +static void unix_state_double_lock(struct sock *sk1, struct sock *sk2)
- +{
- + if (unlikely(sk1 == sk2) || !sk2) {
- + unix_state_lock(sk1);
- + return;
- + }
- + if (sk1 < sk2) {
- + unix_state_lock(sk1);
- + unix_state_lock_nested(sk2);
- + } else {
- + unix_state_lock(sk2);
- + unix_state_lock_nested(sk1);
- + }
- +}
- +
- +static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2)
- +{
- + if (unlikely(sk1 == sk2) || !sk2) {
- + unix_state_unlock(sk1);
- + return;
- + }
- + unix_state_unlock(sk1);
- + unix_state_unlock(sk2);
- +}
- +
- +int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request,
- + struct socket *sock)
- +{
- + struct sock *peer_sk = NULL;
- + u32 sk_req = request & ~NET_PEER_MASK;
- + int error = 0;
- +
- + AA_BUG(!label);
- + AA_BUG(!sock);
- + AA_BUG(!sock->sk);
- + AA_BUG(sock->sk->sk_family != AF_UNIX);
- +
- + /* TODO: update sock label with new task label */
- + unix_state_lock(sock->sk);
- + peer_sk = unix_peer(sock->sk);
- + if (peer_sk)
- + sock_hold(peer_sk);
- + if (!unix_connected(sock) && sk_req) {
- + error = unix_label_sock_perm(label, op, sk_req, sock);
- + if (!error) {
- + // update label
- + }
- + }
- + unix_state_unlock(sock->sk);
- + if (!peer_sk)
- + return error;
- +
- + unix_state_double_lock(sock->sk, peer_sk);
- + if (UNIX_FS(sock->sk)) {
- + error = unix_fs_perm(op, request, label, unix_sk(sock->sk),
- + PATH_SOCK_COND);
- + } else if (UNIX_FS(peer_sk)) {
- + error = unix_fs_perm(op, request, label, unix_sk(peer_sk),
- + PATH_SOCK_COND);
- + } else {
- + struct aa_sk_ctx *pctx = SK_CTX(peer_sk);
- + if (sk_req)
- + error = aa_unix_label_sk_perm(label, op, sk_req,
- + sock->sk);
- + last_error(error,
- + xcheck(aa_unix_peer_perm(label, op,
- + MAY_READ | MAY_WRITE,
- + sock->sk, peer_sk, NULL),
- + aa_unix_peer_perm(pctx->label, op,
- + MAY_READ | MAY_WRITE,
- + peer_sk, sock->sk, label)));
- + }
- +
- + unix_state_double_unlock(sock->sk, peer_sk);
- + sock_put(peer_sk);
- +
- + return error;
- +}
- diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
- index 0aef8e3..d581800 100644
- --- a/security/apparmor/apparmorfs.c
- +++ b/security/apparmor/apparmorfs.c
- @@ -2346,6 +2346,11 @@ static struct aa_sfs_entry aa_sfs_entry_ns[] = {
- { }
- };
-
- +static struct aa_sfs_entry aa_sfs_entry_dbus[] = {
- + AA_SFS_FILE_STRING("mask", "acquire send receive"),
- + { }
- +};
- +
- static struct aa_sfs_entry aa_sfs_entry_query_label[] = {
- AA_SFS_FILE_STRING("perms", "allow deny audit quiet"),
- AA_SFS_FILE_BOOLEAN("data", 1),
- @@ -2370,6 +2375,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
- AA_SFS_DIR("caps", aa_sfs_entry_caps),
- AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace),
- AA_SFS_DIR("signal", aa_sfs_entry_signal),
- + AA_SFS_DIR("dbus", aa_sfs_entry_dbus),
- AA_SFS_DIR("query", aa_sfs_entry_query),
- { }
- };
- diff --git a/security/apparmor/file.c b/security/apparmor/file.c
- index e1b7e936..866272c 100644
- --- a/security/apparmor/file.c
- +++ b/security/apparmor/file.c
- @@ -14,6 +14,7 @@
- #include <linux/fs.h>
- #include <linux/mount.h>
-
- +#include "include/af_unix.h"
- #include "include/apparmor.h"
- #include "include/audit.h"
- #include "include/cred.h"
- @@ -271,7 +272,8 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name,
- {
- int e = 0;
-
- - if (profile_unconfined(profile))
- + if (profile_unconfined(profile) ||
- + ((flags & PATH_SOCK_COND) && !PROFILE_MEDIATES_AF(profile, AF_UNIX)))
- return 0;
- aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms);
- if (request & ~perms->allow)
- diff --git a/security/apparmor/include/af_unix.h b/security/apparmor/include/af_unix.h
- new file mode 100644
- index 0000000..d1b7f23
- --- /dev/null
- +++ b/security/apparmor/include/af_unix.h
- @@ -0,0 +1,114 @@
- +/*
- + * AppArmor security module
- + *
- + * This file contains AppArmor af_unix fine grained mediation
- + *
- + * Copyright 2014 Canonical Ltd.
- + *
- + * This program is free software; you can redistribute it and/or
- + * modify it under the terms of the GNU General Public License as
- + * published by the Free Software Foundation, version 2 of the
- + * License.
- + */
- +#ifndef __AA_AF_UNIX_H
- +
- +#include <net/af_unix.h>
- +
- +#include "label.h"
- +//#include "include/net.h"
- +
- +#define unix_addr_len(L) ((L) - sizeof(sa_family_t))
- +#define unix_abstract_name_len(L) (unix_addr_len(L) - 1)
- +#define unix_abstract_len(U) (unix_abstract_name_len((U)->addr->len))
- +#define addr_unix_abstract_name(B) ((B)[0] == 0)
- +#define addr_unix_anonymous(U) (addr_unix_len(U) <= 0)
- +#define addr_unix_abstract(U) (!addr_unix_anonymous(U) && addr_unix_abstract_name((U)->addr))
- +//#define unix_addr_fs(U) (!unix_addr_anonymous(U) && !unix_addr_abstract_name((U)->addr))
- +
- +#define unix_addr(A) ((struct sockaddr_un *)(A))
- +#define unix_addr_anon(A, L) ((A) && unix_addr_len(L) <= 0)
- +#define unix_addr_fs(A, L) (!unix_addr_anon(A, L) && !addr_unix_abstract_name(unix_addr(A)->sun_path))
- +
- +#define UNIX_ANONYMOUS(U) (!unix_sk(U)->addr)
- +/* from net/unix/af_unix.c */
- +#define UNIX_ABSTRACT(U) (!UNIX_ANONYMOUS(U) && \
- + unix_sk(U)->addr->hash < UNIX_HASH_SIZE)
- +#define UNIX_FS(U) (!UNIX_ANONYMOUS(U) && unix_sk(U)->addr->name->sun_path[0])
- +#define unix_peer(sk) (unix_sk(sk)->peer)
- +#define unix_connected(S) ((S)->state == SS_CONNECTED)
- +
- +static inline void print_unix_addr(struct sockaddr_un *A, int L)
- +{
- + char *buf = (A) ? (char *) &(A)->sun_path : NULL;
- + int len = unix_addr_len(L);
- + if (!buf || len <= 0)
- + printk(" <anonymous>");
- + else if (buf[0])
- + printk(" %s", buf);
- + else
- + /* abstract name len includes leading \0 */
- + printk(" %d @%.*s", len - 1, len - 1, buf+1);
- +};
- +
- +/*
- + printk("%s: %s: f %d, t %d, p %d", __FUNCTION__, \
- + #SK , \
- +*/
- +#define print_unix_sk(SK) \
- +do { \
- + struct unix_sock *u = unix_sk(SK); \
- + printk("%s: f %d, t %d, p %d", #SK , \
- + (SK)->sk_family, (SK)->sk_type, (SK)->sk_protocol); \
- + if (u->addr) \
- + print_unix_addr(u->addr->name, u->addr->len); \
- + else \
- + print_unix_addr(NULL, sizeof(sa_family_t)); \
- + /* printk("\n");*/ \
- +} while (0)
- +
- +#define print_sk(SK) \
- +do { \
- + if (!(SK)) { \
- + printk("%s: %s is null\n", __FUNCTION__, #SK); \
- + } else if ((SK)->sk_family == PF_UNIX) { \
- + print_unix_sk(SK); \
- + printk("\n"); \
- + } else { \
- + printk("%s: %s: family %d\n", __FUNCTION__, #SK , \
- + (SK)->sk_family); \
- + } \
- +} while (0)
- +
- +#define print_sock_addr(U) \
- +do { \
- + printk("%s:\n", __FUNCTION__); \
- + printk(" sock %s:", sock_ctx && sock_ctx->label ? aa_label_printk(sock_ctx->label, GFP_ATOMIC); : "<null>"); print_sk(sock); \
- + printk(" other %s:", other_ctx && other_ctx->label ? aa_label_printk(other_ctx->label, GFP_ATOMIC); : "<null>"); print_sk(other); \
- + printk(" new %s", new_ctx && new_ctx->label ? aa_label_printk(new_ctx->label, GFP_ATOMIC); : "<null>"); print_sk(newsk); \
- +} while (0)
- +
- +
- +
- +
- +int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request,
- + struct sock *sk, struct sock *peer_sk,
- + struct aa_label *peer_label);
- +int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request,
- + struct sock *sk);
- +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock);
- +int aa_unix_create_perm(struct aa_label *label, int family, int type,
- + int protocol);
- +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address,
- + int addrlen);
- +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address,
- + int addrlen);
- +int aa_unix_listen_perm(struct socket *sock, int backlog);
- +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock);
- +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock,
- + struct msghdr *msg, int size);
- +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level,
- + int optname);
- +int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request,
- + struct socket *sock);
- +
- +#endif /* __AA_AF_UNIX_H */
- diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
- index 98a42ef..b4044fa 100644
- --- a/security/apparmor/include/net.h
- +++ b/security/apparmor/include/net.h
- @@ -49,6 +49,7 @@
- struct aa_sk_ctx {
- struct aa_label *label;
- struct aa_label *peer;
- + struct path path;
- };
-
- #define SK_CTX(X) ((X)->sk_security)
- @@ -83,6 +84,9 @@ struct aa_net_compat {
- ({ \
- int __e; \
- switch ((FAMILY)) { \
- + case AF_UNIX: \
- + __e = aa_unix_ ## FN; \
- + break; \
- default: \
- __e = DEF_FN; \
- } \
- diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h
- index 44a7945..44592cd 100644
- --- a/security/apparmor/include/path.h
- +++ b/security/apparmor/include/path.h
- @@ -13,6 +13,7 @@
-
- enum path_flags {
- PATH_IS_DIR = 0x1, /* path is a directory */
- + PATH_SOCK_COND = 0x2,
- PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */
- PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */
- PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */
- diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
- index f904105..f1c9cdc 100644
- --- a/security/apparmor/include/policy.h
- +++ b/security/apparmor/include/policy.h
- @@ -230,9 +230,13 @@ static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile,
- unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET);
- __be16 be_af = cpu_to_be16(AF);
-
- - if (!state)
- - return 0;
- - return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2);
- + if (!state) {
- + state = PROFILE_MEDIATES(profile, AA_CLASS_NET_COMPAT);
- + if (!state)
- + return 0;
- + }
- + state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2);
- + return state;
- }
-
- /**
- diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
- index f72406f..59a8ddd 100644
- --- a/security/apparmor/lsm.c
- +++ b/security/apparmor/lsm.c
- @@ -25,6 +25,7 @@
- #include <net/sock.h>
- #include <uapi/linux/mount.h>
-
- +#include "include/af_unix.h"
- #include "include/apparmor.h"
- #include "include/apparmorfs.h"
- #include "include/audit.h"
- @@ -801,6 +802,7 @@ static void apparmor_sk_free_security(struct sock *sk)
- SK_CTX(sk) = NULL;
- aa_put_label(ctx->label);
- aa_put_label(ctx->peer);
- + path_put(&ctx->path);
- kfree(ctx);
- }
-
- @@ -820,6 +822,99 @@ static void apparmor_sk_clone_security(const struct sock *sk,
- if (new->peer)
- aa_put_label(new->peer);
- new->peer = aa_get_label(ctx->peer);
- + new->path = ctx->path;
- + path_get(&new->path);
- +}
- +
- +static struct path *UNIX_FS_CONN_PATH(struct sock *sk, struct sock *newsk)
- +{
- + if (sk->sk_family == PF_UNIX && UNIX_FS(sk))
- + return &unix_sk(sk)->path;
- + else if (newsk->sk_family == PF_UNIX && UNIX_FS(newsk))
- + return &unix_sk(newsk)->path;
- + return NULL;
- +}
- +
- +/**
- + * apparmor_unix_stream_connect - check perms before making unix domain conn
- + *
- + * peer is locked when this hook is called
- + */
- +static int apparmor_unix_stream_connect(struct sock *sk, struct sock *peer_sk,
- + struct sock *newsk)
- +{
- + struct aa_sk_ctx *sk_ctx = SK_CTX(sk);
- + struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk);
- + struct aa_sk_ctx *new_ctx = SK_CTX(newsk);
- + struct aa_label *label;
- + struct path *path;
- + int error;
- +
- + label = __begin_current_label_crit_section();
- + error = aa_unix_peer_perm(label, OP_CONNECT,
- + (AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE),
- + sk, peer_sk, NULL);
- + if (!UNIX_FS(peer_sk)) {
- + last_error(error,
- + aa_unix_peer_perm(peer_ctx->label, OP_CONNECT,
- + (AA_MAY_ACCEPT | AA_MAY_SEND | AA_MAY_RECEIVE),
- + peer_sk, sk, label));
- + }
- + __end_current_label_crit_section(label);
- +
- + if (error)
- + return error;
- +
- + /* label newsk if it wasn't labeled in post_create. Normally this
- + * would be done in sock_graft, but because we are directly looking
- + * at the peer_sk to obtain peer_labeling for unix socks this
- + * does not work
- + */
- + if (!new_ctx->label)
- + new_ctx->label = aa_get_label(peer_ctx->label);
- +
- + /* Cross reference the peer labels for SO_PEERSEC */
- + if (new_ctx->peer)
- + aa_put_label(new_ctx->peer);
- +
- + if (sk_ctx->peer)
- + aa_put_label(sk_ctx->peer);
- +
- + new_ctx->peer = aa_get_label(sk_ctx->label);
- + sk_ctx->peer = aa_get_label(peer_ctx->label);
- +
- + path = UNIX_FS_CONN_PATH(sk, peer_sk);
- + if (path) {
- + new_ctx->path = *path;
- + sk_ctx->path = *path;
- + path_get(path);
- + path_get(path);
- + }
- + return 0;
- +}
- +
- +/**
- + * apparmor_unix_may_send - check perms before conn or sending unix dgrams
- + *
- + * other is locked when this hook is called
- + *
- + * dgram connect calls may_send, peer setup but path not copied?????
- + */
- +static int apparmor_unix_may_send(struct socket *sock, struct socket *peer)
- +{
- + struct aa_sk_ctx *peer_ctx = SK_CTX(peer->sk);
- + struct aa_label *label;
- + int error;
- +
- + label = __begin_current_label_crit_section();
- + error = xcheck(aa_unix_peer_perm(label, OP_SENDMSG, AA_MAY_SEND,
- + sock->sk, peer->sk, NULL),
- + aa_unix_peer_perm(peer_ctx->label, OP_SENDMSG,
- + AA_MAY_RECEIVE,
- + peer->sk, sock->sk, label));
- + __end_current_label_crit_section(label);
- +
- + return error;
- }
-
- /**
- @@ -1065,11 +1160,25 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
-
- static struct aa_label *sk_peer_label(struct sock *sk)
- {
- + struct sock *peer_sk;
- struct aa_sk_ctx *ctx = SK_CTX(sk);
-
- if (ctx->peer)
- return ctx->peer;
-
- + if (sk->sk_family != PF_UNIX)
- + return ERR_PTR(-ENOPROTOOPT);
- +
- + /* check for sockpair peering which does not go through
- + * security_unix_stream_connect
- + */
- + peer_sk = unix_peer(sk);
- + if (peer_sk) {
- + ctx = SK_CTX(peer_sk);
- + if (ctx->label)
- + return ctx->label;
- + }
- +
- return ERR_PTR(-ENOPROTOOPT);
- }
-
- @@ -1216,6 +1325,9 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
- LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security),
- LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security),
-
- + LSM_HOOK_INIT(unix_stream_connect, apparmor_unix_stream_connect),
- + LSM_HOOK_INIT(unix_may_send, apparmor_unix_may_send),
- +
- LSM_HOOK_INIT(socket_create, apparmor_socket_create),
- LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create),
- LSM_HOOK_INIT(socket_bind, apparmor_socket_bind),
- diff --git a/security/apparmor/net.c b/security/apparmor/net.c
- index e693df8..e2e759b 100644
- --- a/security/apparmor/net.c
- +++ b/security/apparmor/net.c
- @@ -8,6 +8,7 @@
- * Copyright 2009-2017 Canonical Ltd.
- */
-
- +#include "include/af_unix.h"
- #include "include/apparmor.h"
- #include "include/audit.h"
- #include "include/cred.h"
- @@ -26,6 +27,7 @@ struct aa_sfs_entry aa_sfs_entry_network[] = {
-
- struct aa_sfs_entry aa_sfs_entry_network_compat[] = {
- AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK),
- + AA_SFS_FILE_BOOLEAN("af_unix", 1),
- { }
- };
-
- @@ -71,6 +73,36 @@ static const char * const net_mask_names[] = {
- "unknown",
- };
-
- +static void audit_unix_addr(struct audit_buffer *ab, const char *str,
- + struct sockaddr_un *addr, int addrlen)
- +{
- + int len = unix_addr_len(addrlen);
- +
- + if (!addr || len <= 0) {
- + audit_log_format(ab, " %s=none", str);
- + } else if (addr->sun_path[0]) {
- + audit_log_format(ab, " %s=", str);
- + audit_log_untrustedstring(ab, addr->sun_path);
- + } else {
- + audit_log_format(ab, " %s=\"@", str);
- + if (audit_string_contains_control(&addr->sun_path[1], len - 1))
- + audit_log_n_hex(ab, &addr->sun_path[1], len - 1);
- + else
- + audit_log_format(ab, "%.*s", len - 1,
- + &addr->sun_path[1]);
- + audit_log_format(ab, "\"");
- + }
- +}
- +
- +static void audit_unix_sk_addr(struct audit_buffer *ab, const char *str,
- + struct sock *sk)
- +{
- + struct unix_sock *u = unix_sk(sk);
- + if (u && u->addr)
- + audit_unix_addr(ab, str, u->addr->name, u->addr->len);
- + else
- + audit_unix_addr(ab, str, NULL, 0);
- +}
-
- /* audit callback for net specific fields */
- void audit_net_cb(struct audit_buffer *ab, void *va)
- @@ -102,6 +134,23 @@ void audit_net_cb(struct audit_buffer *ab, void *va)
- net_mask_names, NET_PERMS_MASK);
- }
- }
- + if (sa->u.net->family == AF_UNIX) {
- + if ((aad(sa)->request & ~NET_PEER_MASK) && aad(sa)->net.addr)
- + audit_unix_addr(ab, "addr",
- + unix_addr(aad(sa)->net.addr),
- + aad(sa)->net.addrlen);
- + else
- + audit_unix_sk_addr(ab, "addr", sa->u.net->sk);
- + if (aad(sa)->request & NET_PEER_MASK) {
- + if (aad(sa)->net.addr)
- + audit_unix_addr(ab, "peer_addr",
- + unix_addr(aad(sa)->net.addr),
- + aad(sa)->net.addrlen);
- + else
- + audit_unix_sk_addr(ab, "peer_addr",
- + aad(sa)->net.peer_sk);
- + }
- + }
- if (aad(sa)->peer) {
- audit_log_format(ab, " peer=");
- aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
- @@ -202,7 +251,9 @@ int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request,
- AA_BUG(!sock);
- AA_BUG(!sock->sk);
-
- - return aa_label_sk_perm(label, op, request, sock->sk);
- + return af_select(sock->sk->sk_family,
- + file_perm(label, op, request, sock),
- + aa_label_sk_perm(label, op, request, sock->sk));
- }
-
- #ifdef CONFIG_NETWORK_SECMARK
|