Index: coda/coda_vnops.c =================================================================== RCS file: /cvsroot/src/sys/coda/coda_vnops.c,v retrieving revision 1.117 diff -u -p -u -r1.117 coda_vnops.c --- coda/coda_vnops.c 5 Dec 2021 08:10:39 -0000 1.117 +++ coda/coda_vnops.c 27 Mar 2022 01:10:02 -0000 @@ -1198,6 +1198,10 @@ coda_link(void *v) error = EFAULT; /* XXX better value */ goto exit; } + error = kauth_authorize_vnode(cnp->cn_cred, KAUTH_VNODE_ADD_LINK, vp, + dvp, 0); + if (error) + goto exit; error = venus_link(vtomi(vp), &cp->c_fid, &dcp->c_fid, nm, len, cred, l); VOP_UNLOCK(vp); Index: fs/tmpfs/tmpfs_vnops.c =================================================================== RCS file: /cvsroot/src/sys/fs/tmpfs/tmpfs_vnops.c,v retrieving revision 1.148 diff -u -p -u -r1.148 tmpfs_vnops.c --- fs/tmpfs/tmpfs_vnops.c 20 Oct 2021 03:08:17 -0000 1.148 +++ fs/tmpfs/tmpfs_vnops.c 27 Mar 2022 01:10:03 -0000 @@ -801,6 +801,11 @@ tmpfs_link(void *v) goto out; } + error = kauth_authorize_vnode(cnp->cn_cred, KAUTH_VNODE_ADD_LINK, vp, + dvp, 0); + if (error) + goto out; + /* Allocate a new directory entry to represent the inode. */ error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), cnp->cn_nameptr, cnp->cn_namelen, &de); Index: fs/udf/udf_vnops.c =================================================================== RCS file: /cvsroot/src/sys/fs/udf/udf_vnops.c,v retrieving revision 1.119 diff -u -p -u -r1.119 udf_vnops.c --- fs/udf/udf_vnops.c 16 Feb 2022 22:00:56 -0000 1.119 +++ fs/udf/udf_vnops.c 27 Mar 2022 01:10:03 -0000 @@ -1559,18 +1559,21 @@ udf_do_link(struct vnode *dvp, struct vn udf_node = VTOI(vp); error = VOP_GETATTR(vp, &vap, FSCRED); - if (error) { - VOP_UNLOCK(vp); - return error; - } + if (error) + goto out; /* check link count overflow */ if (vap.va_nlink >= (1<<16)-1) { /* uint16_t */ - VOP_UNLOCK(vp); - return EMLINK; + error = EMLINK; + goto out; } + error = kauth_authorize_vnode(cnp->cn_cred, KAUTH_VNODE_ADD_LINK, vp, + dvp, NULL, 0); + if (error) + goto out; error = udf_dir_attach(dir_node->ump, dir_node, udf_node, &vap, cnp); +out: if (error) VOP_UNLOCK(vp); return error; Index: fs/unionfs/unionfs_vnops.c =================================================================== RCS file: /cvsroot/src/sys/fs/unionfs/unionfs_vnops.c,v retrieving revision 1.17 diff -u -p -u -r1.17 unionfs_vnops.c --- fs/unionfs/unionfs_vnops.c 20 Oct 2021 03:08:17 -0000 1.17 +++ fs/unionfs/unionfs_vnops.c 27 Mar 2022 01:10:03 -0000 @@ -566,7 +566,6 @@ unionfs_close_abort: static int unionfs_check_corrected_access(u_short mode, struct vattr *va, kauth_cred_t cred) { - int result; int error; uid_t uid; /* upper side vnode's uid */ gid_t gid; /* upper side vnode's gid */ @@ -590,10 +589,7 @@ unionfs_check_corrected_access(u_short m } /* check group */ - error = kauth_cred_ismember_gid(cred, gid, &result); - if (error != 0) - return error; - if (result) { + if (kauth_cred_groupmember(cred, gid) == 0) { if (mode & VEXEC) mask |= S_IXGRP; if (mode & VREAD) Index: fs/v7fs/v7fs_vnops.c =================================================================== RCS file: /cvsroot/src/sys/fs/v7fs/v7fs_vnops.c,v retrieving revision 1.34 diff -u -p -u -r1.34 v7fs_vnops.c --- fs/v7fs/v7fs_vnops.c 11 Feb 2022 10:55:15 -0000 1.34 +++ fs/v7fs/v7fs_vnops.c 27 Mar 2022 01:10:03 -0000 @@ -745,21 +745,27 @@ v7fs_link(void *v) struct v7fs_inode *p = &node->inode; struct v7fs_self *fs = node->v7fsmount->core; struct componentname *cnp = a->a_cnp; - int error = 0; + int error, abrt = 1; DPRINTF("%p\n", vp); - /* Lock soruce file */ + /* Lock source file */ if ((error = vn_lock(vp, LK_EXCLUSIVE))) { DPRINTF("lock failed. %p\n", vp); - VOP_ABORTOP(dvp, cnp); goto unlock; } + error = kauth_authorize_vnode(cnp->cn_cred, KAUTH_VNODE_ADD_LINK, vp, + dvp, NULL, 0); + if (error) + goto unlock; + abrt = 0; error = v7fs_file_link(fs, parent, p, cnp->cn_nameptr, cnp->cn_namelen); /* Sync dirent size change. */ uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); VOP_UNLOCK(vp); unlock: + if (abrt) + VOP_ABORTOP(dvp, cnp); return error; } Index: kern/kern_auth.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_auth.c,v retrieving revision 1.79 diff -u -p -u -r1.79 kern_auth.c --- kern/kern_auth.c 12 Mar 2022 15:32:32 -0000 1.79 +++ kern/kern_auth.c 27 Mar 2022 01:10:03 -0000 @@ -404,6 +404,25 @@ kauth_cred_ismember_gid(kauth_cred_t cre return (0); } +int +kauth_cred_groupmember(kauth_cred_t cred, gid_t gid) +{ + int ismember, error; + + KASSERT(cred != NULL); + KASSERT(cred != NOCRED); + KASSERT(cred != FSCRED); + + error = kauth_cred_ismember_gid(cred, gid, &ismember); + if (error) + return error; + + if (kauth_cred_getegid(cred) == gid || ismember) + return 0; + + return -1; +} + u_int kauth_cred_ngroups(kauth_cred_t cred) { Index: kern/sysv_ipc.c =================================================================== RCS file: /cvsroot/src/sys/kern/sysv_ipc.c,v retrieving revision 1.41 diff -u -p -u -r1.41 sysv_ipc.c --- kern/sysv_ipc.c 21 Feb 2020 00:26:22 -0000 1.41 +++ kern/sysv_ipc.c 27 Mar 2022 01:10:03 -0000 @@ -258,7 +258,6 @@ sysvipc_listener_cb(kauth_cred_t cred, k void *arg0, void *arg1, void *arg2, void *arg3) { mode_t mask; - int ismember = 0; struct ipc_perm *perm; int mode; enum kauth_system_req req; @@ -290,10 +289,8 @@ sysvipc_listener_cb(kauth_cred_t cred, k return ((perm->mode & mask) == mask ? KAUTH_RESULT_ALLOW : KAUTH_RESULT_DEFER /* EACCES */); } - if (kauth_cred_getegid(cred) == perm->gid || - (kauth_cred_ismember_gid(cred, perm->gid, &ismember) == 0 && ismember) || - kauth_cred_getegid(cred) == perm->cgid || - (kauth_cred_ismember_gid(cred, perm->cgid, &ismember) == 0 && ismember)) { + if (kauth_cred_groupmember(cred, perm->gid) == 0 || + kauth_cred_groupmember(cred, perm->cgid) == 0) { if (mode & IPC_R) mask |= S_IRGRP; if (mode & IPC_W) Index: miscfs/genfs/genfs_vnops.c =================================================================== RCS file: /cvsroot/src/sys/miscfs/genfs/genfs_vnops.c,v retrieving revision 1.217 diff -u -p -u -r1.217 genfs_vnops.c --- miscfs/genfs/genfs_vnops.c 19 Mar 2022 13:52:45 -0000 1.217 +++ miscfs/genfs/genfs_vnops.c 27 Mar 2022 01:10:03 -0000 @@ -663,18 +663,6 @@ genfs_node_wrlocked(struct vnode *vp) return rw_write_held(&gp->g_glock); } -static int -groupmember(gid_t gid, kauth_cred_t cred) -{ - int ismember; - int error = kauth_cred_ismember_gid(cred, gid, &ismember); - if (error) - return error; - if (kauth_cred_getegid(cred) == gid || ismember) - return 0; - return -1; -} - /* * Common filesystem object access control check routine. Accepts a * vnode, cred, uid, gid, mode, acl, requested access mode. @@ -712,7 +700,7 @@ genfs_can_access(vnode_t *vp, kauth_cred /* Otherwise, check the groups (first match) */ /* Otherwise, check the groups. */ - error = groupmember(file_gid, cred); + error = kauth_cred_groupmember(cred, file_gid); if (error > 0) return error; if (error == 0) { @@ -864,7 +852,7 @@ genfs_can_access_acl_posix1e(vnode_t *vp struct acl_entry *ae = &acl->acl_entry[i]; switch (ae->ae_tag) { case ACL_GROUP_OBJ: - error = groupmember(file_gid, cred); + error = kauth_cred_groupmember(cred, file_gid); if (error > 0) return error; if (error) @@ -885,7 +873,7 @@ genfs_can_access_acl_posix1e(vnode_t *vp break; case ACL_GROUP: - error = groupmember(ae->ae_id, cred); + error = kauth_cred_groupmember(cred, ae->ae_id); if (error > 0) return error; if (error) @@ -919,7 +907,7 @@ genfs_can_access_acl_posix1e(vnode_t *vp struct acl_entry *ae = &acl->acl_entry[i]; switch (ae->ae_tag) { case ACL_GROUP_OBJ: - error = groupmember(file_gid, cred); + error = kauth_cred_groupmember(cred, file_gid); if (error > 0) return error; if (error) @@ -935,7 +923,7 @@ genfs_can_access_acl_posix1e(vnode_t *vp goto out; case ACL_GROUP: - error = groupmember(ae->ae_id, cred); + error = kauth_cred_groupmember(cred, ae->ae_id); if (error > 0) return error; if (error) @@ -1053,14 +1041,14 @@ _acl_denies(const struct acl *aclp, int continue; break; case ACL_GROUP_OBJ: - error = groupmember(file_gid, cred); + error = kauth_cred_groupmember(cred, file_gid); if (error > 0) return error; if (error != 0) continue; break; case ACL_GROUP: - error = groupmember(ae->ae_id, cred); + error = kauth_cred_groupmember(cred, ae->ae_id); if (error > 0) return error; if (error != 0) Index: nfs/nfs_vnops.c =================================================================== RCS file: /cvsroot/src/sys/nfs/nfs_vnops.c,v retrieving revision 1.321 diff -u -p -u -r1.321 nfs_vnops.c --- nfs/nfs_vnops.c 20 Oct 2021 03:08:18 -0000 1.321 +++ nfs/nfs_vnops.c 27 Mar 2022 01:10:03 -0000 @@ -2012,6 +2012,13 @@ nfs_link(void *v) return error; } + error = kauth_authorize_vnode(cnp->cn_cred, KAUTH_VNODE_ADD_LINK, vp, + dvp, 0); + if (error) { + VOP_ABORTOP(dvp, cnp); + return error; + } + /* * Push all writes to the server, so that the attribute cache * doesn't get "out of sync" with the server. Index: secmodel/extensions/secmodel_extensions.c =================================================================== RCS file: /cvsroot/src/sys/secmodel/extensions/secmodel_extensions.c,v retrieving revision 1.12 diff -u -p -u -r1.12 secmodel_extensions.c --- secmodel/extensions/secmodel_extensions.c 16 Mar 2020 21:20:12 -0000 1.12 +++ secmodel/extensions/secmodel_extensions.c 27 Mar 2022 01:10:03 -0000 @@ -49,12 +49,14 @@ MODULE(MODULE_CLASS_SECMODEL, extensions static int dovfsusermount; static int curtain; static int user_set_cpu_affinity; +static int hardlink_check_uid; +static int hardlink_check_gid; #ifdef PT_SETDBREGS int user_set_dbregs; #endif -static kauth_listener_t l_system, l_process, l_network; +static kauth_listener_t l_system, l_process, l_network, l_vnode; static secmodel_t extensions_sm; @@ -73,6 +75,8 @@ static int secmodel_extensions_process_c void *, void *, void *, void *, void *); static int secmodel_extensions_network_cb(kauth_cred_t, kauth_action_t, void *, void *, void *, void *, void *); +static int secmodel_extensions_vnode_cb(kauth_cred_t, kauth_action_t, + void *, void *, void *, void *, void *); SYSCTL_SETUP(sysctl_security_extensions_setup, "security extensions sysctl") @@ -150,6 +154,25 @@ SYSCTL_SETUP(sysctl_security_extensions_ CTL_CREATE, CTL_EOL); #endif + sysctl_createv(clog, 0, &rnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "hardlink_check_uid", + SYSCTL_DESCR("Whether unprivileged users can hardlink "\ + "to files they don't own"), + sysctl_extensions_user_handler, 0, + &hardlink_check_uid, 0, + CTL_CREATE, CTL_EOL); + + sysctl_createv(clog, 0, &rnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "hardlink_check_uid", + SYSCTL_DESCR("Whether unprivileged users can hardlink "\ + "to files they that they are not in their " \ + "group membership"), + sysctl_extensions_user_handler, 0, + &hardlink_check_gid, 0, + CTL_CREATE, CTL_EOL); + /* Compatibility: vfs.generic.usermount */ sysctl_createv(clog, 0, NULL, NULL, CTLFLAG_PERMANENT, @@ -280,6 +303,8 @@ secmodel_extensions_start(void) secmodel_extensions_process_cb, NULL); l_network = kauth_listen_scope(KAUTH_SCOPE_NETWORK, secmodel_extensions_network_cb, NULL); + l_vnode = kauth_listen_scope(KAUTH_SCOPE_VNODE, + secmodel_extensions_vnode_cb, NULL); } static void @@ -289,6 +314,7 @@ secmodel_extensions_stop(void) kauth_unlisten_scope(l_system); kauth_unlisten_scope(l_process); kauth_unlisten_scope(l_network); + kauth_unlisten_scope(l_vnode); } static int @@ -503,3 +529,26 @@ secmodel_extensions_network_cb(kauth_cre return (result); } + +static int +secmodel_extensions_vnode_cb(kauth_cred_t cred, kauth_action_t action, + void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) +{ + int error; + struct vattr va; + + if ((action & KAUTH_VNODE_ADD_LINK) == 0) + return KAUTH_RESULT_DEFER; + + error = VOP_GETATTR((vnode_t *)arg0, &va, cred); + if (error) + return KAUTH_RESULT_DENY; + + if (hardlink_check_uid && kauth_cred_geteuid(cred) != va.va_uid) + return KAUTH_RESULT_DENY; + + if (hardlink_check_gid && kauth_cred_groupmember(cred, va.va_gid) != 0) + return KAUTH_RESULT_DENY; + + return KAUTH_RESULT_DEFER; +} Index: sys/kauth.h =================================================================== RCS file: /cvsroot/src/sys/sys/kauth.h,v retrieving revision 1.86 diff -u -p -u -r1.86 kauth.h --- sys/kauth.h 8 Sep 2020 14:12:57 -0000 1.86 +++ sys/kauth.h 27 Mar 2022 01:10:03 -0000 @@ -51,7 +51,7 @@ enum uio_seg; /* Types. */ typedef struct kauth_scope *kauth_scope_t; typedef struct kauth_listener *kauth_listener_t; -typedef uint32_t kauth_action_t; +typedef uint64_t kauth_action_t; typedef int (*kauth_scope_callback_t)(kauth_cred_t, kauth_action_t, void *, void *, void *, void *, void *); typedef struct kauth_key *kauth_key_t; @@ -383,36 +383,37 @@ enum { /* * Vnode scope - action bits. */ -#define KAUTH_VNODE_READ_DATA (1U << 0) +#define KAUTH_VNODE_READ_DATA (1ULL << 0) #define KAUTH_VNODE_LIST_DIRECTORY KAUTH_VNODE_READ_DATA -#define KAUTH_VNODE_WRITE_DATA (1U << 1) +#define KAUTH_VNODE_WRITE_DATA (1ULL << 1) #define KAUTH_VNODE_ADD_FILE KAUTH_VNODE_WRITE_DATA -#define KAUTH_VNODE_EXECUTE (1U << 2) +#define KAUTH_VNODE_EXECUTE (1ULL << 2) #define KAUTH_VNODE_SEARCH KAUTH_VNODE_EXECUTE -#define KAUTH_VNODE_DELETE (1U << 3) -#define KAUTH_VNODE_APPEND_DATA (1U << 4) +#define KAUTH_VNODE_DELETE (1ULL << 3) +#define KAUTH_VNODE_APPEND_DATA (1ULL << 4) #define KAUTH_VNODE_ADD_SUBDIRECTORY KAUTH_VNODE_APPEND_DATA -#define KAUTH_VNODE_READ_TIMES (1U << 5) -#define KAUTH_VNODE_WRITE_TIMES (1U << 6) -#define KAUTH_VNODE_READ_FLAGS (1U << 7) -#define KAUTH_VNODE_WRITE_FLAGS (1U << 8) -#define KAUTH_VNODE_READ_SYSFLAGS (1U << 9) -#define KAUTH_VNODE_WRITE_SYSFLAGS (1U << 10) -#define KAUTH_VNODE_RENAME (1U << 11) -#define KAUTH_VNODE_CHANGE_OWNERSHIP (1U << 12) -#define KAUTH_VNODE_READ_SECURITY (1U << 13) -#define KAUTH_VNODE_WRITE_SECURITY (1U << 14) -#define KAUTH_VNODE_READ_ATTRIBUTES (1U << 15) -#define KAUTH_VNODE_WRITE_ATTRIBUTES (1U << 16) -#define KAUTH_VNODE_READ_EXTATTRIBUTES (1U << 17) -#define KAUTH_VNODE_WRITE_EXTATTRIBUTES (1U << 18) -#define KAUTH_VNODE_RETAIN_SUID (1U << 19) -#define KAUTH_VNODE_RETAIN_SGID (1U << 20) -#define KAUTH_VNODE_REVOKE (1U << 21) - -#define KAUTH_VNODE_IS_EXEC (1U << 29) -#define KAUTH_VNODE_HAS_SYSFLAGS (1U << 30) -#define KAUTH_VNODE_ACCESS (1U << 31) +#define KAUTH_VNODE_READ_TIMES (1ULL << 5) +#define KAUTH_VNODE_WRITE_TIMES (1ULL << 6) +#define KAUTH_VNODE_READ_FLAGS (1ULL << 7) +#define KAUTH_VNODE_WRITE_FLAGS (1ULL << 8) +#define KAUTH_VNODE_READ_SYSFLAGS (1ULL << 9) +#define KAUTH_VNODE_WRITE_SYSFLAGS (1ULL << 10) +#define KAUTH_VNODE_RENAME (1ULL << 11) +#define KAUTH_VNODE_CHANGE_OWNERSHIP (1ULL << 12) +#define KAUTH_VNODE_READ_SECURITY (1ULL << 13) +#define KAUTH_VNODE_WRITE_SECURITY (1ULL << 14) +#define KAUTH_VNODE_READ_ATTRIBUTES (1ULL << 15) +#define KAUTH_VNODE_WRITE_ATTRIBUTES (1ULL << 16) +#define KAUTH_VNODE_READ_EXTATTRIBUTES (1ULL << 17) +#define KAUTH_VNODE_WRITE_EXTATTRIBUTES (1ULL << 18) +#define KAUTH_VNODE_RETAIN_SUID (1ULL << 19) +#define KAUTH_VNODE_RETAIN_SGID (1ULL << 20) +#define KAUTH_VNODE_REVOKE (1ULL << 21) + +#define KAUTH_VNODE_IS_EXEC (1ULL << 29) +#define KAUTH_VNODE_HAS_SYSFLAGS (1ULL << 30) +#define KAUTH_VNODE_ACCESS (1ULL << 31) +#define KAUTH_VNODE_ADD_LINK (1ULL << 32) /* * This is a special fs_decision indication that can be used by file-systems @@ -494,6 +495,7 @@ gid_t kauth_cred_getgid(kauth_cred_t); gid_t kauth_cred_getegid(kauth_cred_t); gid_t kauth_cred_getsvgid(kauth_cred_t); int kauth_cred_ismember_gid(kauth_cred_t, gid_t, int *); +int kauth_cred_groupmember(kauth_cred_t, gid_t); u_int kauth_cred_ngroups(kauth_cred_t); gid_t kauth_cred_group(kauth_cred_t, u_int); Index: sys/param.h =================================================================== RCS file: /cvsroot/src/sys/sys/param.h,v retrieving revision 1.708 diff -u -p -u -r1.708 param.h --- sys/param.h 19 Mar 2022 13:53:32 -0000 1.708 +++ sys/param.h 27 Mar 2022 01:10:03 -0000 @@ -67,7 +67,7 @@ * 2.99.9 (299000900) */ -#define __NetBSD_Version__ 999009500 /* NetBSD 9.99.95 */ +#define __NetBSD_Version__ 999009600 /* NetBSD 9.99.96 */ #define __NetBSD_Prereq__(M,m,p) (((((M) * 100000000) + \ (m) * 1000000) + (p) * 100) <= __NetBSD_Version__) Index: ufs/chfs/chfs_vnops.c =================================================================== RCS file: /cvsroot/src/sys/ufs/chfs/chfs_vnops.c,v retrieving revision 1.47 diff -u -p -u -r1.47 chfs_vnops.c --- ufs/chfs/chfs_vnops.c 7 Dec 2021 21:37:37 -0000 1.47 +++ ufs/chfs/chfs_vnops.c 27 Mar 2022 01:10:03 -0000 @@ -1081,32 +1081,36 @@ chfs_link(void *v) struct componentname *cnp = ((struct vop_link_v2_args *) v)->a_cnp; struct chfs_inode *ip, *parent; - int error = 0; + int error, abrt = 1; if (vp->v_type == VDIR) { - VOP_ABORTOP(dvp, cnp); error = EISDIR; goto out; } if (dvp->v_mount != vp->v_mount) { - VOP_ABORTOP(dvp, cnp); error = EXDEV; goto out; } - if (dvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE))) { - VOP_ABORTOP(dvp, cnp); + if (dvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE))) + goto out; + + error = kauth_authorize_vnode(cnp->cn_cred, KAUTH_VNODE_ADD_LINK, vp, + dvp, NULL, 0); + if (error) goto out; - } parent = VTOI(dvp); ip = VTOI(vp); + abrt = 0; error = chfs_do_link(ip, parent, cnp->cn_nameptr, cnp->cn_namelen, ip->ch_type); if (dvp != vp) VOP_UNLOCK(vp); out: + if (abrt) + VOP_ABORTOP(dvp, cnp); return error; } Index: ufs/ext2fs/ext2fs_vnops.c =================================================================== RCS file: /cvsroot/src/sys/ufs/ext2fs/ext2fs_vnops.c,v retrieving revision 1.136 diff -u -p -u -r1.136 ext2fs_vnops.c --- ufs/ext2fs/ext2fs_vnops.c 20 Oct 2021 03:08:19 -0000 1.136 +++ ufs/ext2fs/ext2fs_vnops.c 27 Mar 2022 01:10:03 -0000 @@ -580,7 +580,7 @@ ext2fs_link(void *v) struct vnode *vp = ap->a_vp; struct componentname *cnp = ap->a_cnp; struct inode *ip; - int error; + int error, abrt = 1; struct ufs_lookup_results *ulr; KASSERT(dvp != vp); @@ -592,23 +592,24 @@ ext2fs_link(void *v) UFS_CHECK_CRAPCOUNTER(VTOI(dvp)); error = vn_lock(vp, LK_EXCLUSIVE); - if (error) { - VOP_ABORTOP(dvp, cnp); + if (error) goto out2; - } + error = kauth_authorize_vnode(cnp->cn_cred, KAUTH_VNODE_ADD_LINK, vp, + dvp, 0); + if (error) + goto out1; ip = VTOI(vp); if ((nlink_t)ip->i_e2fs_nlink >= EXT2FS_LINK_MAX) { - VOP_ABORTOP(dvp, cnp); error = EMLINK; goto out1; } if (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) { - VOP_ABORTOP(dvp, cnp); error = EPERM; goto out1; } ip->i_e2fs_nlink++; ip->i_flag |= IN_CHANGE; + abrt = 0; error = ext2fs_update(vp, NULL, NULL, UPDATE_WAIT); if (!error) error = ext2fs_direnter(ip, dvp, ulr, cnp); @@ -619,6 +620,8 @@ ext2fs_link(void *v) out1: VOP_UNLOCK(vp); out2: + if (abrt) + VOP_ABORTOP(dvp, cnp); return error; } Index: ufs/lfs/ulfs_vnops.c =================================================================== RCS file: /cvsroot/src/sys/ufs/lfs/ulfs_vnops.c,v retrieving revision 1.55 diff -u -p -u -r1.55 ulfs_vnops.c --- ufs/lfs/ulfs_vnops.c 20 Oct 2021 03:08:19 -0000 1.55 +++ ufs/lfs/ulfs_vnops.c 27 Mar 2022 01:10:03 -0000 @@ -561,7 +561,7 @@ ulfs_link(void *v) struct vnode *vp = ap->a_vp; struct componentname *cnp = ap->a_cnp; struct inode *ip; - int error; + int error, abrt = 1; struct ulfs_lookup_results *ulr; KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); @@ -573,26 +573,26 @@ ulfs_link(void *v) ULFS_CHECK_CRAPCOUNTER(VTOI(dvp)); error = vn_lock(vp, LK_EXCLUSIVE); - if (error) { - VOP_ABORTOP(dvp, cnp); + if (error) goto out2; - } if (vp->v_mount != dvp->v_mount) { error = ENOENT; - VOP_ABORTOP(dvp, cnp); goto out2; } ip = VTOI(vp); if ((nlink_t)ip->i_nlink >= LINK_MAX) { - VOP_ABORTOP(dvp, cnp); error = EMLINK; goto out1; } if (ip->i_flags & (IMMUTABLE | APPEND)) { - VOP_ABORTOP(dvp, cnp); error = EPERM; goto out1; } + error = kauth_authorize_vnode(cnp->cn_cred, KAUTH_VNODE_ADD_LINK, vp, + dvp, 0); + if (error) + goto out1; + abrt = 0; ip->i_nlink++; DIP_ASSIGN(ip, nlink, ip->i_nlink); ip->i_state |= IN_CHANGE; @@ -608,6 +608,8 @@ ulfs_link(void *v) } out1: VOP_UNLOCK(vp); + if (abrt) + VOP_ABORTOP(dvp, cnp); out2: return (error); } Index: ufs/ufs/ufs_vnops.c =================================================================== RCS file: /cvsroot/src/sys/ufs/ufs/ufs_vnops.c,v retrieving revision 1.261 diff -u -p -u -r1.261 ufs_vnops.c --- ufs/ufs/ufs_vnops.c 26 Nov 2021 17:35:12 -0000 1.261 +++ ufs/ufs/ufs_vnops.c 27 Mar 2022 01:10:04 -0000 @@ -893,7 +893,7 @@ ufs_link(void *v) struct mount *mp = dvp->v_mount; struct inode *ip; struct direct *newdir; - int error; + int error, abrt = 1; struct ufs_lookup_results *ulr; KASSERT(dvp != vp); @@ -905,29 +905,32 @@ ufs_link(void *v) UFS_CHECK_CRAPCOUNTER(VTOI(dvp)); error = vn_lock(vp, LK_EXCLUSIVE); - if (error) { - VOP_ABORTOP(dvp, cnp); + if (error) goto out2; - } + ip = VTOI(vp); if ((nlink_t)ip->i_nlink >= LINK_MAX) { - VOP_ABORTOP(dvp, cnp); error = EMLINK; goto out1; } if (ip->i_flags & (IMMUTABLE | APPEND)) { - VOP_ABORTOP(dvp, cnp); error = EPERM; goto out1; } + + error = kauth_authorize_vnode(cnp->cn_cred, KAUTH_VNODE_ADD_LINK, vp, + dvp, 0); + if (error) + goto out1; + error = UFS_WAPBL_BEGIN(mp); - if (error) { - VOP_ABORTOP(dvp, cnp); + if (error) goto out1; - } + ip->i_nlink++; DIP_ASSIGN(ip, nlink, ip->i_nlink); ip->i_flag |= IN_CHANGE; + abrt = 0; error = UFS_UPDATE(vp, NULL, NULL, UPDATE_DIROP); if (!error) { newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK); @@ -945,6 +948,8 @@ ufs_link(void *v) out1: VOP_UNLOCK(vp); out2: + if (abrt) + VOP_ABORTOP(dvp, cnp); return (error); }