164562Sgshapiro/* $NetBSD: secmodel_extensions_vfs.c,v 1.1 2023/04/22 13:54:19 riastradh Exp $ */ 264562Sgshapiro 390792Sgshapiro/*- 490792Sgshapiro * Copyright (c) 2011 Elad Efrat <elad@NetBSD.org> 564562Sgshapiro * All rights reserved. 690792Sgshapiro * 764562Sgshapiro * Redistribution and use in source and binary forms, with or without 864562Sgshapiro * modification, are permitted provided that the following conditions 964562Sgshapiro * are met: 1064562Sgshapiro * 1. Redistributions of source code must retain the above copyright 1164562Sgshapiro * notice, this list of conditions and the following disclaimer. 1264562Sgshapiro * 2. Redistributions in binary form must reproduce the above copyright 1364562Sgshapiro * notice, this list of conditions and the following disclaimer in the 1464562Sgshapiro * documentation and/or other materials provided with the distribution. 1564562Sgshapiro * 3. The name of the author may not be used to endorse or promote products 1664562Sgshapiro * derived from this software without specific prior written permission. 1764562Sgshapiro * 1864562Sgshapiro * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1964562Sgshapiro * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2064562Sgshapiro * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2164562Sgshapiro * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2264562Sgshapiro * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2364562Sgshapiro * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2464562Sgshapiro * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2564562Sgshapiro * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2664562Sgshapiro * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2764562Sgshapiro * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2864562Sgshapiro */ 2964562Sgshapiro 3064562Sgshapiro#include <sys/cdefs.h> 3164562Sgshapiro__KERNEL_RCSID(0, "$NetBSD: secmodel_extensions_vfs.c,v 1.1 2023/04/22 13:54:19 riastradh Exp $"); 3264562Sgshapiro 3364562Sgshapiro#include <sys/types.h> 3464562Sgshapiro#include <sys/param.h> 3564562Sgshapiro 3664562Sgshapiro#include <sys/kauth.h> 3764562Sgshapiro#include <sys/vnode.h> 3864562Sgshapiro 3964562Sgshapiro#include <secmodel/secmodel.h> 4064562Sgshapiro#include <secmodel/extensions/extensions.h> 4164562Sgshapiro#include <secmodel/extensions/extensions_impl.h> 4264562Sgshapiro 4364562Sgshapirostatic int dovfsusermount; 4464562Sgshapirostatic int hardlink_check_uid; 4564562Sgshapirostatic int hardlink_check_gid; 4690792Sgshapiro 4764562Sgshapirostatic kauth_listener_t l_system, l_vnode; 4864562Sgshapiro 4964562Sgshapirostatic int secmodel_extensions_system_cb(kauth_cred_t, kauth_action_t, 5064562Sgshapiro void *, void *, void *, void *, void *); 5164562Sgshapirostatic int secmodel_extensions_vnode_cb(kauth_cred_t, kauth_action_t, 5264562Sgshapiro void *, void *, void *, void *, void *); 5364562Sgshapiro 5464562Sgshapirovoid 5564562Sgshapirosecmodel_extensions_vfs_start(void) 5664562Sgshapiro{ 5764562Sgshapiro 5864562Sgshapiro l_system = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, 5964562Sgshapiro secmodel_extensions_system_cb, NULL); 6064562Sgshapiro l_vnode = kauth_listen_scope(KAUTH_SCOPE_VNODE, 6164562Sgshapiro secmodel_extensions_vnode_cb, NULL); 6264562Sgshapiro} 6364562Sgshapiro 6490792Sgshapirovoid 6564562Sgshapirosecmodel_extensions_vfs_stop(void) 6664562Sgshapiro{ 6790792Sgshapiro 6864562Sgshapiro kauth_unlisten_scope(l_system); 6964562Sgshapiro kauth_unlisten_scope(l_vnode); 7064562Sgshapiro} 7164562Sgshapiro 7264562Sgshapirovoid 7364562Sgshapirosecmodel_extensions_vfs_sysctl(struct sysctllog **clog, 7464562Sgshapiro const struct sysctlnode *rnode) 7564562Sgshapiro{ 7664562Sgshapiro 7764562Sgshapiro sysctl_createv(clog, 0, &rnode, NULL, 7864562Sgshapiro CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 7964562Sgshapiro CTLTYPE_INT, "usermount", 8064562Sgshapiro SYSCTL_DESCR("Whether unprivileged users may mount " 8164562Sgshapiro "filesystems"), 8264562Sgshapiro sysctl_extensions_user_handler, 0, &dovfsusermount, 0, 8364562Sgshapiro CTL_CREATE, CTL_EOL); 8464562Sgshapiro 8564562Sgshapiro sysctl_createv(clog, 0, &rnode, NULL, 8664562Sgshapiro CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 8764562Sgshapiro CTLTYPE_INT, "hardlink_check_uid", 8864562Sgshapiro SYSCTL_DESCR("Whether unprivileged users can hardlink "\ 8964562Sgshapiro "to files they don't own"), 9064562Sgshapiro sysctl_extensions_user_handler, 0, 9164562Sgshapiro &hardlink_check_uid, 0, 9264562Sgshapiro CTL_CREATE, CTL_EOL); 9364562Sgshapiro 9464562Sgshapiro sysctl_createv(clog, 0, &rnode, NULL, 9564562Sgshapiro CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 9664562Sgshapiro CTLTYPE_INT, "hardlink_check_gid", 9764562Sgshapiro SYSCTL_DESCR("Whether unprivileged users can hardlink "\ 9864562Sgshapiro "to files that are not in their " \ 9964562Sgshapiro "group membership"), 10064562Sgshapiro sysctl_extensions_user_handler, 0, 10164562Sgshapiro &hardlink_check_gid, 0, 10264562Sgshapiro CTL_CREATE, CTL_EOL); 10364562Sgshapiro 10464562Sgshapiro /* Compatibility: vfs.generic.usermount */ 10564562Sgshapiro sysctl_createv(clog, 0, NULL, NULL, 10664562Sgshapiro CTLFLAG_PERMANENT, 10764562Sgshapiro CTLTYPE_NODE, "generic", 10864562Sgshapiro SYSCTL_DESCR("Non-specific vfs related information"), 10964562Sgshapiro NULL, 0, NULL, 0, 11064562Sgshapiro CTL_VFS, VFS_GENERIC, CTL_EOL); 11164562Sgshapiro 11264562Sgshapiro sysctl_createv(clog, 0, NULL, NULL, 11364562Sgshapiro CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 11464562Sgshapiro CTLTYPE_INT, "usermount", 11564562Sgshapiro SYSCTL_DESCR("Whether unprivileged users may mount " 11664562Sgshapiro "filesystems"), 11790792Sgshapiro sysctl_extensions_user_handler, 0, &dovfsusermount, 0, 11890792Sgshapiro CTL_VFS, VFS_GENERIC, VFS_USERMOUNT, CTL_EOL); 11990792Sgshapiro} 12090792Sgshapiro 12190792Sgshapirostatic int 12290792Sgshapirosecmodel_extensions_system_cb(kauth_cred_t cred, kauth_action_t action, 12390792Sgshapiro void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) 12490792Sgshapiro{ 12590792Sgshapiro vnode_t *vp; 12664562Sgshapiro struct vattr va; 12764562Sgshapiro struct mount *mp; 12864562Sgshapiro u_long flags; 12964562Sgshapiro int result; 13064562Sgshapiro enum kauth_system_req req; 13164562Sgshapiro int error; 13264562Sgshapiro 13364562Sgshapiro req = (enum kauth_system_req)(uintptr_t)arg0; 13464562Sgshapiro result = KAUTH_RESULT_DEFER; 13564562Sgshapiro 13664562Sgshapiro switch (action) { 13764562Sgshapiro case KAUTH_SYSTEM_MOUNT: 13864562Sgshapiro if (dovfsusermount == 0) 13990792Sgshapiro break; 14090792Sgshapiro switch (req) { 14190792Sgshapiro case KAUTH_REQ_SYSTEM_MOUNT_NEW: 14290792Sgshapiro vp = (vnode_t *)arg1; 14390792Sgshapiro mp = vp->v_mount; 14490792Sgshapiro flags = (u_long)arg2; 14590792Sgshapiro 14690792Sgshapiro /* 14790792Sgshapiro * Ensure that the user owns the directory onto which 14890792Sgshapiro * the mount is attempted. 14990792Sgshapiro */ 15090792Sgshapiro vn_lock(vp, LK_SHARED | LK_RETRY); 15190792Sgshapiro error = VOP_GETATTR(vp, &va, cred); 15290792Sgshapiro VOP_UNLOCK(vp); 15390792Sgshapiro if (error) 15490792Sgshapiro break; 15590792Sgshapiro 15690792Sgshapiro if (va.va_uid != kauth_cred_geteuid(cred)) 15790792Sgshapiro break; 15890792Sgshapiro 15990792Sgshapiro error = usermount_common_policy(mp, flags); 16090792Sgshapiro if (error) 16190792Sgshapiro break; 16290792Sgshapiro 16390792Sgshapiro result = KAUTH_RESULT_ALLOW; 16490792Sgshapiro 16590792Sgshapiro break; 16690792Sgshapiro 16790792Sgshapiro case KAUTH_REQ_SYSTEM_MOUNT_UNMOUNT: 16890792Sgshapiro mp = arg1; 16990792Sgshapiro 17064562Sgshapiro /* Must own the mount. */ 17164562Sgshapiro if (mp->mnt_stat.f_owner == kauth_cred_geteuid(cred)) 17264562Sgshapiro result = KAUTH_RESULT_ALLOW; 17364562Sgshapiro 17464562Sgshapiro break; 17564562Sgshapiro 17664562Sgshapiro case KAUTH_REQ_SYSTEM_MOUNT_UPDATE: 17771345Sgshapiro mp = arg1; 17864562Sgshapiro flags = (u_long)arg2; 17964562Sgshapiro 18064562Sgshapiro /* Must own the mount. */ 18164562Sgshapiro if (mp->mnt_stat.f_owner == kauth_cred_geteuid(cred) && 18264562Sgshapiro usermount_common_policy(mp, flags) == 0) 18364562Sgshapiro result = KAUTH_RESULT_ALLOW; 18464562Sgshapiro 18564562Sgshapiro break; 18664562Sgshapiro 18764562Sgshapiro default: 18864562Sgshapiro break; 18964562Sgshapiro } 19064562Sgshapiro break; 19164562Sgshapiro 19264562Sgshapiro default: 19364562Sgshapiro break; 19464562Sgshapiro } 19564562Sgshapiro 19664562Sgshapiro return (result); 19764562Sgshapiro} 19864562Sgshapiro 19964562Sgshapirostatic int 20064562Sgshapirosecmodel_extensions_vnode_cb(kauth_cred_t cred, kauth_action_t action, 20190792Sgshapiro void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) 20290792Sgshapiro{ 20390792Sgshapiro int error; 20490792Sgshapiro bool isroot; 20590792Sgshapiro struct vattr va; 20690792Sgshapiro 20790792Sgshapiro if ((action & KAUTH_VNODE_ADD_LINK) == 0) 20890792Sgshapiro return KAUTH_RESULT_DEFER; 20990792Sgshapiro 21090792Sgshapiro error = VOP_GETATTR((vnode_t *)arg0, &va, cred); 21190792Sgshapiro if (error) 21290792Sgshapiro goto checkroot; 21364562Sgshapiro 21464562Sgshapiro if (hardlink_check_uid && kauth_cred_geteuid(cred) != va.va_uid) 21564562Sgshapiro goto checkroot; 21664562Sgshapiro 21764562Sgshapiro if (hardlink_check_gid && kauth_cred_groupmember(cred, va.va_gid) != 0) 21864562Sgshapiro goto checkroot; 21964562Sgshapiro 22064562Sgshapiro return KAUTH_RESULT_DEFER; 22164562Sgshapirocheckroot: 22264562Sgshapiro error = secmodel_eval("org.netbsd.secmodel.suser", "is-root", 22364562Sgshapiro cred, &isroot); 22464562Sgshapiro if (error || !isroot) 22564562Sgshapiro return KAUTH_RESULT_DENY; 22664562Sgshapiro 22764562Sgshapiro return KAUTH_RESULT_DEFER; 22864562Sgshapiro} 22964562Sgshapiro 23064562Sgshapiro