1 /* Test46.c 2 * 3 * Test getgroups(...) and setgroups system calls 4 * 5 * Please note that getgroups is POSIX defined, but setgroups is not. Errors 6 * related to setgroups are thus not POSIX conformance issues. 7 */ 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <unistd.h> 11 #include <errno.h> 12 #include <string.h> 13 #include <limits.h> 14 #include <dirent.h> 15 #include <sys/types.h> 16 #include <sys/wait.h> 17 #include <sys/stat.h> 18 #include <fcntl.h> 19 20 void api_test(void); 21 void e(int error_no); 22 void group_test(void); 23 void limit_test(void); 24 void group_test_1(void); 25 void group_test_2(void); 26 void group_test_3(void); 27 void group_test_4(void); 28 void group_test_5(void); 29 int dotest(void (*testfunc)(void)); 30 31 int max_error = 5; 32 #include "common.h" 33 34 #define IMAGINARY_GID 100 35 #define IMAGINARY_GID_STR "100" 36 #define IMAGINARY_UID 101 37 #define IMAGINARY_UID_STR "101" 38 #define SET_CREDENTIALS do { \ 39 setgid((IMAGINARY_GID) + 1 ); \ 40 setuid(IMAGINARY_UID); \ 41 } while(0) 42 43 int subtest = -1, errorct = 0; 44 45 int main(int argc, char *argv[]) 46 { 47 int superuser; 48 start(46); 49 50 superuser = (geteuid() == 0); 51 52 if(!superuser) { 53 if(!(setuid(0) || seteuid(0))) { 54 printf("Test 46 has to be run as root; test aborted\n"); 55 exit(1); 56 } 57 } 58 59 limit_test(); /* Perform some tests on POSIX limits */ 60 api_test(); /* Perform some very basic API tests */ 61 group_test(); /* Perform some tests that mimic actual use */ 62 63 quit(); 64 65 return(-1); /* Unreachable */ 66 } 67 68 void limit_test() { 69 /* According to POSIX 2008 a process can have up to NGROUPS_MAX simultaneous 70 * supplementary group IDs. The minimum acceptable value is _POSIX_NGROUPS_MAX. 71 * In turn, _POSIX_NGROUPS_MAX is defined as 8. */ 72 73 subtest = 1; 74 if (_POSIX_NGROUPS_MAX < 8) e(1); 75 if (NGROUPS_MAX < _POSIX_NGROUPS_MAX) e(2); 76 } 77 78 void api_test() { 79 /* int getgroups( int gidsetsize, gid_t grouplist[]); 80 * int setgroups( int size_t size, const gid_t grouplist[]); 81 */ 82 /* The getgroups() function shall fill in the array grouplist with the current 83 * supplementary group IDs of the calling process. It is implementation- 84 * defined whether getgroups() also returns the effective group ID in the 85 * grouplist array. 86 * The gidsetsize argument specifies the number of elements in the array 87 * grouplist. The actual number of group IDs stored in the array shall be 88 * returned. The values of array entries with indices greater than or equal to 89 * the value returned are undefined. 90 * If gidsetsize is 0, getgroups shall return the number of group IDs that it 91 * would otherwise return without modifying the array pointed to by grouplist. 92 * 93 * setgroups() sets the supplementary group IDs for the calling process. The 94 * size argument specifies the number of supplementary group IDs in the buffer 95 * pointed to by grouplist. setgroups() is a privileged operation. 96 */ 97 98 /* Minix does not return the effective group ID with the supplementary groups. 99 * Use getegid() to get that value. In order to call setgroups, a process 100 * must have super user privileges. 101 */ 102 103 int i; 104 gid_t *grouplist, *grouplist2; 105 long ngroups_max; 106 107 subtest = 2; 108 109 /* Ask the system how many groups we're allowed to set */ 110 ngroups_max = sysconf(_SC_NGROUPS_MAX); 111 grouplist = malloc(ngroups_max *sizeof(gid_t)); 112 grouplist2 = malloc(ngroups_max *sizeof(gid_t)); 113 114 /* Let's invent some imaginary groups */ 115 #define START_GID 20001 116 for (i = 0; i < ngroups_max; i++) 117 grouplist[i] = i + START_GID; 118 119 /* Normal usage */ 120 if (setgroups(ngroups_max, grouplist) != 0) e(1); 121 122 /* Try one less than max supported groups */ 123 if (setgroups(ngroups_max - 1, grouplist) != 0) e(2); 124 125 /* Try just one group */ 126 if (setgroups(1, grouplist) != 0) e(3); 127 128 /* Unset all supplementary groups */ 129 if (setgroups(0, grouplist) != 0) e(4); 130 131 /* Should not be allowed to use a negative set size */ 132 if (setgroups(-1, grouplist) == 0) e(5); 133 else if(errno != EINVAL) e(6); /* error must be EINVAL */ 134 135 /* Should not be allowed to set more groups than supported by the system */ 136 if (setgroups(ngroups_max + 1, grouplist) == 0) e(7); 137 else if(errno != EINVAL) e(8); /* error must be EINVAL */ 138 139 /* Should not be allowed to provide an invalid grouplist address */ 140 if (setgroups(ngroups_max, NULL) == 0) e(9); 141 else if(errno != EFAULT) e(10); /* error must be EFAULT */ 142 143 /* The last time we called setgroups with proper parameters, we effectively 144 * cleared the list. Verify that with getgroups(). */ 145 if (getgroups(ngroups_max, grouplist2) != 0) e(11); 146 147 /* Repopulate grouplist with values and read them back */ 148 if (setgroups(ngroups_max, grouplist) != 0) e(12); 149 if (getgroups(0, grouplist2) != ngroups_max) e(13); 150 if (getgroups(ngroups_max, grouplist2) != ngroups_max) e(14); 151 for (i = 0; i < ngroups_max; i++) { 152 if(grouplist[i] != grouplist2[i]) { 153 e(15); 154 break; /* One error message should be enough here */ 155 } 156 } 157 158 /* Should not be able to read less groups than are actually stored. */ 159 if (getgroups(ngroups_max - 1, grouplist2) != -1) e(16); 160 161 /* Repopulate grouplist with only half the groups and read them back */ 162 memset(grouplist2, 0, ngroups_max * sizeof(gid_t)); /* Clear array */ 163 #define HALF_LIST_SIZE ngroups_max / 2 164 if (setgroups(HALF_LIST_SIZE, grouplist) != 0) e(17); 165 if (getgroups(0, grouplist2) != HALF_LIST_SIZE) e(18); 166 if (getgroups(HALF_LIST_SIZE, grouplist2) != HALF_LIST_SIZE) e(19); 167 for (i = 0; i < HALF_LIST_SIZE; i++) { 168 if(grouplist[i] != grouplist2[i]) { 169 e(20); 170 break; /* Also here one message ought to be enough */ 171 } 172 } 173 174 /* Try to read more groups than we have set */ 175 memset(grouplist2, 0, ngroups_max * sizeof(gid_t)); /* Clear array */ 176 if (getgroups(ngroups_max, grouplist2) != HALF_LIST_SIZE) e(21); 177 for (i = 0; i < HALF_LIST_SIZE; i++) { 178 /* Anything above indices 'HALF_LIST_SIZE' is undefined */ 179 if(grouplist[i] != grouplist2[i]) { 180 e(22); 181 break; 182 } 183 } 184 185 /* Try to set too high a group ID */ 186 grouplist2[0] = GID_MAX + 1; /* Out of range */ 187 if (setgroups(1, grouplist2) == 0) e(23); 188 if (errno != EINVAL) e(24); 189 190 free(grouplist); 191 free(grouplist2); 192 } 193 194 void group_test() { 195 /* To test supplemental group support we're going to create a temporary 196 * directory that can only be accessed (x bit) by members of our imaginary 197 * group, read from (r bit) and written to (w bit). 198 * Then we're going to create a file in that directory that's only readable and 199 * writable by the owner, also readable, writable, and both (in that order) by 200 * the imaginary group, and readable, writable, and both by everyone else (2). 201 */ 202 203 int i, round; 204 gid_t *grouplist; 205 long ngroups_max; 206 #define ROUNDS 8 207 208 subtest = 3; 209 210 ngroups_max = sysconf(_SC_NGROUPS_MAX); 211 grouplist = malloc(ngroups_max *sizeof(gid_t)); 212 213 /* Let's invent imaginary groups and user id */ 214 grouplist = malloc(ngroups_max * sizeof(gid_t)); 215 216 /* Now loop a few tests while using different group set sizes */ 217 for(round = 0; round < ROUNDS; round++) { 218 grouplist[round] = IMAGINARY_GID; 219 for(i = 0; i < ngroups_max; i++) { 220 if(i == round) continue; 221 grouplist[i] = IMAGINARY_GID + i + ngroups_max; 222 } 223 setgroups(round+1, grouplist); 224 225 system("rm -rf DIR_046 > /dev/null 2>&1"); 226 system("mkdir DIR_046"); 227 system("chmod u=rwx,g=,o= DIR_046"); /* Only access for superuser */ 228 system("chgrp "IMAGINARY_GID_STR" DIR_046"); /* Make imaginary group 229 * owner */ 230 231 /* Test group access on directories */ 232 if(dotest(group_test_1) != 0) e(1); 233 system("chmod g+r DIR_046"); /* Allow group read access */ 234 if(dotest(group_test_1) == 0) e(2); 235 236 system("chmod g= DIR_046"); 237 if(dotest(group_test_2) != 0) e(3); 238 system("chmod g+x DIR_046"); /* Allow 'search' (i.e., inode data) 239 * access */ 240 if(dotest(group_test_2) == 0) e(4); 241 242 if(dotest(group_test_3) != 0) e(5); 243 system("chmod g+w DIR_046"); /* Allow group write access */ 244 if(dotest(group_test_3) == 0) e(6); 245 246 system("chmod g-wx DIR_046"); /* Remove write and 'search' permission */ 247 if(dotest(group_test_4) != 0) e(7); 248 system("chmod g+w DIR_046"); /* Add write permission */ 249 if(dotest(group_test_4) != 0) e(8); 250 system("chmod g+x DIR_046"); /* Add 'search' permission */ 251 if(dotest(group_test_4) == 0) e(9); 252 253 /* Subdirectories */ 254 system("mkdir -p DIR_046/sub"); 255 system("chmod u=rwx,g=,o= DIR_046"); 256 system("chmod u=rwx,g=,o= DIR_046/sub"); 257 system("chgrp "IMAGINARY_GID_STR" DIR_046/sub"); 258 259 if(dotest(group_test_1) != 0) e(10); 260 if(dotest(group_test_5) != 0) e(11); 261 262 system("chmod g+r DIR_046"); 263 if(dotest(group_test_1) == 0) e(12); 264 if(dotest(group_test_5) != 0) e(13); 265 266 system("chmod g= DIR_046"); 267 if(dotest(group_test_5) != 0) e(14); 268 system("chmod g+r DIR_046/sub"); 269 if(dotest(group_test_5) != 0) e(15); /* We need search permission for 270 * sub directory DIR_046 to be 271 * able to read the contents of 272 * DIR_046/sub */ 273 system("chmod g+x DIR_046"); 274 if(dotest(group_test_1) != 0) e(16); 275 if(dotest(group_test_5) == 0) e(17); 276 system("chmod g+r DIR_046"); 277 if(dotest(group_test_5) == 0) e(18); 278 } 279 system("rm -rf DIR_046"); 280 free(grouplist); 281 } 282 283 int dotest( void (*func)(void) ) { 284 int test_result; 285 286 if(fork() == 0) { 287 (*func)(); 288 exit(1); /* not supposed to be reached */ 289 } 290 else wait(&test_result); 291 292 return(test_result); 293 } 294 295 void group_test_1() { 296 /* Test x bit for group access. Exit value is 1 when we were able to read from 297 * the directory and 0 otherwise. */ 298 DIR *dirp = NULL; 299 300 SET_CREDENTIALS; 301 302 dirp = opendir("DIR_046"); 303 exit(dirp != NULL); /* If not NULL, we were able to access it */ 304 } 305 306 void group_test_2() { 307 /* Test x bit for group access. Exit value is 1 when we were able to access 308 * inode data of the directory and 0 otherwise. */ 309 struct stat buf; 310 int res; 311 312 SET_CREDENTIALS; 313 314 res = stat("DIR_046/.", &buf); 315 exit(res == 0); 316 } 317 318 void group_test_3() { 319 /* Test wx bits for group access. Exit value is 1 when we were able to write to 320 * the directory and 0 otherwise. */ 321 int fd; 322 323 SET_CREDENTIALS; 324 325 fd = open("DIR_046/writetest", O_WRONLY|O_CREAT); 326 327 exit(fd != -1); 328 } 329 330 void group_test_4() { 331 /* Test w bit for group access. Exit value is 1 when we were able to rename a 332 * the directory and 0 otherwise. */ 333 int res; 334 335 SET_CREDENTIALS; 336 337 res = rename("DIR_046/writetest", "DIR_046/renametest"); 338 339 exit(res == 0); 340 } 341 342 void group_test_5() { 343 /* Test x bit for group access. Exit value is 1 when we were able to read from 344 * the directory and 0 otherwise. */ 345 DIR *dirp = NULL; 346 347 SET_CREDENTIALS; 348 349 dirp = opendir("DIR_046/sub"); 350 exit(dirp != NULL); /* If not NULL, we were able to access it */ 351 } 352