1 /*
2  * Copyright (c) International Business Machines  Corp., 2001
3  *  Ported by Wayne Boyer
4  * Copyright (c) Cyril Hrubis <chrubis@suse.cz> 2013
5  *
6  * This program is free software;  you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
14  * the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program;  if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /*
22  * Test Description:
23  *  Verify that, getgroups() system call gets the supplementary group IDs
24  *  of the calling process.
25  *
26  * Expected Result:
27  *  The call succeeds in getting all the supplementary group IDs of the
28  *  calling process. The effective group ID may or may not be returned.
29  */
30 
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <grp.h>
38 #include <sys/stat.h>
39 #include <sys/param.h>
40 #include <pwd.h>
41 
42 #include "test.h"
43 #include "compat_16.h"
44 
45 #define TESTUSER "root"
46 
47 TCID_DEFINE(getgroups03);
48 int TST_TOTAL = 1;
49 
50 static int ngroups;
51 static GID_T groups_list[NGROUPS];
52 static GID_T groups[NGROUPS];
53 
54 static void verify_groups(int ret_ngroups);
55 static void setup(void);
56 static void cleanup(void);
57 
main(int ac,char ** av)58 int main(int ac, char **av)
59 {
60 	int lc;
61 	int gidsetsize = NGROUPS;
62 
63 	tst_parse_opts(ac, av, NULL, NULL);
64 
65 	setup();
66 
67 	for (lc = 0; TEST_LOOPING(lc); lc++) {
68 
69 		tst_count = 0;
70 
71 		TEST(GETGROUPS(cleanup, gidsetsize, groups_list));
72 
73 		if (TEST_RETURN == -1) {
74 			tst_resm(TFAIL | TTERRNO, "getgroups failed");
75 			continue;
76 		}
77 
78 		verify_groups(TEST_RETURN);
79 	}
80 
81 	cleanup();
82 	tst_exit();
83 }
84 
85 /*
86  * readgroups(GID_T *)  - Read supplimentary group ids of "root" user
87  * Scans the /etc/group file to get IDs of all the groups to which TESTUSER
88  * belongs and puts them into the array passed.
89  * Returns the no of gids read.
90  */
readgroups(GID_T groups[NGROUPS])91 static int readgroups(GID_T groups[NGROUPS])
92 {
93 	struct group *grp;
94 	int ngrps = 0;
95 	int i;
96 	int found;
97 	GID_T g;
98 
99 	setgrent();
100 
101 	while ((grp = getgrent()) != 0) {
102 		for (i = 0; grp->gr_mem[i]; i++) {
103 			if (strcmp(grp->gr_mem[i], TESTUSER) == 0) {
104 				groups[ngrps++] = grp->gr_gid;
105 			}
106 		}
107 	}
108 
109 	/* The getgroups specification says:
110 	   It is unspecified whether the effective group ID of the
111 	   calling process is included in the returned list.  (Thus,
112 	   an application should also call getegid(2) and add or
113 	   remove the resulting value.).  So, add the value here if
114 	   it's not in.  */
115 
116 	found = 0;
117 	g = getegid();
118 
119 	for (i = 0; i < ngrps; i++) {
120 		if (groups[i] == g)
121 			found = 1;
122 	}
123 	if (found == 0)
124 		groups[ngrps++] = g;
125 
126 	endgrent();
127 	return ngrps;
128 }
129 
setup(void)130 static void setup(void)
131 {
132 	tst_require_root();
133 
134 	tst_sig(NOFORK, DEF_HANDLER, cleanup);
135 
136 	TEST_PAUSE;
137 
138 	/*
139 	 * Get the IDs of all the groups of "root"
140 	 * from /etc/group file
141 	 */
142 	ngroups = readgroups(groups);
143 
144 	/* Setgroups is called by the login(1) process
145 	 * if the testcase is executed via an ssh session this
146 	 * testcase will fail. So execute setgroups() before executing
147 	 * getgroups()
148 	 */
149 	if (SETGROUPS(cleanup, ngroups, groups) == -1)
150 		tst_brkm(TBROK | TERRNO, cleanup, "setgroups failed");
151 }
152 
153 /*
154  * verify_groups(int)  - Verify supplimentary group id values.
155  *   This function verifies the gid values returned by getgroups() with
156  *   the read values from /etc/group file.
157  *  This function returns flag value which indicates success or failure
158  *  of verification.
159  */
verify_groups(int ret_ngroups)160 static void verify_groups(int ret_ngroups)
161 {
162 	int i, j;
163 	GID_T egid;
164 	int egid_flag = 1;
165 	int fflag = 1;
166 
167 	/*
168 	 * Loop through the array to verify the gids
169 	 * returned by getgroups().
170 	 * First, compare each element of the array
171 	 * returned by getgroups() with that read from
172 	 * group file.
173 	 */
174 	for (i = 0; i < ret_ngroups; i++) {
175 		for (j = 0; j < ngroups; j++) {
176 			if (groups_list[i] != groups[j]) {
177 				/* If loop ends and gids are not matching */
178 				if (j == ngroups - 1) {
179 					tst_resm(TFAIL, "getgroups returned "
180 						 "incorrect gid %d",
181 						 groups_list[i]);
182 					fflag = 0;
183 				} else {
184 					continue;
185 				}
186 			} else {
187 				break;
188 			}
189 		}
190 	}
191 
192 	/* Now do the reverse comparison */
193 	egid = getegid();
194 	for (i = 0; i < ngroups; i++) {
195 		for (j = 0; j < ret_ngroups; j++) {
196 			if (groups[i] != groups_list[j]) {
197 				/*
198 				 * If the loop ends & gids are not matching
199 				 * if gid is not egid, exit with error
200 				 * else egid is returned by getgroups()
201 				 */
202 				if (j == (ret_ngroups - 1)) {
203 					if (groups[i] != egid) {
204 						tst_resm(TFAIL, "getgroups "
205 							 "didn't return %d one "
206 							 "of the gids of %s",
207 							 groups[i], TESTUSER);
208 						fflag = 0;
209 					} else {
210 						/*
211 						 * egid is not present in
212 						 * group_list.
213 						 * Reset the egid flag
214 						 */
215 						egid_flag = 0;
216 					}
217 				}
218 			} else {
219 				break;
220 			}
221 		}
222 	}
223 
224 	/*
225 	 * getgroups() should return the no. of gids for TESTUSER with
226 	 * or without egid taken into account.
227 	 * Decrement ngroups, if egid is not returned by getgroups()
228 	 * Now, if ngroups matches ret_val, as above comparisons of the array
229 	 * are successful, this implies that the array contents match.
230 	 */
231 	if (egid_flag == 0)
232 		ngroups--;
233 	if (ngroups != ret_ngroups) {
234 		tst_resm(TFAIL,
235 			 "getgroups(2) returned incorrect no. of gids %d "
236 			 "(expected %d)", ret_ngroups, ngroups);
237 		fflag = 0;
238 	}
239 
240 	if (fflag)
241 		tst_resm(TPASS, "getgroups functionality correct");
242 }
243 
cleanup(void)244 static void cleanup(void)
245 {
246 }
247