1/* 2 * Copyright (c) Huawei Technologies Co., Ltd., 2015 3 * This program is free software; you can redistribute it and/or modify it 4 * under the terms of the GNU General Public License as published by the Free 5 * Software Foundation; either version 2 of the License, or (at your option) 6 * any later version. This program is distributed in the hope that it will be 7 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 9 * Public License for more details. You should have received a copy of the GNU 10 * General Public License along with this program. 11 */ 12 13/* 14 * Verify that: 15 * /proc/PID/uid_map and /proc/PID/gid_map contains three values separated by 16 * white space: 17 * ID-inside-ns ID-outside-ns length 18 * 19 * ID-outside-ns is interpreted according to which process is opening the file. 20 * If the process opening the file is in the same user namespace as the process 21 * PID, then ID-outside-ns is defined with respect to the parent user namespace. 22 * If the process opening the file is in a different user namespace, then 23 * ID-outside-ns is defined with respect to the user namespace of the process 24 * opening the file. 25 * 26 * The string "deny" would be written to /proc/self/setgroups before GID 27 * check if setgroups is allowed, see kernel commits: 28 * 29 * commit 9cc46516ddf497ea16e8d7cb986ae03a0f6b92f8 30 * Author: Eric W. Biederman <ebiederm@xmission.com> 31 * Date: Tue Dec 2 12:27:26 2014 -0600 32 * userns: Add a knob to disable setgroups on a per user namespace basis 33 * 34 * commit 66d2f338ee4c449396b6f99f5e75cd18eb6df272 35 * Author: Eric W. Biederman <ebiederm@xmission.com> 36 * Date: Fri Dec 5 19:36:04 2014 -0600 37 * userns: Allow setting gid_maps without privilege when setgroups is disabled 38 * 39 */ 40 41#define _GNU_SOURCE 42#include <sys/wait.h> 43#include <assert.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <stdbool.h> 47#include <unistd.h> 48#include <string.h> 49#include <errno.h> 50#include "userns_helper.h" 51#include "test.h" 52 53#define CHILD1UID 0 54#define CHILD1GID 0 55#define CHILD2UID 200 56#define CHILD2GID 200 57#define UID_MAP 0 58#define GID_MAP 1 59 60char *TCID = "user_namespace3"; 61int TST_TOTAL = 1; 62static int cpid1, parentuid, parentgid; 63 64/* 65 * child_fn1() - Inside a new user namespace 66 */ 67static int child_fn1(void) 68{ 69 TST_SAFE_CHECKPOINT_WAIT(NULL, 0); 70 return 0; 71} 72 73/* 74 * child_fn2() - Inside a new user namespace 75 */ 76static int child_fn2(void) 77{ 78 int exit_val = 0; 79 int uid, gid; 80 char cpid1uidpath[BUFSIZ]; 81 char cpid1gidpath[BUFSIZ]; 82 int idinsidens, idoutsidens, length; 83 84 TST_SAFE_CHECKPOINT_WAIT(NULL, 1); 85 86 uid = geteuid(); 87 gid = getegid(); 88 89 if (uid != CHILD2UID || gid != CHILD2GID) { 90 printf("unexpected uid=%d gid=%d\n", uid, gid); 91 exit_val = 1; 92 } 93 94 /*Get the uid parameters of the child_fn2 process.*/ 95 SAFE_FILE_SCANF(NULL, "/proc/self/uid_map", "%d %d %d", &idinsidens, 96 &idoutsidens, &length); 97 98 /* map file format:ID-inside-ns ID-outside-ns length 99 If the process opening the file is in the same user namespace as 100 the process PID, then ID-outside-ns is defined with respect to the 101 parent user namespace.*/ 102 if (idinsidens != CHILD2UID || idoutsidens != parentuid) { 103 printf("child_fn2 checks /proc/cpid2/uid_map:\n"); 104 printf("unexpected: idinsidens=%d idoutsidens=%d\n", 105 idinsidens, idoutsidens); 106 exit_val = 1; 107 } 108 109 sprintf(cpid1uidpath, "/proc/%d/uid_map", cpid1); 110 SAFE_FILE_SCANF(NULL, cpid1uidpath, "%d %d %d", &idinsidens, 111 &idoutsidens, &length); 112 113 /* If the process opening the file is in a different user namespace, 114 then ID-outside-ns is defined with respect to the user namespace 115 of the process opening the file.*/ 116 if (idinsidens != CHILD1UID || idoutsidens != CHILD2UID) { 117 printf("child_fn2 checks /proc/cpid1/uid_map:\n"); 118 printf("unexpected: idinsidens=%d idoutsidens=%d\n", 119 idinsidens, idoutsidens); 120 exit_val = 1; 121 } 122 123 sprintf(cpid1gidpath, "/proc/%d/gid_map", cpid1); 124 SAFE_FILE_SCANF(NULL, "/proc/self/gid_map", "%d %d %d", 125 &idinsidens, &idoutsidens, &length); 126 127 if (idinsidens != CHILD2GID || idoutsidens != parentgid) { 128 printf("child_fn2 checks /proc/cpid2/gid_map:\n"); 129 printf("unexpected: idinsidens=%d idoutsidens=%d\n", 130 idinsidens, idoutsidens); 131 exit_val = 1; 132 } 133 134 SAFE_FILE_SCANF(NULL, cpid1gidpath, "%d %d %d", &idinsidens, 135 &idoutsidens, &length); 136 137 if (idinsidens != CHILD1GID || idoutsidens != CHILD2GID) { 138 printf("child_fn1 checks /proc/cpid1/gid_map:\n"); 139 printf("unexpected: idinsidens=%d idoutsidens=%d\n", 140 idinsidens, idoutsidens); 141 exit_val = 1; 142 } 143 144 TST_SAFE_CHECKPOINT_WAKE(NULL, 0); 145 TST_SAFE_CHECKPOINT_WAKE(NULL, 1); 146 return exit_val; 147} 148 149static void cleanup(void) 150{ 151 tst_rmdir(); 152} 153 154static void setup(void) 155{ 156 check_newuser(); 157 tst_tmpdir(); 158 TST_CHECKPOINT_INIT(NULL); 159} 160 161int main(int argc, char *argv[]) 162{ 163 pid_t cpid2; 164 char path[BUFSIZ]; 165 int lc; 166 int fd; 167 int ret; 168 169 tst_parse_opts(argc, argv, NULL, NULL); 170 setup(); 171 172 for (lc = 0; TEST_LOOPING(lc); lc++) { 173 tst_count = 0; 174 175 parentuid = geteuid(); 176 parentgid = getegid(); 177 178 cpid1 = ltp_clone_quick(CLONE_NEWUSER | SIGCHLD, 179 (void *)child_fn1, NULL); 180 if (cpid1 < 0) 181 tst_brkm(TBROK | TERRNO, cleanup, 182 "cpid1 clone failed"); 183 184 cpid2 = ltp_clone_quick(CLONE_NEWUSER | SIGCHLD, 185 (void *)child_fn2, NULL); 186 if (cpid2 < 0) 187 tst_brkm(TBROK | TERRNO, cleanup, 188 "cpid2 clone failed"); 189 190 if (access("/proc/self/setgroups", F_OK) == 0) { 191 sprintf(path, "/proc/%d/setgroups", cpid1); 192 fd = SAFE_OPEN(cleanup, path, O_WRONLY, 0644); 193 SAFE_WRITE(cleanup, 1, fd, "deny", 4); 194 SAFE_CLOSE(cleanup, fd); 195 /* If the setgroups file has the value "deny", 196 * then the setgroups(2) system call can't 197 * subsequently be reenabled (by writing "allow" to 198 * the file) in this user namespace. (Attempts to 199 * do so will fail with the error EPERM.) 200 */ 201 202 /* test that setgroups can't be re-enabled */ 203 fd = SAFE_OPEN(cleanup, path, O_WRONLY, 0644); 204 ret = write(fd, "allow", 5); 205 206 if (ret != -1) { 207 tst_brkm(TBROK | TERRNO, cleanup, 208 "write action should fail"); 209 } else if (errno != EPERM) { 210 tst_brkm(TBROK | TERRNO, cleanup, 211 "unexpected error: \n"); 212 } 213 SAFE_CLOSE(cleanup, fd); 214 tst_resm(TPASS, "setgroups can't be re-enabled"); 215 216 sprintf(path, "/proc/%d/setgroups", cpid2); 217 fd = SAFE_OPEN(cleanup, path, O_WRONLY, 0644); 218 SAFE_WRITE(cleanup, 1, fd, "deny", 4); 219 SAFE_CLOSE(cleanup, fd); 220 } 221 222 updatemap(cpid1, UID_MAP, CHILD1UID, parentuid, cleanup); 223 updatemap(cpid2, UID_MAP, CHILD2UID, parentuid, cleanup); 224 225 updatemap(cpid1, GID_MAP, CHILD1GID, parentgid, cleanup); 226 updatemap(cpid2, GID_MAP, CHILD2GID, parentgid, cleanup); 227 228 TST_SAFE_CHECKPOINT_WAKE_AND_WAIT(cleanup, 1); 229 230 tst_record_childstatus(cleanup, cpid1); 231 tst_record_childstatus(cleanup, cpid2); 232 } 233 cleanup(); 234 tst_exit(); 235} 236