safesetid-test.c (92c005a1176288c98a0dc49f37376da35bbea071) | safesetid-test.c (b2927170d4fbdf30545eb482133425477625a665) |
---|---|
1// SPDX-License-Identifier: GPL-2.0 2#define _GNU_SOURCE 3#include <stdio.h> 4#include <errno.h> 5#include <pwd.h> 6#include <string.h> 7#include <syscall.h> 8#include <sys/capability.h> 9#include <sys/types.h> 10#include <sys/mount.h> 11#include <sys/prctl.h> 12#include <sys/wait.h> 13#include <stdlib.h> 14#include <unistd.h> 15#include <fcntl.h> 16#include <stdbool.h> 17#include <stdarg.h> 18 | 1// SPDX-License-Identifier: GPL-2.0 2#define _GNU_SOURCE 3#include <stdio.h> 4#include <errno.h> 5#include <pwd.h> 6#include <string.h> 7#include <syscall.h> 8#include <sys/capability.h> 9#include <sys/types.h> 10#include <sys/mount.h> 11#include <sys/prctl.h> 12#include <sys/wait.h> 13#include <stdlib.h> 14#include <unistd.h> 15#include <fcntl.h> 16#include <stdbool.h> 17#include <stdarg.h> 18 |
19/* 20 * NOTES about this test: 21 * - requries libcap-dev to be installed on test system 22 * - requires securityfs to me mounted at /sys/kernel/security, e.g.: 23 * mount -n -t securityfs -o nodev,noexec,nosuid securityfs /sys/kernel/security 24 * - needs CONFIG_SECURITYFS and CONFIG_SAFESETID to be enabled 25 */ 26 |
|
19#ifndef CLONE_NEWUSER 20# define CLONE_NEWUSER 0x10000000 21#endif 22 | 27#ifndef CLONE_NEWUSER 28# define CLONE_NEWUSER 0x10000000 29#endif 30 |
23#define ROOT_USER 0 24#define RESTRICTED_PARENT 1 25#define ALLOWED_CHILD1 2 26#define ALLOWED_CHILD2 3 27#define NO_POLICY_USER 4 | 31#define ROOT_UGID 0 32#define RESTRICTED_PARENT_UGID 1 33#define ALLOWED_CHILD1_UGID 2 34#define ALLOWED_CHILD2_UGID 3 35#define NO_POLICY_UGID 4 |
28 | 36 |
29char* add_whitelist_policy_file = "/sys/kernel/security/safesetid/add_whitelist_policy"; | 37#define UGID_POLICY_STRING "1:2\n1:3\n2:2\n3:3\n" |
30 | 38 |
39char* add_uid_whitelist_policy_file = "/sys/kernel/security/safesetid/uid_allowlist_policy"; 40char* add_gid_whitelist_policy_file = "/sys/kernel/security/safesetid/gid_allowlist_policy"; 41 |
|
31static void die(char *fmt, ...) 32{ 33 va_list ap; 34 va_start(ap, fmt); 35 vfprintf(stderr, fmt, ap); 36 va_end(ap); 37 exit(EXIT_FAILURE); 38} --- 62 unchanged lines hidden (view full) --- 101 102 if (getpwuid(uid) == NULL) { 103 memset(&p,0x00,sizeof(p)); 104 fd=fopen("/etc/passwd","a"); 105 if (fd == NULL) 106 die("couldn't open file\n"); 107 if (fseek(fd, 0, SEEK_END)) 108 die("couldn't fseek\n"); | 42static void die(char *fmt, ...) 43{ 44 va_list ap; 45 va_start(ap, fmt); 46 vfprintf(stderr, fmt, ap); 47 va_end(ap); 48 exit(EXIT_FAILURE); 49} --- 62 unchanged lines hidden (view full) --- 112 113 if (getpwuid(uid) == NULL) { 114 memset(&p,0x00,sizeof(p)); 115 fd=fopen("/etc/passwd","a"); 116 if (fd == NULL) 117 die("couldn't open file\n"); 118 if (fseek(fd, 0, SEEK_END)) 119 die("couldn't fseek\n"); |
109 snprintf(name_str, 10, "%d", uid); | 120 snprintf(name_str, 10, "user %d", uid); |
110 p.pw_name=name_str; 111 p.pw_uid=uid; 112 p.pw_gecos="Test account"; 113 p.pw_dir="/dev/null"; 114 p.pw_shell="/bin/false"; 115 int value = putpwent(&p,fd); 116 if (value != 0) 117 die("putpwent failed\n"); 118 if (fclose(fd)) 119 die("fclose failed\n"); 120 } 121} 122 123static void ensure_securityfs_mounted(void) 124{ | 121 p.pw_name=name_str; 122 p.pw_uid=uid; 123 p.pw_gecos="Test account"; 124 p.pw_dir="/dev/null"; 125 p.pw_shell="/bin/false"; 126 int value = putpwent(&p,fd); 127 if (value != 0) 128 die("putpwent failed\n"); 129 if (fclose(fd)) 130 die("fclose failed\n"); 131 } 132} 133 134static void ensure_securityfs_mounted(void) 135{ |
125 int fd = open(add_whitelist_policy_file, O_WRONLY); | 136 int fd = open(add_uid_whitelist_policy_file, O_WRONLY); |
126 if (fd < 0) { 127 if (errno == ENOENT) { 128 // Need to mount securityfs 129 if (mount("securityfs", "/sys/kernel/security", 130 "securityfs", 0, NULL) < 0) 131 die("mounting securityfs failed\n"); 132 } else { 133 die("couldn't find securityfs for unknown reason\n"); 134 } 135 } else { 136 if (close(fd) != 0) { 137 die("close of %s failed: %s\n", | 137 if (fd < 0) { 138 if (errno == ENOENT) { 139 // Need to mount securityfs 140 if (mount("securityfs", "/sys/kernel/security", 141 "securityfs", 0, NULL) < 0) 142 die("mounting securityfs failed\n"); 143 } else { 144 die("couldn't find securityfs for unknown reason\n"); 145 } 146 } else { 147 if (close(fd) != 0) { 148 die("close of %s failed: %s\n", |
138 add_whitelist_policy_file, strerror(errno)); | 149 add_uid_whitelist_policy_file, strerror(errno)); |
139 } 140 } 141} 142 | 150 } 151 } 152} 153 |
143static void write_policies(void) | 154static void write_uid_policies() |
144{ | 155{ |
145 static char *policy_str = 146 "1:2\n" 147 "1:3\n" 148 "2:2\n" 149 "3:3\n"; | 156 static char *policy_str = UGID_POLICY_STRING; |
150 ssize_t written; 151 int fd; 152 | 157 ssize_t written; 158 int fd; 159 |
153 fd = open(add_whitelist_policy_file, O_WRONLY); | 160 fd = open(add_uid_whitelist_policy_file, O_WRONLY); |
154 if (fd < 0) | 161 if (fd < 0) |
155 die("can't open add_whitelist_policy file\n"); | 162 die("can't open add_uid_whitelist_policy file\n"); |
156 written = write(fd, policy_str, strlen(policy_str)); 157 if (written != strlen(policy_str)) { 158 if (written >= 0) { | 163 written = write(fd, policy_str, strlen(policy_str)); 164 if (written != strlen(policy_str)) { 165 if (written >= 0) { |
159 die("short write to %s\n", add_whitelist_policy_file); | 166 die("short write to %s\n", add_uid_whitelist_policy_file); |
160 } else { 161 die("write to %s failed: %s\n", | 167 } else { 168 die("write to %s failed: %s\n", |
162 add_whitelist_policy_file, strerror(errno)); | 169 add_uid_whitelist_policy_file, strerror(errno)); |
163 } 164 } 165 if (close(fd) != 0) { 166 die("close of %s failed: %s\n", | 170 } 171 } 172 if (close(fd) != 0) { 173 die("close of %s failed: %s\n", |
167 add_whitelist_policy_file, strerror(errno)); | 174 add_uid_whitelist_policy_file, strerror(errno)); |
168 } 169} 170 171static bool test_userns(bool expect_success) 172{ 173 uid_t uid; 174 char map_file_name[32]; 175 size_t sz = sizeof(map_file_name); --- 79 unchanged lines hidden (view full) --- 255 } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); 256 } 257 258 die("should not reach here\n"); 259} 260 261static void ensure_users_exist(void) 262{ | 175 } 176} 177 178static bool test_userns(bool expect_success) 179{ 180 uid_t uid; 181 char map_file_name[32]; 182 size_t sz = sizeof(map_file_name); --- 79 unchanged lines hidden (view full) --- 262 } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); 263 } 264 265 die("should not reach here\n"); 266} 267 268static void ensure_users_exist(void) 269{ |
263 ensure_user_exists(ROOT_USER); 264 ensure_user_exists(RESTRICTED_PARENT); 265 ensure_user_exists(ALLOWED_CHILD1); 266 ensure_user_exists(ALLOWED_CHILD2); 267 ensure_user_exists(NO_POLICY_USER); | 270 ensure_user_exists(ROOT_UGID); 271 ensure_user_exists(RESTRICTED_PARENT_UGID); 272 ensure_user_exists(ALLOWED_CHILD1_UGID); 273 ensure_user_exists(ALLOWED_CHILD2_UGID); 274 ensure_user_exists(NO_POLICY_UGID); |
268} 269 270static void drop_caps(bool setid_retained) 271{ 272 cap_value_t cap_values[] = {CAP_SETUID, CAP_SETGID}; 273 cap_t caps; 274 275 caps = cap_get_proc(); --- 4 unchanged lines hidden (view full) --- 280 cap_set_proc(caps); 281 cap_free(caps); 282} 283 284int main(int argc, char **argv) 285{ 286 ensure_users_exist(); 287 ensure_securityfs_mounted(); | 275} 276 277static void drop_caps(bool setid_retained) 278{ 279 cap_value_t cap_values[] = {CAP_SETUID, CAP_SETGID}; 280 cap_t caps; 281 282 caps = cap_get_proc(); --- 4 unchanged lines hidden (view full) --- 287 cap_set_proc(caps); 288 cap_free(caps); 289} 290 291int main(int argc, char **argv) 292{ 293 ensure_users_exist(); 294 ensure_securityfs_mounted(); |
288 write_policies(); | 295 write_uid_policies(); |
289 290 if (prctl(PR_SET_KEEPCAPS, 1L)) 291 die("Error with set keepcaps\n"); 292 | 296 297 if (prctl(PR_SET_KEEPCAPS, 1L)) 298 die("Error with set keepcaps\n"); 299 |
293 // First test to make sure we can write userns mappings from a user 294 // that doesn't have any restrictions (as long as it has CAP_SETUID); 295 if (setuid(NO_POLICY_USER) < 0) 296 die("Error with set uid(%d)\n", NO_POLICY_USER); 297 if (setgid(NO_POLICY_USER) < 0) 298 die("Error with set gid(%d)\n", NO_POLICY_USER); 299 | 300 // First test to make sure we can write userns mappings from a non-root 301 // user that doesn't have any restrictions (as long as it has 302 // CAP_SETUID); 303 if (setgid(NO_POLICY_UGID) < 0) 304 die("Error with set gid(%d)\n", NO_POLICY_UGID); 305 if (setuid(NO_POLICY_UGID) < 0) 306 die("Error with set uid(%d)\n", NO_POLICY_UGID); |
300 // Take away all but setid caps 301 drop_caps(true); | 307 // Take away all but setid caps 308 drop_caps(true); |
302 | |
303 // Need PR_SET_DUMPABLE flag set so we can write /proc/[pid]/uid_map 304 // from non-root parent process. 305 if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)) 306 die("Error with set dumpable\n"); | 309 // Need PR_SET_DUMPABLE flag set so we can write /proc/[pid]/uid_map 310 // from non-root parent process. 311 if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)) 312 die("Error with set dumpable\n"); |
307 | |
308 if (!test_userns(true)) { 309 die("test_userns failed when it should work\n"); 310 } 311 | 313 if (!test_userns(true)) { 314 die("test_userns failed when it should work\n"); 315 } 316 |
312 if (setuid(RESTRICTED_PARENT) < 0) 313 die("Error with set uid(%d)\n", RESTRICTED_PARENT); 314 if (setgid(RESTRICTED_PARENT) < 0) 315 die("Error with set gid(%d)\n", RESTRICTED_PARENT); | 317 // Now switch to a user/group with restrictions 318 if (setgid(RESTRICTED_PARENT_UGID) < 0) 319 die("Error with set gid(%d)\n", RESTRICTED_PARENT_UGID); 320 if (setuid(RESTRICTED_PARENT_UGID) < 0) 321 die("Error with set uid(%d)\n", RESTRICTED_PARENT_UGID); |
316 | 322 |
317 test_setuid(ROOT_USER, false); 318 test_setuid(ALLOWED_CHILD1, true); 319 test_setuid(ALLOWED_CHILD2, true); 320 test_setuid(NO_POLICY_USER, false); | 323 test_setuid(ROOT_UGID, false); 324 test_setuid(ALLOWED_CHILD1_UGID, true); 325 test_setuid(ALLOWED_CHILD2_UGID, true); 326 test_setuid(NO_POLICY_UGID, false); |
321 322 if (!test_userns(false)) { 323 die("test_userns worked when it should fail\n"); 324 } 325 326 // Now take away all caps 327 drop_caps(false); 328 test_setuid(2, false); 329 test_setuid(3, false); 330 test_setuid(4, false); 331 332 // NOTE: this test doesn't clean up users that were created in 333 // /etc/passwd or flush policies that were added to the LSM. | 327 328 if (!test_userns(false)) { 329 die("test_userns worked when it should fail\n"); 330 } 331 332 // Now take away all caps 333 drop_caps(false); 334 test_setuid(2, false); 335 test_setuid(3, false); 336 test_setuid(4, false); 337 338 // NOTE: this test doesn't clean up users that were created in 339 // /etc/passwd or flush policies that were added to the LSM. |
340 printf("test successful!\n"); |
|
334 return EXIT_SUCCESS; 335} | 341 return EXIT_SUCCESS; 342} |