Home | History | Annotate | Download | only in bltins
      1 /***********************************************************************
      2 *                                                                      *
      3 *               This software is part of the ast package               *
      4 *          Copyright (c) 1982-2009 AT&T Intellectual Property          *
      5 *                      and is licensed under the                       *
      6 *                  Common Public License, Version 1.0                  *
      7 *                    by AT&T Intellectual Property                     *
      8 *                                                                      *
      9 *                A copy of the License is available at                 *
     10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
     11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
     12 *                                                                      *
     13 *              Information and Software Systems Research               *
     14 *                            AT&T Research                             *
     15 *                           Florham Park NJ                            *
     16 *                                                                      *
     17 *                  David Korn <dgk (at) 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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    262 int b___regress__(int argc, char** argv, void *extra)
    263 {
    264 	register Shell_t*	shp = ((Shbltin_t*)extra)->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