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