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