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
*/
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;
AA_BUG(!sock->sk);
AA_BUG(sock->sk->sk_family != PF_UNIX);
- /* TODO: update sock label with new task label */
/* investigate only using lock via unix_peer_get()
* addr only needs the memory barrier, but need to investigate
* path
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
*/
peer_addr, peer_addrlen,
is_unix_fs(peer_sk) ?
&peer_path : NULL,
- pctx->label),
- unix_peer_perm(file->f_cred, pctx->label, op,
+ 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;
}
+
struct aa_file_ctx *ctx = file_ctx(file);
struct aa_label *label = begin_current_label_crit_section();
- spin_lock_init(&ctx->lock);
rcu_assign_pointer(ctx->label, aa_get_label(label));
end_current_label_crit_section(label);
return 0;
return error;
}
+static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t gfp)
+{
+ struct aa_sk_ctx *ctx = aa_sock(sk);
+ struct aa_label *label;
+ bool needput;
+
+ label = __begin_current_label_crit_section(&needput);
+ //spin_lock_init(&ctx->lock);
+ rcu_assign_pointer(ctx->label, aa_get_label(label));
+ rcu_assign_pointer(ctx->peer, NULL);
+ rcu_assign_pointer(ctx->peer_lastupdate, NULL);
+ __end_current_label_crit_section(label, needput);
+ return 0;
+}
+
static void apparmor_sk_free_security(struct sock *sk)
{
struct aa_sk_ctx *ctx = aa_sock(sk);
- aa_put_label(ctx->label);
- aa_put_label(ctx->peer);
+ /* dead these won't be updated any more */
+ aa_put_label(rcu_dereference_protected(ctx->label, true));
+ aa_put_label(rcu_dereference_protected(ctx->peer, true));
+ aa_put_label(rcu_dereference_protected(ctx->peer_lastupdate, true));
}
/**
struct aa_sk_ctx *ctx = aa_sock(sk);
struct aa_sk_ctx *new = aa_sock(newsk);
- if (new->label)
- aa_put_label(new->label);
- new->label = aa_get_label(ctx->label);
+ /* not actually in use yet */
+ if (rcu_access_pointer(ctx->label) != rcu_access_pointer(new->label)) {
+ aa_put_label(rcu_dereference_protected(new->label, true));
+ rcu_assign_pointer(new->label, aa_get_label_rcu(&ctx->label));
+ }
+
+ if (rcu_access_pointer(ctx->peer) != rcu_access_pointer(new->peer)) {
+ aa_put_label(rcu_dereference_protected(new->peer, true));
+ rcu_assign_pointer(new->peer, aa_get_label_rcu(&ctx->peer));
+ }
- if (new->peer)
- aa_put_label(new->peer);
- new->peer = aa_get_label(ctx->peer);
+ if (rcu_access_pointer(ctx->peer_lastupdate) != rcu_access_pointer(new->peer_lastupdate)) {
+ aa_put_label(rcu_dereference_protected(new->peer_lastupdate, true));
+ rcu_assign_pointer(new->peer_lastupdate,
+ aa_get_label_rcu(&ctx->peer_lastupdate));
+ }
}
static int unix_connect_perm(const struct cred *cred, struct aa_label *label,
error = aa_unix_peer_perm(cred, label, OP_CONNECT,
(AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE),
- sk, peer_sk, peer_ctx->label);
+ sk, peer_sk,
+ rcu_dereference_protected(peer_ctx->label,
+ lockdep_is_held(&unix_sk(peer_sk)->lock)));
if (!is_unix_fs(peer_sk)) {
last_error(error,
aa_unix_peer_perm(cred,
- peer_ctx->label, OP_CONNECT,
+ rcu_dereference_protected(peer_ctx->label,
+ lockdep_is_held(&unix_sk(peer_sk)->lock)),
+ OP_CONNECT,
(AA_MAY_ACCEPT | AA_MAY_SEND | AA_MAY_RECEIVE),
- peer_sk, sk, label));
+ peer_sk, sk, label));
}
return error;
}
+/* lockdep check in unix_connect_perm - push sks here to check */
static void unix_connect_peers(struct aa_sk_ctx *sk_ctx,
struct aa_sk_ctx *peer_ctx)
{
/* Cross reference the peer labels for SO_PEERSEC */
- aa_put_label(peer_ctx->peer);
- aa_put_label(sk_ctx->peer);
+ struct aa_label *label = rcu_dereference_protected(sk_ctx->label, true);
+
+ aa_get_label(label);
+ aa_put_label(rcu_dereference_protected(peer_ctx->peer,
+ true));
+ rcu_assign_pointer(peer_ctx->peer, label); /* transfer cnt */
+
+ label = aa_get_label(rcu_dereference_protected(peer_ctx->label,
+ true));
+ //spin_unlock(&peer_ctx->lock);
- peer_ctx->peer = aa_get_label(sk_ctx->label);
- sk_ctx->peer = aa_get_label(peer_ctx->label);
+ //spin_lock(&sk_ctx->lock);
+ aa_put_label(rcu_dereference_protected(sk_ctx->peer,
+ true));
+ aa_put_label(rcu_dereference_protected(sk_ctx->peer_lastupdate,
+ true));
+
+ rcu_assign_pointer(sk_ctx->peer, aa_get_label(label));
+ rcu_assign_pointer(sk_ctx->peer_lastupdate, label); /* transfer cnt */
+ //spin_unlock(&sk_ctx->lock);
}
/**
return error;
/* newsk doesn't go through post_create */
- AA_BUG(new_ctx->label);
- new_ctx->label = aa_get_label(peer_ctx->label);
+ AA_BUG(rcu_access_pointer(new_ctx->label));
+ rcu_assign_pointer(new_ctx->label,
+ aa_get_label(rcu_dereference_protected(peer_ctx->label,
+ true)));
/* Cross reference the peer labels for SO_PEERSEC */
unix_connect_peers(sk_ctx, new_ctx);
label = __begin_current_label_crit_section(&needput);
error = xcheck(aa_unix_peer_perm(current_cred(),
- label, OP_SENDMSG, AA_MAY_SEND,
- sock->sk, peer->sk, peer_ctx->label),
+ label, OP_SENDMSG, AA_MAY_SEND,
+ sock->sk, peer->sk,
+ rcu_dereference_protected(peer_ctx->label,
+ true)),
aa_unix_peer_perm(peer->file ? peer->file->f_cred : NULL,
- peer_ctx->label, OP_SENDMSG,
- AA_MAY_RECEIVE,
- peer->sk, sock->sk, label));
+ rcu_dereference_protected(peer_ctx->label,
+ true),
+ OP_SENDMSG, AA_MAY_RECEIVE, peer->sk,
+ sock->sk, label));
__end_current_label_crit_section(label, needput);
return error;
if (sock->sk) {
struct aa_sk_ctx *ctx = aa_sock(sock->sk);
- aa_put_label(ctx->label);
- ctx->label = aa_get_label(label);
+ /* still not live */
+ aa_put_label(rcu_dereference_protected(ctx->label, true));
+ rcu_assign_pointer(ctx->label, aa_get_label(label));
}
aa_put_label(label);
struct aa_sk_ctx *a_ctx = aa_sock(socka->sk);
struct aa_sk_ctx *b_ctx = aa_sock(sockb->sk);
struct aa_label *label;
- int error = 0;
-
- aa_put_label(a_ctx->label);
- aa_put_label(b_ctx->label);
+ /* socks not live yet - initial values set in sk_alloc */
label = begin_current_label_crit_section();
- a_ctx->label = aa_get_label(label);
- b_ctx->label = aa_get_label(label);
+ if (rcu_access_pointer(a_ctx->label) != label) {
+ AA_BUG("a_ctx != label");
+ aa_put_label(rcu_dereference_protected(a_ctx->label, true));
+ rcu_assign_pointer(a_ctx->label, aa_get_label(label));
+ }
+ if (rcu_access_pointer(b_ctx->label) != label) {
+ AA_BUG("b_ctx != label");
+ aa_put_label(rcu_dereference_protected(b_ctx->label, true));
+ rcu_assign_pointer(b_ctx->label, aa_get_label(label));
+ }
if (socka->sk->sk_family == PF_UNIX) {
/* unix socket pairs by-pass unix_stream_connect */
- if (!error)
- unix_connect_peers(a_ctx, b_ctx);
+ unix_connect_peers(a_ctx, b_ctx);
}
end_current_label_crit_section(label);
- return error;
+ return 0;
}
/**
static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
struct aa_sk_ctx *ctx = aa_sock(sk);
+ int error;
if (!skb->secmark)
return 0;
* If reach here before socket_post_create hook is called, in which
* case label is null, drop the packet.
*/
- if (!ctx->label)
+ if (!rcu_access_pointer(ctx->label))
return -EACCES;
- return apparmor_secmark_check(ctx->label, OP_RECVMSG, AA_MAY_RECEIVE,
- skb->secmark, sk);
+ rcu_read_lock();
+ error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_RECVMSG,
+ AA_MAY_RECEIVE, skb->secmark, sk);
+ rcu_read_unlock();
+
+ return error;
}
#endif
struct aa_sk_ctx *ctx = aa_sock(sk);
struct aa_label *label = ERR_PTR(-ENOPROTOOPT);
- if (ctx->peer)
- return aa_get_label(ctx->peer);
+ if (rcu_access_pointer(ctx->peer))
+ return aa_get_label_rcu(&ctx->peer);
if (sk->sk_family != PF_UNIX)
return ERR_PTR(-ENOPROTOOPT);
struct aa_label *label;
struct aa_label *peer;
- label = begin_current_label_crit_section();
peer = sk_peer_get_label(sock->sk);
if (IS_ERR(peer)) {
error = PTR_ERR(peer);
goto done;
}
+ label = begin_current_label_crit_section();
slen = aa_label_asxprint(&name, labels_ns(label), peer,
FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
FLAG_HIDDEN_UNCONFINED, GFP_KERNEL);
error = -EFAULT;
done_put:
+ end_current_label_crit_section(label);
aa_put_label(peer);
done:
- end_current_label_crit_section(label);
kfree(name);
return error;
}
{
struct aa_sk_ctx *ctx = aa_sock(sk);
- if (!ctx->label)
- ctx->label = aa_get_current_label();
+ /* setup - not live */
+ if (!rcu_access_pointer(ctx->label))
+ rcu_assign_pointer(ctx->label, aa_get_current_label());
}
#ifdef CONFIG_NETWORK_SECMARK
struct request_sock *req)
{
struct aa_sk_ctx *ctx = aa_sock(sk);
+ int error;
if (!skb->secmark)
return 0;
- return apparmor_secmark_check(ctx->label, OP_CONNECT, AA_MAY_CONNECT,
- skb->secmark, sk);
+ rcu_read_lock();
+ error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_CONNECT,
+ AA_MAY_CONNECT, skb->secmark, sk);
+ rcu_read_unlock();
+
+ return error;
}
#endif
LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
+ LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security),
LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security),
LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security),
{
struct aa_sk_ctx *ctx;
struct sock *sk;
+ int error;
if (!skb->secmark)
return NF_ACCEPT;
return NF_ACCEPT;
ctx = aa_sock(sk);
- if (!apparmor_secmark_check(ctx->label, OP_SENDMSG, AA_MAY_SEND,
- skb->secmark, sk))
+ rcu_read_lock();
+ error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_SENDMSG,
+ AA_MAY_SEND, skb->secmark, sk);
+ rcu_read_unlock();
+ if (!error)
return NF_ACCEPT;
return NF_DROP_ERR(-ECONNREFUSED);