| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* Common capabilities, needed by capability.o. |
|---|
| 2 | | - * |
|---|
| 3 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 4 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 5 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 6 | | - * (at your option) any later version. |
|---|
| 7 | | - * |
|---|
| 8 | 3 | */ |
|---|
| 9 | 4 | |
|---|
| 10 | 5 | #include <linux/capability.h> |
|---|
| 11 | 6 | #include <linux/audit.h> |
|---|
| 12 | | -#include <linux/module.h> |
|---|
| 13 | 7 | #include <linux/init.h> |
|---|
| 14 | 8 | #include <linux/kernel.h> |
|---|
| 15 | 9 | #include <linux/lsm_hooks.h> |
|---|
| .. | .. |
|---|
| 58 | 52 | * @cred: The credentials to use |
|---|
| 59 | 53 | * @ns: The user namespace in which we need the capability |
|---|
| 60 | 54 | * @cap: The capability to check for |
|---|
| 61 | | - * @audit: Whether to write an audit message or not |
|---|
| 55 | + * @opts: Bitmask of options defined in include/linux/security.h |
|---|
| 62 | 56 | * |
|---|
| 63 | 57 | * Determine whether the nominated task has the specified capability amongst |
|---|
| 64 | 58 | * its effective set, returning 0 if it does, -ve if it does not. |
|---|
| .. | .. |
|---|
| 303 | 297 | struct inode *inode = d_backing_inode(dentry); |
|---|
| 304 | 298 | int error; |
|---|
| 305 | 299 | |
|---|
| 306 | | - error = __vfs_getxattr(dentry, inode, XATTR_NAME_CAPS, NULL, 0); |
|---|
| 300 | + error = __vfs_getxattr(dentry, inode, XATTR_NAME_CAPS, NULL, 0, |
|---|
| 301 | + XATTR_NOSECURITY); |
|---|
| 307 | 302 | return error > 0; |
|---|
| 308 | 303 | } |
|---|
| 309 | 304 | |
|---|
| .. | .. |
|---|
| 397 | 392 | &tmpbuf, size, GFP_NOFS); |
|---|
| 398 | 393 | dput(dentry); |
|---|
| 399 | 394 | |
|---|
| 400 | | - if (ret < 0 || !tmpbuf) |
|---|
| 401 | | - return ret; |
|---|
| 395 | + if (ret < 0 || !tmpbuf) { |
|---|
| 396 | + size = ret; |
|---|
| 397 | + goto out_free; |
|---|
| 398 | + } |
|---|
| 402 | 399 | |
|---|
| 403 | 400 | fs_ns = inode->i_sb->s_user_ns; |
|---|
| 404 | 401 | cap = (struct vfs_cap_data *) tmpbuf; |
|---|
| .. | .. |
|---|
| 611 | 608 | |
|---|
| 612 | 609 | fs_ns = inode->i_sb->s_user_ns; |
|---|
| 613 | 610 | size = __vfs_getxattr((struct dentry *)dentry, inode, |
|---|
| 614 | | - XATTR_NAME_CAPS, &data, XATTR_CAPS_SZ); |
|---|
| 611 | + XATTR_NAME_CAPS, &data, XATTR_CAPS_SZ, |
|---|
| 612 | + XATTR_NOSECURITY); |
|---|
| 615 | 613 | if (size == -ENODATA || size == -EOPNOTSUPP) |
|---|
| 616 | 614 | /* no data, that's ok */ |
|---|
| 617 | 615 | return -ENODATA; |
|---|
| .. | .. |
|---|
| 662 | 660 | cpu_caps->permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; |
|---|
| 663 | 661 | cpu_caps->inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; |
|---|
| 664 | 662 | |
|---|
| 663 | + cpu_caps->rootid = rootkuid; |
|---|
| 664 | + |
|---|
| 665 | 665 | return 0; |
|---|
| 666 | 666 | } |
|---|
| 667 | 667 | |
|---|
| .. | .. |
|---|
| 670 | 670 | * its xattrs and, if present, apply them to the proposed credentials being |
|---|
| 671 | 671 | * constructed by execve(). |
|---|
| 672 | 672 | */ |
|---|
| 673 | | -static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_fcap) |
|---|
| 673 | +static int get_file_caps(struct linux_binprm *bprm, struct file *file, |
|---|
| 674 | + bool *effective, bool *has_fcap) |
|---|
| 674 | 675 | { |
|---|
| 675 | 676 | int rc = 0; |
|---|
| 676 | 677 | struct cpu_vfs_cap_data vcaps; |
|---|
| .. | .. |
|---|
| 680 | 681 | if (!file_caps_enabled) |
|---|
| 681 | 682 | return 0; |
|---|
| 682 | 683 | |
|---|
| 683 | | - if (!mnt_may_suid(bprm->file->f_path.mnt)) |
|---|
| 684 | + if (!mnt_may_suid(file->f_path.mnt)) |
|---|
| 684 | 685 | return 0; |
|---|
| 685 | 686 | |
|---|
| 686 | 687 | /* |
|---|
| .. | .. |
|---|
| 688 | 689 | * explicit that capability bits are limited to s_user_ns and its |
|---|
| 689 | 690 | * descendants. |
|---|
| 690 | 691 | */ |
|---|
| 691 | | - if (!current_in_userns(bprm->file->f_path.mnt->mnt_sb->s_user_ns)) |
|---|
| 692 | + if (!current_in_userns(file->f_path.mnt->mnt_sb->s_user_ns)) |
|---|
| 692 | 693 | return 0; |
|---|
| 693 | 694 | |
|---|
| 694 | | - rc = get_vfs_caps_from_disk(bprm->file->f_path.dentry, &vcaps); |
|---|
| 695 | + rc = get_vfs_caps_from_disk(file->f_path.dentry, &vcaps); |
|---|
| 695 | 696 | if (rc < 0) { |
|---|
| 696 | 697 | if (rc == -EINVAL) |
|---|
| 697 | 698 | printk(KERN_NOTICE "Invalid argument reading file caps for %s\n", |
|---|
| .. | .. |
|---|
| 702 | 703 | } |
|---|
| 703 | 704 | |
|---|
| 704 | 705 | rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective, has_fcap); |
|---|
| 705 | | - if (rc == -EINVAL) |
|---|
| 706 | | - printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", |
|---|
| 707 | | - __func__, rc, bprm->filename); |
|---|
| 708 | 706 | |
|---|
| 709 | 707 | out: |
|---|
| 710 | 708 | if (rc) |
|---|
| .. | .. |
|---|
| 823 | 821 | } |
|---|
| 824 | 822 | |
|---|
| 825 | 823 | /** |
|---|
| 826 | | - * cap_bprm_set_creds - Set up the proposed credentials for execve(). |
|---|
| 824 | + * cap_bprm_creds_from_file - Set up the proposed credentials for execve(). |
|---|
| 827 | 825 | * @bprm: The execution parameters, including the proposed creds |
|---|
| 826 | + * @file: The file to pull the credentials from |
|---|
| 828 | 827 | * |
|---|
| 829 | 828 | * Set up the proposed credentials for a new execution context being |
|---|
| 830 | 829 | * constructed by execve(). The proposed creds in @bprm->cred is altered, |
|---|
| 831 | 830 | * which won't take effect immediately. Returns 0 if successful, -ve on error. |
|---|
| 832 | 831 | */ |
|---|
| 833 | | -int cap_bprm_set_creds(struct linux_binprm *bprm) |
|---|
| 832 | +int cap_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file) |
|---|
| 834 | 833 | { |
|---|
| 834 | + /* Process setpcap binaries and capabilities for uid 0 */ |
|---|
| 835 | 835 | const struct cred *old = current_cred(); |
|---|
| 836 | 836 | struct cred *new = bprm->cred; |
|---|
| 837 | 837 | bool effective = false, has_fcap = false, is_setid; |
|---|
| 838 | 838 | int ret; |
|---|
| 839 | 839 | kuid_t root_uid; |
|---|
| 840 | 840 | |
|---|
| 841 | | - new->cap_ambient = old->cap_ambient; |
|---|
| 842 | 841 | if (WARN_ON(!cap_ambient_invariant_ok(old))) |
|---|
| 843 | 842 | return -EPERM; |
|---|
| 844 | 843 | |
|---|
| 845 | | - ret = get_file_caps(bprm, &effective, &has_fcap); |
|---|
| 844 | + ret = get_file_caps(bprm, file, &effective, &has_fcap); |
|---|
| 846 | 845 | if (ret < 0) |
|---|
| 847 | 846 | return ret; |
|---|
| 848 | 847 | |
|---|
| .. | .. |
|---|
| 911 | 910 | return -EPERM; |
|---|
| 912 | 911 | |
|---|
| 913 | 912 | /* Check for privilege-elevated exec. */ |
|---|
| 914 | | - bprm->cap_elevated = 0; |
|---|
| 915 | 913 | if (is_setid || |
|---|
| 916 | 914 | (!__is_real(root_uid, new) && |
|---|
| 917 | 915 | (effective || |
|---|
| 918 | 916 | __cap_grew(permitted, ambient, new)))) |
|---|
| 919 | | - bprm->cap_elevated = 1; |
|---|
| 917 | + bprm->secureexec = 1; |
|---|
| 920 | 918 | |
|---|
| 921 | 919 | return 0; |
|---|
| 922 | 920 | } |
|---|
| .. | .. |
|---|
| 942 | 940 | |
|---|
| 943 | 941 | /* Ignore non-security xattrs */ |
|---|
| 944 | 942 | if (strncmp(name, XATTR_SECURITY_PREFIX, |
|---|
| 945 | | - sizeof(XATTR_SECURITY_PREFIX) - 1) != 0) |
|---|
| 943 | + XATTR_SECURITY_PREFIX_LEN) != 0) |
|---|
| 946 | 944 | return 0; |
|---|
| 947 | 945 | |
|---|
| 948 | 946 | /* |
|---|
| .. | .. |
|---|
| 974 | 972 | |
|---|
| 975 | 973 | /* Ignore non-security xattrs */ |
|---|
| 976 | 974 | if (strncmp(name, XATTR_SECURITY_PREFIX, |
|---|
| 977 | | - sizeof(XATTR_SECURITY_PREFIX) - 1) != 0) |
|---|
| 975 | + XATTR_SECURITY_PREFIX_LEN) != 0) |
|---|
| 978 | 976 | return 0; |
|---|
| 979 | 977 | |
|---|
| 980 | 978 | if (strcmp(name, XATTR_NAME_CAPS) == 0) { |
|---|
| .. | .. |
|---|
| 1366 | 1364 | |
|---|
| 1367 | 1365 | #ifdef CONFIG_SECURITY |
|---|
| 1368 | 1366 | |
|---|
| 1369 | | -struct security_hook_list capability_hooks[] __lsm_ro_after_init = { |
|---|
| 1367 | +static struct security_hook_list capability_hooks[] __lsm_ro_after_init = { |
|---|
| 1370 | 1368 | LSM_HOOK_INIT(capable, cap_capable), |
|---|
| 1371 | 1369 | LSM_HOOK_INIT(settime, cap_settime), |
|---|
| 1372 | 1370 | LSM_HOOK_INIT(ptrace_access_check, cap_ptrace_access_check), |
|---|
| 1373 | 1371 | LSM_HOOK_INIT(ptrace_traceme, cap_ptrace_traceme), |
|---|
| 1374 | 1372 | LSM_HOOK_INIT(capget, cap_capget), |
|---|
| 1375 | 1373 | LSM_HOOK_INIT(capset, cap_capset), |
|---|
| 1376 | | - LSM_HOOK_INIT(bprm_set_creds, cap_bprm_set_creds), |
|---|
| 1374 | + LSM_HOOK_INIT(bprm_creds_from_file, cap_bprm_creds_from_file), |
|---|
| 1377 | 1375 | LSM_HOOK_INIT(inode_need_killpriv, cap_inode_need_killpriv), |
|---|
| 1378 | 1376 | LSM_HOOK_INIT(inode_killpriv, cap_inode_killpriv), |
|---|
| 1379 | 1377 | LSM_HOOK_INIT(inode_getsecurity, cap_inode_getsecurity), |
|---|
| .. | .. |
|---|
| 1387 | 1385 | LSM_HOOK_INIT(vm_enough_memory, cap_vm_enough_memory), |
|---|
| 1388 | 1386 | }; |
|---|
| 1389 | 1387 | |
|---|
| 1390 | | -void __init capability_add_hooks(void) |
|---|
| 1388 | +static int __init capability_init(void) |
|---|
| 1391 | 1389 | { |
|---|
| 1392 | 1390 | security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks), |
|---|
| 1393 | 1391 | "capability"); |
|---|
| 1392 | + return 0; |
|---|
| 1394 | 1393 | } |
|---|
| 1395 | 1394 | |
|---|
| 1395 | +DEFINE_LSM(capability) = { |
|---|
| 1396 | + .name = "capability", |
|---|
| 1397 | + .order = LSM_ORDER_FIRST, |
|---|
| 1398 | + .init = capability_init, |
|---|
| 1399 | +}; |
|---|
| 1400 | + |
|---|
| 1396 | 1401 | #endif /* CONFIG_SECURITY */ |
|---|