From df9215be9011b8724cc6ef6c76c5869aa6f0fd7c Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Sat, 26 Mar 2022 20:13:04 +0000
Subject: [PATCH] link(2): Require caller to own file and be in file's group.

---
 sys/kern/vfs_syscalls.c | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 695010d1da0a..54b22a6fda6f 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -2566,9 +2566,10 @@ do_sys_mkfifoat(struct lwp *l, int fdat, const char *path, mode_t mode)
 /* ARGSUSED */
 int
 do_sys_linkat(struct lwp *l, int fdpath, const char *path, int fdlink,
-    const char *link, int follow, register_t *retval) 
+    const char *link, int follow, register_t *retval)
 {
 	struct vnode *vp;
+	struct vattr vattr;
 	struct pathbuf *linkpb;
 	struct nameidata nd;
 	namei_simple_flags_t ns_flags;
@@ -2603,6 +2604,28 @@ do_sys_linkat(struct lwp *l, int fdpath, const char *path, int fdlink,
 		error = EXDEV;
 		goto abortop;
 	}
+	/*
+	 * Prevent hard-linking files you don't own, or in a group that
+	 * you're not in.
+	 */
+	error = VOP_GETATTR(vp, &vattr, l->l_cred);
+	if (error)
+		goto abortop;
+	if (vattr.va_uid != kauth_cred_geteuid(l->l_cred)) {
+		error = EPERM;
+		goto abortop;
+	}
+	if (vattr.va_gid != kauth_cred_getegid(l->l_cred)) {
+		int groupmember;
+		error = kauth_cred_ismember_gid(l->l_cred, vattr.va_gid,
+		    &groupmember);
+		if (error)
+			goto abortop;
+		if (!groupmember) {
+			error = EPERM;
+			goto abortop;
+		}
+	}
 	error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
 	VOP_UNLOCK(nd.ni_dvp);
 	vrele(nd.ni_dvp);