Deleted Added
full compact
priv_vfs_clearsugid.c (162271) priv_vfs_clearsugid.c (172106)
1/*-
2 * Copyright (c) 2006 nCircle Network Security, Inc.
1/*-
2 * Copyright (c) 2006 nCircle Network Security, Inc.
3 * Copyright (c) 2007 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * This software was developed by Robert N. M. Watson for the TrustedBSD
6 * Project under contract to nCircle Network Security, Inc.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:

--- 10 unchanged lines hidden (view full) ---

21 * INC., OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
23 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
4 * All rights reserved.
5 *
6 * This software was developed by Robert N. M. Watson for the TrustedBSD
7 * Project under contract to nCircle Network Security, Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:

--- 10 unchanged lines hidden (view full) ---

22 * INC., OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
29 * $FreeBSD: head/tools/regression/priv/priv_vfs_clearsugid.c 162271 2006-09-13 09:05:39Z rwatson $
30 * $FreeBSD: head/tools/regression/priv/priv_vfs_clearsugid.c 172106 2007-09-09 23:08:39Z rwatson $
30 */
31
32/*
33 * There are three cases in which the file system will clear the setuid or
31 */
32
33/*
34 * There are three cases in which the file system will clear the setuid or
34 * setgid bits on a file when running as !root:
35 * setgid bits on a file when running unprivileged:
35 *
36 * - When the file is chown()'d and either of the uid or the gid is changed.
36 *
37 * - When the file is chown()'d and either of the uid or the gid is changed.
38 * (currently, only changing the file gid applies, as privilege is required
39 * to change the uid).
37 *
40 *
38 * - The file is written to succeesfully.
41 * - The file is written to successfully.
39 *
40 * - An extended attribute of the file is written to successfully.
41 *
42 *
43 * - An extended attribute of the file is written to successfully.
44 *
42 * Test each case first as root (that flags aren't cleared), and then as
43 * !root, to check they are cleared.
45 * In each case, check that the flags are cleared if unprivileged, and that
46 * they aren't cleared if privileged.
47 *
48 * We can't use expect() as we're looking for side-effects rather than
49 * success/failure of the system call.
44 */
45
46#include <sys/types.h>
47#include <sys/extattr.h>
48#include <sys/stat.h>
49
50#include <err.h>
51#include <fcntl.h>
52#include <stdlib.h>
53#include <string.h>
54#include <unistd.h>
55
56#include "main.h"
57
50 */
51
52#include <sys/types.h>
53#include <sys/extattr.h>
54#include <sys/stat.h>
55
56#include <err.h>
57#include <fcntl.h>
58#include <stdlib.h>
59#include <string.h>
60#include <unistd.h>
61
62#include "main.h"
63
58static const gid_t gidset[] = {GID_WHEEL, GID_OWNER, GID_OTHER};
64static char fpath[1024];
65static int fpath_initialized;
59
60/*
66
67/*
61 * Confirm that the setuid bit is set on a file. Don't return on failure.
68 * If running as root, check that SUID is still set; otherwise, check that it
69 * is not.
62 */
63static void
70 */
71static void
64confirm_setuid(char *fpathp, char *test_case)
72confirm_sugid(char *test_case, int asroot, int injail)
65{
66 struct stat sb;
67
73{
74 struct stat sb;
75
68 if (stat(fpathp, &sb) < 0) {
69 warn("%s stat(%s)", test_case, fpathp);
70 (void)seteuid(UID_ROOT);
71 (void)unlink(fpathp);
72 exit(-1);
76 if (stat(fpath, &sb) < 0) {
77 warn("%s stat(%s)", test_case, fpath);
78 return;
73 }
79 }
74 if (!(sb.st_mode & S_ISUID)) {
75 warnx("case %s stat(%s) not setuid", test_case, fpathp);
76 (void)seteuid(UID_ROOT);
77 (void)unlink(fpathp);
78 exit(-1);
80 if (asroot) {
81 if (!(sb.st_mode & S_ISUID))
82 warnx("%s(root, %s): !SUID", test_case, injail ?
83 "jail" : "!jail");
84 } else {
85 if (sb.st_mode & S_ISUID)
86 warnx("%s(!root, %s): SUID", test_case, injail ?
87 "jail" : "!jail");
79 }
80}
81
88 }
89}
90
82/*
83 * Confirm that the setuid bit is not set on a file. Don't return on failure.
84 */
85static void
86confirm_notsetuid(char *fpathp, char *test_case)
91int
92priv_vfs_clearsugid_setup(int asroot, int injail, struct test *test)
87{
93{
88 struct stat sb;
89
94
90 if (stat(fpathp, &sb) < 0) {
91 warn("%s stat(%s)", test_case, fpathp);
92 (void)seteuid(UID_ROOT);
93 (void)unlink(fpathp);
94 exit(-1);
95 }
96 if (sb.st_mode & S_ISUID) {
97 warnx("case %s stat(%s) is setuid", test_case, fpathp);
98 (void)seteuid(UID_ROOT);
99 (void)unlink(fpathp);
100 exit(-1);
101 }
95 setup_file("priv_vfs_clearsugid_setup: fpath", fpath, UID_OWNER,
96 GID_OTHER, 0600 | S_ISUID);
97 fpath_initialized = 1;
98 return (0);
102}
103
99}
100
101void
102priv_vfs_clearsugid_chgrp(int asroot, int injail, struct test *test)
103{
104
105 if (chown(fpath, -1, asroot ? GID_WHEEL : GID_OWNER) < 0)
106 err(-1, "priv_vfs_clearsugid_chgrp(%s, %s): chrgrp",
107 asroot ? "root" : "!root", injail ? "jail" : "!jail");
108 confirm_sugid("priv_vfs_clearsugid_chgrp", asroot, injail);
109}
110
104#define EA_NAMESPACE EXTATTR_NAMESPACE_USER
105#define EA_NAME "clearsugid"
106#define EA_DATA "test"
107#define EA_SIZE (strlen(EA_DATA))
111#define EA_NAMESPACE EXTATTR_NAMESPACE_USER
112#define EA_NAME "clearsugid"
113#define EA_DATA "test"
114#define EA_SIZE (strlen(EA_DATA))
115
108void
116void
109priv_vfs_clearsugid(void)
117priv_vfs_clearsugid_extattr(int asroot, int injail, struct test *test)
110{
118{
111 char ch, fpath[1024];
112 int fd;
113
119
114 assert_root();
115
116 /*
117 * Before starting on work, set up group IDs so that the process can
118 * change the group ID of the file without privilege, in order to see
119 * the effects. That way privilege is only required to maintain the
120 * setuid bit. For the chown() test, we change only the group id, as
121 * that can be done with or without privilege.
122 */
123 if (setgroups(3, gidset) < 0)
124 err(-1, "setgroups(2, {%d, %d})", GID_WHEEL, GID_OWNER);
125
126 /*
127 * chown() with privilege.
128 */
129 setup_file(fpath, UID_ROOT, GID_WHEEL, 0600 | S_ISUID);
130 if (chown(fpath, -1, GID_OTHER) < 0)
131 warn("chown(%s, -1, %d) as root", fpath, GID_OTHER);
132 confirm_setuid(fpath, "chown as root");
133 (void)unlink(fpath);
134
135 /*
136 * write() with privilege.
137 */
138 setup_file(fpath, UID_ROOT, GID_WHEEL, 0600 | S_ISUID);
139 fd = open(fpath, O_RDWR);
140 if (fd < 0) {
141 warn("open(%s) as root", fpath);
142 goto out;
143 }
144 ch = 0;
145 if (write(fd, &ch, sizeof(ch)) < 0) {
146 warn("write(%s) as root", fpath);
147 goto out;
148 }
149 close(fd);
150 confirm_setuid(fpath, "write as root");
151 (void)unlink(fpath);
152
153 /*
154 * extwrite() with privilege.
155 */
156 setup_file(fpath, UID_ROOT, GID_WHEEL, 0600 | S_ISUID);
157 if (extattr_set_file(fpath, EA_NAMESPACE, EA_NAME, EA_DATA, EA_SIZE)
120 if (extattr_set_file(fpath, EA_NAMESPACE, EA_NAME, EA_DATA, EA_SIZE)
158 < 0) {
159 warn("extattr_set_file(%s, user, %s, %s, %d) as root",
160 fpath, EA_NAME, EA_DATA, EA_SIZE);
161 goto out;
162 }
163 confirm_setuid(fpath, "extwrite as root");
164 (void)unlink(fpath);
121 < 0)
122 err(-1,
123 "priv_vfs_clearsugid_extattr(%s, %s): extattr_set_file",
124 asroot ? "root" : "!root", injail ? "jail" : "!jail");
125 confirm_sugid("priv_vfs_clearsugid_extattr", asroot, injail);
126}
165
127
166 /*
167 * chown() without privilege.
168 */
169 setup_file(fpath, UID_OWNER, GID_OWNER, 0600 | S_ISUID);
170 set_euid(UID_OWNER);
171 if (chown(fpath, -1, GID_OTHER) < 0)
172 warn("chown(%s, -1, %d) as !root", fpath, GID_OTHER);
173 set_euid(UID_ROOT);
174 confirm_notsetuid(fpath, "chown as !root");
175 (void)unlink(fpath);
128void
129priv_vfs_clearsugid_write(int asroot, int injail, struct test *test)
130{
131 int fd;
176
132
177 /*
178 * write() without privilege.
179 */
180 setup_file(fpath, UID_OWNER, GID_OWNER, 0600 | S_ISUID);
181 set_euid(UID_OWNER);
182 fd = open(fpath, O_RDWR);
133 fd = open(fpath, O_RDWR);
183 if (fd < 0) {
184 warn("open(%s) as !root", fpath);
185 goto out;
186 }
187 ch = 0;
188 if (write(fd, &ch, sizeof(ch)) < 0) {
189 warn("write(%s) as !root", fpath);
190 goto out;
191 }
192 close(fd);
193 set_euid(UID_ROOT);
194 confirm_notsetuid(fpath, "write as !root");
195 (void)unlink(fpath);
134 if (fd < 0)
135 err(-1, "priv_vfs_clearsugid_write(%s, %s): open",
136 asroot ? "root" : "!root", injail ? "jail" : "!jail");
137 if (write(fd, EA_DATA, EA_SIZE) < 0)
138 err(-1, "priv_vfs_clearsugid_write(%s, %s): write",
139 asroot ? "root" : "!root", injail ? "jail" : "!jail");
140 (void)close(fd);
141 confirm_sugid("priv_vfs_clearsugid_write", asroot, injail);
142}
196
143
197 /*
198 * extwrite() without privilege.
199 */
200 setup_file(fpath, UID_OWNER, GID_OWNER, 0600 | S_ISUID);
201 set_euid(UID_OWNER);
202 if (extattr_set_file(fpath, EA_NAMESPACE, EA_NAME, EA_DATA, EA_SIZE)
203 < 0) {
204 warn("extattr_set_file(%s, user, %s, %s, %d) as !root",
205 fpath, EA_NAME, EA_DATA, EA_SIZE);
206 goto out;
207 }
208 set_euid(UID_ROOT);
209 confirm_notsetuid(fpath, "extwrite as !root");
210 (void)unlink(fpath);
144void
145priv_vfs_clearsugid_cleanup(int asroot, int injail, struct test *test)
146{
211
147
212out:
213 (void)seteuid(UID_ROOT);
214 (void)unlink(fpath);
148 if (fpath_initialized) {
149 (void)unlink(fpath);
150 fpath_initialized = 0;
151 }
215}
152}