1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1982-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                  David Korn <dgk@research.att.com>                   *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  * regression test intercept control
23  * enable with SHOPT_REGRESS==1 in Makefile
24  * not for production use
25  * see --man for details
26  * all string constants inline here instead of in data/...
27  *
28  *   David Korn
29  *   at&t research
30  */
31 
32 #include	"defs.h"
33 
34 #if SHOPT_REGRESS
35 
36 #include	<error.h>
37 #include	<ls.h>
38 #include	"io.h"
39 #include	"builtins.h"
40 #include	<tmx.h>
41 
42 #define REGRESS_HEADER	"ksh:REGRESS:"
43 
44 #define TRACE(r,i,f)		sh_regress(REGRESS_##r, i, sfprints f, __LINE__, __FILE__)
45 
46 static const char	usage[] =
47 "[-1p0?\n@(#)$Id: __regress__ (AT&T Research) 2009-03-29 $\n]"
48 USAGE_LICENSE
49 "[+NAME?__regress__ - shell regression test intercept control]"
50 "[+DESCRIPTION?\b__regress__\b controls the regression test intercepts "
51     "for shells compiled with SHOPT_REGRESS==1. Shells compiled this way are "
52     "for testing only. In addition to \b__regress__\b and the \b--regress\b "
53     "command line option, these shells may contain system library function "
54     "intercepts that behave different from the native counterparts.]"
55 "[+?Each option controls a different test and possibly a different set "
56     "of intercepts. The options are interpreted \bdd\b(1) style -- '-' or "
57     "'--' prefix not required. This simplifies the specification of the "
58     "command line \b--regress\b=\avalue\a option, where \avalue\a is passed "
59     "as an option to the \b__regress__\b builtin. Typically regression test "
60     "intercepts are enabled with one or more command line \b--regress\b "
61     "options, with optional specific calls to \b__regress__\b in test "
62     "scripts to enable/disable intercepts as the test progresses.]"
63 "[+?Each enabled intercept may result in trace lines of the form \b" REGRESS_HEADER
64     "\aoption\a:\aintercept\a:\ainfo\a on the standard error, where "
65     "\aoption\a is one of the options below, \aintercept\a is the name of "
66     "the specific intercept for \aoption\a, and \ainfo\a is \aoption\a "
67     "specific information. Unless noted otherwise, one regression test trace "
68     "line is produced each time an enabled intercept is called.]"
69 "[101:egid?The intercept effective gid is set to \aoriginal-egid\a. The "
70     "effective gid of the underlying system process is not affected. The "
71     "trace line info is either \begid==rgid\b or \begid!=rgid\b. The "
72     "intercepts are:]#?[original-egid:=1]"
73     "{"
74         "[+getegid()?The intercept effecive gid is returned. The "
75             "\bsetgid\b() intercept may change this between the real gid and "
76             "\aoriginal-egid\a.]"
77         "[+setgid(gid)?Sets the intercept effective gid to \agid\a. "
78             "Fails if \agid\a is neither the real gid nor "
79             "\aoriginal-egid\a.]"
80     "}"
81 "[102:euid?The intercept effective uid is set to \aoriginal-euid\a. The "
82     "effective uid of the underlying system process is not affected. The "
83     "trace line info is either \beuid==ruid\b or \beuid!=ruid\b. The "
84     "intercepts are:]#?[original-euid:=1]"
85     "{"
86         "[+geteuid()?The intercept effecive uid is returned. The "
87             "\bsetuid\b() intercept may change this between the real uid and "
88             "\aoriginal-euid\a.]"
89         "[+setuid(uid)?Sets the intercept effective uid to \auid\a. "
90             "Fails if \auid\a is neither the real uid nor "
91             "\aoriginal-euid\a.]"
92     "}"
93 "[103:p_suid?Specifies a value for SHOPT_P_SUID. Effective uids greater "
94     "than the non-privileged-uid disable the priveleged mode. The intercepts "
95     "are:]#?[non-privileged-uid:=1]"
96     "{"
97         "[+SHOPT_P_SUID?The SHOPT_P_SUID macro value is overridden by "
98             "\bp_suid\b. A trace line is output for each SHOPT_P_SUID "
99             "access.]"
100     "}"
101 "[104:source?The intercepts are:]"
102     "{"
103         "[+sh_source()?The trace line info is the path of the script "
104             "being sourced. Used to trace shell startup scripts.]"
105     "}"
106 "[105:etc?Map file paths matching \b/etc/\b* to \aetc-dir\a/*. The "
107     "intercepts are:]:[etc-dir:=/etc]"
108     "{"
109         "[+sh_open()?Paths matching \b/etc/\b* are changed to "
110             "\aetc-dir\a/*.]"
111     "}"
112 "[+SEE ALSO?\bksh\b(1), \bregress\b(1), \brt\b(1)]"
113 ;
114 
115 static const char*	regress_options[] =
116 {
117 	"ERROR",
118 	"egid",
119 	"euid",
120 	"p_suid",
121 	"source",
122 	"etc",
123 };
124 
sh_regress_init(Shell_t * shp)125 void sh_regress_init(Shell_t* shp)
126 {
127 	static Regress_t	state;
128 
129 	shp->regress = &state;
130 }
131 
132 /*
133  * regress info trace output
134  */
135 
sh_regress(unsigned int index,const char * intercept,const char * info,unsigned int line,const char * file)136 void sh_regress(unsigned int index, const char* intercept, const char* info, unsigned int line, const char* file)
137 {
138 	char*	name;
139 	char	buf[16];
140 
141 	if (index >= 1 && index <= elementsof(regress_options))
142 		name = (char*)regress_options[index];
143 	else
144 		sfsprintf(name = buf, sizeof(buf), "%u", index);
145 	sfprintf(sfstderr, REGRESS_HEADER "%s:%s:%s\n", name, intercept, fmtesc(info));
146 }
147 
148 /*
149  * egid intercepts
150  */
151 
152 static gid_t	intercept_sgid = 0;
153 static gid_t	intercept_egid = -1;
154 static gid_t	intercept_rgid = -1;
155 
getegid(void)156 gid_t getegid(void)
157 {
158 	if (intercept_rgid == -1)
159 		intercept_rgid = getgid();
160 	if (sh_isregress(REGRESS_egid))
161 	{
162 		TRACE(egid, "getegid", ("%s", intercept_egid == intercept_rgid ? "egid==rgid" : "egid!=rgid"));
163 		return intercept_egid;
164 	}
165 	return intercept_rgid;
166 }
167 
setgid(gid_t gid)168 int setgid(gid_t gid)
169 {
170 	if (intercept_rgid == -1)
171 		intercept_rgid = getgid();
172 	if (sh_isregress(REGRESS_egid))
173 	{
174 		if (gid != intercept_rgid && gid != intercept_sgid)
175 		{
176 			TRACE(egid, "setgid", ("%s", "EPERM"));
177 			errno = EPERM;
178 			return -1;
179 		}
180 		intercept_egid = gid;
181 		TRACE(egid, "setgid", ("%s", intercept_egid == intercept_rgid ? "egid==rgid" : "egid!=rgid"));
182 	}
183 	else if (gid != intercept_rgid)
184 	{
185 		errno = EPERM;
186 		return -1;
187 	}
188 	return 0;
189 }
190 
191 /*
192  * euid intercepts
193  */
194 
195 static uid_t	intercept_suid = 0;
196 static uid_t	intercept_euid = -1;
197 static uid_t	intercept_ruid = -1;
198 
geteuid(void)199 uid_t geteuid(void)
200 {
201 	if (intercept_ruid == -1)
202 		intercept_ruid = getuid();
203 	if (sh_isregress(REGRESS_euid))
204 	{
205 		TRACE(euid, "geteuid", ("%s", intercept_euid == intercept_ruid ? "euid==ruid" : "euid!=ruid"));
206 		return intercept_euid;
207 	}
208 	return intercept_ruid;
209 }
210 
setuid(uid_t uid)211 int setuid(uid_t uid)
212 {
213 	if (intercept_ruid == -1)
214 		intercept_ruid = getuid();
215 	if (sh_isregress(REGRESS_euid))
216 	{
217 		if (uid != intercept_ruid && uid != intercept_suid)
218 		{
219 			TRACE(euid, "setuid", ("%s", "EPERM"));
220 			errno = EPERM;
221 			return -1;
222 		}
223 		intercept_euid = uid;
224 		TRACE(euid, "setuid", ("%s", intercept_euid == intercept_ruid ? "euid==ruid" : "euid!=ruid"));
225 	}
226 	else if (uid != intercept_ruid)
227 	{
228 		errno = EPERM;
229 		return -1;
230 	}
231 	return 0;
232 }
233 
234 /*
235  * p_suid intercept
236  */
237 
238 static uid_t	intercept_p_suid = 0x7fffffff;
239 
sh_regress_p_suid(unsigned int line,const char * file)240 uid_t sh_regress_p_suid(unsigned int line, const char* file)
241 {
242 	REGRESS(p_suid, "SHOPT_P_SUID", ("%d", intercept_p_suid));
243 	return intercept_p_suid;
244 }
245 
246 /*
247  * p_suid intercept
248  */
249 
250 static char*	intercept_etc = 0;
251 
sh_regress_etc(const char * path,unsigned int line,const char * file)252 char* sh_regress_etc(const char* path, unsigned int line, const char* file)
253 {
254 	REGRESS(etc, "sh_open", ("%s => %s%s", path, intercept_etc, path+4));
255 	return intercept_etc;
256 }
257 
258 /*
259  * __regress__ builtin
260  */
261 
b___regress__(int argc,char ** argv,Shbltin_t * context)262 int b___regress__(int argc, char** argv, Shbltin_t *context)
263 {
264 	register Shell_t*	shp = context->shp;
265 	int			n;
266 
267 	for (;;)
268 	{
269 		switch (n = optget(argv, usage))
270 		{
271 		case '?':
272 			errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
273 			break;
274 		case ':':
275 			errormsg(SH_DICT, 2, "%s", opt_info.arg);
276 			break;
277 		case 0:
278 			break;
279 		default:
280 			if (n < -100)
281 			{
282 				n = -(n + 100);
283 				if (opt_info.arg || opt_info.number)
284 					sh_onregress(n);
285 				else
286 					sh_offregress(n);
287 				switch (n)
288 				{
289 				case REGRESS_egid:
290 					if (sh_isregress(n))
291 					{
292 						intercept_egid = intercept_sgid = (gid_t)opt_info.number;
293 						TRACE(egid, argv[0], ("%d", intercept_egid));
294 					}
295 					else
296 						TRACE(egid, argv[0], ("%s", "off"));
297 					break;
298 				case REGRESS_euid:
299 					if (sh_isregress(n))
300 					{
301 						intercept_euid = intercept_suid = (uid_t)opt_info.number;
302 						TRACE(euid, argv[0], ("%d", intercept_euid));
303 					}
304 					else
305 						TRACE(euid, argv[0], ("%s", "off"));
306 					break;
307 				case REGRESS_p_suid:
308 					if (sh_isregress(n))
309 					{
310 						intercept_p_suid = (uid_t)opt_info.number;
311 						TRACE(p_suid, argv[0], ("%d", intercept_p_suid));
312 					}
313 					else
314 						TRACE(p_suid, argv[0], ("%s", "off"));
315 					break;
316 				case REGRESS_source:
317 					TRACE(source, argv[0], ("%s", sh_isregress(n) ? "on" : "off"));
318 					break;
319 				case REGRESS_etc:
320 					if (sh_isregress(n))
321 					{
322 						intercept_etc = opt_info.arg;
323 						TRACE(etc, argv[0], ("%s", intercept_etc));
324 					}
325 					else
326 						TRACE(etc, argv[0], ("%s", "off"));
327 					break;
328 				}
329 			}
330 			continue;
331 		}
332 		break;
333 	}
334 	if (error_info.errors || *(argv + opt_info.index))
335 		errormsg(SH_DICT, ERROR_usage(2), "%s", optusage(NiL));
336 	return 0;
337 }
338 
339 #else
340 
341 NoN(regress)
342 
343 #endif
344