1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2021 SUSE LLC <mdoucha@suse.cz>
4f08c3bdfSopenharmony_ci */
5f08c3bdfSopenharmony_ci/*\
6f08c3bdfSopenharmony_ci * [Description]
7f08c3bdfSopenharmony_ci *
8f08c3bdfSopenharmony_ci * CVE-2018-13405
9f08c3bdfSopenharmony_ci *
10f08c3bdfSopenharmony_ci * Check for possible privilege escalation through creating files with setgid
11f08c3bdfSopenharmony_ci * bit set inside a setgid directory owned by a group which the user does not
12f08c3bdfSopenharmony_ci * belong to.
13f08c3bdfSopenharmony_ci *
14f08c3bdfSopenharmony_ci * Fixed in:
15f08c3bdfSopenharmony_ci *
16f08c3bdfSopenharmony_ci *  commit 0fa3ecd87848c9c93c2c828ef4c3a8ca36ce46c7
17f08c3bdfSopenharmony_ci *  Author: Linus Torvalds <torvalds@linux-foundation.org>
18f08c3bdfSopenharmony_ci *  Date:   Tue Jul 3 17:10:19 2018 -0700
19f08c3bdfSopenharmony_ci *
20f08c3bdfSopenharmony_ci *  Fix up non-directory creation in SGID directories
21f08c3bdfSopenharmony_ci *
22f08c3bdfSopenharmony_ci * This fix is incomplete if file is on xfs filesystem.
23f08c3bdfSopenharmony_ci *
24f08c3bdfSopenharmony_ci * Fixed in:
25f08c3bdfSopenharmony_ci *
26f08c3bdfSopenharmony_ci *  commit 01ea173e103edd5ec41acec65b9261b87e123fc2
27f08c3bdfSopenharmony_ci *  Author: Christoph Hellwig <hch@lst.de>
28f08c3bdfSopenharmony_ci *  Date:   Fri Jan 22 16:48:18 2021 -0800
29f08c3bdfSopenharmony_ci *
30f08c3bdfSopenharmony_ci *  xfs: fix up non-directory creation in SGID directories
31f08c3bdfSopenharmony_ci *
32f08c3bdfSopenharmony_ci * When use acl or umask, it still has bug.
33f08c3bdfSopenharmony_ci *
34f08c3bdfSopenharmony_ci * Fixed in:
35f08c3bdfSopenharmony_ci *
36f08c3bdfSopenharmony_ci *  commit 1639a49ccdce58ea248841ed9b23babcce6dbb0b
37f08c3bdfSopenharmony_ci *  Author: Yang Xu <xuyang2018.jy@fujitsu.com>
38f08c3bdfSopenharmony_ci *  Date:   Thu July 14 14:11:27 2022 +0800
39f08c3bdfSopenharmony_ci *
40f08c3bdfSopenharmony_ci *  fs: move S_ISGID stripping into the vfs_*() helpers
41f08c3bdfSopenharmony_ci */
42f08c3bdfSopenharmony_ci
43f08c3bdfSopenharmony_ci#include <stdlib.h>
44f08c3bdfSopenharmony_ci#include <sys/types.h>
45f08c3bdfSopenharmony_ci#include <pwd.h>
46f08c3bdfSopenharmony_ci#include "tst_test.h"
47f08c3bdfSopenharmony_ci#include "tst_uid.h"
48f08c3bdfSopenharmony_ci
49f08c3bdfSopenharmony_ci#define MODE_RWX        0777
50f08c3bdfSopenharmony_ci#define MODE_SGID       (S_ISGID|0777)
51f08c3bdfSopenharmony_ci
52f08c3bdfSopenharmony_ci#define MNTPOINT	"mntpoint"
53f08c3bdfSopenharmony_ci#define WORKDIR		MNTPOINT "/testdir"
54f08c3bdfSopenharmony_ci#define CREAT_FILE	WORKDIR "/creat.tmp"
55f08c3bdfSopenharmony_ci#define OPEN_FILE	WORKDIR "/open.tmp"
56f08c3bdfSopenharmony_ci
57f08c3bdfSopenharmony_cistatic gid_t free_gid;
58f08c3bdfSopenharmony_cistatic int fd = -1;
59f08c3bdfSopenharmony_ci
60f08c3bdfSopenharmony_cistatic struct tcase {
61f08c3bdfSopenharmony_ci	const char *msg;
62f08c3bdfSopenharmony_ci	int mask;
63f08c3bdfSopenharmony_ci} tcases[] = {
64f08c3bdfSopenharmony_ci	{"umask(0)", 0},
65f08c3bdfSopenharmony_ci	{"umask(S_IXGRP)", S_IXGRP}
66f08c3bdfSopenharmony_ci};
67f08c3bdfSopenharmony_ci
68f08c3bdfSopenharmony_cistatic void setup(void)
69f08c3bdfSopenharmony_ci{
70f08c3bdfSopenharmony_ci	struct stat buf;
71f08c3bdfSopenharmony_ci	struct passwd *ltpuser = SAFE_GETPWNAM("nobody");
72f08c3bdfSopenharmony_ci
73f08c3bdfSopenharmony_ci	tst_res(TINFO, "User nobody: uid = %d, gid = %d", (int)ltpuser->pw_uid,
74f08c3bdfSopenharmony_ci		(int)ltpuser->pw_gid);
75f08c3bdfSopenharmony_ci	free_gid = tst_get_free_gid(ltpuser->pw_gid);
76f08c3bdfSopenharmony_ci
77f08c3bdfSopenharmony_ci	/* Create directories and set permissions */
78f08c3bdfSopenharmony_ci	SAFE_MKDIR(WORKDIR, MODE_RWX);
79f08c3bdfSopenharmony_ci	SAFE_CHOWN(WORKDIR, ltpuser->pw_uid, free_gid);
80f08c3bdfSopenharmony_ci	SAFE_CHMOD(WORKDIR, MODE_SGID);
81f08c3bdfSopenharmony_ci	SAFE_STAT(WORKDIR, &buf);
82f08c3bdfSopenharmony_ci
83f08c3bdfSopenharmony_ci	if (!(buf.st_mode & S_ISGID))
84f08c3bdfSopenharmony_ci		tst_brk(TBROK, "%s: Setgid bit not set", WORKDIR);
85f08c3bdfSopenharmony_ci
86f08c3bdfSopenharmony_ci	if (buf.st_gid != free_gid) {
87f08c3bdfSopenharmony_ci		tst_brk(TBROK, "%s: Incorrect group, %u != %u", WORKDIR,
88f08c3bdfSopenharmony_ci			buf.st_gid, free_gid);
89f08c3bdfSopenharmony_ci	}
90f08c3bdfSopenharmony_ci
91f08c3bdfSopenharmony_ci	/* Switch user */
92f08c3bdfSopenharmony_ci	SAFE_SETGID(ltpuser->pw_gid);
93f08c3bdfSopenharmony_ci	SAFE_SETREUID(-1, ltpuser->pw_uid);
94f08c3bdfSopenharmony_ci}
95f08c3bdfSopenharmony_ci
96f08c3bdfSopenharmony_cistatic void file_test(const char *name)
97f08c3bdfSopenharmony_ci{
98f08c3bdfSopenharmony_ci	struct stat buf;
99f08c3bdfSopenharmony_ci
100f08c3bdfSopenharmony_ci	SAFE_STAT(name, &buf);
101f08c3bdfSopenharmony_ci
102f08c3bdfSopenharmony_ci	if (buf.st_gid != free_gid) {
103f08c3bdfSopenharmony_ci		tst_res(TFAIL, "%s: Incorrect group, %u != %u", name,
104f08c3bdfSopenharmony_ci			buf.st_gid, free_gid);
105f08c3bdfSopenharmony_ci	} else {
106f08c3bdfSopenharmony_ci		tst_res(TPASS, "%s: Owned by correct group", name);
107f08c3bdfSopenharmony_ci	}
108f08c3bdfSopenharmony_ci
109f08c3bdfSopenharmony_ci	if (buf.st_mode & S_ISGID)
110f08c3bdfSopenharmony_ci		tst_res(TFAIL, "%s: Setgid bit is set", name);
111f08c3bdfSopenharmony_ci	else
112f08c3bdfSopenharmony_ci		tst_res(TPASS, "%s: Setgid bit not set", name);
113f08c3bdfSopenharmony_ci}
114f08c3bdfSopenharmony_ci
115f08c3bdfSopenharmony_cistatic void run(unsigned int n)
116f08c3bdfSopenharmony_ci{
117f08c3bdfSopenharmony_ci	struct tcase *tc = &tcases[n];
118f08c3bdfSopenharmony_ci
119f08c3bdfSopenharmony_ci	umask(tc->mask);
120f08c3bdfSopenharmony_ci	tst_res(TINFO, "File created with %s", tc->msg);
121f08c3bdfSopenharmony_ci
122f08c3bdfSopenharmony_ci	fd = SAFE_CREAT(CREAT_FILE, MODE_SGID);
123f08c3bdfSopenharmony_ci	SAFE_CLOSE(fd);
124f08c3bdfSopenharmony_ci	file_test(CREAT_FILE);
125f08c3bdfSopenharmony_ci
126f08c3bdfSopenharmony_ci	fd = SAFE_OPEN(OPEN_FILE, O_CREAT | O_EXCL | O_RDWR, MODE_SGID);
127f08c3bdfSopenharmony_ci	file_test(OPEN_FILE);
128f08c3bdfSopenharmony_ci	SAFE_CLOSE(fd);
129f08c3bdfSopenharmony_ci
130f08c3bdfSopenharmony_ci	/* Cleanup between loops */
131f08c3bdfSopenharmony_ci	tst_purge_dir(WORKDIR);
132f08c3bdfSopenharmony_ci}
133f08c3bdfSopenharmony_ci
134f08c3bdfSopenharmony_cistatic void cleanup(void)
135f08c3bdfSopenharmony_ci{
136f08c3bdfSopenharmony_ci	if (fd >= 0)
137f08c3bdfSopenharmony_ci		SAFE_CLOSE(fd);
138f08c3bdfSopenharmony_ci}
139f08c3bdfSopenharmony_ci
140f08c3bdfSopenharmony_cistatic struct tst_test test = {
141f08c3bdfSopenharmony_ci	.test = run,
142f08c3bdfSopenharmony_ci	.setup = setup,
143f08c3bdfSopenharmony_ci	.cleanup = cleanup,
144f08c3bdfSopenharmony_ci	.needs_root = 1,
145f08c3bdfSopenharmony_ci	.all_filesystems = 1,
146f08c3bdfSopenharmony_ci	.mount_device = 1,
147f08c3bdfSopenharmony_ci	.mntpoint = MNTPOINT,
148f08c3bdfSopenharmony_ci	.tcnt = ARRAY_SIZE(tcases),
149f08c3bdfSopenharmony_ci	.skip_filesystems = (const char*[]) {
150f08c3bdfSopenharmony_ci		"exfat",
151f08c3bdfSopenharmony_ci		"ntfs",
152f08c3bdfSopenharmony_ci		"vfat",
153f08c3bdfSopenharmony_ci		NULL
154f08c3bdfSopenharmony_ci	},
155f08c3bdfSopenharmony_ci	.tags = (const struct tst_tag[]) {
156f08c3bdfSopenharmony_ci		{"linux-git", "0fa3ecd87848"},
157f08c3bdfSopenharmony_ci		{"CVE", "2018-13405"},
158f08c3bdfSopenharmony_ci		{"CVE", "2021-4037"},
159f08c3bdfSopenharmony_ci		{"linux-git", "01ea173e103e"},
160f08c3bdfSopenharmony_ci		{"linux-git", "1639a49ccdce"},
161f08c3bdfSopenharmony_ci		{"linux-git", "426b4ca2d6a5"},
162f08c3bdfSopenharmony_ci		{}
163f08c3bdfSopenharmony_ci	},
164f08c3bdfSopenharmony_ci};
165