Path: blob/main/sys/contrib/openzfs/module/os/linux/zfs/policy.c
48774 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/2122/*23* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.24* Copyright 2013, Joyent, Inc. All rights reserved.25* Copyright (C) 2016 Lawrence Livermore National Security, LLC.26* Copyright (c) 2025, Rob Norris <[email protected]>27*28* For Linux the vast majority of this enforcement is already handled via29* the standard Linux VFS permission checks. However certain administrative30* commands which bypass the standard mechanisms may need to make use of31* this functionality.32*/3334#include <sys/policy.h>35#include <linux/security.h>36#include <linux/vfs_compat.h>3738static int39priv_policy_ns(const cred_t *cr, int capability, int err,40struct user_namespace *ns)41{42/*43* The passed credentials cannot be directly verified because Linux44* only provides an interface to check the *current* process45* credentials. In order to handle this we check if the passed in46* creds match the current process credentials or the kcred. If not,47* we swap the passed credentials into the current task, perform the48* check, and then revert it before returning.49*/50const cred_t *old =51(cr != CRED() && cr != kcred) ? override_creds(cr) : NULL;5253#if defined(CONFIG_USER_NS)54if (ns ? ns_capable(ns, capability) : capable(capability))55#else56if (capable(capability))57#endif58err = 0;5960if (old)61revert_creds(old);6263return (err);64}6566static int67priv_policy(const cred_t *cr, int capability, int err)68{69return (priv_policy_ns(cr, capability, err, cr->user_ns));70}7172static int73priv_policy_user(const cred_t *cr, int capability, int err)74{75/*76* All priv_policy_user checks are preceded by kuid/kgid_has_mapping()77* checks. If we cannot do them, we shouldn't be using ns_capable()78* since we don't know whether the affected files are valid in our79* namespace.80*/81#if defined(CONFIG_USER_NS)82return (priv_policy_ns(cr, capability, err, cr->user_ns));83#else84return (priv_policy_ns(cr, capability, err, NULL));85#endif86}8788/*89* Checks for operations that are either client-only or are used by90* both clients and servers.91*/92int93secpolicy_nfs(const cred_t *cr)94{95return (priv_policy(cr, CAP_SYS_ADMIN, EPERM));96}9798/*99* Catch all system configuration.100*/101int102secpolicy_sys_config(const cred_t *cr, boolean_t checkonly)103{104return (priv_policy(cr, CAP_SYS_ADMIN, EPERM));105}106107/*108* Like secpolicy_vnode_access() but we get the actual wanted mode and the109* current mode of the file, not the missing bits.110*111* Enforced in the Linux VFS.112*/113int114secpolicy_vnode_access2(const cred_t *cr, struct inode *ip, uid_t owner,115mode_t curmode, mode_t wantmode)116{117return (0);118}119120/*121* This is a special routine for ZFS; it is used to determine whether122* any of the privileges in effect allow any form of access to the123* file. There's no reason to audit this or any reason to record124* this. More work is needed to do the "KPLD" stuff.125*/126int127secpolicy_vnode_any_access(const cred_t *cr, struct inode *ip, uid_t owner)128{129if (crgetuid(cr) == owner)130return (0);131132if (zpl_inode_owner_or_capable(zfs_init_idmap, ip))133return (0);134135#if defined(CONFIG_USER_NS)136if (!kuid_has_mapping(cr->user_ns, SUID_TO_KUID(owner)))137return (EPERM);138#endif139140if (priv_policy_user(cr, CAP_DAC_OVERRIDE, EPERM) == 0)141return (0);142143if (priv_policy_user(cr, CAP_DAC_READ_SEARCH, EPERM) == 0)144return (0);145146return (EPERM);147}148149/*150* Determine if subject can chown owner of a file.151*/152int153secpolicy_vnode_chown(const cred_t *cr, uid_t owner)154{155if (crgetuid(cr) == owner)156return (0);157158#if defined(CONFIG_USER_NS)159if (!kuid_has_mapping(cr->user_ns, SUID_TO_KUID(owner)))160return (EPERM);161#endif162163return (priv_policy_user(cr, CAP_FOWNER, EPERM));164}165166/*167* Determine if subject can change group ownership of a file.168*/169int170secpolicy_vnode_create_gid(const cred_t *cr)171{172return (priv_policy(cr, CAP_SETGID, EPERM));173}174175/*176* Policy determines whether we can remove an entry from a directory,177* regardless of permission bits.178*/179int180secpolicy_vnode_remove(const cred_t *cr)181{182return (priv_policy(cr, CAP_FOWNER, EPERM));183}184185/*186* Determine that subject can modify the mode of a file. allzone privilege187* needed when modifying root owned object.188*/189int190secpolicy_vnode_setdac(const cred_t *cr, uid_t owner)191{192if (crgetuid(cr) == owner)193return (0);194195#if defined(CONFIG_USER_NS)196if (!kuid_has_mapping(cr->user_ns, SUID_TO_KUID(owner)))197return (EPERM);198#endif199200return (priv_policy_user(cr, CAP_FOWNER, EPERM));201}202203/*204* Are we allowed to retain the set-uid/set-gid bits when205* changing ownership or when writing to a file?206* "issuid" should be true when set-uid; only in that case207* root ownership is checked (setgid is assumed).208*209* Enforced in the Linux VFS.210*/211int212secpolicy_vnode_setid_retain(struct znode *zp __maybe_unused, const cred_t *cr,213boolean_t issuidroot)214{215return (priv_policy_user(cr, CAP_FSETID, EPERM));216}217218/*219* Determine that subject can set the file setgid flag.220*/221int222secpolicy_vnode_setids_setgids(const cred_t *cr, gid_t gid, zidmap_t *mnt_ns,223struct user_namespace *fs_ns)224{225gid = zfs_gid_to_vfsgid(mnt_ns, fs_ns, gid);226#if defined(CONFIG_USER_NS)227if (!kgid_has_mapping(cr->user_ns, SGID_TO_KGID(gid)))228return (EPERM);229#endif230if (crgetgid(cr) != gid && !groupmember(gid, cr))231return (priv_policy_user(cr, CAP_FSETID, EPERM));232233return (0);234}235236/*237* Determine if the subject can inject faults in the ZFS fault injection238* framework. Requires all privileges.239*/240int241secpolicy_zinject(const cred_t *cr)242{243return (priv_policy(cr, CAP_SYS_ADMIN, EACCES));244}245246/*247* Determine if the subject has permission to manipulate ZFS datasets248* (not pools). Equivalent to the SYS_MOUNT privilege.249*/250int251secpolicy_zfs(const cred_t *cr)252{253return (priv_policy(cr, CAP_SYS_ADMIN, EACCES));254}255256void257secpolicy_setid_clear(vattr_t *vap, cred_t *cr)258{259if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0 &&260secpolicy_vnode_setid_retain(NULL, cr,261(vap->va_mode & S_ISUID) != 0 &&262(vap->va_mask & AT_UID) != 0 && vap->va_uid == 0) != 0) {263vap->va_mask |= AT_MODE;264vap->va_mode &= ~(S_ISUID|S_ISGID);265}266}267268/*269* Determine that subject can set the file setid flags.270*/271static int272secpolicy_vnode_setid_modify(const cred_t *cr, uid_t owner, zidmap_t *mnt_ns,273struct user_namespace *fs_ns)274{275owner = zfs_uid_to_vfsuid(mnt_ns, fs_ns, owner);276277if (crgetuid(cr) == owner)278return (0);279280#if defined(CONFIG_USER_NS)281if (!kuid_has_mapping(cr->user_ns, SUID_TO_KUID(owner)))282return (EPERM);283#endif284285return (priv_policy_user(cr, CAP_FSETID, EPERM));286}287288/*289* Determine that subject can make a file a "sticky".290*291* Enforced in the Linux VFS.292*/293static int294secpolicy_vnode_stky_modify(const cred_t *cr)295{296return (0);297}298299int300secpolicy_setid_setsticky_clear(struct inode *ip, vattr_t *vap,301const vattr_t *ovap, cred_t *cr, zidmap_t *mnt_ns,302struct user_namespace *fs_ns)303{304int error;305306if ((vap->va_mode & S_ISUID) != 0 &&307(error = secpolicy_vnode_setid_modify(cr,308ovap->va_uid, mnt_ns, fs_ns)) != 0) {309return (error);310}311312/*313* Check privilege if attempting to set the314* sticky bit on a non-directory.315*/316if (!S_ISDIR(ip->i_mode) && (vap->va_mode & S_ISVTX) != 0 &&317secpolicy_vnode_stky_modify(cr) != 0) {318vap->va_mode &= ~S_ISVTX;319}320321/*322* Check for privilege if attempting to set the323* group-id bit.324*/325if ((vap->va_mode & S_ISGID) != 0 &&326secpolicy_vnode_setids_setgids(cr, ovap->va_gid,327mnt_ns, fs_ns) != 0) {328vap->va_mode &= ~S_ISGID;329}330331return (0);332}333334/*335* Check privileges for setting xvattr attributes336*/337int338secpolicy_xvattr(xvattr_t *xvap, uid_t owner, cred_t *cr, mode_t type)339{340return (secpolicy_vnode_chown(cr, owner));341}342343/*344* Check privileges for setattr attributes.345*346* Enforced in the Linux VFS.347*/348int349secpolicy_vnode_setattr(cred_t *cr, struct inode *ip, struct vattr *vap,350const struct vattr *ovap, int flags,351int unlocked_access(void *, int, cred_t *), void *node)352{353return (0);354}355356/*357* Check privileges for links.358*359* Enforced in the Linux VFS.360*/361int362secpolicy_basic_link(const cred_t *cr)363{364return (0);365}366367368