1/* $NetBSD: crontab.c,v 1.3.8.1 2012/03/07 23:41:17 riz Exp $ */ 2 3/* Copyright 1988,1990,1993,1994 by Paul Vixie 4 * All rights reserved 5 */ 6 7/* 8 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 9 * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 10 * 11 * Permission to use, copy, modify, and distribute this software for any 12 * purpose with or without fee is hereby granted, provided that the above 13 * copyright notice and this permission notice appear in all copies. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 */ 23#include <sys/cdefs.h> 24#if !defined(lint) && !defined(LINT) 25#if 0 26static char rcsid[] = "Id: crontab.c,v 1.12 2004/01/23 18:56:42 vixie Exp"; 27#else 28__RCSID("$NetBSD: crontab.c,v 1.3.8.1 2012/03/07 23:41:17 riz Exp $"); 29#endif 30#endif 31 32/* crontab - install and manage per-user crontab files 33 * vix 02may87 [RCS has the rest of the log] 34 * vix 26jan87 [original] 35 */ 36 37#define MAIN_PROGRAM 38 39#include "cron.h" 40 41#define NHEADER_LINES 3 42 43enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace }; 44 45#if DEBUGGING 46static const char *Options[] = { 47 "???", "list", "delete", "edit", "replace" }; 48static const char *getoptargs = "u:lerx:"; 49#else 50static const char *getoptargs = "u:ler"; 51#endif 52 53static PID_T Pid; 54static char User[MAX_UNAME], RealUser[MAX_UNAME]; 55static char Filename[MAX_FNAME], TempFilename[MAX_FNAME]; 56static FILE *NewCrontab; 57static int CheckErrorCount; 58static enum opt_t Option; 59static struct passwd *pw; 60static void list_cmd(void), 61 delete_cmd(void), 62 edit_cmd(void), 63 poke_daemon(void), 64 check_error(const char *), 65 parse_args(int c, char *v[]); 66static int replace_cmd(void); 67static int allowed(const char *, const char *, const char *); 68static int in_file(const char *, FILE *, int); 69static int relinguish_priv(void); 70static int regain_priv(void); 71 72static void 73usage(const char *msg) { 74 (void)fprintf(stderr, "%s: usage error: %s\n", getprogname(), msg); 75 (void)fprintf(stderr, "usage:\t%s [-u user] file\n", getprogname()); 76 (void)fprintf(stderr, "\t%s [-u user] [ -e | -l | -r ]\n", getprogname()); 77 (void)fprintf(stderr, "\t\t(default operation is replace, per 1003.2)\n"); 78 (void)fprintf(stderr, "\t-e\t(edit user's crontab)\n"); 79 (void)fprintf(stderr, "\t-l\t(list user's crontab)\n"); 80 (void)fprintf(stderr, "\t-r\t(delete user's crontab)\n"); 81 exit(ERROR_EXIT); 82} 83 84static uid_t euid, ruid; 85static gid_t egid, rgid; 86 87int 88main(int argc, char *argv[]) { 89 int exitstatus; 90 91 setprogname(argv[0]); 92 Pid = getpid(); 93 (void)setlocale(LC_ALL, ""); 94 95 euid = geteuid(); 96 egid = getegid(); 97 ruid = getuid(); 98 rgid = getgid(); 99 100 if (euid == ruid && ruid != 0) 101 errx(ERROR_EXIT, "Not installed setuid root"); 102 103 (void)setvbuf(stderr, NULL, _IOLBF, 0); 104 parse_args(argc, argv); /* sets many globals, opens a file */ 105 set_cron_cwd(); 106 if (!allowed(RealUser, CRON_ALLOW, CRON_DENY)) { 107 (void)fprintf(stderr, 108 "You `%s' are not allowed to use this program `%s'\n", 109 User, getprogname()); 110 (void)fprintf(stderr, "See crontab(1) for more information\n"); 111 log_it(RealUser, Pid, "AUTH", "crontab command not allowed"); 112 exit(ERROR_EXIT); 113 } 114 exitstatus = OK_EXIT; 115 switch (Option) { 116 case opt_unknown: 117 usage("unrecognized option"); 118 exitstatus = ERROR_EXIT; 119 break; 120 case opt_list: 121 list_cmd(); 122 break; 123 case opt_delete: 124 delete_cmd(); 125 break; 126 case opt_edit: 127 edit_cmd(); 128 break; 129 case opt_replace: 130 if (replace_cmd() < 0) 131 exitstatus = ERROR_EXIT; 132 break; 133 default: 134 abort(); 135 } 136 exit(exitstatus); 137 /*NOTREACHED*/ 138} 139 140static void 141parse_args(int argc, char *argv[]) { 142 int argch; 143 144 if (!(pw = getpwuid(getuid()))) { 145 errx(ERROR_EXIT, 146 "your UID isn't in the passwd file. bailingo out"); 147 } 148 if (strlen(pw->pw_name) >= sizeof User) { 149 errx(ERROR_EXIT, "username too long"); 150 } 151 (void)strlcpy(User, pw->pw_name, sizeof(User)); 152 (void)strlcpy(RealUser, User, sizeof(RealUser)); 153 Filename[0] = '\0'; 154 Option = opt_unknown; 155 while (-1 != (argch = getopt(argc, argv, getoptargs))) { 156 switch (argch) { 157#if DEBUGGING 158 case 'x': 159 if (!set_debug_flags(optarg)) 160 usage("bad debug option"); 161 break; 162#endif 163 case 'u': 164 if (MY_UID(pw) != ROOT_UID) { 165 errx(ERROR_EXIT, 166 "must be privileged to use -u"); 167 } 168 if (!(pw = getpwnam(optarg))) { 169 errx(ERROR_EXIT, "user `%s' unknown", optarg); 170 } 171 if (strlen(optarg) >= sizeof User) 172 usage("username too long"); 173 (void) strlcpy(User, optarg, sizeof(User)); 174 break; 175 case 'l': 176 if (Option != opt_unknown) 177 usage("only one operation permitted"); 178 Option = opt_list; 179 break; 180 case 'r': 181 if (Option != opt_unknown) 182 usage("only one operation permitted"); 183 Option = opt_delete; 184 break; 185 case 'e': 186 if (Option != opt_unknown) 187 usage("only one operation permitted"); 188 Option = opt_edit; 189 break; 190 default: 191 usage("unrecognized option"); 192 } 193 } 194 195 endpwent(); 196 197 if (Option != opt_unknown) { 198 if (argv[optind] != NULL) 199 usage("no arguments permitted after this option"); 200 } else { 201 if (argv[optind] != NULL) { 202 Option = opt_replace; 203 if (strlen(argv[optind]) >= sizeof Filename) 204 usage("filename too long"); 205 (void)strlcpy(Filename, argv[optind], sizeof(Filename)); 206 } else 207 usage("file name must be specified for replace"); 208 } 209 210 if (Option == opt_replace) { 211 /* we have to open the file here because we're going to 212 * chdir(2) into /var/cron before we get around to 213 * reading the file. 214 */ 215 if (!strcmp(Filename, "-")) 216 NewCrontab = stdin; 217 else { 218 /* relinquish the setuid status of the binary during 219 * the open, lest nonroot users read files they should 220 * not be able to read. we can't use access() here 221 * since there's a race condition. thanks go out to 222 * Arnt Gulbrandsen <agulbra@pvv.unit.no> for spotting 223 * the race. 224 */ 225 226 if (relinguish_priv() < OK) { 227 err(ERROR_EXIT, "swapping uids"); 228 } 229 if (!(NewCrontab = fopen(Filename, "r"))) { 230 err(ERROR_EXIT, "cannot open `%s'", Filename); 231 } 232 if (regain_priv() < OK) { 233 err(ERROR_EXIT, "swapping uids back"); 234 } 235 } 236 } 237 238 Debug(DMISC, ("user=%s, file=%s, option=%s\n", 239 User, Filename, Options[(int)Option])); 240} 241 242static void 243skip_header(int *pch, FILE *f) 244{ 245 int ch; 246 int x; 247 248 /* ignore the top few comments since we probably put them there. 249 */ 250 for (x = 0; x < NHEADER_LINES; x++) { 251 ch = get_char(f); 252 if (EOF == ch) 253 break; 254 if ('#' != ch) 255 break; 256 while (EOF != (ch = get_char(f))) 257 if (ch == '\n') 258 break; 259 if (EOF == ch) 260 break; 261 } 262 if (ch == '\n') 263 ch = get_char(f); 264 265 *pch = ch; 266} 267 268static void 269list_cmd(void) { 270 char n[MAX_FNAME]; 271 FILE *f; 272 int ch; 273 274 log_it(RealUser, Pid, "LIST", User); 275 if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) { 276 errx(ERROR_EXIT, "path too long"); 277 } 278 if (!(f = fopen(n, "r"))) { 279 if (errno == ENOENT) 280 errx(ERROR_EXIT, "no crontab for `%s'", User); 281 else 282 err(ERROR_EXIT, "Cannot open `%s'", n); 283 } 284 285 /* file is open. copy to stdout, close. 286 */ 287 Set_LineNum(1); 288 skip_header(&ch, f); 289 for (; EOF != ch; ch = get_char(f)) 290 (void)putchar(ch); 291 (void)fclose(f); 292} 293 294static void 295delete_cmd(void) { 296 char n[MAX_FNAME]; 297 298 log_it(RealUser, Pid, "DELETE", User); 299 if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) { 300 errx(ERROR_EXIT, "path too long"); 301 } 302 if (unlink(n) != 0) { 303 if (errno == ENOENT) 304 errx(ERROR_EXIT, "no crontab for `%s'", User); 305 else 306 err(ERROR_EXIT, "cannot unlink `%s'", n); 307 } 308 poke_daemon(); 309} 310 311static void 312check_error(const char *msg) { 313 CheckErrorCount++; 314 (void)fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg); 315} 316 317static void 318edit_cmd(void) { 319 char n[MAX_FNAME], q[MAX_TEMPSTR]; 320 const char *editor; 321 FILE *f; 322 int ch, t, x; 323 sig_t oint, oabrt, oquit, ohup; 324 struct stat statbuf; 325 struct utimbuf utimebuf; 326 long mtimensec; 327 WAIT_T waiter; 328 PID_T pid, xpid; 329 330 log_it(RealUser, Pid, "BEGIN EDIT", User); 331 if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) { 332 errx(ERROR_EXIT, "path too long"); 333 } 334 if (!(f = fopen(n, "r"))) { 335 if (errno != ENOENT) { 336 err(ERROR_EXIT, "cannot open `%s'", n); 337 } 338 warnx("no crontab for `%s' - using an empty one", User); 339 if (!(f = fopen(_PATH_DEVNULL, "r"))) { 340 err(ERROR_EXIT, "cannot open `%s'", _PATH_DEVNULL); 341 } 342 } 343 344 if (fstat(fileno(f), &statbuf) < 0) { 345 warn("cannot stat crontab file"); 346 goto fatal; 347 } 348 utimebuf.actime = statbuf.st_atime; 349 utimebuf.modtime = statbuf.st_mtime; 350 mtimensec = statbuf.st_mtimensec; 351 352 /* Turn off signals. */ 353 ohup = signal(SIGHUP, SIG_IGN); 354 oint = signal(SIGINT, SIG_IGN); 355 oquit = signal(SIGQUIT, SIG_IGN); 356 oabrt = signal(SIGABRT, SIG_IGN); 357 358 if (!glue_strings(Filename, sizeof Filename, _PATH_TMP, 359 "crontab.XXXXXXXXXX", '/')) { 360 warnx("path too long"); 361 goto fatal; 362 } 363 if (-1 == (t = mkstemp(Filename))) { 364 warn("cannot create `%s'", Filename); 365 goto fatal; 366 } 367#ifdef HAS_FCHOWN 368 x = fchown(t, MY_UID(pw), MY_GID(pw)); 369#else 370 x = chown(Filename, MY_UID(pw), MY_GID(pw)); 371#endif 372 if (x < 0) { 373 warn("cannot chown `%s'", Filename); 374 goto fatal; 375 } 376 if (!(NewCrontab = fdopen(t, "r+"))) { 377 warn("cannot open fd"); 378 goto fatal; 379 } 380 381 Set_LineNum(1); 382 383 skip_header(&ch, f); 384 385 /* copy the rest of the crontab (if any) to the temp file. 386 */ 387 for (; EOF != ch; ch = get_char(f)) 388 (void)putc(ch, NewCrontab); 389 (void)fclose(f); 390 if (fflush(NewCrontab) < OK) { 391 err(ERROR_EXIT, "cannot flush output for `%s'", Filename); 392 } 393 (void)utime(Filename, &utimebuf); 394 again: 395 rewind(NewCrontab); 396 if (ferror(NewCrontab)) { 397 warn("error while writing new crontab to `%s'", Filename); 398 fatal: 399 (void)unlink(Filename); 400 exit(ERROR_EXIT); 401 } 402 403 if (((editor = getenv("VISUAL")) == NULL || *editor == '\0') && 404 ((editor = getenv("EDITOR")) == NULL || *editor == '\0')) { 405 editor = EDITOR; 406 } 407 408 /* we still have the file open. editors will generally rewrite the 409 * original file rather than renaming/unlinking it and starting a 410 * new one; even backup files are supposed to be made by copying 411 * rather than by renaming. if some editor does not support this, 412 * then don't use it. the security problems are more severe if we 413 * close and reopen the file around the edit. 414 */ 415 416 switch (pid = fork()) { 417 case -1: 418 warn("cannot fork"); 419 goto fatal; 420 case 0: 421 /* child */ 422 if (setgid(MY_GID(pw)) < 0) { 423 err(ERROR_EXIT, "cannot setgid(getgid())"); 424 } 425 if (setuid(MY_UID(pw)) < 0) { 426 err(ERROR_EXIT, "cannot setuid(getuid())"); 427 } 428 if (chdir(_PATH_TMP) < 0) { 429 err(ERROR_EXIT, "cannot chdir to `%s'", _PATH_TMP); 430 } 431 if (!glue_strings(q, sizeof q, editor, Filename, ' ')) { 432 errx(ERROR_EXIT, "editor command line too long"); 433 } 434 (void)execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", q, (char *)0); 435 err(ERROR_EXIT, "cannot start `%s'", editor); 436 /*NOTREACHED*/ 437 default: 438 /* parent */ 439 break; 440 } 441 442 /* parent */ 443 for (;;) { 444 xpid = waitpid(pid, &waiter, WUNTRACED); 445 if (xpid == -1) { 446 if (errno != EINTR) 447 warn("waitpid() failed waiting for PID %ld " 448 "from `%s'", (long)pid, editor); 449 } else if (xpid != pid) { 450 warnx("wrong PID (%ld != %ld) from `%s'", 451 (long)xpid, (long)pid, editor); 452 goto fatal; 453 } else if (WIFSTOPPED(waiter)) { 454 (void)kill(getpid(), WSTOPSIG(waiter)); 455 } else if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) { 456 warnx("`%s' exited with status %d\n", 457 editor, WEXITSTATUS(waiter)); 458 goto fatal; 459 } else if (WIFSIGNALED(waiter)) { 460 warnx("`%s' killed; signal %d (%score dumped)", 461 editor, WTERMSIG(waiter), 462 WCOREDUMP(waiter) ? "" : "no "); 463 goto fatal; 464 } else 465 break; 466 } 467 (void)signal(SIGHUP, ohup); 468 (void)signal(SIGINT, oint); 469 (void)signal(SIGQUIT, oquit); 470 (void)signal(SIGABRT, oabrt); 471 472 if (fstat(t, &statbuf) < 0) { 473 warn("cannot stat `%s'", Filename); 474 goto fatal; 475 } 476 if (utimebuf.modtime == statbuf.st_mtime && 477 mtimensec == statbuf.st_mtimensec) { 478 warnx("no changes made to crontab"); 479 goto remove; 480 } 481 warnx("installing new crontab"); 482 switch (replace_cmd()) { 483 case 0: 484 break; 485 case -1: 486 for (;;) { 487 (void)fpurge(stdin); 488 (void)printf("Do you want to retry the same edit? "); 489 (void)fflush(stdout); 490 q[0] = '\0'; 491 (void) fgets(q, (int)sizeof(q), stdin); 492 switch (q[0]) { 493 case 'y': 494 case 'Y': 495 goto again; 496 case 'n': 497 case 'N': 498 goto abandon; 499 default: 500 (void)printf("Enter Y or N\n"); 501 } 502 } 503 /*NOTREACHED*/ 504 case -2: 505 abandon: 506 warnx("edits left in `%s'", Filename); 507 goto done; 508 default: 509 warnx("panic: bad switch() in replace_cmd()"); 510 goto fatal; 511 } 512 remove: 513 (void)unlink(Filename); 514 done: 515 log_it(RealUser, Pid, "END EDIT", User); 516} 517 518/* returns 0 on success 519 * -1 on syntax error 520 * -2 on install error 521 */ 522static int 523replace_cmd(void) { 524 char n[MAX_FNAME], n2[MAX_FNAME], envstr[MAX_ENVSTR], lastch; 525 FILE *tmp, *fmaxtabsize; 526 int ch, eof, fd; 527 int error = 0; 528 entry *e; 529 sig_t oint, oabrt, oquit, ohup; 530 uid_t file_owner; 531 time_t now = time(NULL); 532 char **envp = env_init(); 533 size_t maxtabsize; 534 struct stat statbuf; 535 536 if (envp == NULL) { 537 warn("Cannot allocate memory."); 538 return (-2); 539 } 540 541 if (!glue_strings(TempFilename, sizeof TempFilename, SPOOL_DIR, 542 "tmp.XXXXXXXXXX", '/')) { 543 TempFilename[0] = '\0'; 544 warnx("path too long"); 545 return (-2); 546 } 547 if ((fd = mkstemp(TempFilename)) == -1 || !(tmp = fdopen(fd, "w+"))) { 548 warn("cannot create `%s'", TempFilename); 549 if (fd != -1) { 550 (void)close(fd); 551 (void)unlink(TempFilename); 552 } 553 TempFilename[0] = '\0'; 554 return (-2); 555 } 556 557 ohup = signal(SIGHUP, SIG_IGN); 558 oint = signal(SIGINT, SIG_IGN); 559 oquit = signal(SIGQUIT, SIG_IGN); 560 oabrt = signal(SIGABRT, SIG_IGN); 561 562 /* Make sure that the crontab is not an unreasonable size. 563 * 564 * XXX This is subject to a race condition--the user could 565 * add stuff to the file after we've checked the size but 566 * before we slurp it in and write it out. We can't just move 567 * the test to test the temp file we later create, because by 568 * that time we've already filled up the crontab disk. Probably 569 * the right thing to do is to do a bytecount in the copy loop 570 * rather than stating the file we're about to read. 571 */ 572 (void)snprintf(n2, sizeof(n2), "%s/%s", CRONDIR, MAXTABSIZE_FILE); 573 if ((fmaxtabsize = fopen(n2, "r")) != NULL) { 574 if (fgets(n2, (int)sizeof(n2), fmaxtabsize) == NULL) { 575 maxtabsize = 0; 576 } else { 577 maxtabsize = atoi(n2); 578 } 579 (void)fclose(fmaxtabsize); 580 } else { 581 maxtabsize = MAXTABSIZE_DEFAULT; 582 } 583 584 if (fstat(fileno(NewCrontab), &statbuf)) { 585 warn("error stat'ing crontab input"); 586 error = -2; 587 goto done; 588 } 589 if ((uintmax_t)statbuf.st_size > (uintmax_t)maxtabsize) { 590 warnx("%ld bytes is larger than the maximum size of %ld bytes", 591 (long) statbuf.st_size, (long) maxtabsize); 592 error = -2; 593 goto done; 594 } 595 596 /* write a signature at the top of the file. 597 * 598 * VERY IMPORTANT: make sure NHEADER_LINES agrees with this code. 599 */ 600 (void)fprintf(tmp, "# DO NOT EDIT THIS FILE - edit the master and reinstall.\n"); 601 (void)fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now)); 602 (void)fprintf(tmp, "# (Cron version %s -- %s)\n", CRON_VERSION, "$NetBSD: crontab.c,v 1.3.8.1 2012/03/07 23:41:17 riz Exp $"); 603 604 /* copy the crontab to the tmp 605 */ 606 (void)rewind(NewCrontab); 607 Set_LineNum(1); 608 lastch = EOF; 609 while (EOF != (ch = get_char(NewCrontab))) 610 (void)putc(lastch = ch, tmp); 611 612 if (lastch != (char)EOF && lastch != '\n') { 613 warnx("missing trailing newline in `%s'", Filename); 614 error = -1; 615 goto done; 616 } 617 618 if (ferror(NewCrontab)) { 619 warn("error while reading `%s'", Filename); 620 error = -2; 621 goto done; 622 } 623 624 (void)ftruncate(fileno(tmp), ftell(tmp)); 625 /* XXX this should be a NOOP - is */ 626 (void)fflush(tmp); 627 628 if (ferror(tmp)) { 629 (void)fclose(tmp); 630 warn("error while writing new crontab to `%s'", TempFilename); 631 error = -2; 632 goto done; 633 } 634 635 /* check the syntax of the file being installed. 636 */ 637 638 /* BUG: was reporting errors after the EOF if there were any errors 639 * in the file proper -- kludged it by stopping after first error. 640 * vix 31mar87 641 */ 642 Set_LineNum(1 - NHEADER_LINES); 643 CheckErrorCount = 0; eof = FALSE; 644 while (!CheckErrorCount && !eof) { 645 switch (load_env(envstr, tmp)) { 646 case ERR: 647 /* check for data before the EOF */ 648 if (envstr[0] != '\0') { 649 Set_LineNum(LineNumber + 1); 650 check_error("premature EOF"); 651 } 652 eof = TRUE; 653 break; 654 case FALSE: 655 e = load_entry(tmp, check_error, pw, envp); 656 if (e) 657 free(e); 658 break; 659 case TRUE: 660 break; 661 } 662 } 663 664 if (CheckErrorCount != 0) { 665 warnx("errors in crontab file, can't install."); 666 (void)fclose(tmp); 667 error = -1; 668 goto done; 669 } 670 671 file_owner = (getgid() == getegid()) ? ROOT_UID : pw->pw_uid; 672 673#ifdef HAVE_FCHOWN 674 error = fchown(fileno(tmp), file_owner, (uid_t)-1); 675#else 676 error = chown(TempFilename, file_owner, (gid_t)-1); 677#endif 678 if (error < OK) { 679 warn("cannot chown `%s'", TempFilename); 680 (void)fclose(tmp); 681 error = -2; 682 goto done; 683 } 684 685 if (fclose(tmp) == EOF) { 686 warn("error closing file"); 687 error = -2; 688 goto done; 689 } 690 691 if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) { 692 warnx("path too long"); 693 error = -2; 694 goto done; 695 } 696 if (rename(TempFilename, n)) { 697 warn("error renaming `%s' to `%s'", TempFilename, n); 698 error = -2; 699 goto done; 700 } 701 TempFilename[0] = '\0'; 702 log_it(RealUser, Pid, "REPLACE", User); 703 704 poke_daemon(); 705 706done: 707 (void)signal(SIGHUP, ohup); 708 (void)signal(SIGINT, oint); 709 (void)signal(SIGQUIT, oquit); 710 (void)signal(SIGABRT, oabrt); 711 if (TempFilename[0]) { 712 (void) unlink(TempFilename); 713 TempFilename[0] = '\0'; 714 } 715 return (error); 716} 717 718static void 719poke_daemon(void) { 720#ifdef HAVE_UTIMES 721 struct timeval tvs[2]; 722 struct timezone tz; 723 724 (void) gettimeofday(&tvs[0], &tz); 725 tvs[1] = tvs[0]; 726 if (utimes(SPOOL_DIR, tvs) < OK) 727#else 728 if (utime(SPOOL_DIR, NULL) < OK) 729#endif /* HAVE_UTIMES */ 730 warn("can't update mtime on spooldir %s", SPOOL_DIR); 731} 732/* int allowed(const char *username, const char *allow_file, const char *deny_file) 733 * returns TRUE if (allow_file exists and user is listed) 734 * or (deny_file exists and user is NOT listed). 735 * root is always allowed. 736 */ 737static int 738allowed(const char *username, const char *allow_file, const char *deny_file) { 739 FILE *fp; 740 int isallowed; 741 742 if (strcmp(username, ROOT_USER) == 0) 743 return (TRUE); 744#ifdef ALLOW_ONLY_ROOT 745 isallowed = FALSE; 746#else 747 isallowed = TRUE; 748#endif 749 if ((fp = fopen(allow_file, "r")) != NULL) { 750 isallowed = in_file(username, fp, FALSE); 751 (void)fclose(fp); 752 } else if ((fp = fopen(deny_file, "r")) != NULL) { 753 isallowed = !in_file(username, fp, FALSE); 754 (void)fclose(fp); 755 } 756 return (isallowed); 757} 758/* int in_file(const char *string, FILE *file, int error) 759 * return TRUE if one of the lines in file matches string exactly, 760 * FALSE if no lines match, and error on error. 761 */ 762static int 763in_file(const char *string, FILE *file, int error) 764{ 765 char line[MAX_TEMPSTR]; 766 char *endp; 767 768 if (fseek(file, 0L, SEEK_SET)) 769 return (error); 770 while (fgets(line, MAX_TEMPSTR, file)) { 771 if (line[0] != '\0') { 772 endp = &line[strlen(line) - 1]; 773 if (*endp != '\n') 774 return (error); 775 *endp = '\0'; 776 if (0 == strcmp(line, string)) 777 return (TRUE); 778 } 779 } 780 if (ferror(file)) 781 return (error); 782 return (FALSE); 783} 784 785#ifdef HAVE_SAVED_UIDS 786 787static int relinguish_priv(void) { 788 return (setegid(rgid) || seteuid(ruid)) ? -1 : 0; 789} 790 791static int regain_priv(void) { 792 return (setegid(egid) || seteuid(euid)) ? -1 : 0; 793} 794 795#else /*HAVE_SAVED_UIDS*/ 796 797static int relinguish_priv(void) { 798 return (setregid(egid, rgid) || setreuid(euid, ruid)) ? -1 : 0; 799} 800 801static int regain_priv(void) { 802 return (setregid(rgid, egid) || setreuid(ruid, euid)) ? -1 : 0; 803} 804#endif /*HAVE_SAVED_UIDS*/ 805