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