xref: /minix3/minix/tests/test46.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1433d6423SLionel Sambuc /* Test46.c
2433d6423SLionel Sambuc  *
3433d6423SLionel Sambuc  * Test getgroups(...) and setgroups system calls
4433d6423SLionel Sambuc  *
5433d6423SLionel Sambuc  * Please note that getgroups is POSIX defined, but setgroups is not. Errors
6433d6423SLionel Sambuc  * related to setgroups are thus not POSIX conformance issues.
7433d6423SLionel Sambuc  */
8433d6423SLionel Sambuc #include <stdio.h>
9433d6423SLionel Sambuc #include <stdlib.h>
10433d6423SLionel Sambuc #include <unistd.h>
11433d6423SLionel Sambuc #include <errno.h>
12433d6423SLionel Sambuc #include <string.h>
13433d6423SLionel Sambuc #include <limits.h>
14433d6423SLionel Sambuc #include <dirent.h>
15433d6423SLionel Sambuc #include <sys/types.h>
16433d6423SLionel Sambuc #include <sys/wait.h>
17433d6423SLionel Sambuc #include <sys/stat.h>
18433d6423SLionel Sambuc #include <fcntl.h>
19433d6423SLionel Sambuc 
20433d6423SLionel Sambuc void api_test(void);
21433d6423SLionel Sambuc void e(int error_no);
22433d6423SLionel Sambuc void group_test(void);
23433d6423SLionel Sambuc void limit_test(void);
24433d6423SLionel Sambuc void group_test_1(void);
25433d6423SLionel Sambuc void group_test_2(void);
26433d6423SLionel Sambuc void group_test_3(void);
27433d6423SLionel Sambuc void group_test_4(void);
28433d6423SLionel Sambuc void group_test_5(void);
29433d6423SLionel Sambuc int dotest(void (*testfunc)(void));
30433d6423SLionel Sambuc 
31433d6423SLionel Sambuc int max_error = 5;
32433d6423SLionel Sambuc #include "common.h"
33433d6423SLionel Sambuc 
34433d6423SLionel Sambuc #define IMAGINARY_GID 100
35433d6423SLionel Sambuc #define IMAGINARY_GID_STR "100"
36433d6423SLionel Sambuc #define IMAGINARY_UID 101
37433d6423SLionel Sambuc #define IMAGINARY_UID_STR "101"
38433d6423SLionel Sambuc #define SET_CREDENTIALS do { \
39433d6423SLionel Sambuc 			  setgid((IMAGINARY_GID) + 1 ); \
40433d6423SLionel Sambuc 			  setuid(IMAGINARY_UID); \
41433d6423SLionel Sambuc 			} while(0)
42433d6423SLionel Sambuc 
43433d6423SLionel Sambuc int subtest = -1, errorct = 0;
44433d6423SLionel Sambuc 
main(int argc,char * argv[])45433d6423SLionel Sambuc int main(int argc, char *argv[])
46433d6423SLionel Sambuc {
47433d6423SLionel Sambuc   start(46);
48433d6423SLionel Sambuc 
49*0a6a1f1dSLionel Sambuc   setuid(geteuid());
50433d6423SLionel Sambuc 
51*0a6a1f1dSLionel Sambuc   if(getuid() != 0) {
52433d6423SLionel Sambuc 	printf("Test 46 has to be run as root; test aborted\n");
53433d6423SLionel Sambuc 	exit(1);
54433d6423SLionel Sambuc   }
55433d6423SLionel Sambuc 
56433d6423SLionel Sambuc   limit_test();	/* Perform some tests on POSIX limits */
57433d6423SLionel Sambuc   api_test();	/* Perform some very basic API tests */
58433d6423SLionel Sambuc   group_test();	/* Perform some tests that mimic actual use */
59433d6423SLionel Sambuc 
60433d6423SLionel Sambuc   quit();
61433d6423SLionel Sambuc 
62433d6423SLionel Sambuc   return(-1);	/* Unreachable */
63433d6423SLionel Sambuc }
64433d6423SLionel Sambuc 
limit_test()65433d6423SLionel Sambuc void limit_test() {
66433d6423SLionel Sambuc /* According to POSIX 2008 a process can have up to NGROUPS_MAX simultaneous
67433d6423SLionel Sambuc  * supplementary group IDs. The minimum acceptable value is _POSIX_NGROUPS_MAX.
68433d6423SLionel Sambuc  * In turn, _POSIX_NGROUPS_MAX is defined as 8. */
69433d6423SLionel Sambuc 
70433d6423SLionel Sambuc   subtest = 1;
71433d6423SLionel Sambuc   if (_POSIX_NGROUPS_MAX < 8) e(1);
72433d6423SLionel Sambuc   if (NGROUPS_MAX < _POSIX_NGROUPS_MAX) e(2);
73433d6423SLionel Sambuc }
74433d6423SLionel Sambuc 
api_test()75433d6423SLionel Sambuc void api_test() {
76433d6423SLionel Sambuc /* int getgroups( int gidsetsize, gid_t grouplist[]);
77433d6423SLionel Sambuc  * int setgroups( int size_t size, const gid_t grouplist[]);
78433d6423SLionel Sambuc  */
79433d6423SLionel Sambuc /* The getgroups() function shall fill in the array grouplist with the current
80433d6423SLionel Sambuc  * supplementary group IDs of the calling process. It is implementation-
81433d6423SLionel Sambuc  * defined whether getgroups() also returns the effective group ID in the
82433d6423SLionel Sambuc  * grouplist array.
83433d6423SLionel Sambuc  * The gidsetsize argument specifies the number of elements in the array
84433d6423SLionel Sambuc  * grouplist. The actual number of group IDs stored in the array shall be
85433d6423SLionel Sambuc  * returned. The values of array entries with indices greater than or equal to
86433d6423SLionel Sambuc  * the value returned are undefined.
87433d6423SLionel Sambuc  * If gidsetsize is 0, getgroups shall return the number of group IDs that it
88433d6423SLionel Sambuc  * would otherwise return without modifying the array pointed to by grouplist.
89433d6423SLionel Sambuc  *
90433d6423SLionel Sambuc  * setgroups() sets the supplementary group IDs for the calling process. The
91433d6423SLionel Sambuc  * size argument specifies the number of supplementary group IDs in the buffer
92433d6423SLionel Sambuc  * pointed to by grouplist. setgroups() is a privileged operation.
93433d6423SLionel Sambuc  */
94433d6423SLionel Sambuc 
95433d6423SLionel Sambuc  /* Minix does not return the effective group ID with the supplementary groups.
96433d6423SLionel Sambuc   * Use getegid() to get that value. In order to call setgroups, a process
97433d6423SLionel Sambuc   * must have super user privileges.
98433d6423SLionel Sambuc   */
99433d6423SLionel Sambuc 
100433d6423SLionel Sambuc   int i;
101433d6423SLionel Sambuc   gid_t *grouplist, *grouplist2;
102433d6423SLionel Sambuc   long ngroups_max;
103433d6423SLionel Sambuc 
104433d6423SLionel Sambuc   subtest = 2;
105433d6423SLionel Sambuc 
106433d6423SLionel Sambuc   /* Ask the system how many groups we're allowed to set */
107433d6423SLionel Sambuc   ngroups_max = sysconf(_SC_NGROUPS_MAX);
108433d6423SLionel Sambuc   grouplist = malloc(ngroups_max *sizeof(gid_t));
109433d6423SLionel Sambuc   grouplist2 = malloc(ngroups_max *sizeof(gid_t));
110433d6423SLionel Sambuc 
111433d6423SLionel Sambuc   /* Let's invent some imaginary groups */
112433d6423SLionel Sambuc #define START_GID 20001
113433d6423SLionel Sambuc   for (i = 0; i < ngroups_max; i++)
114433d6423SLionel Sambuc 	grouplist[i] = i + START_GID;
115433d6423SLionel Sambuc 
116433d6423SLionel Sambuc   /* Normal usage */
117433d6423SLionel Sambuc   if (setgroups(ngroups_max, grouplist) != 0) e(1);
118433d6423SLionel Sambuc 
119433d6423SLionel Sambuc   /* Try one less than max supported groups */
120433d6423SLionel Sambuc   if (setgroups(ngroups_max - 1, grouplist) != 0) e(2);
121433d6423SLionel Sambuc 
122433d6423SLionel Sambuc   /* Try just one group */
123433d6423SLionel Sambuc   if (setgroups(1, grouplist) != 0) e(3);
124433d6423SLionel Sambuc 
125433d6423SLionel Sambuc   /* Unset all supplementary groups */
126433d6423SLionel Sambuc   if (setgroups(0, grouplist) != 0) e(4);
127433d6423SLionel Sambuc 
128433d6423SLionel Sambuc   /* Should not be allowed to use a negative set size */
129433d6423SLionel Sambuc   if (setgroups(-1, grouplist) == 0) e(5);
130433d6423SLionel Sambuc   else if(errno != EINVAL) e(6); /* error must be EINVAL */
131433d6423SLionel Sambuc 
132433d6423SLionel Sambuc   /* Should not be allowed to set more groups than supported by the system */
133433d6423SLionel Sambuc   if (setgroups(ngroups_max + 1, grouplist) == 0) e(7);
134433d6423SLionel Sambuc   else if(errno != EINVAL) e(8); /* error must be EINVAL */
135433d6423SLionel Sambuc 
136433d6423SLionel Sambuc   /* Should not be allowed to provide an invalid grouplist address */
137433d6423SLionel Sambuc   if (setgroups(ngroups_max, NULL) == 0) e(9);
138433d6423SLionel Sambuc   else if(errno != EFAULT) e(10); /* error must be EFAULT */
139433d6423SLionel Sambuc 
140433d6423SLionel Sambuc   /* The last time we called setgroups with proper parameters, we effectively
141433d6423SLionel Sambuc    * cleared the list. Verify that with getgroups(). */
142433d6423SLionel Sambuc   if (getgroups(ngroups_max, grouplist2) != 0) e(11);
143433d6423SLionel Sambuc 
144433d6423SLionel Sambuc   /* Repopulate grouplist with values and read them back */
145433d6423SLionel Sambuc   if (setgroups(ngroups_max, grouplist) != 0) e(12);
146433d6423SLionel Sambuc   if (getgroups(0, grouplist2) != ngroups_max) e(13);
147433d6423SLionel Sambuc   if (getgroups(ngroups_max, grouplist2) != ngroups_max) e(14);
148433d6423SLionel Sambuc   for (i = 0; i < ngroups_max; i++) {
149433d6423SLionel Sambuc   	if(grouplist[i] != grouplist2[i]) {
150433d6423SLionel Sambuc 		e(15);
151433d6423SLionel Sambuc 		break; /* One error message should be enough here */
152433d6423SLionel Sambuc 	}
153433d6423SLionel Sambuc   }
154433d6423SLionel Sambuc 
155433d6423SLionel Sambuc   /* Should not be able to read less groups than are actually stored. */
156433d6423SLionel Sambuc   if (getgroups(ngroups_max - 1, grouplist2) != -1) e(16);
157433d6423SLionel Sambuc 
158433d6423SLionel Sambuc   /* Repopulate grouplist with only half the groups and read them back */
159433d6423SLionel Sambuc   memset(grouplist2, 0, ngroups_max * sizeof(gid_t)); /* Clear array */
160433d6423SLionel Sambuc #define HALF_LIST_SIZE ngroups_max / 2
161433d6423SLionel Sambuc   if (setgroups(HALF_LIST_SIZE, grouplist) != 0) e(17);
162433d6423SLionel Sambuc   if (getgroups(0, grouplist2) != HALF_LIST_SIZE) e(18);
163433d6423SLionel Sambuc   if (getgroups(HALF_LIST_SIZE, grouplist2) != HALF_LIST_SIZE) e(19);
164433d6423SLionel Sambuc   for (i = 0; i < HALF_LIST_SIZE; i++) {
165433d6423SLionel Sambuc   	if(grouplist[i] != grouplist2[i]) {
166433d6423SLionel Sambuc   		e(20);
167433d6423SLionel Sambuc   		break; /* Also here one message ought to be enough */
168433d6423SLionel Sambuc   	}
169433d6423SLionel Sambuc   }
170433d6423SLionel Sambuc 
171433d6423SLionel Sambuc   /* Try to read more groups than we have set */
172433d6423SLionel Sambuc   memset(grouplist2, 0, ngroups_max * sizeof(gid_t)); /* Clear array */
173433d6423SLionel Sambuc   if (getgroups(ngroups_max, grouplist2) != HALF_LIST_SIZE) e(21);
174433d6423SLionel Sambuc   for (i = 0; i < HALF_LIST_SIZE; i++) {
175433d6423SLionel Sambuc 	/* Anything above indices 'HALF_LIST_SIZE' is undefined */
176433d6423SLionel Sambuc 	if(grouplist[i] != grouplist2[i]) {
177433d6423SLionel Sambuc 		e(22);
178433d6423SLionel Sambuc 		break;
179433d6423SLionel Sambuc 	}
180433d6423SLionel Sambuc   }
181433d6423SLionel Sambuc 
182433d6423SLionel Sambuc   /* Try to set too high a group ID */
183433d6423SLionel Sambuc   grouplist2[0] = GID_MAX + 1;	/* Out of range */
184433d6423SLionel Sambuc   if (setgroups(1, grouplist2) == 0) e(23);
185433d6423SLionel Sambuc   if (errno != EINVAL) e(24);
186433d6423SLionel Sambuc 
187433d6423SLionel Sambuc   free(grouplist);
188433d6423SLionel Sambuc   free(grouplist2);
189433d6423SLionel Sambuc }
190433d6423SLionel Sambuc 
group_test()191433d6423SLionel Sambuc void group_test() {
192433d6423SLionel Sambuc /* To test supplemental group support we're going to create a temporary
193433d6423SLionel Sambuc  * directory that can only be accessed (x bit) by members of our imaginary
194433d6423SLionel Sambuc  * group, read from (r bit) and written to (w bit).
195433d6423SLionel Sambuc  * Then we're going to create a file in that directory that's only readable and
196433d6423SLionel Sambuc  * writable by the owner, also readable, writable, and both (in that order) by
197433d6423SLionel Sambuc  * the imaginary group, and readable, writable, and both by everyone else (2).
198433d6423SLionel Sambuc  */
199433d6423SLionel Sambuc 
200433d6423SLionel Sambuc   int i, round;
201433d6423SLionel Sambuc   gid_t *grouplist;
202433d6423SLionel Sambuc   long ngroups_max;
203433d6423SLionel Sambuc #define ROUNDS 8
204433d6423SLionel Sambuc 
205433d6423SLionel Sambuc   subtest = 3;
206433d6423SLionel Sambuc 
207433d6423SLionel Sambuc   ngroups_max = sysconf(_SC_NGROUPS_MAX);
208433d6423SLionel Sambuc   grouplist = malloc(ngroups_max *sizeof(gid_t));
209433d6423SLionel Sambuc 
210433d6423SLionel Sambuc   /* Let's invent imaginary groups and user id */
211433d6423SLionel Sambuc   grouplist = malloc(ngroups_max * sizeof(gid_t));
212433d6423SLionel Sambuc 
213433d6423SLionel Sambuc   /* Now loop a few tests while using different group set sizes */
214433d6423SLionel Sambuc   for(round = 0; round < ROUNDS; round++) {
215433d6423SLionel Sambuc 	grouplist[round] = IMAGINARY_GID;
216433d6423SLionel Sambuc 	for(i = 0; i < ngroups_max; i++) {
217433d6423SLionel Sambuc 		if(i == round) continue;
218433d6423SLionel Sambuc 		grouplist[i] = IMAGINARY_GID + i + ngroups_max;
219433d6423SLionel Sambuc   	}
220433d6423SLionel Sambuc 	setgroups(round+1, grouplist);
221433d6423SLionel Sambuc 
222433d6423SLionel Sambuc 	system("rm -rf DIR_046 > /dev/null 2>&1");
223433d6423SLionel Sambuc 	system("mkdir DIR_046");
224433d6423SLionel Sambuc 	system("chmod u=rwx,g=,o= DIR_046"); /* Only access for superuser */
225433d6423SLionel Sambuc 	system("chgrp "IMAGINARY_GID_STR" DIR_046"); /* Make imaginary group
226433d6423SLionel Sambuc 						      * owner */
227433d6423SLionel Sambuc 
228433d6423SLionel Sambuc 	/* Test group access on directories */
229433d6423SLionel Sambuc 	if(dotest(group_test_1) != 0) e(1);
230433d6423SLionel Sambuc 	system("chmod g+r DIR_046"); /* Allow group read access */
231433d6423SLionel Sambuc 	if(dotest(group_test_1) == 0) e(2);
232433d6423SLionel Sambuc 
233433d6423SLionel Sambuc 	system("chmod g= DIR_046");
234433d6423SLionel Sambuc 	if(dotest(group_test_2) != 0) e(3);
235433d6423SLionel Sambuc 	system("chmod g+x DIR_046"); /* Allow 'search' (i.e., inode data)
236433d6423SLionel Sambuc 				      * access */
237433d6423SLionel Sambuc 	if(dotest(group_test_2) == 0) e(4);
238433d6423SLionel Sambuc 
239433d6423SLionel Sambuc 	if(dotest(group_test_3) != 0) e(5);
240433d6423SLionel Sambuc 	system("chmod g+w DIR_046"); /* Allow group write access */
241433d6423SLionel Sambuc 	if(dotest(group_test_3) == 0) e(6);
242433d6423SLionel Sambuc 
243433d6423SLionel Sambuc 	system("chmod g-wx DIR_046"); /* Remove write and 'search' permission */
244433d6423SLionel Sambuc 	if(dotest(group_test_4) != 0) e(7);
245433d6423SLionel Sambuc 	system("chmod g+w DIR_046"); /* Add write permission */
246433d6423SLionel Sambuc 	if(dotest(group_test_4) != 0) e(8);
247433d6423SLionel Sambuc 	system("chmod g+x DIR_046"); /* Add 'search' permission */
248433d6423SLionel Sambuc 	if(dotest(group_test_4) == 0) e(9);
249433d6423SLionel Sambuc 
250433d6423SLionel Sambuc 	/* Subdirectories */
251433d6423SLionel Sambuc 	system("mkdir -p DIR_046/sub");
252433d6423SLionel Sambuc 	system("chmod u=rwx,g=,o= DIR_046");
253433d6423SLionel Sambuc 	system("chmod u=rwx,g=,o= DIR_046/sub");
254433d6423SLionel Sambuc 	system("chgrp "IMAGINARY_GID_STR" DIR_046/sub");
255433d6423SLionel Sambuc 
256433d6423SLionel Sambuc 	if(dotest(group_test_1) != 0) e(10);
257433d6423SLionel Sambuc 	if(dotest(group_test_5) != 0) e(11);
258433d6423SLionel Sambuc 
259433d6423SLionel Sambuc 	system("chmod g+r DIR_046");
260433d6423SLionel Sambuc 	if(dotest(group_test_1) == 0) e(12);
261433d6423SLionel Sambuc 	if(dotest(group_test_5) != 0) e(13);
262433d6423SLionel Sambuc 
263433d6423SLionel Sambuc 	system("chmod g= DIR_046");
264433d6423SLionel Sambuc 	if(dotest(group_test_5) != 0) e(14);
265433d6423SLionel Sambuc 	system("chmod g+r DIR_046/sub");
266433d6423SLionel Sambuc 	if(dotest(group_test_5) != 0) e(15); /* We need search permission for
267433d6423SLionel Sambuc 					      * sub directory DIR_046 to be
268433d6423SLionel Sambuc 					      * able to read the contents of
269433d6423SLionel Sambuc 					      * DIR_046/sub */
270433d6423SLionel Sambuc 	system("chmod g+x DIR_046");
271433d6423SLionel Sambuc 	if(dotest(group_test_1) != 0) e(16);
272433d6423SLionel Sambuc 	if(dotest(group_test_5) == 0) e(17);
273433d6423SLionel Sambuc 	system("chmod g+r DIR_046");
274433d6423SLionel Sambuc 	if(dotest(group_test_5) == 0) e(18);
275433d6423SLionel Sambuc   }
276433d6423SLionel Sambuc   system("rm -rf DIR_046");
277433d6423SLionel Sambuc   free(grouplist);
278433d6423SLionel Sambuc }
279433d6423SLionel Sambuc 
dotest(void (* func)(void))280433d6423SLionel Sambuc int dotest( void (*func)(void) ) {
281433d6423SLionel Sambuc   int test_result;
282433d6423SLionel Sambuc 
283cbc8a0dfSDavid van Moolenbroek   if(fork() == 0) {
284cbc8a0dfSDavid van Moolenbroek 	(*func)();
285cbc8a0dfSDavid van Moolenbroek 	exit(1); /* not supposed to be reached */
286cbc8a0dfSDavid van Moolenbroek   }
287433d6423SLionel Sambuc   else wait(&test_result);
288433d6423SLionel Sambuc 
289433d6423SLionel Sambuc   return(test_result);
290433d6423SLionel Sambuc }
291433d6423SLionel Sambuc 
group_test_1()292433d6423SLionel Sambuc void group_test_1() {
293433d6423SLionel Sambuc /* Test x bit for group access. Exit value is 1 when we were able to read from
294433d6423SLionel Sambuc  * the directory and 0 otherwise. */
295433d6423SLionel Sambuc   DIR *dirp = NULL;
296433d6423SLionel Sambuc 
297433d6423SLionel Sambuc   SET_CREDENTIALS;
298433d6423SLionel Sambuc 
299433d6423SLionel Sambuc   dirp = opendir("DIR_046");
300433d6423SLionel Sambuc   exit(dirp != NULL); /* If not NULL, we were able to access it */
301433d6423SLionel Sambuc }
302433d6423SLionel Sambuc 
group_test_2()303433d6423SLionel Sambuc void group_test_2() {
304433d6423SLionel Sambuc /* Test x bit for group access. Exit value is 1 when we were able to access
305433d6423SLionel Sambuc  * inode data of the directory and 0 otherwise. */
306433d6423SLionel Sambuc   struct stat buf;
307433d6423SLionel Sambuc   int res;
308433d6423SLionel Sambuc 
309433d6423SLionel Sambuc   SET_CREDENTIALS;
310433d6423SLionel Sambuc 
311433d6423SLionel Sambuc   res = stat("DIR_046/.", &buf);
312433d6423SLionel Sambuc   exit(res == 0);
313433d6423SLionel Sambuc }
314433d6423SLionel Sambuc 
group_test_3()315433d6423SLionel Sambuc void group_test_3() {
316433d6423SLionel Sambuc /* Test wx bits for group access. Exit value is 1 when we were able to write to
317433d6423SLionel Sambuc  * the directory and 0 otherwise. */
318433d6423SLionel Sambuc   int fd;
319433d6423SLionel Sambuc 
320433d6423SLionel Sambuc   SET_CREDENTIALS;
321433d6423SLionel Sambuc 
322433d6423SLionel Sambuc   fd = open("DIR_046/writetest", O_WRONLY|O_CREAT);
323433d6423SLionel Sambuc 
324433d6423SLionel Sambuc   exit(fd != -1);
325433d6423SLionel Sambuc }
326433d6423SLionel Sambuc 
group_test_4()327433d6423SLionel Sambuc void group_test_4() {
328433d6423SLionel Sambuc /* Test w bit for group access. Exit value is 1 when we were able to rename a
329433d6423SLionel Sambuc  * the directory and 0 otherwise. */
330433d6423SLionel Sambuc   int res;
331433d6423SLionel Sambuc 
332433d6423SLionel Sambuc   SET_CREDENTIALS;
333433d6423SLionel Sambuc 
334433d6423SLionel Sambuc   res = rename("DIR_046/writetest", "DIR_046/renametest");
335433d6423SLionel Sambuc 
336433d6423SLionel Sambuc   exit(res == 0);
337433d6423SLionel Sambuc }
338433d6423SLionel Sambuc 
group_test_5()339433d6423SLionel Sambuc void group_test_5() {
340433d6423SLionel Sambuc /* Test x bit for group access. Exit value is 1 when we were able to read from
341433d6423SLionel Sambuc  * the directory and 0 otherwise. */
342433d6423SLionel Sambuc   DIR *dirp = NULL;
343433d6423SLionel Sambuc 
344433d6423SLionel Sambuc   SET_CREDENTIALS;
345433d6423SLionel Sambuc 
346433d6423SLionel Sambuc   dirp = opendir("DIR_046/sub");
347433d6423SLionel Sambuc   exit(dirp != NULL); /* If not NULL, we were able to access it */
348433d6423SLionel Sambuc }
349