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