1 /* $NetBSD: secmodel_extensions_vfs.c,v 1.1 2023/04/22 13:54:19 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2011 Elad Efrat <elad@NetBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __KERNEL_RCSID(0, "$NetBSD: secmodel_extensions_vfs.c,v 1.1 2023/04/22 13:54:19 riastradh Exp $"); 32 33 #include <sys/types.h> 34 #include <sys/param.h> 35 36 #include <sys/kauth.h> 37 #include <sys/vnode.h> 38 39 #include <secmodel/secmodel.h> 40 #include <secmodel/extensions/extensions.h> 41 #include <secmodel/extensions/extensions_impl.h> 42 43 static int dovfsusermount; 44 static int hardlink_check_uid; 45 static int hardlink_check_gid; 46 47 static kauth_listener_t l_system, l_vnode; 48 49 static int secmodel_extensions_system_cb(kauth_cred_t, kauth_action_t, 50 void *, void *, void *, void *, void *); 51 static int secmodel_extensions_vnode_cb(kauth_cred_t, kauth_action_t, 52 void *, void *, void *, void *, void *); 53 54 void 55 secmodel_extensions_vfs_start(void) 56 { 57 58 l_system = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, 59 secmodel_extensions_system_cb, NULL); 60 l_vnode = kauth_listen_scope(KAUTH_SCOPE_VNODE, 61 secmodel_extensions_vnode_cb, NULL); 62 } 63 64 void 65 secmodel_extensions_vfs_stop(void) 66 { 67 68 kauth_unlisten_scope(l_system); 69 kauth_unlisten_scope(l_vnode); 70 } 71 72 void 73 secmodel_extensions_vfs_sysctl(struct sysctllog **clog, 74 const struct sysctlnode *rnode) 75 { 76 77 sysctl_createv(clog, 0, &rnode, NULL, 78 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 79 CTLTYPE_INT, "usermount", 80 SYSCTL_DESCR("Whether unprivileged users may mount " 81 "filesystems"), 82 sysctl_extensions_user_handler, 0, &dovfsusermount, 0, 83 CTL_CREATE, CTL_EOL); 84 85 sysctl_createv(clog, 0, &rnode, NULL, 86 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 87 CTLTYPE_INT, "hardlink_check_uid", 88 SYSCTL_DESCR("Whether unprivileged users can hardlink "\ 89 "to files they don't own"), 90 sysctl_extensions_user_handler, 0, 91 &hardlink_check_uid, 0, 92 CTL_CREATE, CTL_EOL); 93 94 sysctl_createv(clog, 0, &rnode, NULL, 95 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 96 CTLTYPE_INT, "hardlink_check_gid", 97 SYSCTL_DESCR("Whether unprivileged users can hardlink "\ 98 "to files that are not in their " \ 99 "group membership"), 100 sysctl_extensions_user_handler, 0, 101 &hardlink_check_gid, 0, 102 CTL_CREATE, CTL_EOL); 103 104 /* Compatibility: vfs.generic.usermount */ 105 sysctl_createv(clog, 0, NULL, NULL, 106 CTLFLAG_PERMANENT, 107 CTLTYPE_NODE, "generic", 108 SYSCTL_DESCR("Non-specific vfs related information"), 109 NULL, 0, NULL, 0, 110 CTL_VFS, VFS_GENERIC, CTL_EOL); 111 112 sysctl_createv(clog, 0, NULL, NULL, 113 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 114 CTLTYPE_INT, "usermount", 115 SYSCTL_DESCR("Whether unprivileged users may mount " 116 "filesystems"), 117 sysctl_extensions_user_handler, 0, &dovfsusermount, 0, 118 CTL_VFS, VFS_GENERIC, VFS_USERMOUNT, CTL_EOL); 119 } 120 121 static int 122 secmodel_extensions_system_cb(kauth_cred_t cred, kauth_action_t action, 123 void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) 124 { 125 vnode_t *vp; 126 struct vattr va; 127 struct mount *mp; 128 u_long flags; 129 int result; 130 enum kauth_system_req req; 131 int error; 132 133 req = (enum kauth_system_req)(uintptr_t)arg0; 134 result = KAUTH_RESULT_DEFER; 135 136 switch (action) { 137 case KAUTH_SYSTEM_MOUNT: 138 if (dovfsusermount == 0) 139 break; 140 switch (req) { 141 case KAUTH_REQ_SYSTEM_MOUNT_NEW: 142 vp = (vnode_t *)arg1; 143 mp = vp->v_mount; 144 flags = (u_long)arg2; 145 146 /* 147 * Ensure that the user owns the directory onto which 148 * the mount is attempted. 149 */ 150 vn_lock(vp, LK_SHARED | LK_RETRY); 151 error = VOP_GETATTR(vp, &va, cred); 152 VOP_UNLOCK(vp); 153 if (error) 154 break; 155 156 if (va.va_uid != kauth_cred_geteuid(cred)) 157 break; 158 159 error = usermount_common_policy(mp, flags); 160 if (error) 161 break; 162 163 result = KAUTH_RESULT_ALLOW; 164 165 break; 166 167 case KAUTH_REQ_SYSTEM_MOUNT_UNMOUNT: 168 mp = arg1; 169 170 /* Must own the mount. */ 171 if (mp->mnt_stat.f_owner == kauth_cred_geteuid(cred)) 172 result = KAUTH_RESULT_ALLOW; 173 174 break; 175 176 case KAUTH_REQ_SYSTEM_MOUNT_UPDATE: 177 mp = arg1; 178 flags = (u_long)arg2; 179 180 /* Must own the mount. */ 181 if (mp->mnt_stat.f_owner == kauth_cred_geteuid(cred) && 182 usermount_common_policy(mp, flags) == 0) 183 result = KAUTH_RESULT_ALLOW; 184 185 break; 186 187 default: 188 break; 189 } 190 break; 191 192 default: 193 break; 194 } 195 196 return (result); 197 } 198 199 static int 200 secmodel_extensions_vnode_cb(kauth_cred_t cred, kauth_action_t action, 201 void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) 202 { 203 int error; 204 bool isroot; 205 struct vattr va; 206 207 if ((action & KAUTH_VNODE_ADD_LINK) == 0) 208 return KAUTH_RESULT_DEFER; 209 210 error = VOP_GETATTR((vnode_t *)arg0, &va, cred); 211 if (error) 212 goto checkroot; 213 214 if (hardlink_check_uid && kauth_cred_geteuid(cred) != va.va_uid) 215 goto checkroot; 216 217 if (hardlink_check_gid && kauth_cred_groupmember(cred, va.va_gid) != 0) 218 goto checkroot; 219 220 return KAUTH_RESULT_DEFER; 221 checkroot: 222 error = secmodel_eval("org.netbsd.secmodel.suser", "is-root", 223 cred, &isroot); 224 if (error || !isroot) 225 return KAUTH_RESULT_DENY; 226 227 return KAUTH_RESULT_DEFER; 228 } 229 230