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.
15 #include <sys/types.h>
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));
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); \
42 int subtest
= -1, errorct
= 0;
44 int main(int argc
, char *argv
[])
49 superuser
= (geteuid() == 0);
52 if(!(setuid(0) || seteuid(0))) {
53 printf("Test 46 has to be run as root; test aborted\n");
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 */
64 return(-1); /* Unreachable */
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. */
73 if (_POSIX_NGROUPS_MAX
< 8) e(1);
74 if (NGROUPS_MAX
< _POSIX_NGROUPS_MAX
) e(2);
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
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.
103 gid_t
*grouplist
, *grouplist2
;
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
;
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
]) {
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
]) {
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
]) {
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);
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).
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
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)
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);
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
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");
282 int dotest( void (*func
)(void) ) {
285 if(fork() == 0) (*func
)();
286 else wait(&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. */
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. */
310 res
= stat("DIR_046/.", &buf
);
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. */
321 fd
= open("DIR_046/writetest", O_WRONLY
|O_CREAT
);
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. */
333 res
= rename("DIR_046/writetest", "DIR_046/renametest");
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. */
345 dirp
= opendir("DIR_046/sub");
346 exit(dirp
!= NULL
); /* If not NULL, we were able to access it */