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} |