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@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