Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
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  * NOTES about this test:
0022  * - requries libcap-dev to be installed on test system
0023  * - requires securityfs to me mounted at /sys/kernel/security, e.g.:
0024  * mount -n -t securityfs -o nodev,noexec,nosuid securityfs /sys/kernel/security
0025  * - needs CONFIG_SECURITYFS and CONFIG_SAFESETID to be enabled
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             // Need to mount securityfs
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) {    /* Code executed by child */
0250         // Give parent 1 second to write map file
0251         sleep(1);
0252         exit(EXIT_SUCCESS);
0253     } else {        /* Code executed by parent */
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) {        /* Code executed by child */
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 {         /* Code executed by parent */
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) {        /* Code executed by child */
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 {         /* Code executed by parent */
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) {        /* Code executed by child */
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 {         /* Code executed by parent */
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     // First test to make sure we can write userns mappings from a non-root
0488     // user that doesn't have any restrictions (as long as it has
0489     // CAP_SETUID);
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     // Take away all but setid caps
0495     drop_caps(true);
0496     // Need PR_SET_DUMPABLE flag set so we can write /proc/[pid]/uid_map
0497     // from non-root parent process.
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     // Now switch to a user/group with restrictions
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     // Now take away all caps
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     // NOTE: this test doesn't clean up users that were created in
0539     // /etc/passwd or flush policies that were added to the LSM.
0540     printf("test successful!\n");
0541     return EXIT_SUCCESS;
0542 }