1 /*
2  * Copyright (c) International Business Machines  Corp., 2001
3  *
4  * This program is free software;  you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12  * the GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program;  if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  *
18  * Ported by John George
19  */
20 
21 /*
22  * Test setregid() when executed by a non-root user.
23  */
24 
25 #include <errno.h>
26 #include <grp.h>
27 #include <stdlib.h>
28 #include <pwd.h>
29 #include <string.h>
30 #include <sys/wait.h>
31 
32 #include "test.h"
33 #include "compat_16.h"
34 
35 TCID_DEFINE(setregid03);
36 static int fail = -1;
37 static int pass;
38 static gid_t neg_one = -1;
39 
40 /* flag to tell parent if child passed or failed. */
41 static int flag;
42 
43 struct group users, sys, root, bin;
44 struct passwd nobody;
45 /*
46  * The following structure contains all test data.  Each structure in the array
47  * is used for a separate test.  The tests are executed in the for loop below.
48  */
49 
50 struct test_data_t {
51 	gid_t *real_gid;
52 	gid_t *eff_gid;
53 	int *exp_ret;
54 	struct group *exp_real_usr;
55 	struct group *exp_eff_usr;
56 	char *test_msg;
57 } test_data[] = {
58 	{
59 	&sys.gr_gid, &bin.gr_gid, &pass, &sys, &bin,
60 		    "After setregid(sys, bin),"}, {
61 	&neg_one, &sys.gr_gid, &pass, &sys, &sys, "After setregid(-1, sys)"},
62 	{
63 	&neg_one, &bin.gr_gid, &pass, &sys, &bin, "After setregid(-1, bin),"},
64 	{
65 	&bin.gr_gid, &neg_one, &pass, &bin, &bin, "After setregid(bin, -1),"},
66 	{
67 	&neg_one, &neg_one, &pass, &bin, &bin, "After setregid(-1, -1),"}, {
68 	&neg_one, &bin.gr_gid, &pass, &bin, &bin, "After setregid(-1, bin),"},
69 	{
70 	&bin.gr_gid, &neg_one, &pass, &bin, &bin, "After setregid(bin, -1),"},
71 	{
72 	&bin.gr_gid, &bin.gr_gid, &pass, &bin, &bin,
73 		    "After setregid(bin, bin),"}, {
74 	&sys.gr_gid, &neg_one, &fail, &bin, &bin, "After setregid(sys, -1)"},
75 	{
76 	&neg_one, &sys.gr_gid, &fail, &bin, &bin, "After setregid(-1, sys)"},
77 	{
78 	&sys.gr_gid, &sys.gr_gid, &fail, &bin, &bin,
79 		    "After setregid(sys, sys)"},};
80 
81 int TST_TOTAL = sizeof(test_data) / sizeof(test_data[0]);
82 
83 static void setup(void);
84 static void gid_verify(struct group *ru, struct group *eu, char *when);
85 
main(int ac,char ** av)86 int main(int ac, char **av)
87 {
88 	int lc;
89 
90 	tst_parse_opts(ac, av, NULL, NULL);
91 
92 	setup();
93 
94 	for (lc = 0; TEST_LOOPING(lc); lc++) {
95 		pid_t pid;
96 		int status, i;
97 
98 		pass = 0;
99 		flag = 0;
100 
101 		tst_count = 0;
102 
103 		/* set the appropriate ownership values */
104 		if (SETREGID(NULL, sys.gr_gid, bin.gr_gid) == -1)
105 			tst_brkm(TBROK, NULL, "Initial setregid failed");
106 
107 		if (seteuid(nobody.pw_uid) == -1)
108 			tst_brkm(TBROK, NULL, "Initial seteuid failed");
109 
110 		if ((pid = FORK_OR_VFORK()) == -1) {
111 			tst_brkm(TBROK, NULL, "fork failed");
112 		} else if (pid == 0) {	/* child */
113 			for (i = 0; i < TST_TOTAL; i++) {
114 				gid_t test_ret;
115 				/* Set the real or effective group id */
116 				TEST(SETREGID(NULL, *test_data[i].real_gid,
117 					      *test_data[i].eff_gid));
118 				test_ret = TEST_RETURN;
119 
120 				if (test_ret == *test_data[i].exp_ret) {
121 					if (test_ret == neg_one) {
122 						if (TEST_ERRNO != EPERM) {
123 							tst_resm(TFAIL,
124 								 "setregid(%d, %d) "
125 								 "did not set errno "
126 								 "value as expected.",
127 								 *test_data
128 								 [i].real_gid,
129 								 *test_data
130 								 [i].eff_gid);
131 							fail = -1;
132 							continue;
133 						} else {
134 							tst_resm(TPASS,
135 								 "setregid(%d, %d) "
136 								 "failed as expected.",
137 								 *test_data
138 								 [i].real_gid,
139 								 *test_data
140 								 [i].eff_gid);
141 						}
142 					} else {
143 						tst_resm(TPASS,
144 							 "setregid(%d, %d) "
145 							 "succeeded as expected.",
146 							 *test_data[i].real_gid,
147 							 *test_data[i].eff_gid);
148 					}
149 				} else {
150 					tst_resm(TFAIL, "setregid(%d, %d) "
151 						 "did not return as expected.",
152 						 *test_data[i].real_gid,
153 						 *test_data[i].eff_gid);
154 					flag = -1;
155 				}
156 				if (test_ret == -1) {
157 				}
158 
159 				gid_verify(test_data[i].exp_real_usr,
160 					   test_data[i].exp_eff_usr,
161 					   test_data[i].test_msg);
162 			}
163 			exit(flag);
164 		} else {	/* parent */
165 			waitpid(pid, &status, 0);
166 			if (WEXITSTATUS(status) != 0) {
167 				tst_resm(TFAIL, "test failed within "
168 					 "child process.");
169 			}
170 		}
171 	}
172 
173 	tst_exit();
174 }
175 
setup(void)176 static void setup(void)
177 {
178 	struct group *junk;
179 
180 	tst_require_root();
181 
182 	tst_sig(FORK, DEF_HANDLER, NULL);
183 
184 	if (getpwnam("nobody") == NULL)
185 		tst_brkm(TBROK, NULL, "nobody must be a valid user.");
186 	nobody = *(getpwnam("nobody"));
187 
188 #define GET_GID(group) do { \
189 	junk = getgrnam(#group); \
190 	if (junk == NULL) { \
191 		tst_brkm(TBROK, NULL, "%s must be a valid group", #group); \
192 	} \
193 	GID16_CHECK(junk->gr_gid, setregid, NULL); \
194 	group = *(junk); \
195 } while (0)
196 
197 	GET_GID(users);
198 	GET_GID(sys);
199 	GET_GID(bin);
200 
201 	TEST_PAUSE;
202 }
203 
gid_verify(struct group * rg,struct group * eg,char * when)204 static void gid_verify(struct group *rg, struct group *eg, char *when)
205 {
206 	if ((getgid() != rg->gr_gid) || (getegid() != eg->gr_gid)) {
207 		tst_resm(TFAIL, "ERROR: %s real gid = %d; effective gid = %d",
208 			 when, getgid(), getegid());
209 		tst_resm(TINFO, "Expected: real gid = %d; effective gid = %d",
210 			 rg->gr_gid, eg->gr_gid);
211 		flag = -1;
212 	} else {
213 		tst_resm(TPASS, "real or effective gid was modified as expected");
214 	}
215 }
216