Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

apparmor: Remove use of the double lock

The use of the double lock is not necessary and problematic. Instead
pull the bits that need locks into their own sections and grab the
needed references.

Fixes: c05e705812d1 ("apparmor: add fine grained af_unix mediation")
Signed-off-by: John Johansen <john.johansen@canonical.com>

+104 -102
+101 -96
security/apparmor/af_unix.c
··· 30 30 } 31 31 32 32 static int unix_fs_perm(const char *op, u32 mask, const struct cred *subj_cred, 33 - struct aa_label *label, struct unix_sock *u) 33 + struct aa_label *label, struct path *path) 34 34 { 35 35 AA_BUG(!label); 36 - AA_BUG(!u); 37 - AA_BUG(!is_unix_fs(aa_unix_sk(u))); 36 + AA_BUG(!path); 38 37 39 38 if (unconfined(label) || !label_mediates(label, AA_CLASS_FILE)) 40 39 return 0; ··· 42 43 /* if !u->path.dentry socket is being shutdown - implicit delegation 43 44 * until obj delegation is supported 44 45 */ 45 - if (u->path.dentry) { 46 + if (path->dentry) { 46 47 /* the sunpath may not be valid for this ns so use the path */ 47 - struct path_cond cond = { u->path.dentry->d_inode->i_uid, 48 - u->path.dentry->d_inode->i_mode 48 + struct path_cond cond = { path->dentry->d_inode->i_uid, 49 + path->dentry->d_inode->i_mode 49 50 }; 50 51 51 - return aa_path_perm(op, subj_cred, label, &u->path, 52 + return aa_path_perm(op, subj_cred, label, path, 52 53 PATH_SOCK_COND, mask, &cond); 53 54 } /* else implicitly delegated */ 54 55 ··· 101 102 return state; 102 103 } 103 104 105 + struct sockaddr_un *aa_sunaddr(const struct unix_sock *u, int *addrlen) 106 + { 107 + struct unix_address *addr; 108 + 109 + /* memory barrier is sufficient see note in net/unix/af_unix.c */ 110 + addr = smp_load_acquire(&u->addr); 111 + if (addr) { 112 + *addrlen = addr->len; 113 + return addr->name; 114 + } 115 + *addrlen = 0; 116 + return NULL; 117 + } 118 + 104 119 static aa_state_t match_to_sk(struct aa_policydb *policy, 105 120 aa_state_t state, u32 request, 106 121 struct unix_sock *u, struct aa_perms **p, 107 122 const char **info) 108 123 { 109 - struct sockaddr_un *addr = NULL; 110 - int addrlen = 0; 111 - 112 - if (u->addr) { 113 - addr = u->addr->name; 114 - addrlen = u->addr->len; 115 - } 124 + int addrlen; 125 + struct sockaddr_un *addr = aa_sunaddr(u, &addrlen); 116 126 117 127 return match_to_local(policy, state, request, u->sk.sk_type, 118 128 u->sk.sk_protocol, addr, addrlen, p, info); ··· 371 363 372 364 /* null peer_label is allowed, in which case the peer_sk label is used */ 373 365 static int profile_peer_perm(struct aa_profile *profile, u32 request, 374 - struct sock *sk, struct sock *peer_sk, 366 + struct sock *sk, struct sockaddr_un *peer_addr, 367 + int peer_addrlen, 375 368 struct aa_label *peer_label, 376 369 struct apparmor_audit_data *ad) 377 370 { ··· 384 375 AA_BUG(!profile); 385 376 AA_BUG(profile_unconfined(profile)); 386 377 AA_BUG(!sk); 387 - AA_BUG(!peer_sk); 378 + AA_BUG(!peer_label); 388 379 AA_BUG(!ad); 389 - AA_BUG(is_unix_fs(peer_sk)); /* currently always calls unix_fs_perm */ 390 380 391 381 state = RULE_MEDIATES_v9NET(rules); 392 382 if (state) { 393 - struct aa_sk_ctx *peer_ctx = aa_sock(peer_sk); 394 383 struct aa_profile *peerp; 395 - struct sockaddr_un *addr = NULL; 396 - int len = 0; 397 384 398 - if (unix_sk(peer_sk)->addr) { 399 - addr = unix_sk(peer_sk)->addr->name; 400 - len = unix_sk(peer_sk)->addr->len; 401 - } 402 385 state = match_to_peer(rules->policy, state, request, 403 386 unix_sk(sk), 404 - addr, len, &p, &ad->info); 405 - if (!peer_label) 406 - peer_label = peer_ctx->label; 387 + peer_addr, peer_addrlen, &p, &ad->info); 407 388 408 389 return fn_for_each_in_ns(peer_label, peerp, 409 390 match_label(profile, rules, state, request, ··· 421 422 return 0; 422 423 } 423 424 424 - int aa_unix_label_sk_perm(const struct cred *subj_cred, 425 - struct aa_label *label, const char *op, u32 request, 426 - struct sock *sk) 425 + int aa_unix_label_sk_perm(const struct cred *subj_cred, struct aa_label *label, 426 + const char *op, u32 request, struct sock *sk) 427 427 { 428 428 if (!unconfined(label)) { 429 429 struct aa_profile *profile; ··· 434 436 return 0; 435 437 } 436 438 437 - static int unix_label_sock_perm(const struct cred *subj_cred, 438 - struct aa_label *label, const char *op, 439 - u32 request, struct socket *sock) 440 - { 441 - if (unconfined(label)) 442 - return 0; 443 - if (is_unix_fs(sock->sk)) 444 - return unix_fs_perm(op, request, subj_cred, label, 445 - unix_sk(sock->sk)); 446 - 447 - return aa_unix_label_sk_perm(subj_cred, label, op, request, sock->sk); 448 - } 449 - 450 439 /* revalidation, get/set attr, shutdown */ 451 440 int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock) 452 441 { ··· 441 456 int error; 442 457 443 458 label = begin_current_label_crit_section(); 444 - error = unix_label_sock_perm(current_cred(), label, op, request, sock); 459 + if (is_unix_fs(sock->sk)) 460 + error = unix_fs_perm(op, request, current_cred(), label, 461 + &unix_sk(sock->sk)->path); 462 + else 463 + error = aa_unix_label_sk_perm(current_cred(), label, op, 464 + request, sock->sk); 445 465 end_current_label_crit_section(label); 446 466 447 467 return error; ··· 454 464 455 465 static int valid_addr(struct sockaddr *addr, int addr_len) 456 466 { 457 - struct sockaddr_un *sunaddr = (struct sockaddr_un *)addr; 467 + struct sockaddr_un *sunaddr = unix_addr(addr); 458 468 459 469 /* addr_len == offsetof(struct sockaddr_un, sun_path) is autobind */ 460 470 if (addr_len < offsetof(struct sockaddr_un, sun_path) || ··· 576 586 return error; 577 587 } 578 588 589 + static int unix_peer_perm(const struct cred *subj_cred, 590 + struct aa_label *label, const char *op, u32 request, 591 + struct sock *sk, struct sockaddr_un *peer_addr, 592 + int peer_addrlen, struct aa_label *peer_label) 593 + { 594 + struct aa_profile *profile; 595 + DEFINE_AUDIT_SK(ad, op, subj_cred, sk); 596 + 597 + ad.net.addr = peer_addr; 598 + ad.net.addrlen = peer_addrlen; 599 + 600 + return fn_for_each_confined(label, profile, 601 + profile_peer_perm(profile, request, sk, 602 + peer_addr, peer_addrlen, peer_label, &ad)); 603 + } 604 + 579 605 /** 580 606 * 581 607 * Requires: lock held on both @sk and @peer_sk ··· 608 602 AA_BUG(!label); 609 603 AA_BUG(!sk); 610 604 AA_BUG(!peer_sk); 605 + AA_BUG(!peer_label); 611 606 612 607 if (is_unix_fs(aa_unix_sk(peeru))) { 613 - return unix_fs_perm(op, request, subj_cred, label, peeru); 608 + return unix_fs_perm(op, request, subj_cred, label, 609 + &peeru->path); 614 610 } else if (is_unix_fs(aa_unix_sk(u))) { 615 - return unix_fs_perm(op, request, subj_cred, label, u); 611 + return unix_fs_perm(op, request, subj_cred, label, &u->path); 616 612 } else if (!unconfined(label)) { 617 - struct aa_profile *profile; 618 - DEFINE_AUDIT_SK(ad, op, subj_cred, sk); 613 + int plen; 614 + struct sockaddr_un *paddr = aa_sunaddr(unix_sk(peer_sk), 615 + &plen); 619 616 620 - ad.net.peer_sk = peer_sk; 621 - 622 - return fn_for_each_confined(label, profile, 623 - profile_peer_perm(profile, request, sk, 624 - peer_sk, peer_label, &ad)); 617 + return unix_peer_perm(subj_cred, label, op, request, 618 + sk, paddr, plen, peer_label); 625 619 } 626 620 627 621 return 0; 628 622 } 629 623 630 - static void unix_state_double_lock(struct sock *sk1, struct sock *sk2) 631 - { 632 - if (unlikely(sk1 == sk2) || !sk2) { 633 - unix_state_lock(sk1); 634 - return; 635 - } 636 - if (sk1 < sk2) { 637 - unix_state_lock(sk1); 638 - unix_state_lock(sk2); 639 - } else { 640 - unix_state_lock(sk2); 641 - unix_state_lock(sk1); 642 - } 643 - } 644 - 645 - static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2) 646 - { 647 - if (unlikely(sk1 == sk2) || !sk2) { 648 - unix_state_unlock(sk1); 649 - return; 650 - } 651 - unix_state_unlock(sk1); 652 - unix_state_unlock(sk2); 653 - } 654 - 655 - /* TODO: examine replacing double lock with cached addr */ 656 - 624 + /* This fn is only checked if something has changed in the security 625 + * boundaries. Otherwise cached info off file is sufficient 626 + */ 657 627 int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label, 658 628 const char *op, u32 request, struct file *file) 659 629 { 660 630 struct socket *sock = (struct socket *) file->private_data; 631 + struct sockaddr_un *addr, *peer_addr; 632 + int addrlen, peer_addrlen; 661 633 struct sock *peer_sk = NULL; 662 634 u32 sk_req = request & ~NET_PEER_MASK; 635 + struct path path; 663 636 bool is_sk_fs; 664 637 int error = 0; 665 638 ··· 648 663 AA_BUG(sock->sk->sk_family != PF_UNIX); 649 664 650 665 /* TODO: update sock label with new task label */ 666 + /* investigate only using lock via unix_peer_get() 667 + * addr only needs the memory barrier, but need to investigate 668 + * path 669 + */ 651 670 unix_state_lock(sock->sk); 652 671 peer_sk = unix_peer(sock->sk); 653 672 if (peer_sk) 654 673 sock_hold(peer_sk); 655 674 656 675 is_sk_fs = is_unix_fs(sock->sk); 676 + addr = aa_sunaddr(unix_sk(sock->sk), &addrlen); 677 + path = unix_sk(sock->sk)->path; 678 + unix_state_unlock(sock->sk); 679 + 657 680 if (is_sk_fs && peer_sk) 658 681 sk_req = request; 659 - if (sk_req) 660 - error = unix_label_sock_perm(subj_cred, label, op, sk_req, 661 - sock); 662 - unix_state_unlock(sock->sk); 682 + if (sk_req) { 683 + if (is_sk_fs) 684 + error = unix_fs_perm(op, sk_req, subj_cred, label, 685 + &path); 686 + else 687 + error = aa_unix_label_sk_perm(subj_cred, label, op, 688 + sk_req, sock->sk); 689 + } 663 690 if (!peer_sk) 664 - return error; 691 + goto out; 665 692 666 - unix_state_double_lock(sock->sk, peer_sk); 693 + peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen); 694 + 695 + struct path peer_path; 696 + 697 + peer_path = unix_sk(peer_sk)->path; 667 698 if (!is_sk_fs && is_unix_fs(peer_sk)) { 668 699 last_error(error, 669 700 unix_fs_perm(op, request, subj_cred, label, 670 - unix_sk(peer_sk))); 701 + &peer_path)); 671 702 } else if (!is_sk_fs) { 672 703 struct aa_sk_ctx *pctx = aa_sock(peer_sk); 673 704 705 + /* no fs check of aa_unix_peer_perm because conditions above 706 + * ensure they will never be done 707 + */ 674 708 last_error(error, 675 - xcheck(aa_unix_peer_perm(subj_cred, label, op, 676 - MAY_READ | MAY_WRITE, 677 - sock->sk, peer_sk, NULL), 678 - aa_unix_peer_perm(file->f_cred, pctx->label, op, 679 - MAY_READ | MAY_WRITE, 680 - peer_sk, sock->sk, label))); 709 + xcheck(unix_peer_perm(subj_cred, label, op, 710 + MAY_READ | MAY_WRITE, sock->sk, 711 + peer_addr, peer_addrlen, 712 + pctx->label), 713 + unix_peer_perm(file->f_cred, pctx->label, op, 714 + MAY_READ | MAY_WRITE, peer_sk, 715 + addr, addrlen, label))); 681 716 } 682 - unix_state_double_unlock(sock->sk, peer_sk); 683 - 684 717 sock_put(peer_sk); 718 + 719 + out: 685 720 686 721 return error; 687 722 }
+1
security/apparmor/include/af_unix.h
··· 31 31 #define is_unix_connected(S) ((S)->state == SS_CONNECTED) 32 32 33 33 34 + struct sockaddr_un *aa_sunaddr(const struct unix_sock *u, int *addrlen); 34 35 int aa_unix_peer_perm(const struct cred *subj_cred, 35 36 struct aa_label *label, const char *op, u32 request, 36 37 struct sock *sk, struct sock *peer_sk,
-1
security/apparmor/include/audit.h
··· 138 138 }; 139 139 struct { 140 140 int type, protocol; 141 - struct sock *peer_sk; 142 141 void *addr; 143 142 int addrlen; 144 143 } net;
+2 -2
security/apparmor/lsm.c
··· 1112 1112 1113 1113 error = aa_unix_peer_perm(cred, label, OP_CONNECT, 1114 1114 (AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE), 1115 - sk, peer_sk, NULL); 1115 + sk, peer_sk, peer_ctx->label); 1116 1116 if (!is_unix_fs(peer_sk)) { 1117 1117 last_error(error, 1118 1118 aa_unix_peer_perm(cred, ··· 1184 1184 label = __begin_current_label_crit_section(&needput); 1185 1185 error = xcheck(aa_unix_peer_perm(current_cred(), 1186 1186 label, OP_SENDMSG, AA_MAY_SEND, 1187 - sock->sk, peer->sk, NULL), 1187 + sock->sk, peer->sk, peer_ctx->label), 1188 1188 aa_unix_peer_perm(peer->file ? peer->file->f_cred : NULL, 1189 1189 peer_ctx->label, OP_SENDMSG, 1190 1190 AA_MAY_RECEIVE,
-3
security/apparmor/net.c
··· 148 148 audit_unix_addr(ab, "peer_addr", 149 149 unix_addr(ad->net.addr), 150 150 ad->net.addrlen); 151 - else 152 - audit_unix_sk_addr(ab, "peer_addr", 153 - ad->net.peer_sk); 154 151 } 155 152 } 156 153 if (ad->peer) {