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$ 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 */ 46 struct cred { 47 uid_t cr_euid, cr_ruid, cr_svuid; 48 int cr_issetugid; 49 }; 50 51 /* 52 * Description of a scenario. 53 */ 54 struct 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 */ 66 static 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 */ 87 static 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 }; 134 int 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 */ 140 static char * 141 errno_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 */ 165 static int 166 cred_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 */ 183 int 184 setugid(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 */ 201 static int 202 cred_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 */ 237 static void 238 cred_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 */ 256 static int 257 enact_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 389 void 390 enact_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