175422Srwatson/*- 275422Srwatson * Copyright (c) 2001 Robert N. M. Watson 375422Srwatson * All rights reserved. 475422Srwatson * 575422Srwatson * Redistribution and use in source and binary forms, with or without 675422Srwatson * modification, are permitted provided that the following conditions 775422Srwatson * are met: 875422Srwatson * 1. Redistributions of source code must retain the above copyright 975422Srwatson * notice, this list of conditions and the following disclaimer. 1075422Srwatson * 2. Redistributions in binary form must reproduce the above copyright 1175422Srwatson * notice, this list of conditions and the following disclaimer in the 1275422Srwatson * documentation and/or other materials provided with the distribution. 1375422Srwatson * 1475422Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1575422Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1675422Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1775422Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1875422Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1975422Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2075422Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2175422Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2275422Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2375422Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2475422Srwatson * SUCH DAMAGE. 2575422Srwatson * 2675422Srwatson * $FreeBSD$ 2775422Srwatson */ 2875422Srwatson 2975485Srwatson#include <sys/param.h> 3075485Srwatson#include <sys/uio.h> 3175422Srwatson#include <sys/ptrace.h> 3275422Srwatson#include <sys/time.h> 3375422Srwatson#include <sys/resource.h> 3475422Srwatson#include <sys/syscall.h> 3575422Srwatson#include <sys/wait.h> 3675485Srwatson#include <sys/ktrace.h> 3775422Srwatson 3875422Srwatson#include <assert.h> 3975422Srwatson#include <errno.h> 4075422Srwatson#include <signal.h> 4175422Srwatson#include <stdio.h> 4275422Srwatson#include <string.h> 4375422Srwatson#include <unistd.h> 4475422Srwatson 4575422Srwatson/* 4675422Srwatson * Relevant parts of a process credential. 4775422Srwatson */ 4875422Srwatsonstruct cred { 4975422Srwatson uid_t cr_euid, cr_ruid, cr_svuid; 5075422Srwatson int cr_issetugid; 5175422Srwatson}; 5275422Srwatson 5375422Srwatson/* 5475422Srwatson * Description of a scenario. 5575422Srwatson */ 5675422Srwatsonstruct scenario { 5775422Srwatson struct cred *sc_cred1, *sc_cred2; /* credentials of p1 and p2 */ 5875482Srwatson int sc_canptrace_errno; /* desired ptrace failure */ 5975485Srwatson int sc_canktrace_errno; /* desired ktrace failure */ 6075447Srwatson int sc_cansighup_errno; /* desired SIGHUP failure */ 6175447Srwatson int sc_cansigsegv_errno; /* desired SIGSEGV failure */ 6275422Srwatson int sc_cansee_errno; /* desired getprio failure */ 6375422Srwatson int sc_cansched_errno; /* desired setprio failure */ 6475422Srwatson char *sc_name; /* test name */ 6575422Srwatson}; 6675422Srwatson 6775422Srwatson/* 6875422Srwatson * Table of relevant credential combinations. 6975422Srwatson */ 7075422Srwatsonstatic struct cred creds[] = { 7175422Srwatson/* euid ruid svuid issetugid */ 7275422Srwatson/* 0 */ { 0, 0, 0, 0 }, /* privileged */ 7375422Srwatson/* 1 */ { 0, 0, 0, 1 }, /* privileged + issetugid */ 7475422Srwatson/* 2 */ { 1000, 1000, 1000, 0 }, /* unprivileged1 */ 7575422Srwatson/* 3 */ { 1000, 1000, 1000, 1 }, /* unprivileged1 + issetugid */ 7675422Srwatson/* 4 */ { 1001, 1001, 1001, 0 }, /* unprivileged2 */ 7775422Srwatson/* 5 */ { 1001, 1001, 1001, 1 }, /* unprivileged2 + issetugid */ 7875422Srwatson/* 6 */ { 1000, 0, 0, 0 }, /* daemon1 */ 7975422Srwatson/* 7 */ { 1000, 0, 0, 1 }, /* daemon1 + issetugid */ 8075422Srwatson/* 8 */ { 1001, 0, 0, 0 }, /* daemon2 */ 8175422Srwatson/* 9 */ { 1001, 0, 0, 1 }, /* daemon2 + issetugid */ 8275422Srwatson/* 10 */{ 0, 1000, 1000, 0 }, /* setuid1 */ 8375422Srwatson/* 11 */{ 0, 1000, 1000, 1 }, /* setuid1 + issetugid */ 8475422Srwatson/* 12 */{ 0, 1001, 1001, 0 }, /* setuid2 */ 8575422Srwatson/* 13 */{ 0, 1001, 1001, 1 }, /* setuid2 + issetugid */ 8675422Srwatson}; 8775422Srwatson 8875422Srwatson/* 8975422Srwatson * Table of scenarios. 9075422Srwatson */ 9175422Srwatsonstatic const struct scenario scenarios[] = { 9275485Srwatson/* cred1 cred2 ptrace ktrace, sighup sigsegv see sched name */ 9375486Srwatson/* privileged on privileged */ 9475485Srwatson{ &creds[0], &creds[0], 0, 0, 0, 0, 0, 0, "0. priv on priv"}, 9575485Srwatson{ &creds[0], &creds[1], 0, 0, 0, 0, 0, 0, "1. priv on priv"}, 9675485Srwatson{ &creds[1], &creds[0], 0, 0, 0, 0, 0, 0, "2. priv on priv"}, 9775485Srwatson{ &creds[1], &creds[1], 0, 0, 0, 0, 0, 0, "3. priv on priv"}, 9875422Srwatson/* privileged on unprivileged */ 9975485Srwatson{ &creds[0], &creds[2], 0, 0, 0, 0, 0, 0, "4. priv on unpriv1"}, 10075485Srwatson{ &creds[0], &creds[3], 0, 0, 0, 0, 0, 0, "5. priv on unpriv1"}, 10175485Srwatson{ &creds[1], &creds[2], 0, 0, 0, 0, 0, 0, "6. priv on unpriv1"}, 10275485Srwatson{ &creds[1], &creds[3], 0, 0, 0, 0, 0, 0, "7. priv on unpriv1"}, 10375422Srwatson/* unprivileged on privileged */ 10475485Srwatson{ &creds[2], &creds[0], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "8. unpriv1 on priv"}, 10575485Srwatson{ &creds[2], &creds[1], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "9. unpriv1 on priv"}, 10675485Srwatson{ &creds[3], &creds[0], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "10. unpriv1 on priv"}, 10775485Srwatson{ &creds[3], &creds[1], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "11. unpriv1 on priv"}, 10875422Srwatson/* unprivileged on same unprivileged */ 10975485Srwatson{ &creds[2], &creds[2], 0, 0, 0, 0, 0, 0, "12. unpriv1 on unpriv1"}, 11075485Srwatson{ &creds[2], &creds[3], EPERM, EPERM, 0, EPERM, 0, 0, "13. unpriv1 on unpriv1"}, 11175485Srwatson{ &creds[3], &creds[2], 0, 0, 0, 0, 0, 0, "14. unpriv1 on unpriv1"}, 11275485Srwatson{ &creds[3], &creds[3], EPERM, EPERM, 0, EPERM, 0, 0, "15. unpriv1 on unpriv1"}, 11375422Srwatson/* unprivileged on different unprivileged */ 11475485Srwatson{ &creds[2], &creds[4], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "16. unpriv1 on unpriv2"}, 11575485Srwatson{ &creds[2], &creds[5], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "17. unpriv1 on unpriv2"}, 11675485Srwatson{ &creds[3], &creds[4], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "18. unpriv1 on unpriv2"}, 11775485Srwatson{ &creds[3], &creds[5], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "19. unpriv1 on unpriv2"}, 11875422Srwatson/* unprivileged on daemon, same */ 11975485Srwatson{ &creds[2], &creds[6], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "20. unpriv1 on daemon1"}, 12075485Srwatson{ &creds[2], &creds[7], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "21. unpriv1 on daemon1"}, 12175485Srwatson{ &creds[3], &creds[6], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "22. unpriv1 on daemon1"}, 12275485Srwatson{ &creds[3], &creds[7], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "23. unpriv1 on daemon1"}, 12375422Srwatson/* unprivileged on daemon, different */ 12475485Srwatson{ &creds[2], &creds[8], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "24. unpriv1 on daemon2"}, 12575485Srwatson{ &creds[2], &creds[9], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "25. unpriv1 on daemon2"}, 12675485Srwatson{ &creds[3], &creds[8], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "26. unpriv1 on daemon2"}, 12775485Srwatson{ &creds[3], &creds[9], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "27. unpriv1 on daemon2"}, 12875422Srwatson/* unprivileged on setuid, same */ 12975485Srwatson{ &creds[2], &creds[10], EPERM, EPERM, 0, 0, 0, 0, "28. unpriv1 on setuid1"}, 13075485Srwatson{ &creds[2], &creds[11], EPERM, EPERM, 0, EPERM, 0, 0, "29. unpriv1 on setuid1"}, 13175485Srwatson{ &creds[3], &creds[10], EPERM, EPERM, 0, 0, 0, 0, "30. unpriv1 on setuid1"}, 13275485Srwatson{ &creds[3], &creds[11], EPERM, EPERM, 0, EPERM, 0, 0, "31. unpriv1 on setuid1"}, 13375422Srwatson/* unprivileged on setuid, different */ 13475485Srwatson{ &creds[2], &creds[12], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "32. unpriv1 on setuid2"}, 13575485Srwatson{ &creds[2], &creds[13], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "33. unpriv1 on setuid2"}, 13675485Srwatson{ &creds[3], &creds[12], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "34. unpriv1 on setuid2"}, 13775485Srwatson{ &creds[3], &creds[13], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "35. unpriv1 on setuid2"}, 13875422Srwatson}; 13975422Srwatsonint scenarios_count = sizeof(scenarios) / sizeof(struct scenario); 14075422Srwatson 14175422Srwatson/* 14275422Srwatson * Convert an error number to a compact string representation. For now, 14375422Srwatson * implement only the error numbers we are likely to see. 14475422Srwatson */ 14575422Srwatsonstatic char * 14675422Srwatsonerrno_to_string(int error) 14775422Srwatson{ 14875422Srwatson 14975422Srwatson switch (error) { 15075422Srwatson case EPERM: 15175422Srwatson return ("EPERM"); 15275422Srwatson case EACCES: 15375422Srwatson return ("EACCES"); 15475422Srwatson case EINVAL: 15575422Srwatson return ("EINVAL"); 15675422Srwatson case ENOSYS: 15775422Srwatson return ("ENOSYS"); 15875422Srwatson case ESRCH: 15975422Srwatson return ("ESRCH"); 16075447Srwatson case EOPNOTSUPP: 16175447Srwatson return ("EOPNOTSUPP"); 16275422Srwatson case 0: 16375422Srwatson return ("0"); 16475422Srwatson default: 16575485Srwatson printf("%d\n", error); 16675422Srwatson return ("unknown"); 16775422Srwatson } 16875422Srwatson} 16975422Srwatson 17075422Srwatson/* 17175422Srwatson * Return a process credential describing the current process. 17275422Srwatson */ 17375422Srwatsonstatic int 17475422Srwatsoncred_get(struct cred *cred) 17575422Srwatson{ 17675422Srwatson int error; 17775422Srwatson 17875422Srwatson error = getresuid(&cred->cr_ruid, &cred->cr_euid, &cred->cr_svuid); 17975422Srwatson if (error) 18075422Srwatson return (error); 18175422Srwatson 18275422Srwatson cred->cr_issetugid = issetugid(); 18375422Srwatson 18475422Srwatson return (0); 18575422Srwatson} 18675422Srwatson 18775422Srwatson/* 18875422Srwatson * Userland stub for __setsugid() to take into account possible presence 18975422Srwatson * in C library, kernel, et al. 19075422Srwatson */ 19175422Srwatsonint 19275422Srwatsonsetugid(int flag) 19375422Srwatson{ 19475422Srwatson 19575422Srwatson#ifdef SETSUGID_SUPPORTED 19675422Srwatson return (__setugid(flag)); 19775422Srwatson#else 19875422Srwatson#ifdef SETSUGID_SUPPORTED_BUT_NO_LIBC_STUB 19975422Srwatson return (syscall(374, flag)); 20075422Srwatson#else 20175422Srwatson return (ENOSYS); 20275422Srwatson#endif 20375422Srwatson#endif 20475422Srwatson} 20575422Srwatson 20675422Srwatson/* 20775422Srwatson * Set the current process's credentials to match the passed credential. 20875422Srwatson */ 20975422Srwatsonstatic int 21075422Srwatsoncred_set(struct cred *cred) 21175422Srwatson{ 21275422Srwatson int error; 21375422Srwatson 21475422Srwatson error = setresuid(cred->cr_ruid, cred->cr_euid, cred->cr_svuid); 21575422Srwatson if (error) 21675422Srwatson return (error); 21775422Srwatson 21875422Srwatson error = setugid(cred->cr_issetugid); 21975422Srwatson if (error) { 22075422Srwatson perror("__setugid"); 22175422Srwatson return (error); 22275422Srwatson } 22375422Srwatson 22475422Srwatson#ifdef CHECK_CRED_SET 22575422Srwatson { 22675422Srwatson uid_t ruid, euid, svuid; 22775422Srwatson error = getresuid(&ruid, &euid, &svuid); 22875422Srwatson if (error) { 22975422Srwatson perror("getresuid"); 23075422Srwatson return (-1); 23175422Srwatson } 23275422Srwatson assert(ruid == cred->cr_ruid); 23375422Srwatson assert(euid == cred->cr_euid); 23475422Srwatson assert(svuid == cred->cr_svuid); 23575422Srwatson assert(cred->cr_issetugid == issetugid()); 23675422Srwatson } 23775422Srwatson#endif /* !CHECK_CRED_SET */ 23875422Srwatson 23975422Srwatson return (0); 24075422Srwatson} 24175422Srwatson 24275422Srwatson/* 24375422Srwatson * Print the passed process credential to the passed I/O stream. 24475422Srwatson */ 24575422Srwatsonstatic void 24675422Srwatsoncred_print(FILE *output, struct cred *cred) 24775422Srwatson{ 24875422Srwatson 24975422Srwatson fprintf(output, "(e:%d r:%d s:%d P_SUGID:%d)", cred->cr_euid, 25075422Srwatson cred->cr_ruid, cred->cr_svuid, cred->cr_issetugid); 25175422Srwatson} 25275422Srwatson 25375422Srwatson#define LOOP_PTRACE 0 25475485Srwatson#define LOOP_KTRACE 1 25575485Srwatson#define LOOP_SIGHUP 2 25675485Srwatson#define LOOP_SIGSEGV 3 25775485Srwatson#define LOOP_SEE 4 25875485Srwatson#define LOOP_SCHED 5 25975422Srwatson#define LOOP_MAX LOOP_SCHED 26075422Srwatson 26175422Srwatson/* 26275422Srwatson * Enact a scenario by looping through the four test cases for the scenario, 26375422Srwatson * spawning off pairs of processes with the desired credentials, and 26475422Srwatson * reporting results to stdout. 26575422Srwatson */ 26675422Srwatsonstatic int 26775422Srwatsonenact_scenario(int scenario) 26875422Srwatson{ 26975422Srwatson pid_t pid1, pid2; 27075485Srwatson char *name, *tracefile; 27175422Srwatson int error, desirederror, loop; 27275422Srwatson 27375422Srwatson for (loop = 0; loop < LOOP_MAX+1; loop++) { 27475422Srwatson /* 27575422Srwatson * Spawn the first child, target of the operation. 27675422Srwatson */ 27775422Srwatson pid1 = fork(); 27875422Srwatson switch (pid1) { 27975422Srwatson case -1: 28075422Srwatson return (-1); 28175422Srwatson case 0: 28275422Srwatson /* child */ 28375422Srwatson error = cred_set(scenarios[scenario].sc_cred2); 28475422Srwatson if (error) { 28575422Srwatson perror("cred_set"); 28675422Srwatson return (error); 28775422Srwatson } 28875422Srwatson /* 200 seconds should be plenty of time. */ 28975422Srwatson sleep(200); 29075422Srwatson exit(0); 29175422Srwatson default: 29275422Srwatson /* parent */ 293132296Srwatson break; 29475422Srwatson } 29575422Srwatson 29675422Srwatson /* 29775422Srwatson * XXX 29875422Srwatson * This really isn't ideal -- give proc 1 a chance to set 29975422Srwatson * its credentials, or we may get spurious errors. Really, 30075422Srwatson * some for of IPC should be used to allow the parent to 30175422Srwatson * wait for the first child to be ready before spawning 30275422Srwatson * the second child. 30375422Srwatson */ 30475422Srwatson sleep(1); 30575422Srwatson 30675422Srwatson /* 30775422Srwatson * Spawn the second child, source of the operation. 30875422Srwatson */ 30975422Srwatson pid2 = fork(); 31075422Srwatson switch (pid2) { 31175422Srwatson case -1: 31275422Srwatson return (-1); 31375422Srwatson 31475422Srwatson case 0: 31575422Srwatson /* child */ 31675422Srwatson error = cred_set(scenarios[scenario].sc_cred1); 31775422Srwatson if (error) { 31875422Srwatson perror("cred_set"); 31975422Srwatson return (error); 32075422Srwatson } 32175422Srwatson 32275422Srwatson /* 32375422Srwatson * Initialize errno to zero so as to catch any 32475422Srwatson * generated errors. In each case, perform the 32575422Srwatson * operation. Preserve the error number for later 32675422Srwatson * use so it doesn't get stomped on by any I/O. 32775422Srwatson * Determine the desired error for the given case 32875422Srwatson * by extracting it from the scenario table. 32975422Srwatson * Initialize a function name string for output 33075422Srwatson * prettiness. 33175422Srwatson */ 33275422Srwatson errno = 0; 33375422Srwatson switch (loop) { 33475422Srwatson case LOOP_PTRACE: 33575422Srwatson error = ptrace(PT_ATTACH, pid1, NULL, 0); 33675422Srwatson error = errno; 33775422Srwatson name = "ptrace"; 33875422Srwatson desirederror = 33975482Srwatson scenarios[scenario].sc_canptrace_errno; 34075422Srwatson break; 34175485Srwatson case LOOP_KTRACE: 34275485Srwatson tracefile = mktemp("/tmp/testuid_ktrace.XXXXXX"); 34375485Srwatson if (tracefile == NULL) { 34475485Srwatson error = errno; 34575485Srwatson perror("mktemp"); 34675485Srwatson break; 34775485Srwatson } 34875485Srwatson error = ktrace(tracefile, KTROP_SET, 34975485Srwatson KTRFAC_SYSCALL, pid1); 35075485Srwatson error = errno; 35175485Srwatson name = "ktrace"; 35275485Srwatson desirederror = 35375485Srwatson scenarios[scenario].sc_canktrace_errno; 35475485Srwatson unlink(tracefile); 35575485Srwatson break; 35675447Srwatson case LOOP_SIGHUP: 35775422Srwatson error = kill(pid1, SIGHUP); 35875422Srwatson error = errno; 35975447Srwatson name = "sighup"; 36075422Srwatson desirederror = 36175447Srwatson scenarios[scenario].sc_cansighup_errno; 36275422Srwatson break; 36375447Srwatson case LOOP_SIGSEGV: 36475447Srwatson error = kill(pid1, SIGSEGV); 36575447Srwatson error = errno; 36675447Srwatson name = "sigsegv"; 36775447Srwatson desirederror = 36875447Srwatson scenarios[scenario].sc_cansigsegv_errno; 36975447Srwatson break; 37075422Srwatson case LOOP_SEE: 37175422Srwatson getpriority(PRIO_PROCESS, pid1); 37275422Srwatson error = errno; 37375422Srwatson name = "see"; 37475422Srwatson desirederror = 37575422Srwatson scenarios[scenario].sc_cansee_errno; 37675422Srwatson break; 37775422Srwatson case LOOP_SCHED: 37875422Srwatson error = setpriority(PRIO_PROCESS, pid1, 37975422Srwatson 0); 38075422Srwatson error = errno; 38175422Srwatson name = "sched"; 38275422Srwatson desirederror = 38375422Srwatson scenarios[scenario].sc_cansched_errno; 38475422Srwatson break; 38575422Srwatson default: 38675422Srwatson name = "broken"; 38775422Srwatson } 38875422Srwatson 38975422Srwatson if (error != desirederror) { 39075422Srwatson fprintf(stdout, 39175422Srwatson "[%s].%s: expected %s, got %s\n ", 39275422Srwatson scenarios[scenario].sc_name, name, 39375422Srwatson errno_to_string(desirederror), 39475422Srwatson errno_to_string(error)); 39575422Srwatson cred_print(stdout, 39675422Srwatson scenarios[scenario].sc_cred1); 39775422Srwatson cred_print(stdout, 39875422Srwatson scenarios[scenario].sc_cred2); 39975422Srwatson fprintf(stdout, "\n"); 40075422Srwatson } 40175422Srwatson 40275422Srwatson exit(0); 40375422Srwatson 40475422Srwatson default: 40575422Srwatson /* parent */ 406132296Srwatson break; 40775422Srwatson } 40875422Srwatson 40975422Srwatson error = waitpid(pid2, NULL, 0); 41075422Srwatson /* 41175422Srwatson * Once pid2 has died, it's safe to kill pid1, if it's still 41275422Srwatson * alive. Mask signal failure in case the test actually 41375422Srwatson * killed pid1 (not unlikely: can occur in both signal and 41475422Srwatson * ptrace cases). 41575422Srwatson */ 41675422Srwatson kill(pid1, SIGKILL); 41775422Srwatson error = waitpid(pid2, NULL, 0); 41875422Srwatson } 41975422Srwatson 42075422Srwatson return (0); 42175422Srwatson} 42275422Srwatson 42375422Srwatsonvoid 42475422Srwatsonenact_scenarios(void) 42575422Srwatson{ 42675422Srwatson int i, error; 42775422Srwatson 42875422Srwatson for (i = 0; i < scenarios_count; i++) { 42975422Srwatson error = enact_scenario(i); 43075422Srwatson if (error) 43175422Srwatson perror("enact_scenario"); 43275422Srwatson } 43375422Srwatson} 434