162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#define _GNU_SOURCE 362306a36Sopenharmony_ci#include <stdio.h> 462306a36Sopenharmony_ci#include <errno.h> 562306a36Sopenharmony_ci#include <pwd.h> 662306a36Sopenharmony_ci#include <grp.h> 762306a36Sopenharmony_ci#include <string.h> 862306a36Sopenharmony_ci#include <syscall.h> 962306a36Sopenharmony_ci#include <sys/capability.h> 1062306a36Sopenharmony_ci#include <sys/types.h> 1162306a36Sopenharmony_ci#include <sys/mount.h> 1262306a36Sopenharmony_ci#include <sys/prctl.h> 1362306a36Sopenharmony_ci#include <sys/wait.h> 1462306a36Sopenharmony_ci#include <stdlib.h> 1562306a36Sopenharmony_ci#include <unistd.h> 1662306a36Sopenharmony_ci#include <fcntl.h> 1762306a36Sopenharmony_ci#include <stdbool.h> 1862306a36Sopenharmony_ci#include <stdarg.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * NOTES about this test: 2262306a36Sopenharmony_ci * - requries libcap-dev to be installed on test system 2362306a36Sopenharmony_ci * - requires securityfs to me mounted at /sys/kernel/security, e.g.: 2462306a36Sopenharmony_ci * mount -n -t securityfs -o nodev,noexec,nosuid securityfs /sys/kernel/security 2562306a36Sopenharmony_ci * - needs CONFIG_SECURITYFS and CONFIG_SAFESETID to be enabled 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#ifndef CLONE_NEWUSER 2962306a36Sopenharmony_ci# define CLONE_NEWUSER 0x10000000 3062306a36Sopenharmony_ci#endif 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define ROOT_UGID 0 3362306a36Sopenharmony_ci#define RESTRICTED_PARENT_UGID 1 3462306a36Sopenharmony_ci#define ALLOWED_CHILD1_UGID 2 3562306a36Sopenharmony_ci#define ALLOWED_CHILD2_UGID 3 3662306a36Sopenharmony_ci#define NO_POLICY_UGID 4 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define UGID_POLICY_STRING "1:2\n1:3\n2:2\n3:3\n" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cichar* add_uid_whitelist_policy_file = "/sys/kernel/security/safesetid/uid_allowlist_policy"; 4162306a36Sopenharmony_cichar* add_gid_whitelist_policy_file = "/sys/kernel/security/safesetid/gid_allowlist_policy"; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void die(char *fmt, ...) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci va_list ap; 4662306a36Sopenharmony_ci va_start(ap, fmt); 4762306a36Sopenharmony_ci vfprintf(stderr, fmt, ap); 4862306a36Sopenharmony_ci va_end(ap); 4962306a36Sopenharmony_ci exit(EXIT_FAILURE); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic bool vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci char buf[4096]; 5562306a36Sopenharmony_ci int fd; 5662306a36Sopenharmony_ci ssize_t written; 5762306a36Sopenharmony_ci int buf_len; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci buf_len = vsnprintf(buf, sizeof(buf), fmt, ap); 6062306a36Sopenharmony_ci if (buf_len < 0) { 6162306a36Sopenharmony_ci printf("vsnprintf failed: %s\n", 6262306a36Sopenharmony_ci strerror(errno)); 6362306a36Sopenharmony_ci return false; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci if (buf_len >= sizeof(buf)) { 6662306a36Sopenharmony_ci printf("vsnprintf output truncated\n"); 6762306a36Sopenharmony_ci return false; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci fd = open(filename, O_WRONLY); 7162306a36Sopenharmony_ci if (fd < 0) { 7262306a36Sopenharmony_ci if ((errno == ENOENT) && enoent_ok) 7362306a36Sopenharmony_ci return true; 7462306a36Sopenharmony_ci return false; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci written = write(fd, buf, buf_len); 7762306a36Sopenharmony_ci if (written != buf_len) { 7862306a36Sopenharmony_ci if (written >= 0) { 7962306a36Sopenharmony_ci printf("short write to %s\n", filename); 8062306a36Sopenharmony_ci return false; 8162306a36Sopenharmony_ci } else { 8262306a36Sopenharmony_ci printf("write to %s failed: %s\n", 8362306a36Sopenharmony_ci filename, strerror(errno)); 8462306a36Sopenharmony_ci return false; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci if (close(fd) != 0) { 8862306a36Sopenharmony_ci printf("close of %s failed: %s\n", 8962306a36Sopenharmony_ci filename, strerror(errno)); 9062306a36Sopenharmony_ci return false; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci return true; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic bool write_file(char *filename, char *fmt, ...) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci va_list ap; 9862306a36Sopenharmony_ci bool ret; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci va_start(ap, fmt); 10162306a36Sopenharmony_ci ret = vmaybe_write_file(false, filename, fmt, ap); 10262306a36Sopenharmony_ci va_end(ap); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return ret; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void ensure_user_exists(uid_t uid) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct passwd p; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci FILE *fd; 11262306a36Sopenharmony_ci char name_str[10]; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (getpwuid(uid) == NULL) { 11562306a36Sopenharmony_ci memset(&p,0x00,sizeof(p)); 11662306a36Sopenharmony_ci fd=fopen("/etc/passwd","a"); 11762306a36Sopenharmony_ci if (fd == NULL) 11862306a36Sopenharmony_ci die("couldn't open file\n"); 11962306a36Sopenharmony_ci if (fseek(fd, 0, SEEK_END)) 12062306a36Sopenharmony_ci die("couldn't fseek\n"); 12162306a36Sopenharmony_ci snprintf(name_str, 10, "user %d", uid); 12262306a36Sopenharmony_ci p.pw_name=name_str; 12362306a36Sopenharmony_ci p.pw_uid=uid; 12462306a36Sopenharmony_ci p.pw_gid=uid; 12562306a36Sopenharmony_ci p.pw_gecos="Test account"; 12662306a36Sopenharmony_ci p.pw_dir="/dev/null"; 12762306a36Sopenharmony_ci p.pw_shell="/bin/false"; 12862306a36Sopenharmony_ci int value = putpwent(&p,fd); 12962306a36Sopenharmony_ci if (value != 0) 13062306a36Sopenharmony_ci die("putpwent failed\n"); 13162306a36Sopenharmony_ci if (fclose(fd)) 13262306a36Sopenharmony_ci die("fclose failed\n"); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void ensure_group_exists(gid_t gid) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct group g; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci FILE *fd; 14162306a36Sopenharmony_ci char name_str[10]; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (getgrgid(gid) == NULL) { 14462306a36Sopenharmony_ci memset(&g,0x00,sizeof(g)); 14562306a36Sopenharmony_ci fd=fopen("/etc/group","a"); 14662306a36Sopenharmony_ci if (fd == NULL) 14762306a36Sopenharmony_ci die("couldn't open group file\n"); 14862306a36Sopenharmony_ci if (fseek(fd, 0, SEEK_END)) 14962306a36Sopenharmony_ci die("couldn't fseek group file\n"); 15062306a36Sopenharmony_ci snprintf(name_str, 10, "group %d", gid); 15162306a36Sopenharmony_ci g.gr_name=name_str; 15262306a36Sopenharmony_ci g.gr_gid=gid; 15362306a36Sopenharmony_ci g.gr_passwd=NULL; 15462306a36Sopenharmony_ci g.gr_mem=NULL; 15562306a36Sopenharmony_ci int value = putgrent(&g,fd); 15662306a36Sopenharmony_ci if (value != 0) 15762306a36Sopenharmony_ci die("putgrent failed\n"); 15862306a36Sopenharmony_ci if (fclose(fd)) 15962306a36Sopenharmony_ci die("fclose failed\n"); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void ensure_securityfs_mounted(void) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci int fd = open(add_uid_whitelist_policy_file, O_WRONLY); 16662306a36Sopenharmony_ci if (fd < 0) { 16762306a36Sopenharmony_ci if (errno == ENOENT) { 16862306a36Sopenharmony_ci // Need to mount securityfs 16962306a36Sopenharmony_ci if (mount("securityfs", "/sys/kernel/security", 17062306a36Sopenharmony_ci "securityfs", 0, NULL) < 0) 17162306a36Sopenharmony_ci die("mounting securityfs failed\n"); 17262306a36Sopenharmony_ci } else { 17362306a36Sopenharmony_ci die("couldn't find securityfs for unknown reason\n"); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci } else { 17662306a36Sopenharmony_ci if (close(fd) != 0) { 17762306a36Sopenharmony_ci die("close of %s failed: %s\n", 17862306a36Sopenharmony_ci add_uid_whitelist_policy_file, strerror(errno)); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void write_uid_policies() 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci static char *policy_str = UGID_POLICY_STRING; 18662306a36Sopenharmony_ci ssize_t written; 18762306a36Sopenharmony_ci int fd; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci fd = open(add_uid_whitelist_policy_file, O_WRONLY); 19062306a36Sopenharmony_ci if (fd < 0) 19162306a36Sopenharmony_ci die("can't open add_uid_whitelist_policy file\n"); 19262306a36Sopenharmony_ci written = write(fd, policy_str, strlen(policy_str)); 19362306a36Sopenharmony_ci if (written != strlen(policy_str)) { 19462306a36Sopenharmony_ci if (written >= 0) { 19562306a36Sopenharmony_ci die("short write to %s\n", add_uid_whitelist_policy_file); 19662306a36Sopenharmony_ci } else { 19762306a36Sopenharmony_ci die("write to %s failed: %s\n", 19862306a36Sopenharmony_ci add_uid_whitelist_policy_file, strerror(errno)); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci if (close(fd) != 0) { 20262306a36Sopenharmony_ci die("close of %s failed: %s\n", 20362306a36Sopenharmony_ci add_uid_whitelist_policy_file, strerror(errno)); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic void write_gid_policies() 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci static char *policy_str = UGID_POLICY_STRING; 21062306a36Sopenharmony_ci ssize_t written; 21162306a36Sopenharmony_ci int fd; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci fd = open(add_gid_whitelist_policy_file, O_WRONLY); 21462306a36Sopenharmony_ci if (fd < 0) 21562306a36Sopenharmony_ci die("can't open add_gid_whitelist_policy file\n"); 21662306a36Sopenharmony_ci written = write(fd, policy_str, strlen(policy_str)); 21762306a36Sopenharmony_ci if (written != strlen(policy_str)) { 21862306a36Sopenharmony_ci if (written >= 0) { 21962306a36Sopenharmony_ci die("short write to %s\n", add_gid_whitelist_policy_file); 22062306a36Sopenharmony_ci } else { 22162306a36Sopenharmony_ci die("write to %s failed: %s\n", 22262306a36Sopenharmony_ci add_gid_whitelist_policy_file, strerror(errno)); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci if (close(fd) != 0) { 22662306a36Sopenharmony_ci die("close of %s failed: %s\n", 22762306a36Sopenharmony_ci add_gid_whitelist_policy_file, strerror(errno)); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic bool test_userns(bool expect_success) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci uid_t uid; 23562306a36Sopenharmony_ci char map_file_name[32]; 23662306a36Sopenharmony_ci size_t sz = sizeof(map_file_name); 23762306a36Sopenharmony_ci pid_t cpid; 23862306a36Sopenharmony_ci bool success; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci uid = getuid(); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci int clone_flags = CLONE_NEWUSER; 24362306a36Sopenharmony_ci cpid = syscall(SYS_clone, clone_flags, NULL); 24462306a36Sopenharmony_ci if (cpid == -1) { 24562306a36Sopenharmony_ci printf("clone failed"); 24662306a36Sopenharmony_ci return false; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (cpid == 0) { /* Code executed by child */ 25062306a36Sopenharmony_ci // Give parent 1 second to write map file 25162306a36Sopenharmony_ci sleep(1); 25262306a36Sopenharmony_ci exit(EXIT_SUCCESS); 25362306a36Sopenharmony_ci } else { /* Code executed by parent */ 25462306a36Sopenharmony_ci if(snprintf(map_file_name, sz, "/proc/%d/uid_map", cpid) < 0) { 25562306a36Sopenharmony_ci printf("preparing file name string failed"); 25662306a36Sopenharmony_ci return false; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci success = write_file(map_file_name, "0 %d 1", uid); 25962306a36Sopenharmony_ci return success == expect_success; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci printf("should not reach here"); 26362306a36Sopenharmony_ci return false; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic void test_setuid(uid_t child_uid, bool expect_success) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci pid_t cpid, w; 26962306a36Sopenharmony_ci int wstatus; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci cpid = fork(); 27262306a36Sopenharmony_ci if (cpid == -1) { 27362306a36Sopenharmony_ci die("fork\n"); 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (cpid == 0) { /* Code executed by child */ 27762306a36Sopenharmony_ci if (setuid(child_uid) < 0) 27862306a36Sopenharmony_ci exit(EXIT_FAILURE); 27962306a36Sopenharmony_ci if (getuid() == child_uid) 28062306a36Sopenharmony_ci exit(EXIT_SUCCESS); 28162306a36Sopenharmony_ci else 28262306a36Sopenharmony_ci exit(EXIT_FAILURE); 28362306a36Sopenharmony_ci } else { /* Code executed by parent */ 28462306a36Sopenharmony_ci do { 28562306a36Sopenharmony_ci w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED); 28662306a36Sopenharmony_ci if (w == -1) { 28762306a36Sopenharmony_ci die("waitpid\n"); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (WIFEXITED(wstatus)) { 29162306a36Sopenharmony_ci if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) { 29262306a36Sopenharmony_ci if (expect_success) { 29362306a36Sopenharmony_ci return; 29462306a36Sopenharmony_ci } else { 29562306a36Sopenharmony_ci die("unexpected success\n"); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci } else { 29862306a36Sopenharmony_ci if (expect_success) { 29962306a36Sopenharmony_ci die("unexpected failure\n"); 30062306a36Sopenharmony_ci } else { 30162306a36Sopenharmony_ci return; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci } else if (WIFSIGNALED(wstatus)) { 30562306a36Sopenharmony_ci if (WTERMSIG(wstatus) == 9) { 30662306a36Sopenharmony_ci if (expect_success) 30762306a36Sopenharmony_ci die("killed unexpectedly\n"); 30862306a36Sopenharmony_ci else 30962306a36Sopenharmony_ci return; 31062306a36Sopenharmony_ci } else { 31162306a36Sopenharmony_ci die("unexpected signal: %d\n", wstatus); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci } else { 31462306a36Sopenharmony_ci die("unexpected status: %d\n", wstatus); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci die("should not reach here\n"); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void test_setgid(gid_t child_gid, bool expect_success) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci pid_t cpid, w; 32562306a36Sopenharmony_ci int wstatus; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci cpid = fork(); 32862306a36Sopenharmony_ci if (cpid == -1) { 32962306a36Sopenharmony_ci die("fork\n"); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (cpid == 0) { /* Code executed by child */ 33362306a36Sopenharmony_ci if (setgid(child_gid) < 0) 33462306a36Sopenharmony_ci exit(EXIT_FAILURE); 33562306a36Sopenharmony_ci if (getgid() == child_gid) 33662306a36Sopenharmony_ci exit(EXIT_SUCCESS); 33762306a36Sopenharmony_ci else 33862306a36Sopenharmony_ci exit(EXIT_FAILURE); 33962306a36Sopenharmony_ci } else { /* Code executed by parent */ 34062306a36Sopenharmony_ci do { 34162306a36Sopenharmony_ci w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED); 34262306a36Sopenharmony_ci if (w == -1) { 34362306a36Sopenharmony_ci die("waitpid\n"); 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (WIFEXITED(wstatus)) { 34762306a36Sopenharmony_ci if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) { 34862306a36Sopenharmony_ci if (expect_success) { 34962306a36Sopenharmony_ci return; 35062306a36Sopenharmony_ci } else { 35162306a36Sopenharmony_ci die("unexpected success\n"); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci } else { 35462306a36Sopenharmony_ci if (expect_success) { 35562306a36Sopenharmony_ci die("unexpected failure\n"); 35662306a36Sopenharmony_ci } else { 35762306a36Sopenharmony_ci return; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci } else if (WIFSIGNALED(wstatus)) { 36162306a36Sopenharmony_ci if (WTERMSIG(wstatus) == 9) { 36262306a36Sopenharmony_ci if (expect_success) 36362306a36Sopenharmony_ci die("killed unexpectedly\n"); 36462306a36Sopenharmony_ci else 36562306a36Sopenharmony_ci return; 36662306a36Sopenharmony_ci } else { 36762306a36Sopenharmony_ci die("unexpected signal: %d\n", wstatus); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci } else { 37062306a36Sopenharmony_ci die("unexpected status: %d\n", wstatus); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci die("should not reach here\n"); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void test_setgroups(gid_t* child_groups, size_t len, bool expect_success) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci pid_t cpid, w; 38162306a36Sopenharmony_ci int wstatus; 38262306a36Sopenharmony_ci gid_t groupset[len]; 38362306a36Sopenharmony_ci int i, j; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci cpid = fork(); 38662306a36Sopenharmony_ci if (cpid == -1) { 38762306a36Sopenharmony_ci die("fork\n"); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (cpid == 0) { /* Code executed by child */ 39162306a36Sopenharmony_ci if (setgroups(len, child_groups) != 0) 39262306a36Sopenharmony_ci exit(EXIT_FAILURE); 39362306a36Sopenharmony_ci if (getgroups(len, groupset) != len) 39462306a36Sopenharmony_ci exit(EXIT_FAILURE); 39562306a36Sopenharmony_ci for (i = 0; i < len; i++) { 39662306a36Sopenharmony_ci for (j = 0; j < len; j++) { 39762306a36Sopenharmony_ci if (child_groups[i] == groupset[j]) 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci if (j == len - 1) 40062306a36Sopenharmony_ci exit(EXIT_FAILURE); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci exit(EXIT_SUCCESS); 40462306a36Sopenharmony_ci } else { /* Code executed by parent */ 40562306a36Sopenharmony_ci do { 40662306a36Sopenharmony_ci w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED); 40762306a36Sopenharmony_ci if (w == -1) { 40862306a36Sopenharmony_ci die("waitpid\n"); 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (WIFEXITED(wstatus)) { 41262306a36Sopenharmony_ci if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) { 41362306a36Sopenharmony_ci if (expect_success) { 41462306a36Sopenharmony_ci return; 41562306a36Sopenharmony_ci } else { 41662306a36Sopenharmony_ci die("unexpected success\n"); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci } else { 41962306a36Sopenharmony_ci if (expect_success) { 42062306a36Sopenharmony_ci die("unexpected failure\n"); 42162306a36Sopenharmony_ci } else { 42262306a36Sopenharmony_ci return; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci } else if (WIFSIGNALED(wstatus)) { 42662306a36Sopenharmony_ci if (WTERMSIG(wstatus) == 9) { 42762306a36Sopenharmony_ci if (expect_success) 42862306a36Sopenharmony_ci die("killed unexpectedly\n"); 42962306a36Sopenharmony_ci else 43062306a36Sopenharmony_ci return; 43162306a36Sopenharmony_ci } else { 43262306a36Sopenharmony_ci die("unexpected signal: %d\n", wstatus); 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci } else { 43562306a36Sopenharmony_ci die("unexpected status: %d\n", wstatus); 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci die("should not reach here\n"); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic void ensure_users_exist(void) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci ensure_user_exists(ROOT_UGID); 44762306a36Sopenharmony_ci ensure_user_exists(RESTRICTED_PARENT_UGID); 44862306a36Sopenharmony_ci ensure_user_exists(ALLOWED_CHILD1_UGID); 44962306a36Sopenharmony_ci ensure_user_exists(ALLOWED_CHILD2_UGID); 45062306a36Sopenharmony_ci ensure_user_exists(NO_POLICY_UGID); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic void ensure_groups_exist(void) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci ensure_group_exists(ROOT_UGID); 45662306a36Sopenharmony_ci ensure_group_exists(RESTRICTED_PARENT_UGID); 45762306a36Sopenharmony_ci ensure_group_exists(ALLOWED_CHILD1_UGID); 45862306a36Sopenharmony_ci ensure_group_exists(ALLOWED_CHILD2_UGID); 45962306a36Sopenharmony_ci ensure_group_exists(NO_POLICY_UGID); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic void drop_caps(bool setid_retained) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci cap_value_t cap_values[] = {CAP_SETUID, CAP_SETGID}; 46562306a36Sopenharmony_ci cap_t caps; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci caps = cap_get_proc(); 46862306a36Sopenharmony_ci if (setid_retained) 46962306a36Sopenharmony_ci cap_set_flag(caps, CAP_EFFECTIVE, 2, cap_values, CAP_SET); 47062306a36Sopenharmony_ci else 47162306a36Sopenharmony_ci cap_clear(caps); 47262306a36Sopenharmony_ci cap_set_proc(caps); 47362306a36Sopenharmony_ci cap_free(caps); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciint main(int argc, char **argv) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci ensure_groups_exist(); 47962306a36Sopenharmony_ci ensure_users_exist(); 48062306a36Sopenharmony_ci ensure_securityfs_mounted(); 48162306a36Sopenharmony_ci write_uid_policies(); 48262306a36Sopenharmony_ci write_gid_policies(); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (prctl(PR_SET_KEEPCAPS, 1L)) 48562306a36Sopenharmony_ci die("Error with set keepcaps\n"); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci // First test to make sure we can write userns mappings from a non-root 48862306a36Sopenharmony_ci // user that doesn't have any restrictions (as long as it has 48962306a36Sopenharmony_ci // CAP_SETUID); 49062306a36Sopenharmony_ci if (setgid(NO_POLICY_UGID) < 0) 49162306a36Sopenharmony_ci die("Error with set gid(%d)\n", NO_POLICY_UGID); 49262306a36Sopenharmony_ci if (setuid(NO_POLICY_UGID) < 0) 49362306a36Sopenharmony_ci die("Error with set uid(%d)\n", NO_POLICY_UGID); 49462306a36Sopenharmony_ci // Take away all but setid caps 49562306a36Sopenharmony_ci drop_caps(true); 49662306a36Sopenharmony_ci // Need PR_SET_DUMPABLE flag set so we can write /proc/[pid]/uid_map 49762306a36Sopenharmony_ci // from non-root parent process. 49862306a36Sopenharmony_ci if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)) 49962306a36Sopenharmony_ci die("Error with set dumpable\n"); 50062306a36Sopenharmony_ci if (!test_userns(true)) { 50162306a36Sopenharmony_ci die("test_userns failed when it should work\n"); 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci // Now switch to a user/group with restrictions 50562306a36Sopenharmony_ci if (setgid(RESTRICTED_PARENT_UGID) < 0) 50662306a36Sopenharmony_ci die("Error with set gid(%d)\n", RESTRICTED_PARENT_UGID); 50762306a36Sopenharmony_ci if (setuid(RESTRICTED_PARENT_UGID) < 0) 50862306a36Sopenharmony_ci die("Error with set uid(%d)\n", RESTRICTED_PARENT_UGID); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci test_setuid(ROOT_UGID, false); 51162306a36Sopenharmony_ci test_setuid(ALLOWED_CHILD1_UGID, true); 51262306a36Sopenharmony_ci test_setuid(ALLOWED_CHILD2_UGID, true); 51362306a36Sopenharmony_ci test_setuid(NO_POLICY_UGID, false); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci test_setgid(ROOT_UGID, false); 51662306a36Sopenharmony_ci test_setgid(ALLOWED_CHILD1_UGID, true); 51762306a36Sopenharmony_ci test_setgid(ALLOWED_CHILD2_UGID, true); 51862306a36Sopenharmony_ci test_setgid(NO_POLICY_UGID, false); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci gid_t allowed_supp_groups[2] = {ALLOWED_CHILD1_UGID, ALLOWED_CHILD2_UGID}; 52162306a36Sopenharmony_ci gid_t disallowed_supp_groups[2] = {ROOT_UGID, NO_POLICY_UGID}; 52262306a36Sopenharmony_ci test_setgroups(allowed_supp_groups, 2, true); 52362306a36Sopenharmony_ci test_setgroups(disallowed_supp_groups, 2, false); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (!test_userns(false)) { 52662306a36Sopenharmony_ci die("test_userns worked when it should fail\n"); 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci // Now take away all caps 53062306a36Sopenharmony_ci drop_caps(false); 53162306a36Sopenharmony_ci test_setuid(2, false); 53262306a36Sopenharmony_ci test_setuid(3, false); 53362306a36Sopenharmony_ci test_setuid(4, false); 53462306a36Sopenharmony_ci test_setgid(2, false); 53562306a36Sopenharmony_ci test_setgid(3, false); 53662306a36Sopenharmony_ci test_setgid(4, false); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci // NOTE: this test doesn't clean up users that were created in 53962306a36Sopenharmony_ci // /etc/passwd or flush policies that were added to the LSM. 54062306a36Sopenharmony_ci printf("test successful!\n"); 54162306a36Sopenharmony_ci return EXIT_SUCCESS; 54262306a36Sopenharmony_ci} 543