0001
0002 #define _GNU_SOURCE
0003 #include <stdio.h>
0004 #include <errno.h>
0005 #include <pwd.h>
0006 #include <grp.h>
0007 #include <string.h>
0008 #include <syscall.h>
0009 #include <sys/capability.h>
0010 #include <sys/types.h>
0011 #include <sys/mount.h>
0012 #include <sys/prctl.h>
0013 #include <sys/wait.h>
0014 #include <stdlib.h>
0015 #include <unistd.h>
0016 #include <fcntl.h>
0017 #include <stdbool.h>
0018 #include <stdarg.h>
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028 #ifndef CLONE_NEWUSER
0029 # define CLONE_NEWUSER 0x10000000
0030 #endif
0031
0032 #define ROOT_UGID 0
0033 #define RESTRICTED_PARENT_UGID 1
0034 #define ALLOWED_CHILD1_UGID 2
0035 #define ALLOWED_CHILD2_UGID 3
0036 #define NO_POLICY_UGID 4
0037
0038 #define UGID_POLICY_STRING "1:2\n1:3\n2:2\n3:3\n"
0039
0040 char* add_uid_whitelist_policy_file = "/sys/kernel/security/safesetid/uid_allowlist_policy";
0041 char* add_gid_whitelist_policy_file = "/sys/kernel/security/safesetid/gid_allowlist_policy";
0042
0043 static void die(char *fmt, ...)
0044 {
0045 va_list ap;
0046 va_start(ap, fmt);
0047 vfprintf(stderr, fmt, ap);
0048 va_end(ap);
0049 exit(EXIT_FAILURE);
0050 }
0051
0052 static bool vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap)
0053 {
0054 char buf[4096];
0055 int fd;
0056 ssize_t written;
0057 int buf_len;
0058
0059 buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
0060 if (buf_len < 0) {
0061 printf("vsnprintf failed: %s\n",
0062 strerror(errno));
0063 return false;
0064 }
0065 if (buf_len >= sizeof(buf)) {
0066 printf("vsnprintf output truncated\n");
0067 return false;
0068 }
0069
0070 fd = open(filename, O_WRONLY);
0071 if (fd < 0) {
0072 if ((errno == ENOENT) && enoent_ok)
0073 return true;
0074 return false;
0075 }
0076 written = write(fd, buf, buf_len);
0077 if (written != buf_len) {
0078 if (written >= 0) {
0079 printf("short write to %s\n", filename);
0080 return false;
0081 } else {
0082 printf("write to %s failed: %s\n",
0083 filename, strerror(errno));
0084 return false;
0085 }
0086 }
0087 if (close(fd) != 0) {
0088 printf("close of %s failed: %s\n",
0089 filename, strerror(errno));
0090 return false;
0091 }
0092 return true;
0093 }
0094
0095 static bool write_file(char *filename, char *fmt, ...)
0096 {
0097 va_list ap;
0098 bool ret;
0099
0100 va_start(ap, fmt);
0101 ret = vmaybe_write_file(false, filename, fmt, ap);
0102 va_end(ap);
0103
0104 return ret;
0105 }
0106
0107 static void ensure_user_exists(uid_t uid)
0108 {
0109 struct passwd p;
0110
0111 FILE *fd;
0112 char name_str[10];
0113
0114 if (getpwuid(uid) == NULL) {
0115 memset(&p,0x00,sizeof(p));
0116 fd=fopen("/etc/passwd","a");
0117 if (fd == NULL)
0118 die("couldn't open file\n");
0119 if (fseek(fd, 0, SEEK_END))
0120 die("couldn't fseek\n");
0121 snprintf(name_str, 10, "user %d", uid);
0122 p.pw_name=name_str;
0123 p.pw_uid=uid;
0124 p.pw_gid=uid;
0125 p.pw_gecos="Test account";
0126 p.pw_dir="/dev/null";
0127 p.pw_shell="/bin/false";
0128 int value = putpwent(&p,fd);
0129 if (value != 0)
0130 die("putpwent failed\n");
0131 if (fclose(fd))
0132 die("fclose failed\n");
0133 }
0134 }
0135
0136 static void ensure_group_exists(gid_t gid)
0137 {
0138 struct group g;
0139
0140 FILE *fd;
0141 char name_str[10];
0142
0143 if (getgrgid(gid) == NULL) {
0144 memset(&g,0x00,sizeof(g));
0145 fd=fopen("/etc/group","a");
0146 if (fd == NULL)
0147 die("couldn't open group file\n");
0148 if (fseek(fd, 0, SEEK_END))
0149 die("couldn't fseek group file\n");
0150 snprintf(name_str, 10, "group %d", gid);
0151 g.gr_name=name_str;
0152 g.gr_gid=gid;
0153 g.gr_passwd=NULL;
0154 g.gr_mem=NULL;
0155 int value = putgrent(&g,fd);
0156 if (value != 0)
0157 die("putgrent failed\n");
0158 if (fclose(fd))
0159 die("fclose failed\n");
0160 }
0161 }
0162
0163 static void ensure_securityfs_mounted(void)
0164 {
0165 int fd = open(add_uid_whitelist_policy_file, O_WRONLY);
0166 if (fd < 0) {
0167 if (errno == ENOENT) {
0168
0169 if (mount("securityfs", "/sys/kernel/security",
0170 "securityfs", 0, NULL) < 0)
0171 die("mounting securityfs failed\n");
0172 } else {
0173 die("couldn't find securityfs for unknown reason\n");
0174 }
0175 } else {
0176 if (close(fd) != 0) {
0177 die("close of %s failed: %s\n",
0178 add_uid_whitelist_policy_file, strerror(errno));
0179 }
0180 }
0181 }
0182
0183 static void write_uid_policies()
0184 {
0185 static char *policy_str = UGID_POLICY_STRING;
0186 ssize_t written;
0187 int fd;
0188
0189 fd = open(add_uid_whitelist_policy_file, O_WRONLY);
0190 if (fd < 0)
0191 die("can't open add_uid_whitelist_policy file\n");
0192 written = write(fd, policy_str, strlen(policy_str));
0193 if (written != strlen(policy_str)) {
0194 if (written >= 0) {
0195 die("short write to %s\n", add_uid_whitelist_policy_file);
0196 } else {
0197 die("write to %s failed: %s\n",
0198 add_uid_whitelist_policy_file, strerror(errno));
0199 }
0200 }
0201 if (close(fd) != 0) {
0202 die("close of %s failed: %s\n",
0203 add_uid_whitelist_policy_file, strerror(errno));
0204 }
0205 }
0206
0207 static void write_gid_policies()
0208 {
0209 static char *policy_str = UGID_POLICY_STRING;
0210 ssize_t written;
0211 int fd;
0212
0213 fd = open(add_gid_whitelist_policy_file, O_WRONLY);
0214 if (fd < 0)
0215 die("can't open add_gid_whitelist_policy file\n");
0216 written = write(fd, policy_str, strlen(policy_str));
0217 if (written != strlen(policy_str)) {
0218 if (written >= 0) {
0219 die("short write to %s\n", add_gid_whitelist_policy_file);
0220 } else {
0221 die("write to %s failed: %s\n",
0222 add_gid_whitelist_policy_file, strerror(errno));
0223 }
0224 }
0225 if (close(fd) != 0) {
0226 die("close of %s failed: %s\n",
0227 add_gid_whitelist_policy_file, strerror(errno));
0228 }
0229 }
0230
0231
0232 static bool test_userns(bool expect_success)
0233 {
0234 uid_t uid;
0235 char map_file_name[32];
0236 size_t sz = sizeof(map_file_name);
0237 pid_t cpid;
0238 bool success;
0239
0240 uid = getuid();
0241
0242 int clone_flags = CLONE_NEWUSER;
0243 cpid = syscall(SYS_clone, clone_flags, NULL);
0244 if (cpid == -1) {
0245 printf("clone failed");
0246 return false;
0247 }
0248
0249 if (cpid == 0) {
0250
0251 sleep(1);
0252 exit(EXIT_SUCCESS);
0253 } else {
0254 if(snprintf(map_file_name, sz, "/proc/%d/uid_map", cpid) < 0) {
0255 printf("preparing file name string failed");
0256 return false;
0257 }
0258 success = write_file(map_file_name, "0 %d 1", uid);
0259 return success == expect_success;
0260 }
0261
0262 printf("should not reach here");
0263 return false;
0264 }
0265
0266 static void test_setuid(uid_t child_uid, bool expect_success)
0267 {
0268 pid_t cpid, w;
0269 int wstatus;
0270
0271 cpid = fork();
0272 if (cpid == -1) {
0273 die("fork\n");
0274 }
0275
0276 if (cpid == 0) {
0277 if (setuid(child_uid) < 0)
0278 exit(EXIT_FAILURE);
0279 if (getuid() == child_uid)
0280 exit(EXIT_SUCCESS);
0281 else
0282 exit(EXIT_FAILURE);
0283 } else {
0284 do {
0285 w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED);
0286 if (w == -1) {
0287 die("waitpid\n");
0288 }
0289
0290 if (WIFEXITED(wstatus)) {
0291 if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
0292 if (expect_success) {
0293 return;
0294 } else {
0295 die("unexpected success\n");
0296 }
0297 } else {
0298 if (expect_success) {
0299 die("unexpected failure\n");
0300 } else {
0301 return;
0302 }
0303 }
0304 } else if (WIFSIGNALED(wstatus)) {
0305 if (WTERMSIG(wstatus) == 9) {
0306 if (expect_success)
0307 die("killed unexpectedly\n");
0308 else
0309 return;
0310 } else {
0311 die("unexpected signal: %d\n", wstatus);
0312 }
0313 } else {
0314 die("unexpected status: %d\n", wstatus);
0315 }
0316 } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
0317 }
0318
0319 die("should not reach here\n");
0320 }
0321
0322 static void test_setgid(gid_t child_gid, bool expect_success)
0323 {
0324 pid_t cpid, w;
0325 int wstatus;
0326
0327 cpid = fork();
0328 if (cpid == -1) {
0329 die("fork\n");
0330 }
0331
0332 if (cpid == 0) {
0333 if (setgid(child_gid) < 0)
0334 exit(EXIT_FAILURE);
0335 if (getgid() == child_gid)
0336 exit(EXIT_SUCCESS);
0337 else
0338 exit(EXIT_FAILURE);
0339 } else {
0340 do {
0341 w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED);
0342 if (w == -1) {
0343 die("waitpid\n");
0344 }
0345
0346 if (WIFEXITED(wstatus)) {
0347 if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
0348 if (expect_success) {
0349 return;
0350 } else {
0351 die("unexpected success\n");
0352 }
0353 } else {
0354 if (expect_success) {
0355 die("unexpected failure\n");
0356 } else {
0357 return;
0358 }
0359 }
0360 } else if (WIFSIGNALED(wstatus)) {
0361 if (WTERMSIG(wstatus) == 9) {
0362 if (expect_success)
0363 die("killed unexpectedly\n");
0364 else
0365 return;
0366 } else {
0367 die("unexpected signal: %d\n", wstatus);
0368 }
0369 } else {
0370 die("unexpected status: %d\n", wstatus);
0371 }
0372 } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
0373 }
0374
0375 die("should not reach here\n");
0376 }
0377
0378 static void test_setgroups(gid_t* child_groups, size_t len, bool expect_success)
0379 {
0380 pid_t cpid, w;
0381 int wstatus;
0382 gid_t groupset[len];
0383 int i, j;
0384
0385 cpid = fork();
0386 if (cpid == -1) {
0387 die("fork\n");
0388 }
0389
0390 if (cpid == 0) {
0391 if (setgroups(len, child_groups) != 0)
0392 exit(EXIT_FAILURE);
0393 if (getgroups(len, groupset) != len)
0394 exit(EXIT_FAILURE);
0395 for (i = 0; i < len; i++) {
0396 for (j = 0; j < len; j++) {
0397 if (child_groups[i] == groupset[j])
0398 break;
0399 if (j == len - 1)
0400 exit(EXIT_FAILURE);
0401 }
0402 }
0403 exit(EXIT_SUCCESS);
0404 } else {
0405 do {
0406 w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED);
0407 if (w == -1) {
0408 die("waitpid\n");
0409 }
0410
0411 if (WIFEXITED(wstatus)) {
0412 if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
0413 if (expect_success) {
0414 return;
0415 } else {
0416 die("unexpected success\n");
0417 }
0418 } else {
0419 if (expect_success) {
0420 die("unexpected failure\n");
0421 } else {
0422 return;
0423 }
0424 }
0425 } else if (WIFSIGNALED(wstatus)) {
0426 if (WTERMSIG(wstatus) == 9) {
0427 if (expect_success)
0428 die("killed unexpectedly\n");
0429 else
0430 return;
0431 } else {
0432 die("unexpected signal: %d\n", wstatus);
0433 }
0434 } else {
0435 die("unexpected status: %d\n", wstatus);
0436 }
0437 } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
0438 }
0439
0440 die("should not reach here\n");
0441 }
0442
0443
0444 static void ensure_users_exist(void)
0445 {
0446 ensure_user_exists(ROOT_UGID);
0447 ensure_user_exists(RESTRICTED_PARENT_UGID);
0448 ensure_user_exists(ALLOWED_CHILD1_UGID);
0449 ensure_user_exists(ALLOWED_CHILD2_UGID);
0450 ensure_user_exists(NO_POLICY_UGID);
0451 }
0452
0453 static void ensure_groups_exist(void)
0454 {
0455 ensure_group_exists(ROOT_UGID);
0456 ensure_group_exists(RESTRICTED_PARENT_UGID);
0457 ensure_group_exists(ALLOWED_CHILD1_UGID);
0458 ensure_group_exists(ALLOWED_CHILD2_UGID);
0459 ensure_group_exists(NO_POLICY_UGID);
0460 }
0461
0462 static void drop_caps(bool setid_retained)
0463 {
0464 cap_value_t cap_values[] = {CAP_SETUID, CAP_SETGID};
0465 cap_t caps;
0466
0467 caps = cap_get_proc();
0468 if (setid_retained)
0469 cap_set_flag(caps, CAP_EFFECTIVE, 2, cap_values, CAP_SET);
0470 else
0471 cap_clear(caps);
0472 cap_set_proc(caps);
0473 cap_free(caps);
0474 }
0475
0476 int main(int argc, char **argv)
0477 {
0478 ensure_groups_exist();
0479 ensure_users_exist();
0480 ensure_securityfs_mounted();
0481 write_uid_policies();
0482 write_gid_policies();
0483
0484 if (prctl(PR_SET_KEEPCAPS, 1L))
0485 die("Error with set keepcaps\n");
0486
0487
0488
0489
0490 if (setgid(NO_POLICY_UGID) < 0)
0491 die("Error with set gid(%d)\n", NO_POLICY_UGID);
0492 if (setuid(NO_POLICY_UGID) < 0)
0493 die("Error with set uid(%d)\n", NO_POLICY_UGID);
0494
0495 drop_caps(true);
0496
0497
0498 if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0))
0499 die("Error with set dumpable\n");
0500 if (!test_userns(true)) {
0501 die("test_userns failed when it should work\n");
0502 }
0503
0504
0505 if (setgid(RESTRICTED_PARENT_UGID) < 0)
0506 die("Error with set gid(%d)\n", RESTRICTED_PARENT_UGID);
0507 if (setuid(RESTRICTED_PARENT_UGID) < 0)
0508 die("Error with set uid(%d)\n", RESTRICTED_PARENT_UGID);
0509
0510 test_setuid(ROOT_UGID, false);
0511 test_setuid(ALLOWED_CHILD1_UGID, true);
0512 test_setuid(ALLOWED_CHILD2_UGID, true);
0513 test_setuid(NO_POLICY_UGID, false);
0514
0515 test_setgid(ROOT_UGID, false);
0516 test_setgid(ALLOWED_CHILD1_UGID, true);
0517 test_setgid(ALLOWED_CHILD2_UGID, true);
0518 test_setgid(NO_POLICY_UGID, false);
0519
0520 gid_t allowed_supp_groups[2] = {ALLOWED_CHILD1_UGID, ALLOWED_CHILD2_UGID};
0521 gid_t disallowed_supp_groups[2] = {ROOT_UGID, NO_POLICY_UGID};
0522 test_setgroups(allowed_supp_groups, 2, true);
0523 test_setgroups(disallowed_supp_groups, 2, false);
0524
0525 if (!test_userns(false)) {
0526 die("test_userns worked when it should fail\n");
0527 }
0528
0529
0530 drop_caps(false);
0531 test_setuid(2, false);
0532 test_setuid(3, false);
0533 test_setuid(4, false);
0534 test_setgid(2, false);
0535 test_setgid(3, false);
0536 test_setgid(4, false);
0537
0538
0539
0540 printf("test successful!\n");
0541 return EXIT_SUCCESS;
0542 }