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
secmodel_extensions_vfs_start(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
secmodel_extensions_vfs_stop(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
secmodel_extensions_vfs_sysctl(struct sysctllog ** clog,const struct sysctlnode * rnode)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
secmodel_extensions_system_cb(kauth_cred_t cred,kauth_action_t action,void * cookie,void * arg0,void * arg1,void * arg2,void * arg3)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
secmodel_extensions_vnode_cb(kauth_cred_t cred,kauth_action_t action,void * cookie,void * arg0,void * arg1,void * arg2,void * arg3)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