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