| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * AppArmor security module
- *
- * This file contains AppArmor af_unix fine grained mediation
- *
- * Copyright 2023 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 <linux/fs.h>
- #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_unix_sk(struct unix_sock *u)
- {
- return &u->sk;
- }
- static int unix_fs_perm(const char *op, u32 mask, const struct cred *subj_cred,
- struct aa_label *label, const struct path *path)
- {
- AA_BUG(!label);
- AA_BUG(!path);
- if (unconfined(label) || !label_mediates(label, AA_CLASS_FILE))
- return 0;
- mask &= NET_FS_PERMS;
- /* if !u->path.dentry socket is being shutdown - implicit delegation
- * until obj delegation is supported
- */
- if (path->dentry) {
- /* the sunpath may not be valid for this ns so use the path */
- struct inode *inode = path->dentry->d_inode;
- vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_idmap(path->mnt), inode);
- struct path_cond cond = {
- .uid = vfsuid_into_kuid(vfsuid),
- .mode = inode->i_mode,
- };
- return aa_path_perm(op, subj_cred, label, path,
- PATH_SOCK_COND, mask, &cond);
- } /* else implicitly delegated */
- return 0;
- }
- /* match_addr special constants */
- #define ABSTRACT_ADDR "\x00" /* abstract socket addr */
- #define ANONYMOUS_ADDR "\x01" /* anonymous endpoint, no addr */
- #define DISCONNECTED_ADDR "\x02" /* addr is another namespace */
- #define SHUTDOWN_ADDR "\x03" /* path addr is shutdown and cleared */
- #define FS_ADDR "/" /* path addr in fs */
- static aa_state_t match_addr(struct aa_dfa *dfa, aa_state_t state,
- struct sockaddr_un *addr, int addrlen)
- {
- if (addr)
- /* include leading \0 */
- state = aa_dfa_match_len(dfa, state, addr->sun_path,
- unix_addr_len(addrlen));
- else
- state = aa_dfa_match_len(dfa, state, ANONYMOUS_ADDR, 1);
- /* todo: could change to out of band for cleaner separation */
- state = aa_dfa_null_transition(dfa, state);
- return state;
- }
- static aa_state_t match_to_local(struct aa_policydb *policy,
- aa_state_t state, u32 request,
- int type, int protocol,
- struct sockaddr_un *addr, int addrlen,
- struct aa_perms **p,
- const char **info)
- {
- state = aa_match_to_prot(policy, state, request, PF_UNIX, type,
- protocol, NULL, info);
- if (state) {
- state = match_addr(policy->dfa, state, addr, addrlen);
- if (state) {
- /* todo: local label matching */
- state = aa_dfa_null_transition(policy->dfa, state);
- if (!state)
- *info = "failed local label match";
- } else {
- *info = "failed local address match";
- }
- }
- return state;
- }
- struct sockaddr_un *aa_sunaddr(const struct unix_sock *u, int *addrlen)
- {
- struct unix_address *addr;
- /* memory barrier is sufficient see note in net/unix/af_unix.c */
- addr = smp_load_acquire(&u->addr);
- if (addr) {
- *addrlen = addr->len;
- return addr->name;
- }
- *addrlen = 0;
- return NULL;
- }
- static aa_state_t match_to_sk(struct aa_policydb *policy,
- aa_state_t state, u32 request,
- struct unix_sock *u, struct aa_perms **p,
- const char **info)
- {
- int addrlen;
- struct sockaddr_un *addr = aa_sunaddr(u, &addrlen);
- return match_to_local(policy, state, request, u->sk.sk_type,
- u->sk.sk_protocol, addr, addrlen, p, info);
- }
- #define CMD_ADDR 1
- #define CMD_LISTEN 2
- #define CMD_OPT 4
- static aa_state_t match_to_cmd(struct aa_policydb *policy, aa_state_t state,
- u32 request, struct unix_sock *u,
- char cmd, struct aa_perms **p,
- const char **info)
- {
- AA_BUG(!p);
- state = match_to_sk(policy, state, request, u, p, info);
- if (state && !*p) {
- state = aa_dfa_match_len(policy->dfa, state, &cmd, 1);
- if (!state)
- *info = "failed cmd selection match";
- }
- return state;
- }
- static aa_state_t match_to_peer(struct aa_policydb *policy, aa_state_t state,
- u32 request, struct unix_sock *u,
- struct sockaddr_un *peer_addr, int peer_addrlen,
- struct aa_perms **p, const char **info)
- {
- AA_BUG(!p);
- state = match_to_cmd(policy, state, request, u, CMD_ADDR, p, info);
- if (state && !*p) {
- state = match_addr(policy->dfa, state, peer_addr, peer_addrlen);
- if (!state)
- *info = "failed peer address match";
- }
- return state;
- }
- static aa_state_t match_label(struct aa_profile *profile,
- struct aa_ruleset *rule, aa_state_t state,
- u32 request, struct aa_profile *peer,
- struct aa_perms *p,
- struct apparmor_audit_data *ad)
- {
- AA_BUG(!profile);
- AA_BUG(!peer);
- ad->peer = &peer->label;
- if (state && !p) {
- state = aa_dfa_match(rule->policy->dfa, state,
- peer->base.hname);
- if (!state)
- ad->info = "failed peer label match";
- }
- return aa_do_perms(profile, rule->policy, state, request, p, ad);
- }
- /* 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.
- * v8 - same as v7
- */
- static int profile_create_perm(struct aa_profile *profile, int family,
- int type, int protocol,
- struct apparmor_audit_data *ad)
- {
- struct aa_ruleset *rules = profile->label.rules[0];
- aa_state_t state;
- AA_BUG(!profile);
- AA_BUG(profile_unconfined(profile));
- state = RULE_MEDIATES_v9NET(rules);
- if (state) {
- state = aa_match_to_prot(rules->policy, state, AA_MAY_CREATE,
- PF_UNIX, type, protocol, NULL,
- &ad->info);
- return aa_do_perms(profile, rules->policy, state, AA_MAY_CREATE,
- NULL, ad);
- }
- return aa_profile_af_perm(profile, ad, AA_MAY_CREATE, family, type,
- protocol);
- }
- static int profile_sk_perm(struct aa_profile *profile,
- struct apparmor_audit_data *ad,
- u32 request, struct sock *sk, const struct path *path)
- {
- struct aa_ruleset *rules = profile->label.rules[0];
- struct aa_perms *p = NULL;
- aa_state_t state;
- AA_BUG(!profile);
- AA_BUG(!sk);
- AA_BUG(profile_unconfined(profile));
- state = RULE_MEDIATES_v9NET(rules);
- if (state) {
- if (is_unix_fs(sk))
- return unix_fs_perm(ad->op, request, ad->subj_cred,
- &profile->label,
- &unix_sk(sk)->path);
- state = match_to_sk(rules->policy, state, request, unix_sk(sk),
- &p, &ad->info);
- return aa_do_perms(profile, rules->policy, state, request, p,
- ad);
- }
- return aa_profile_af_sk_perm(profile, ad, request, sk);
- }
- static int profile_bind_perm(struct aa_profile *profile, struct sock *sk,
- struct apparmor_audit_data *ad)
- {
- struct aa_ruleset *rules = profile->label.rules[0];
- struct aa_perms *p = NULL;
- aa_state_t state;
- AA_BUG(!profile);
- AA_BUG(!sk);
- AA_BUG(!ad);
- AA_BUG(profile_unconfined(profile));
- state = RULE_MEDIATES_v9NET(rules);
- if (state) {
- if (is_unix_addr_fs(ad->net.addr, ad->net.addrlen))
- /* under v7-9 fs hook handles bind */
- return 0;
- /* bind for abstract socket */
- state = match_to_local(rules->policy, state, AA_MAY_BIND,
- sk->sk_type, sk->sk_protocol,
- unix_addr(ad->net.addr),
- ad->net.addrlen,
- &p, &ad->info);
- return aa_do_perms(profile, rules->policy, state, AA_MAY_BIND,
- p, ad);
- }
- return aa_profile_af_sk_perm(profile, ad, AA_MAY_BIND, sk);
- }
- static int profile_listen_perm(struct aa_profile *profile, struct sock *sk,
- int backlog, struct apparmor_audit_data *ad)
- {
- struct aa_ruleset *rules = profile->label.rules[0];
- struct aa_perms *p = NULL;
- aa_state_t state;
- AA_BUG(!profile);
- AA_BUG(!sk);
- AA_BUG(!ad);
- AA_BUG(profile_unconfined(profile));
- state = RULE_MEDIATES_v9NET(rules);
- if (state) {
- __be16 b = cpu_to_be16(backlog);
- if (is_unix_fs(sk))
- return unix_fs_perm(ad->op, AA_MAY_LISTEN,
- ad->subj_cred, &profile->label,
- &unix_sk(sk)->path);
- state = match_to_cmd(rules->policy, state, AA_MAY_LISTEN,
- unix_sk(sk), CMD_LISTEN, &p, &ad->info);
- if (state && !p) {
- state = aa_dfa_match_len(rules->policy->dfa, state,
- (char *) &b, 2);
- if (!state)
- ad->info = "failed listen backlog match";
- }
- return aa_do_perms(profile, rules->policy, state, AA_MAY_LISTEN,
- p, ad);
- }
- return aa_profile_af_sk_perm(profile, ad, AA_MAY_LISTEN, sk);
- }
- static int profile_accept_perm(struct aa_profile *profile,
- struct sock *sk,
- struct apparmor_audit_data *ad)
- {
- struct aa_ruleset *rules = profile->label.rules[0];
- struct aa_perms *p = NULL;
- aa_state_t state;
- AA_BUG(!profile);
- AA_BUG(!sk);
- AA_BUG(!ad);
- AA_BUG(profile_unconfined(profile));
- state = RULE_MEDIATES_v9NET(rules);
- if (state) {
- if (is_unix_fs(sk))
- return unix_fs_perm(ad->op, AA_MAY_ACCEPT,
- ad->subj_cred, &profile->label,
- &unix_sk(sk)->path);
- state = match_to_sk(rules->policy, state, AA_MAY_ACCEPT,
- unix_sk(sk), &p, &ad->info);
- return aa_do_perms(profile, rules->policy, state, AA_MAY_ACCEPT,
- p, ad);
- }
- return aa_profile_af_sk_perm(profile, ad, AA_MAY_ACCEPT, sk);
- }
- static int profile_opt_perm(struct aa_profile *profile, u32 request,
- struct sock *sk, int optname,
- struct apparmor_audit_data *ad)
- {
- struct aa_ruleset *rules = profile->label.rules[0];
- struct aa_perms *p = NULL;
- aa_state_t state;
- AA_BUG(!profile);
- AA_BUG(!sk);
- AA_BUG(!ad);
- AA_BUG(profile_unconfined(profile));
- state = RULE_MEDIATES_v9NET(rules);
- if (state) {
- __be16 b = cpu_to_be16(optname);
- if (is_unix_fs(sk))
- return unix_fs_perm(ad->op, request,
- ad->subj_cred, &profile->label,
- &unix_sk(sk)->path);
- state = match_to_cmd(rules->policy, state, request, unix_sk(sk),
- CMD_OPT, &p, &ad->info);
- if (state && !p) {
- state = aa_dfa_match_len(rules->policy->dfa, state,
- (char *) &b, 2);
- if (!state)
- ad->info = "failed sockopt match";
- }
- return aa_do_perms(profile, rules->policy, state, request, p,
- ad);
- }
- return aa_profile_af_sk_perm(profile, ad, request, sk);
- }
- /* null peer_label is allowed, in which case the peer_sk label is used */
- static int profile_peer_perm(struct aa_profile *profile, u32 request,
- struct sock *sk, const struct path *path,
- struct sockaddr_un *peer_addr,
- int peer_addrlen, const struct path *peer_path,
- struct aa_label *peer_label,
- struct apparmor_audit_data *ad)
- {
- struct aa_ruleset *rules = profile->label.rules[0];
- struct aa_perms *p = NULL;
- aa_state_t state;
- AA_BUG(!profile);
- AA_BUG(profile_unconfined(profile));
- AA_BUG(!sk);
- AA_BUG(!peer_label);
- AA_BUG(!ad);
- state = RULE_MEDIATES_v9NET(rules);
- if (state) {
- struct aa_profile *peerp;
- if (peer_path)
- return unix_fs_perm(ad->op, request, ad->subj_cred,
- &profile->label, peer_path);
- else if (path)
- return unix_fs_perm(ad->op, request, ad->subj_cred,
- &profile->label, path);
- state = match_to_peer(rules->policy, state, request,
- unix_sk(sk),
- peer_addr, peer_addrlen, &p, &ad->info);
- return fn_for_each_in_scope(peer_label, peerp,
- match_label(profile, rules, state, request,
- peerp, p, ad));
- }
- return aa_profile_af_sk_perm(profile, ad, request, sk);
- }
- /* -------------------------------- */
- int aa_unix_create_perm(struct aa_label *label, int family, int type,
- int protocol)
- {
- if (!unconfined(label)) {
- struct aa_profile *profile;
- DEFINE_AUDIT_NET(ad, OP_CREATE, current_cred(), NULL, family,
- type, protocol);
- return fn_for_each_confined(label, profile,
- profile_create_perm(profile, family, type,
- protocol, &ad));
- }
- return 0;
- }
- static int aa_unix_label_sk_perm(const struct cred *subj_cred,
- struct aa_label *label,
- const char *op, u32 request, struct sock *sk,
- const struct path *path)
- {
- if (!unconfined(label)) {
- struct aa_profile *profile;
- DEFINE_AUDIT_SK(ad, op, subj_cred, sk);
- return fn_for_each_confined(label, profile,
- profile_sk_perm(profile, &ad, request, sk,
- path));
- }
- return 0;
- }
- /* revalidation, get/set attr, shutdown */
- 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 = aa_unix_label_sk_perm(current_cred(), label, op,
- request, sock->sk,
- is_unix_fs(sock->sk) ? &unix_sk(sock->sk)->path : NULL);
- end_current_label_crit_section(label);
- return error;
- }
- static int valid_addr(struct sockaddr *addr, int addr_len)
- {
- struct sockaddr_un *sunaddr = unix_addr(addr);
- /* addr_len == offsetof(struct sockaddr_un, sun_path) is autobind */
- if (addr_len < offsetof(struct sockaddr_un, sun_path) ||
- addr_len > sizeof(*sunaddr))
- return -EINVAL;
- return 0;
- }
- int aa_unix_bind_perm(struct socket *sock, struct sockaddr *addr,
- int addrlen)
- {
- struct aa_profile *profile;
- struct aa_label *label;
- int error = 0;
- error = valid_addr(addr, addrlen);
- if (error)
- return error;
- label = begin_current_label_crit_section();
- /* fs bind is handled by mknod */
- if (!unconfined(label)) {
- DEFINE_AUDIT_SK(ad, OP_BIND, current_cred(), sock->sk);
- ad.net.addr = unix_addr(addr);
- ad.net.addrlen = addrlen;
- error = fn_for_each_confined(label, profile,
- profile_bind_perm(profile, sock->sk, &ad));
- }
- end_current_label_crit_section(label);
- return error;
- }
- /*
- * unix connections are covered by the
- * - unix_stream_connect (stream) and unix_may_send hooks (dgram)
- * - fs connect is handled by open
- * This is just here to document this is not needed for af_unix
- *
- int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address,
- int addrlen)
- {
- return 0;
- }
- */
- 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)) {
- DEFINE_AUDIT_SK(ad, OP_LISTEN, current_cred(), sock->sk);
- error = fn_for_each_confined(label, profile,
- profile_listen_perm(profile, sock->sk,
- backlog, &ad));
- }
- end_current_label_crit_section(label);
- return error;
- }
- /* 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)) {
- DEFINE_AUDIT_SK(ad, OP_ACCEPT, current_cred(), sock->sk);
- error = fn_for_each_confined(label, profile,
- profile_accept_perm(profile, sock->sk, &ad));
- }
- 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, but connect + socket transfer is
- * sufficient. This is just here to document this is not needed for af_unix
- *
- * sendmsg, recvmsg
- int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock,
- struct msghdr *msg, int size)
- {
- return 0;
- }
- */
- 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)) {
- DEFINE_AUDIT_SK(ad, op, current_cred(), sock->sk);
- error = fn_for_each_confined(label, profile,
- profile_opt_perm(profile, request, sock->sk,
- optname, &ad));
- }
- end_current_label_crit_section(label);
- return error;
- }
- static int unix_peer_perm(const struct cred *subj_cred,
- struct aa_label *label, const char *op, u32 request,
- struct sock *sk, const struct path *path,
- struct sockaddr_un *peer_addr, int peer_addrlen,
- const struct path *peer_path, struct aa_label *peer_label)
- {
- struct aa_profile *profile;
- DEFINE_AUDIT_SK(ad, op, subj_cred, sk);
- ad.net.peer.addr = peer_addr;
- ad.net.peer.addrlen = peer_addrlen;
- return fn_for_each_confined(label, profile,
- profile_peer_perm(profile, request, sk, path,
- peer_addr, peer_addrlen, peer_path,
- peer_label, &ad));
- }
- /**
- *
- * Requires: lock held on both @sk and @peer_sk
- * called by unix_stream_connect, unix_may_send
- */
- int aa_unix_peer_perm(const struct cred *subj_cred,
- 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);
- int plen;
- struct sockaddr_un *paddr = aa_sunaddr(unix_sk(peer_sk), &plen);
- AA_BUG(!label);
- AA_BUG(!sk);
- AA_BUG(!peer_sk);
- AA_BUG(!peer_label);
- return unix_peer_perm(subj_cred, label, op, request, sk,
- is_unix_fs(sk) ? &u->path : NULL,
- paddr, plen,
- is_unix_fs(peer_sk) ? &peeru->path : NULL,
- peer_label);
- }
- /* sk_plabel for comparison only */
- static void update_sk_ctx(struct sock *sk, struct aa_label *label,
- struct aa_label *plabel)
- {
- struct aa_label *l, *old;
- struct aa_sk_ctx *ctx = aa_sock(sk);
- bool update_sk;
- rcu_read_lock();
- update_sk = (plabel &&
- (plabel != rcu_access_pointer(ctx->peer_lastupdate) ||
- !aa_label_is_subset(plabel, rcu_dereference(ctx->peer)))) ||
- !__aa_subj_label_is_cached(label, rcu_dereference(ctx->label));
- rcu_read_unlock();
- if (!update_sk)
- return;
- spin_lock(&unix_sk(sk)->lock);
- old = rcu_dereference_protected(ctx->label,
- lockdep_is_held(&unix_sk(sk)->lock));
- l = aa_label_merge(old, label, GFP_ATOMIC);
- if (l) {
- if (l != old) {
- rcu_assign_pointer(ctx->label, l);
- aa_put_label(old);
- } else
- aa_put_label(l);
- }
- if (plabel && rcu_access_pointer(ctx->peer_lastupdate) != plabel) {
- old = rcu_dereference_protected(ctx->peer, lockdep_is_held(&unix_sk(sk)->lock));
- if (old == plabel) {
- rcu_assign_pointer(ctx->peer_lastupdate, plabel);
- } else if (aa_label_is_subset(plabel, old)) {
- rcu_assign_pointer(ctx->peer_lastupdate, plabel);
- rcu_assign_pointer(ctx->peer, aa_get_label(plabel));
- aa_put_label(old);
- } /* else race or a subset - don't update */
- }
- spin_unlock(&unix_sk(sk)->lock);
- }
- static void update_peer_ctx(struct sock *sk, struct aa_sk_ctx *ctx,
- struct aa_label *label)
- {
- struct aa_label *l, *old;
- spin_lock(&unix_sk(sk)->lock);
- old = rcu_dereference_protected(ctx->peer,
- lockdep_is_held(&unix_sk(sk)->lock));
- l = aa_label_merge(old, label, GFP_ATOMIC);
- if (l) {
- if (l != old) {
- rcu_assign_pointer(ctx->peer, l);
- aa_put_label(old);
- } else
- aa_put_label(l);
- }
- spin_unlock(&unix_sk(sk)->lock);
- }
- /* This fn is only checked if something has changed in the security
- * boundaries. Otherwise cached info off file is sufficient
- */
- int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
- const char *op, u32 request, struct file *file)
- {
- struct socket *sock = (struct socket *) file->private_data;
- struct sockaddr_un *addr, *peer_addr;
- int addrlen, peer_addrlen;
- struct aa_label *plabel = NULL;
- struct sock *peer_sk = NULL;
- u32 sk_req = request & ~NET_PEER_MASK;
- struct path path;
- bool is_sk_fs;
- int error = 0;
- AA_BUG(!label);
- AA_BUG(!sock);
- AA_BUG(!sock->sk);
- AA_BUG(sock->sk->sk_family != PF_UNIX);
- /* investigate only using lock via unix_peer_get()
- * addr only needs the memory barrier, but need to investigate
- * path
- */
- unix_state_lock(sock->sk);
- peer_sk = unix_peer(sock->sk);
- if (peer_sk)
- sock_hold(peer_sk);
- is_sk_fs = is_unix_fs(sock->sk);
- addr = aa_sunaddr(unix_sk(sock->sk), &addrlen);
- path = unix_sk(sock->sk)->path;
- unix_state_unlock(sock->sk);
- if (is_sk_fs && peer_sk)
- sk_req = request;
- if (sk_req) {
- error = aa_unix_label_sk_perm(subj_cred, label, op,
- sk_req, sock->sk,
- is_sk_fs ? &path : NULL);
- }
- if (!peer_sk)
- goto out;
- peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen);
- struct path peer_path;
- peer_path = unix_sk(peer_sk)->path;
- if (!is_sk_fs && is_unix_fs(peer_sk)) {
- last_error(error,
- unix_fs_perm(op, request, subj_cred, label,
- is_unix_fs(peer_sk) ? &peer_path : NULL));
- } else if (!is_sk_fs) {
- struct aa_label *plabel;
- struct aa_sk_ctx *pctx = aa_sock(peer_sk);
- rcu_read_lock();
- plabel = aa_get_label_rcu(&pctx->label);
- rcu_read_unlock();
- /* no fs check of aa_unix_peer_perm because conditions above
- * ensure they will never be done
- */
- last_error(error,
- xcheck(unix_peer_perm(subj_cred, label, op,
- MAY_READ | MAY_WRITE, sock->sk,
- is_sk_fs ? &path : NULL,
- peer_addr, peer_addrlen,
- is_unix_fs(peer_sk) ?
- &peer_path : NULL,
- plabel),
- unix_peer_perm(file->f_cred, plabel, op,
- MAY_READ | MAY_WRITE, peer_sk,
- is_unix_fs(peer_sk) ?
- &peer_path : NULL,
- addr, addrlen,
- is_sk_fs ? &path : NULL,
- label)));
- if (!error && !__aa_subj_label_is_cached(plabel, label))
- update_peer_ctx(peer_sk, pctx, label);
- }
- sock_put(peer_sk);
- out:
- /* update peer cache to latest successful perm check */
- if (error == 0)
- update_sk_ctx(sock->sk, label, plabel);
- aa_put_label(plabel);
- return error;
- }
|