utimens: fix authorization during change of file timestamps
MODIFY
include/sys/cred.h
include/sys/cred.h
@@ -66,8 +66,9 @@ bool cred_groupmember(gid_t gid, cred_t *cred);
/* VFS checks */
bool cred_can_chmod(uid_t f_owner, gid_t f_group, cred_t *cred, mode_t mode);
bool cred_can_chown(uid_t f_owner, cred_t *cred, uid_t new_uid, gid_t new_gid);
+bool cred_can_utime(vnode_t *vn, uid_t f_owner, cred_t *cred,
+ va_flags_t vaflags);
int cred_can_access(vattr_t *va, cred_t *cred, accmode_t mode);
-int cred_can_utime(vnode_t *vn, uid_t f_owner, cred_t *cred);
/* setlogin() check */
bool cred_can_setlogin(cred_t *cred);

MODIFY
include/sys/vnode.h
include/sys/vnode.h
@@ -24,6 +24,11 @@ typedef struct cred cred_t;
* vnodeops should not modify attributes set to VNOVAL. */
#define VNOVAL (-1)
+/* Flags for vattr:va_flags. */
+typedef enum {
+ VA_UTIMES_NULL = 1, /* Utimes argument was null. */
+} va_flags_t;
+
/* vnode access modes
* VADMIN - owner of file (root has VADMIN to all files) */
typedef enum { VEXEC = 1, VWRITE = 2, VREAD = 4, VADMIN = 8 } accmode_t;
@@ -113,6 +118,7 @@ typedef struct vattr {
uid_t va_uid; /* owner user id */
gid_t va_gid; /* owner group id */
size_t va_size; /* file size in bytes */
+ va_flags_t va_flags; /* additional flags */
timespec_t va_atime; /* time of last access */
timespec_t va_mtime; /* time of last data modification */
timespec_t va_ctime; /* time of last file status change */

MODIFY
sys/kern/cred_checks.c
sys/kern/cred_checks.c
@@ -123,9 +123,16 @@ check:
return (mode & granted) == mode ? 0 : EACCES;
}
-int cred_can_utime(vnode_t *vn, uid_t f_owner, cred_t *cred) {
- if (f_owner == cred->cr_euid)
- return 0;
+bool cred_can_utime(vnode_t *vn, uid_t f_owner, cred_t *cred,
+ va_flags_t vaflags) {
+ /* owner and root can change times */
+ if (f_owner == cred->cr_euid || cred->cr_euid == 0)
+ return true;
+
+ /* any other user can change times only to current time... */
+ if ((vaflags & VA_UTIMES_NULL) == 0)
+ return false;
- return VOP_ACCESS(vn, VWRITE, cred);
+ /* and only when he has write permission */
+ return VOP_ACCESS(vn, VWRITE, cred) == 0;
}

MODIFY
sys/kern/tmpfs.c
sys/kern/tmpfs.c
@@ -310,7 +310,7 @@ static void tmpfs_dir_detach(tmpfs_node_t *dv, tmpfs_dirent_t *de);
static blkptr_t *tmpfs_get_blk(tmpfs_node_t *v, size_t blkno);
static int tmpfs_resize(tmpfs_mount_t *tfm, tmpfs_node_t *v, size_t newsize);
static int tmpfs_chtimes(tmpfs_node_t *v, timespec_t *atime, timespec_t *mtime,
- cred_t *cred);
+ cred_t *cred, va_flags_t vaflags);
static void tmpfs_update_time(tmpfs_node_t *v, tmpfs_time_type_t type);
/* tmpfs readdir operations */
@@ -510,7 +510,8 @@ static int tmpfs_vop_setattr(vnode_t *v, vattr_t *va, cred_t *cred) {
}
if (va->va_atime.tv_sec != VNOVAL || va->va_mtime.tv_sec != VNOVAL) {
- if ((error = tmpfs_chtimes(node, &va->va_atime, &va->va_mtime, cred)))
+ if ((error = tmpfs_chtimes(node, &va->va_atime, &va->va_mtime, cred,
+ va->va_flags)))
return error;
}
@@ -997,10 +998,9 @@ static int tmpfs_resize(tmpfs_mount_t *tfm, tmpfs_node_t *v, size_t newsize) {
}
static int tmpfs_chtimes(tmpfs_node_t *v, timespec_t *atime, timespec_t *mtime,
- cred_t *cred) {
- int err;
- if ((err = cred_can_utime(v->tfn_vnode, v->tfn_uid, cred)))
- return err;
+ cred_t *cred, va_flags_t vaflags) {
+ if (!cred_can_utime(v->tfn_vnode, v->tfn_uid, cred, vaflags))
+ return EPERM;
mtx_lock(&v->tfn_timelock);
if (atime->tv_sec != VNOVAL)

MODIFY
sys/kern/vfs_syscalls.c
sys/kern/vfs_syscalls.c
@@ -699,6 +699,7 @@ static int vfs_utimens(vnode_t *v, timespec_t times[2], cred_t *cred) {
if (times == NULL) {
va.va_atime = va.va_mtime = nanotime();
+ va.va_flags = VA_UTIMES_NULL;
} else {
va.va_atime = times[0];
va.va_mtime = times[1];

MODIFY
sys/kern/vfs_vnode.c
sys/kern/vfs_vnode.c
@@ -174,6 +174,7 @@ void vattr_null(vattr_t *va) {
va->va_uid = VNOVAL;
va->va_gid = VNOVAL;
va->va_size = VNOVAL;
+ va->va_flags = 0;
va->va_atime.tv_sec = va->va_mtime.tv_sec = va->va_ctime.tv_sec = VNOVAL;
va->va_atime.tv_nsec = va->va_mtime.tv_nsec = va->va_ctime.tv_nsec = VNOVAL;
}

utimens: fix authorization during change of file timestamps (#1104)