scenario.c revision 75485
168349Sobrien/*- 268349Sobrien * Copyright (c) 2001 Robert N. M. Watson 3226048Sobrien * All rights reserved. 468349Sobrien * 568349Sobrien * Redistribution and use in source and binary forms, with or without 668349Sobrien * modification, are permitted provided that the following conditions 768349Sobrien * are met: 868349Sobrien * 1. Redistributions of source code must retain the above copyright 968349Sobrien * notice, this list of conditions and the following disclaimer. 1068349Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1168349Sobrien * notice, this list of conditions and the following disclaimer in the 1268349Sobrien * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/tools/regression/security/proc_to_proc/scenario.c 75485 2001-04-13 16:25:25Z rwatson $ 27 */ 28 29#include <sys/param.h> 30#include <sys/uio.h> 31#include <sys/ptrace.h> 32#include <sys/time.h> 33#include <sys/resource.h> 34#include <sys/syscall.h> 35#include <sys/wait.h> 36#include <sys/ktrace.h> 37 38#include <assert.h> 39#include <errno.h> 40#include <signal.h> 41#include <stdio.h> 42#include <string.h> 43#include <unistd.h> 44 45/* 46 * Relevant parts of a process credential. 47 */ 48struct cred { 49 uid_t cr_euid, cr_ruid, cr_svuid; 50 int cr_issetugid; 51}; 52 53/* 54 * Description of a scenario. 55 */ 56struct scenario { 57 struct cred *sc_cred1, *sc_cred2; /* credentials of p1 and p2 */ 58 int sc_canptrace_errno; /* desired ptrace failure */ 59 int sc_canktrace_errno; /* desired ktrace failure */ 60 int sc_cansighup_errno; /* desired SIGHUP failure */ 61 int sc_cansigsegv_errno; /* desired SIGSEGV failure */ 62 int sc_cansee_errno; /* desired getprio failure */ 63 int sc_cansched_errno; /* desired setprio failure */ 64 char *sc_name; /* test name */ 65}; 66 67/* 68 * Table of relevant credential combinations. 69 */ 70static struct cred creds[] = { 71/* euid ruid svuid issetugid */ 72/* 0 */ { 0, 0, 0, 0 }, /* privileged */ 73/* 1 */ { 0, 0, 0, 1 }, /* privileged + issetugid */ 74/* 2 */ { 1000, 1000, 1000, 0 }, /* unprivileged1 */ 75/* 3 */ { 1000, 1000, 1000, 1 }, /* unprivileged1 + issetugid */ 76/* 4 */ { 1001, 1001, 1001, 0 }, /* unprivileged2 */ 77/* 5 */ { 1001, 1001, 1001, 1 }, /* unprivileged2 + issetugid */ 78/* 6 */ { 1000, 0, 0, 0 }, /* daemon1 */ 79/* 7 */ { 1000, 0, 0, 1 }, /* daemon1 + issetugid */ 80/* 8 */ { 1001, 0, 0, 0 }, /* daemon2 */ 81/* 9 */ { 1001, 0, 0, 1 }, /* daemon2 + issetugid */ 82/* 10 */{ 0, 1000, 1000, 0 }, /* setuid1 */ 83/* 11 */{ 0, 1000, 1000, 1 }, /* setuid1 + issetugid */ 84/* 12 */{ 0, 1001, 1001, 0 }, /* setuid2 */ 85/* 13 */{ 0, 1001, 1001, 1 }, /* setuid2 + issetugid */ 86}; 87 88/* 89 * Table of scenarios. 90 */ 91static const struct scenario scenarios[] = { 92/* cred1 cred2 ptrace ktrace, sighup sigsegv see sched name */ 93{ &creds[0], &creds[0], 0, 0, 0, 0, 0, 0, "0. priv on priv"}, 94{ &creds[0], &creds[1], 0, 0, 0, 0, 0, 0, "1. priv on priv"}, 95{ &creds[1], &creds[0], 0, 0, 0, 0, 0, 0, "2. priv on priv"}, 96{ &creds[1], &creds[1], 0, 0, 0, 0, 0, 0, "3. priv on priv"}, 97/* privileged on unprivileged */ 98{ &creds[0], &creds[2], 0, 0, 0, 0, 0, 0, "4. priv on unpriv1"}, 99{ &creds[0], &creds[3], 0, 0, 0, 0, 0, 0, "5. priv on unpriv1"}, 100{ &creds[1], &creds[2], 0, 0, 0, 0, 0, 0, "6. priv on unpriv1"}, 101{ &creds[1], &creds[3], 0, 0, 0, 0, 0, 0, "7. priv on unpriv1"}, 102/* unprivileged on privileged */ 103{ &creds[2], &creds[0], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "8. unpriv1 on priv"}, 104{ &creds[2], &creds[1], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "9. unpriv1 on priv"}, 105{ &creds[3], &creds[0], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "10. unpriv1 on priv"}, 106{ &creds[3], &creds[1], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "11. unpriv1 on priv"}, 107/* unprivileged on same unprivileged */ 108{ &creds[2], &creds[2], 0, 0, 0, 0, 0, 0, "12. unpriv1 on unpriv1"}, 109{ &creds[2], &creds[3], EPERM, EPERM, 0, EPERM, 0, 0, "13. unpriv1 on unpriv1"}, 110{ &creds[3], &creds[2], 0, 0, 0, 0, 0, 0, "14. unpriv1 on unpriv1"}, 111{ &creds[3], &creds[3], EPERM, EPERM, 0, EPERM, 0, 0, "15. unpriv1 on unpriv1"}, 112/* unprivileged on different unprivileged */ 113{ &creds[2], &creds[4], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "16. unpriv1 on unpriv2"}, 114{ &creds[2], &creds[5], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "17. unpriv1 on unpriv2"}, 115{ &creds[3], &creds[4], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "18. unpriv1 on unpriv2"}, 116{ &creds[3], &creds[5], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "19. unpriv1 on unpriv2"}, 117/* unprivileged on daemon, same */ 118{ &creds[2], &creds[6], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "20. unpriv1 on daemon1"}, 119{ &creds[2], &creds[7], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "21. unpriv1 on daemon1"}, 120{ &creds[3], &creds[6], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "22. unpriv1 on daemon1"}, 121{ &creds[3], &creds[7], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "23. unpriv1 on daemon1"}, 122/* unprivileged on daemon, different */ 123{ &creds[2], &creds[8], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "24. unpriv1 on daemon2"}, 124{ &creds[2], &creds[9], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "25. unpriv1 on daemon2"}, 125{ &creds[3], &creds[8], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "26. unpriv1 on daemon2"}, 126{ &creds[3], &creds[9], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "27. unpriv1 on daemon2"}, 127/* unprivileged on setuid, same */ 128{ &creds[2], &creds[10], EPERM, EPERM, 0, 0, 0, 0, "28. unpriv1 on setuid1"}, 129{ &creds[2], &creds[11], EPERM, EPERM, 0, EPERM, 0, 0, "29. unpriv1 on setuid1"}, 130{ &creds[3], &creds[10], EPERM, EPERM, 0, 0, 0, 0, "30. unpriv1 on setuid1"}, 131{ &creds[3], &creds[11], EPERM, EPERM, 0, EPERM, 0, 0, "31. unpriv1 on setuid1"}, 132/* unprivileged on setuid, different */ 133{ &creds[2], &creds[12], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "32. unpriv1 on setuid2"}, 134{ &creds[2], &creds[13], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "33. unpriv1 on setuid2"}, 135{ &creds[3], &creds[12], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "34. unpriv1 on setuid2"}, 136{ &creds[3], &creds[13], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "35. unpriv1 on setuid2"}, 137}; 138int scenarios_count = sizeof(scenarios) / sizeof(struct scenario); 139 140/* 141 * Convert an error number to a compact string representation. For now, 142 * implement only the error numbers we are likely to see. 143 */ 144static char * 145errno_to_string(int error) 146{ 147 148 switch (error) { 149 case EPERM: 150 return ("EPERM"); 151 case EACCES: 152 return ("EACCES"); 153 case EINVAL: 154 return ("EINVAL"); 155 case ENOSYS: 156 return ("ENOSYS"); 157 case ESRCH: 158 return ("ESRCH"); 159 case EOPNOTSUPP: 160 return ("EOPNOTSUPP"); 161 case 0: 162 return ("0"); 163 default: 164 printf("%d\n", error); 165 return ("unknown"); 166 } 167} 168 169/* 170 * Return a process credential describing the current process. 171 */ 172static int 173cred_get(struct cred *cred) 174{ 175 int error; 176 177 error = getresuid(&cred->cr_ruid, &cred->cr_euid, &cred->cr_svuid); 178 if (error) 179 return (error); 180 181 cred->cr_issetugid = issetugid(); 182 183 return (0); 184} 185 186/* 187 * Userland stub for __setsugid() to take into account possible presence 188 * in C library, kernel, et al. 189 */ 190int 191setugid(int flag) 192{ 193 194#ifdef SETSUGID_SUPPORTED 195 return (__setugid(flag)); 196#else 197#ifdef SETSUGID_SUPPORTED_BUT_NO_LIBC_STUB 198 return (syscall(374, flag)); 199#else 200 return (ENOSYS); 201#endif 202#endif 203} 204 205/* 206 * Set the current process's credentials to match the passed credential. 207 */ 208static int 209cred_set(struct cred *cred) 210{ 211 int error; 212 213 error = setresuid(cred->cr_ruid, cred->cr_euid, cred->cr_svuid); 214 if (error) 215 return (error); 216 217 error = setugid(cred->cr_issetugid); 218 if (error) { 219 perror("__setugid"); 220 return (error); 221 } 222 223#ifdef CHECK_CRED_SET 224 { 225 uid_t ruid, euid, svuid; 226 error = getresuid(&ruid, &euid, &svuid); 227 if (error) { 228 perror("getresuid"); 229 return (-1); 230 } 231 assert(ruid == cred->cr_ruid); 232 assert(euid == cred->cr_euid); 233 assert(svuid == cred->cr_svuid); 234 assert(cred->cr_issetugid == issetugid()); 235 } 236#endif /* !CHECK_CRED_SET */ 237 238 return (0); 239} 240 241/* 242 * Print the passed process credential to the passed I/O stream. 243 */ 244static void 245cred_print(FILE *output, struct cred *cred) 246{ 247 248 fprintf(output, "(e:%d r:%d s:%d P_SUGID:%d)", cred->cr_euid, 249 cred->cr_ruid, cred->cr_svuid, cred->cr_issetugid); 250} 251 252#define LOOP_PTRACE 0 253#define LOOP_KTRACE 1 254#define LOOP_SIGHUP 2 255#define LOOP_SIGSEGV 3 256#define LOOP_SEE 4 257#define LOOP_SCHED 5 258#define LOOP_MAX LOOP_SCHED 259 260/* 261 * Enact a scenario by looping through the four test cases for the scenario, 262 * spawning off pairs of processes with the desired credentials, and 263 * reporting results to stdout. 264 */ 265static int 266enact_scenario(int scenario) 267{ 268 pid_t pid1, pid2; 269 char *name, *tracefile; 270 int error, desirederror, loop; 271 272 for (loop = 0; loop < LOOP_MAX+1; loop++) { 273 /* 274 * Spawn the first child, target of the operation. 275 */ 276 pid1 = fork(); 277 switch (pid1) { 278 case -1: 279 return (-1); 280 case 0: 281 /* child */ 282 error = cred_set(scenarios[scenario].sc_cred2); 283 if (error) { 284 perror("cred_set"); 285 return (error); 286 } 287 /* 200 seconds should be plenty of time. */ 288 sleep(200); 289 exit(0); 290 default: 291 /* parent */ 292 } 293 294 /* 295 * XXX 296 * This really isn't ideal -- give proc 1 a chance to set 297 * its credentials, or we may get spurious errors. Really, 298 * some for of IPC should be used to allow the parent to 299 * wait for the first child to be ready before spawning 300 * the second child. 301 */ 302 sleep(1); 303 304 /* 305 * Spawn the second child, source of the operation. 306 */ 307 pid2 = fork(); 308 switch (pid2) { 309 case -1: 310 return (-1); 311 312 case 0: 313 /* child */ 314 error = cred_set(scenarios[scenario].sc_cred1); 315 if (error) { 316 perror("cred_set"); 317 return (error); 318 } 319 320 /* 321 * Initialize errno to zero so as to catch any 322 * generated errors. In each case, perform the 323 * operation. Preserve the error number for later 324 * use so it doesn't get stomped on by any I/O. 325 * Determine the desired error for the given case 326 * by extracting it from the scenario table. 327 * Initialize a function name string for output 328 * prettiness. 329 */ 330 errno = 0; 331 switch (loop) { 332 case LOOP_PTRACE: 333 error = ptrace(PT_ATTACH, pid1, NULL, 0); 334 error = errno; 335 name = "ptrace"; 336 desirederror = 337 scenarios[scenario].sc_canptrace_errno; 338 break; 339 case LOOP_KTRACE: 340 tracefile = mktemp("/tmp/testuid_ktrace.XXXXXX"); 341 if (tracefile == NULL) { 342 error = errno; 343 perror("mktemp"); 344 break; 345 } 346 error = ktrace(tracefile, KTROP_SET, 347 KTRFAC_SYSCALL, pid1); 348 error = errno; 349 name = "ktrace"; 350 desirederror = 351 scenarios[scenario].sc_canktrace_errno; 352 unlink(tracefile); 353 break; 354 case LOOP_SIGHUP: 355 error = kill(pid1, SIGHUP); 356 error = errno; 357 name = "sighup"; 358 desirederror = 359 scenarios[scenario].sc_cansighup_errno; 360 break; 361 case LOOP_SIGSEGV: 362 error = kill(pid1, SIGSEGV); 363 error = errno; 364 name = "sigsegv"; 365 desirederror = 366 scenarios[scenario].sc_cansigsegv_errno; 367 break; 368 case LOOP_SEE: 369 getpriority(PRIO_PROCESS, pid1); 370 error = errno; 371 name = "see"; 372 desirederror = 373 scenarios[scenario].sc_cansee_errno; 374 break; 375 case LOOP_SCHED: 376 error = setpriority(PRIO_PROCESS, pid1, 377 0); 378 error = errno; 379 name = "sched"; 380 desirederror = 381 scenarios[scenario].sc_cansched_errno; 382 break; 383 default: 384 name = "broken"; 385 } 386 387 if (error != desirederror) { 388 fprintf(stdout, 389 "[%s].%s: expected %s, got %s\n ", 390 scenarios[scenario].sc_name, name, 391 errno_to_string(desirederror), 392 errno_to_string(error)); 393 cred_print(stdout, 394 scenarios[scenario].sc_cred1); 395 cred_print(stdout, 396 scenarios[scenario].sc_cred2); 397 fprintf(stdout, "\n"); 398 } 399 400 exit(0); 401 402 default: 403 /* parent */ 404 } 405 406 error = waitpid(pid2, NULL, 0); 407 /* 408 * Once pid2 has died, it's safe to kill pid1, if it's still 409 * alive. Mask signal failure in case the test actually 410 * killed pid1 (not unlikely: can occur in both signal and 411 * ptrace cases). 412 */ 413 kill(pid1, SIGKILL); 414 error = waitpid(pid2, NULL, 0); 415 } 416 417 return (0); 418} 419 420void 421enact_scenarios(void) 422{ 423 int i, error; 424 425 for (i = 0; i < scenarios_count; i++) { 426 error = enact_scenario(i); 427 if (error) 428 perror("enact_scenario"); 429 } 430} 431