root/tools/testing/selftests/safesetid/safesetid-test.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. die
  2. vmaybe_write_file
  3. write_file
  4. ensure_user_exists
  5. ensure_securityfs_mounted
  6. write_policies
  7. test_userns
  8. test_setuid
  9. ensure_users_exist
  10. drop_caps
  11. main

   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 #ifndef CLONE_NEWUSER
  20 # define CLONE_NEWUSER 0x10000000
  21 #endif
  22 
  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
  28 
  29 char* add_whitelist_policy_file = "/sys/kernel/security/safesetid/add_whitelist_policy";
  30 
  31 static 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 }
  39 
  40 static bool vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap)
  41 {
  42         char buf[4096];
  43         int fd;
  44         ssize_t written;
  45         int buf_len;
  46 
  47         buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
  48         if (buf_len < 0) {
  49                 printf("vsnprintf failed: %s\n",
  50                     strerror(errno));
  51                 return false;
  52         }
  53         if (buf_len >= sizeof(buf)) {
  54                 printf("vsnprintf output truncated\n");
  55                 return false;
  56         }
  57 
  58         fd = open(filename, O_WRONLY);
  59         if (fd < 0) {
  60                 if ((errno == ENOENT) && enoent_ok)
  61                         return true;
  62                 return false;
  63         }
  64         written = write(fd, buf, buf_len);
  65         if (written != buf_len) {
  66                 if (written >= 0) {
  67                         printf("short write to %s\n", filename);
  68                         return false;
  69                 } else {
  70                         printf("write to %s failed: %s\n",
  71                                 filename, strerror(errno));
  72                         return false;
  73                 }
  74         }
  75         if (close(fd) != 0) {
  76                 printf("close of %s failed: %s\n",
  77                         filename, strerror(errno));
  78                 return false;
  79         }
  80         return true;
  81 }
  82 
  83 static bool write_file(char *filename, char *fmt, ...)
  84 {
  85         va_list ap;
  86         bool ret;
  87 
  88         va_start(ap, fmt);
  89         ret = vmaybe_write_file(false, filename, fmt, ap);
  90         va_end(ap);
  91 
  92         return ret;
  93 }
  94 
  95 static void ensure_user_exists(uid_t uid)
  96 {
  97         struct passwd p;
  98 
  99         FILE *fd;
 100         char name_str[10];
 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");
 109                 snprintf(name_str, 10, "%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 
 123 static void ensure_securityfs_mounted(void)
 124 {
 125         int fd = open(add_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",
 138                                 add_whitelist_policy_file, strerror(errno));
 139                 }
 140         }
 141 }
 142 
 143 static void write_policies(void)
 144 {
 145         static char *policy_str =
 146                 "1:2\n"
 147                 "1:3\n"
 148                 "2:2\n"
 149                 "3:3\n";
 150         ssize_t written;
 151         int fd;
 152 
 153         fd = open(add_whitelist_policy_file, O_WRONLY);
 154         if (fd < 0)
 155                 die("cant open add_whitelist_policy file\n");
 156         written = write(fd, policy_str, strlen(policy_str));
 157         if (written != strlen(policy_str)) {
 158                 if (written >= 0) {
 159                         die("short write to %s\n", add_whitelist_policy_file);
 160                 } else {
 161                         die("write to %s failed: %s\n",
 162                                 add_whitelist_policy_file, strerror(errno));
 163                 }
 164         }
 165         if (close(fd) != 0) {
 166                 die("close of %s failed: %s\n",
 167                         add_whitelist_policy_file, strerror(errno));
 168         }
 169 }
 170 
 171 static 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);
 176         pid_t cpid;
 177         bool success;
 178 
 179         uid = getuid();
 180 
 181         int clone_flags = CLONE_NEWUSER;
 182         cpid = syscall(SYS_clone, clone_flags, NULL);
 183         if (cpid == -1) {
 184             printf("clone failed");
 185             return false;
 186         }
 187 
 188         if (cpid == 0) {        /* Code executed by child */
 189                 // Give parent 1 second to write map file
 190                 sleep(1);
 191                 exit(EXIT_SUCCESS);
 192         } else {                /* Code executed by parent */
 193                 if(snprintf(map_file_name, sz, "/proc/%d/uid_map", cpid) < 0) {
 194                         printf("preparing file name string failed");
 195                         return false;
 196                 }
 197                 success = write_file(map_file_name, "0 0 1", uid);
 198                 return success == expect_success;
 199         }
 200 
 201         printf("should not reach here");
 202         return false;
 203 }
 204 
 205 static void test_setuid(uid_t child_uid, bool expect_success)
 206 {
 207         pid_t cpid, w;
 208         int wstatus;
 209 
 210         cpid = fork();
 211         if (cpid == -1) {
 212                 die("fork\n");
 213         }
 214 
 215         if (cpid == 0) {            /* Code executed by child */
 216                 if (setuid(child_uid) < 0)
 217                         exit(EXIT_FAILURE);
 218                 if (getuid() == child_uid)
 219                         exit(EXIT_SUCCESS);
 220                 else
 221                         exit(EXIT_FAILURE);
 222         } else {                 /* Code executed by parent */
 223                 do {
 224                         w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED);
 225                         if (w == -1) {
 226                                 die("waitpid\n");
 227                         }
 228 
 229                         if (WIFEXITED(wstatus)) {
 230                                 if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
 231                                         if (expect_success) {
 232                                                 return;
 233                                         } else {
 234                                                 die("unexpected success\n");
 235                                         }
 236                                 } else {
 237                                         if (expect_success) {
 238                                                 die("unexpected failure\n");
 239                                         } else {
 240                                                 return;
 241                                         }
 242                                 }
 243                         } else if (WIFSIGNALED(wstatus)) {
 244                                 if (WTERMSIG(wstatus) == 9) {
 245                                         if (expect_success)
 246                                                 die("killed unexpectedly\n");
 247                                         else
 248                                                 return;
 249                                 } else {
 250                                         die("unexpected signal: %d\n", wstatus);
 251                                 }
 252                         } else {
 253                                 die("unexpected status: %d\n", wstatus);
 254                         }
 255                 } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
 256         }
 257 
 258         die("should not reach here\n");
 259 }
 260 
 261 static void ensure_users_exist(void)
 262 {
 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);
 268 }
 269 
 270 static 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();
 276         if (setid_retained)
 277                 cap_set_flag(caps, CAP_EFFECTIVE, 2, cap_values, CAP_SET);
 278         else
 279                 cap_clear(caps);
 280         cap_set_proc(caps);
 281         cap_free(caps);
 282 }
 283 
 284 int main(int argc, char **argv)
 285 {
 286         ensure_users_exist();
 287         ensure_securityfs_mounted();
 288         write_policies();
 289 
 290         if (prctl(PR_SET_KEEPCAPS, 1L))
 291                 die("Error with set keepcaps\n");
 292 
 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         // Take away all but setid caps
 301         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");
 307 
 308         if (!test_userns(true)) {
 309                 die("test_userns failed when it should work\n");
 310         }
 311 
 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);
 316 
 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);
 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.
 334         return EXIT_SUCCESS;
 335 }

/* [<][>][^][v][top][bottom][index][help] */