1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2022 FUJITSU LIMITED. All rights reserved.
4f08c3bdfSopenharmony_ci * Author: Yang Xu <xuyang2018.jy@fujitsu.com>
5f08c3bdfSopenharmony_ci */
6f08c3bdfSopenharmony_ci
7f08c3bdfSopenharmony_ci/*\
8f08c3bdfSopenharmony_ci * [Description]
9f08c3bdfSopenharmony_ci *
10f08c3bdfSopenharmony_ci * Check setgid strip logic whether works correctly when creating tmpfile under
11f08c3bdfSopenharmony_ci * filesystem without POSIX ACL supported(by using noacl mount option). Test it
12f08c3bdfSopenharmony_ci * with umask S_IXGRP and also check file mode whether has filtered S_IXGRP.
13f08c3bdfSopenharmony_ci *
14f08c3bdfSopenharmony_ci * Fixed in:
15f08c3bdfSopenharmony_ci *
16f08c3bdfSopenharmony_ci *  commit ac6800e279a22b28f4fc21439843025a0d5bf03e
17f08c3bdfSopenharmony_ci *  Author: Yang Xu <xuyang2018.jy@fujitsu.com>
18f08c3bdfSopenharmony_ci *  Date:   Thu July 14 14:11:26 2022 +0800
19f08c3bdfSopenharmony_ci *
20f08c3bdfSopenharmony_ci *  fs: Add missing umask strip in vfs_tmpfile
21f08c3bdfSopenharmony_ci *
22f08c3bdfSopenharmony_ci * The most code is pasted form creat09.c.
23f08c3bdfSopenharmony_ci */
24f08c3bdfSopenharmony_ci
25f08c3bdfSopenharmony_ci#define _GNU_SOURCE
26f08c3bdfSopenharmony_ci#include <stdlib.h>
27f08c3bdfSopenharmony_ci#include <sys/types.h>
28f08c3bdfSopenharmony_ci#include <pwd.h>
29f08c3bdfSopenharmony_ci#include <sys/mount.h>
30f08c3bdfSopenharmony_ci#include <unistd.h>
31f08c3bdfSopenharmony_ci#include <stdio.h>
32f08c3bdfSopenharmony_ci#include "tst_test.h"
33f08c3bdfSopenharmony_ci#include "lapi/fcntl.h"
34f08c3bdfSopenharmony_ci#include "tst_uid.h"
35f08c3bdfSopenharmony_ci#include "tst_safe_file_at.h"
36f08c3bdfSopenharmony_ci
37f08c3bdfSopenharmony_ci#define MODE_RWX        0777
38f08c3bdfSopenharmony_ci#define MODE_SGID       (S_ISGID|0777)
39f08c3bdfSopenharmony_ci#define MNTPOINT	"mntpoint"
40f08c3bdfSopenharmony_ci#define WORKDIR		MNTPOINT "/testdir"
41f08c3bdfSopenharmony_ci#define OPEN_FILE	"open.tmp"
42f08c3bdfSopenharmony_ci
43f08c3bdfSopenharmony_cistatic gid_t free_gid;
44f08c3bdfSopenharmony_cistatic int tmpfile_fd = -1, dir_fd = -1, mount_flag;
45f08c3bdfSopenharmony_cistatic struct passwd *ltpuser;
46f08c3bdfSopenharmony_ci
47f08c3bdfSopenharmony_cistatic void do_mount(const char *source, const char *target,
48f08c3bdfSopenharmony_ci	const char *filesystemtype, unsigned long mountflags,
49f08c3bdfSopenharmony_ci	const void *data)
50f08c3bdfSopenharmony_ci{
51f08c3bdfSopenharmony_ci	TEST(mount(source, target, filesystemtype, mountflags, data));
52f08c3bdfSopenharmony_ci
53f08c3bdfSopenharmony_ci	if (TST_RET == -1 && TST_ERR == EINVAL)
54f08c3bdfSopenharmony_ci		tst_brk(TCONF, "Kernel does not support noacl feature");
55f08c3bdfSopenharmony_ci
56f08c3bdfSopenharmony_ci	if (TST_RET == -1) {
57f08c3bdfSopenharmony_ci		tst_brk(TBROK | TTERRNO, "mount(%s, %s, %s, %lu, %p) failed",
58f08c3bdfSopenharmony_ci			source, target, filesystemtype, mountflags, data);
59f08c3bdfSopenharmony_ci	}
60f08c3bdfSopenharmony_ci
61f08c3bdfSopenharmony_ci	if (TST_RET)
62f08c3bdfSopenharmony_ci		tst_brk(TBROK, "Invalid mount return value %ld", TST_RET);
63f08c3bdfSopenharmony_ci
64f08c3bdfSopenharmony_ci	mount_flag = 1;
65f08c3bdfSopenharmony_ci}
66f08c3bdfSopenharmony_ci
67f08c3bdfSopenharmony_cistatic void open_tmpfile_supported(int dirfd)
68f08c3bdfSopenharmony_ci{
69f08c3bdfSopenharmony_ci	TEST(openat(dirfd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID));
70f08c3bdfSopenharmony_ci
71f08c3bdfSopenharmony_ci	if (TST_RET == -1) {
72f08c3bdfSopenharmony_ci		if (errno == ENOTSUP)
73f08c3bdfSopenharmony_ci			tst_brk(TCONF, "fs doesn't support O_TMPFILE");
74f08c3bdfSopenharmony_ci		else
75f08c3bdfSopenharmony_ci			tst_brk(TBROK | TTERRNO, "openat(%d, O_TMPFILE) failed", dirfd);
76f08c3bdfSopenharmony_ci	}
77f08c3bdfSopenharmony_ci
78f08c3bdfSopenharmony_ci	if (TST_RET < 0)
79f08c3bdfSopenharmony_ci		tst_brk(TBROK, "Invalid openat return value %ld", TST_RET);
80f08c3bdfSopenharmony_ci
81f08c3bdfSopenharmony_ci	SAFE_CLOSE(TST_RET);
82f08c3bdfSopenharmony_ci}
83f08c3bdfSopenharmony_ci
84f08c3bdfSopenharmony_cistatic void setup(void)
85f08c3bdfSopenharmony_ci{
86f08c3bdfSopenharmony_ci	struct stat buf;
87f08c3bdfSopenharmony_ci
88f08c3bdfSopenharmony_ci	ltpuser = SAFE_GETPWNAM("nobody");
89f08c3bdfSopenharmony_ci
90f08c3bdfSopenharmony_ci	do_mount(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, "noacl");
91f08c3bdfSopenharmony_ci
92f08c3bdfSopenharmony_ci	tst_res(TINFO, "User nobody: uid = %d, gid = %d", (int)ltpuser->pw_uid,
93f08c3bdfSopenharmony_ci		(int)ltpuser->pw_gid);
94f08c3bdfSopenharmony_ci	free_gid = tst_get_free_gid(ltpuser->pw_gid);
95f08c3bdfSopenharmony_ci
96f08c3bdfSopenharmony_ci	/* Create directories and set permissions */
97f08c3bdfSopenharmony_ci	SAFE_MKDIR(WORKDIR, MODE_RWX);
98f08c3bdfSopenharmony_ci	dir_fd = SAFE_OPEN(WORKDIR, O_RDONLY, O_DIRECTORY);
99f08c3bdfSopenharmony_ci	open_tmpfile_supported(dir_fd);
100f08c3bdfSopenharmony_ci
101f08c3bdfSopenharmony_ci	SAFE_CHOWN(WORKDIR, ltpuser->pw_uid, free_gid);
102f08c3bdfSopenharmony_ci	SAFE_CHMOD(WORKDIR, MODE_SGID);
103f08c3bdfSopenharmony_ci	SAFE_STAT(WORKDIR, &buf);
104f08c3bdfSopenharmony_ci
105f08c3bdfSopenharmony_ci	if (!(buf.st_mode & S_ISGID))
106f08c3bdfSopenharmony_ci		tst_brk(TBROK, "%s: Setgid bit not set", WORKDIR);
107f08c3bdfSopenharmony_ci
108f08c3bdfSopenharmony_ci	if (buf.st_gid != free_gid) {
109f08c3bdfSopenharmony_ci		tst_brk(TBROK, "%s: Incorrect group, %u != %u", WORKDIR,
110f08c3bdfSopenharmony_ci			buf.st_gid, free_gid);
111f08c3bdfSopenharmony_ci	}
112f08c3bdfSopenharmony_ci
113f08c3bdfSopenharmony_ci	/* Switch user */
114f08c3bdfSopenharmony_ci	SAFE_SETGID(ltpuser->pw_gid);
115f08c3bdfSopenharmony_ci	SAFE_SETREUID(-1, ltpuser->pw_uid);
116f08c3bdfSopenharmony_ci}
117f08c3bdfSopenharmony_ci
118f08c3bdfSopenharmony_cistatic void file_test(int dfd, const char *path, int flags)
119f08c3bdfSopenharmony_ci{
120f08c3bdfSopenharmony_ci	struct stat buf;
121f08c3bdfSopenharmony_ci
122f08c3bdfSopenharmony_ci	SAFE_FSTATAT(dfd, path, &buf, flags);
123f08c3bdfSopenharmony_ci
124f08c3bdfSopenharmony_ci	TST_EXP_EQ_LI(buf.st_gid, free_gid);
125f08c3bdfSopenharmony_ci
126f08c3bdfSopenharmony_ci	if (buf.st_mode & S_ISGID)
127f08c3bdfSopenharmony_ci		tst_res(TFAIL, "%s: Setgid bit is set", path);
128f08c3bdfSopenharmony_ci	else
129f08c3bdfSopenharmony_ci		tst_res(TPASS, "%s: Setgid bit not set", path);
130f08c3bdfSopenharmony_ci
131f08c3bdfSopenharmony_ci	if (buf.st_mode & S_IXGRP)
132f08c3bdfSopenharmony_ci		tst_res(TFAIL, "%s: S_IXGRP bit is set", path);
133f08c3bdfSopenharmony_ci	else
134f08c3bdfSopenharmony_ci		tst_res(TPASS, "%s: S_IXGRP bit is not set", path);
135f08c3bdfSopenharmony_ci}
136f08c3bdfSopenharmony_ci
137f08c3bdfSopenharmony_cistatic void run(void)
138f08c3bdfSopenharmony_ci{
139f08c3bdfSopenharmony_ci	char path[PATH_MAX];
140f08c3bdfSopenharmony_ci
141f08c3bdfSopenharmony_ci	umask(S_IXGRP);
142f08c3bdfSopenharmony_ci	tmpfile_fd = SAFE_OPENAT(dir_fd, ".", O_TMPFILE | O_RDWR, MODE_SGID);
143f08c3bdfSopenharmony_ci	snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd);
144f08c3bdfSopenharmony_ci	SAFE_LINKAT(AT_FDCWD, path, dir_fd, OPEN_FILE, AT_SYMLINK_FOLLOW);
145f08c3bdfSopenharmony_ci	file_test(dir_fd, OPEN_FILE, 0);
146f08c3bdfSopenharmony_ci	SAFE_CLOSE(tmpfile_fd);
147f08c3bdfSopenharmony_ci	/* Cleanup between loops */
148f08c3bdfSopenharmony_ci	tst_purge_dir(WORKDIR);
149f08c3bdfSopenharmony_ci}
150f08c3bdfSopenharmony_ci
151f08c3bdfSopenharmony_cistatic void cleanup(void)
152f08c3bdfSopenharmony_ci{
153f08c3bdfSopenharmony_ci	SAFE_SETREUID(-1, 0);
154f08c3bdfSopenharmony_ci
155f08c3bdfSopenharmony_ci	if (tmpfile_fd >= 0)
156f08c3bdfSopenharmony_ci		SAFE_CLOSE(tmpfile_fd);
157f08c3bdfSopenharmony_ci	if (dir_fd >= 0)
158f08c3bdfSopenharmony_ci		SAFE_CLOSE(dir_fd);
159f08c3bdfSopenharmony_ci	if (mount_flag && tst_umount(MNTPOINT))
160f08c3bdfSopenharmony_ci		tst_res(TWARN | TERRNO, "umount(%s)", MNTPOINT);
161f08c3bdfSopenharmony_ci}
162f08c3bdfSopenharmony_ci
163f08c3bdfSopenharmony_cistatic struct tst_test test = {
164f08c3bdfSopenharmony_ci	.test_all = run,
165f08c3bdfSopenharmony_ci	.setup = setup,
166f08c3bdfSopenharmony_ci	.cleanup = cleanup,
167f08c3bdfSopenharmony_ci	.needs_root = 1,
168f08c3bdfSopenharmony_ci	.all_filesystems = 1,
169f08c3bdfSopenharmony_ci	.format_device = 1,
170f08c3bdfSopenharmony_ci	.mntpoint = MNTPOINT,
171f08c3bdfSopenharmony_ci	.skip_filesystems = (const char*[]) {
172f08c3bdfSopenharmony_ci		"exfat",
173f08c3bdfSopenharmony_ci		"ntfs",
174f08c3bdfSopenharmony_ci		"vfat",
175f08c3bdfSopenharmony_ci		NULL
176f08c3bdfSopenharmony_ci	},
177f08c3bdfSopenharmony_ci	.tags = (const struct tst_tag[]) {
178f08c3bdfSopenharmony_ci		{"linux-git", "ac6800e279a2"},
179f08c3bdfSopenharmony_ci		{"linux-git", "426b4ca2d6a5"},
180f08c3bdfSopenharmony_ci		{}
181f08c3bdfSopenharmony_ci	},
182f08c3bdfSopenharmony_ci};
183