diff --git a/sys/kern/vfs_extattr.c b/sys/kern/vfs_extattr.c index 79a0ce932123..d06ba27c5596 100644 --- a/sys/kern/vfs_extattr.c +++ b/sys/kern/vfs_extattr.c @@ -1358,6 +1358,48 @@ link(td, uap) return (kern_link(td, uap->path, uap->link, UIO_USERSPACE)); } +SYSCTL_DECL(_security_bsd); + +static int hardlink_check_uid = 0; +SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_uid, CTLFLAG_RW, + &hardlink_check_uid, 0, + "Unprivileged processes cannot create hard links to files owned by other " + "users"); +static int hardlink_check_gid = 0; +SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_gid, CTLFLAG_RW, + &hardlink_check_gid, 0, + "Unprivileged processes cannot create hard links to files owned by other " + "groups"); + +static int +can_hardlink(struct vnode *vp, struct thread *td, struct ucred *cred) +{ + struct vattr va; + int error; + + if (suser_cred(cred, PRISON_ROOT) == 0) + return (0); + + if (!hardlink_check_uid && !hardlink_check_gid) + return (0); + + error = VOP_GETATTR(vp, &va, cred, td); + if (error != 0) + return (error); + + if (hardlink_check_uid) { + if (cred->cr_uid != va.va_uid) + return (EPERM); + } + + if (hardlink_check_gid) { + if (!groupmember(va.va_gid, cred)) + return (EPERM); + } + + return (0); +} + int kern_link(struct thread *td, char *path, char *link, enum uio_seg segflg) { @@ -1393,9 +1435,11 @@ kern_link(struct thread *td, char *path, char *link, enum uio_seg segflg) == 0) { VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); + error = can_hardlink(vp, td, td->td_ucred); + if (error == 0) #ifdef MAC - error = mac_check_vnode_link(td->td_ucred, nd.ni_dvp, - vp, &nd.ni_cnd); + error = mac_check_vnode_link(td->td_ucred, + nd.ni_dvp, vp, &nd.ni_cnd); if (error == 0) #endif error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 79a0ce932123..d06ba27c5596 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -1358,6 +1358,48 @@ link(td, uap) return (kern_link(td, uap->path, uap->link, UIO_USERSPACE)); } +SYSCTL_DECL(_security_bsd); + +static int hardlink_check_uid = 0; +SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_uid, CTLFLAG_RW, + &hardlink_check_uid, 0, + "Unprivileged processes cannot create hard links to files owned by other " + "users"); +static int hardlink_check_gid = 0; +SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_gid, CTLFLAG_RW, + &hardlink_check_gid, 0, + "Unprivileged processes cannot create hard links to files owned by other " + "groups"); + +static int +can_hardlink(struct vnode *vp, struct thread *td, struct ucred *cred) +{ + struct vattr va; + int error; + + if (suser_cred(cred, PRISON_ROOT) == 0) + return (0); + + if (!hardlink_check_uid && !hardlink_check_gid) + return (0); + + error = VOP_GETATTR(vp, &va, cred, td); + if (error != 0) + return (error); + + if (hardlink_check_uid) { + if (cred->cr_uid != va.va_uid) + return (EPERM); + } + + if (hardlink_check_gid) { + if (!groupmember(va.va_gid, cred)) + return (EPERM); + } + + return (0); +} + int kern_link(struct thread *td, char *path, char *link, enum uio_seg segflg) { @@ -1393,9 +1435,11 @@ kern_link(struct thread *td, char *path, char *link, enum uio_seg segflg) == 0) { VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); + error = can_hardlink(vp, td, td->td_ucred); + if (error == 0) #ifdef MAC - error = mac_check_vnode_link(td->td_ucred, nd.ni_dvp, - vp, &nd.ni_cnd); + error = mac_check_vnode_link(td->td_ucred, + nd.ni_dvp, vp, &nd.ni_cnd); if (error == 0) #endif error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);