1 /* $OpenBSD: misc.c,v 1.11 2001/12/12 19:02:50 millert Exp $ */ 2 /* Copyright 1988,1990,1993,1994 by Paul Vixie 3 * All rights reserved 4 */ 5 6 /* 7 * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 14 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 16 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 17 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 18 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 19 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20 * SOFTWARE. 21 */ 22 23 #if !defined(lint) && !defined(LINT) 24 static char rcsid[] = "$OpenBSD: misc.c,v 1.11 2001/12/12 19:02:50 millert Exp $"; 25 #endif 26 27 /* vix 26jan87 [RCS has the rest of the log] 28 * vix 30dec86 [written] 29 */ 30 31 32 #include "cron.h" 33 #if SYS_TIME_H 34 # include <sys/time.h> 35 #else 36 # include <time.h> 37 #endif 38 #include <sys/file.h> 39 #include <sys/stat.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #if defined(SYSLOG) 43 # include <syslog.h> 44 #endif 45 46 47 #if defined(LOG_CRON) && defined(LOG_FILE) 48 # undef LOG_FILE 49 #endif 50 51 #if defined(LOG_DAEMON) && !defined(LOG_CRON) 52 # define LOG_CRON LOG_DAEMON 53 #endif 54 55 56 static int LogFD = ERR; 57 58 /* 59 * glue_strings is the overflow-safe equivalent of 60 * sprintf(buffer, "%s%c%s", a, separator, b); 61 * 62 * returns 1 on success, 0 on failure. 'buffer' MUST NOT be used if 63 * glue_strings fails. 64 */ 65 int 66 glue_strings(buffer, buffer_size, a, b, separator) 67 char *buffer; 68 int buffer_size; 69 char *a; 70 char *b; 71 int separator; 72 { 73 char *buf; 74 char *buf_end; 75 76 if (buffer_size <= 0) 77 return (0); 78 buf_end = buffer + buffer_size; 79 buf = buffer; 80 81 for ( /* nothing */; buf < buf_end && *a != '\0'; buf++, a++ ) 82 *buf = *a; 83 if (buf == buf_end) 84 return (0); 85 if (separator != '/' || buf == buffer || buf[-1] != '/') 86 *buf++ = separator; 87 if (buf == buf_end) 88 return (0); 89 for ( /* nothing */; buf < buf_end && *b != '\0'; buf++, b++ ) 90 *buf = *b; 91 if (buf == buf_end) 92 return (0); 93 *buf = '\0'; 94 return (1); 95 } 96 97 int 98 strcmp_until(const char *left, const char *right, int until) { 99 while (*left && *left != until && *left == *right) { 100 left++; 101 right++; 102 } 103 104 if ((*left=='\0' || *left == until) && 105 (*right=='\0' || *right == until)) { 106 return (0); 107 } 108 return (*left - *right); 109 } 110 111 112 /* strdtb(s) - delete trailing blanks in string 's' and return new length 113 */ 114 int 115 strdtb(s) 116 char *s; 117 { 118 char *x = s; 119 120 /* scan forward to the null 121 */ 122 while (*x) 123 x++; 124 125 /* scan backward to either the first character before the string, 126 * or the last non-blank in the string, whichever comes first. 127 */ 128 do {x--;} 129 while (x >= s && isspace(*x)); 130 131 /* one character beyond where we stopped above is where the null 132 * goes. 133 */ 134 *++x = '\0'; 135 136 /* the difference between the position of the null character and 137 * the position of the first character of the string is the length. 138 */ 139 return (x - s); 140 } 141 142 143 int 144 set_debug_flags(flags) 145 char *flags; 146 { 147 /* debug flags are of the form flag[,flag ...] 148 * 149 * if an error occurs, print a message to stdout and return FALSE. 150 * otherwise return TRUE after setting ERROR_FLAGS. 151 */ 152 153 #if !DEBUGGING 154 155 printf("this program was compiled without debugging enabled\n"); 156 return (FALSE); 157 158 #else /* DEBUGGING */ 159 160 char *pc = flags; 161 162 DebugFlags = 0; 163 164 while (*pc) { 165 const char **test; 166 int mask; 167 168 /* try to find debug flag name in our list. 169 */ 170 for ( test = DebugFlagNames, mask = 1; 171 *test != NULL && strcmp_until(*test, pc, ','); 172 test++, mask <<= 1 173 ) 174 ; 175 176 if (!*test) { 177 fprintf(stderr, 178 "unrecognized debug flag <%s> <%s>\n", 179 flags, pc); 180 return (FALSE); 181 } 182 183 DebugFlags |= mask; 184 185 /* skip to the next flag 186 */ 187 while (*pc && *pc != ',') 188 pc++; 189 if (*pc == ',') 190 pc++; 191 } 192 193 if (DebugFlags) { 194 int flag; 195 196 fprintf(stderr, "debug flags enabled:"); 197 198 for (flag = 0; DebugFlagNames[flag]; flag++) 199 if (DebugFlags & (1 << flag)) 200 fprintf(stderr, " %s", DebugFlagNames[flag]); 201 fprintf(stderr, "\n"); 202 } 203 204 return (TRUE); 205 206 #endif /* DEBUGGING */ 207 } 208 209 210 void 211 set_cron_uid() 212 { 213 #if defined(BSD) || defined(POSIX) 214 if (seteuid(ROOT_UID) < OK) { 215 perror("seteuid"); 216 exit(ERROR_EXIT); 217 } 218 #else 219 if (setuid(ROOT_UID) < OK) { 220 perror("setuid"); 221 exit(ERROR_EXIT); 222 } 223 #endif 224 } 225 226 227 void 228 set_cron_cwd() 229 { 230 struct stat sb; 231 232 /* first check for CRONDIR ("/var/cron" or some such) 233 */ 234 if (stat(CRONDIR, &sb) < OK && errno == ENOENT) { 235 perror(CRONDIR); 236 if (OK == mkdir(CRONDIR, 0700)) { 237 fprintf(stderr, "%s: created\n", CRONDIR); 238 stat(CRONDIR, &sb); 239 } else { 240 fprintf(stderr, "%s: ", CRONDIR); 241 perror("mkdir"); 242 exit(ERROR_EXIT); 243 } 244 } 245 if (!(sb.st_mode & S_IFDIR)) { 246 fprintf(stderr, "'%s' is not a directory, bailing out.\n", 247 CRONDIR); 248 exit(ERROR_EXIT); 249 } 250 if (chdir(CRONDIR) < OK) { 251 fprintf(stderr, "cannot chdir(%s), bailing out.\n", CRONDIR); 252 perror(CRONDIR); 253 exit(ERROR_EXIT); 254 } 255 256 /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such) 257 */ 258 if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) { 259 perror(SPOOL_DIR); 260 if (OK == mkdir(SPOOL_DIR, 0700)) { 261 fprintf(stderr, "%s: created\n", SPOOL_DIR); 262 stat(SPOOL_DIR, &sb); 263 } else { 264 fprintf(stderr, "%s: ", SPOOL_DIR); 265 perror("mkdir"); 266 exit(ERROR_EXIT); 267 } 268 } 269 if (!(sb.st_mode & S_IFDIR)) { 270 fprintf(stderr, "'%s' is not a directory, bailing out.\n", 271 SPOOL_DIR); 272 exit(ERROR_EXIT); 273 } 274 } 275 276 277 /* acquire_daemonlock() - write our PID into /etc/cron.pid, unless 278 * another daemon is already running, which we detect here. 279 * 280 * note: main() calls us twice; once before forking, once after. 281 * we maintain static storage of the file pointer so that we 282 * can rewrite our PID into the PIDFILE after the fork. 283 * 284 * it would be great if fflush() disassociated the file buffer. 285 */ 286 void 287 acquire_daemonlock(closeflag) 288 int closeflag; 289 { 290 static FILE *fp = NULL; 291 char buf[3*MAX_FNAME]; 292 char pidfile[MAX_FNAME]; 293 int fd; 294 PID_T otherpid; 295 296 if (closeflag && fp) { 297 fclose(fp); 298 fp = NULL; 299 return; 300 } 301 302 if (!fp) { 303 if (!glue_strings(pidfile, sizeof pidfile, PIDDIR, 304 PIDFILE, '/')) { 305 fprintf(stderr, "%s%s: path too long\n", 306 PIDDIR, PIDFILE); 307 log_it("CRON", getpid(), "DEATH", "path too long"); 308 exit(ERROR_EXIT); 309 } 310 if ((-1 == (fd = open(pidfile, O_RDWR|O_CREAT, 0644))) || 311 (NULL == (fp = fdopen(fd, "r+")))) { 312 snprintf(buf, sizeof buf, "can't open or create %s: %s", 313 pidfile, strerror(errno)); 314 fprintf(stderr, "%s: %s\n", ProgramName, buf); 315 log_it("CRON", getpid(), "DEATH", buf); 316 exit(ERROR_EXIT); 317 } 318 319 if (flock(fd, LOCK_EX|LOCK_NB) < OK) { 320 int save_errno = errno; 321 322 if (fscanf(fp, "%d", &otherpid) == 1) 323 snprintf(buf, sizeof buf, 324 "can't lock %s, otherpid may be %d: %s", 325 pidfile, otherpid, strerror(save_errno)); 326 else 327 snprintf(buf, sizeof buf, 328 "can't lock %s, otherpid unknown: %s", 329 pidfile, strerror(save_errno)); 330 fprintf(stderr, "%s: %s\n", ProgramName, buf); 331 log_it("CRON", getpid(), "DEATH", buf); 332 exit(ERROR_EXIT); 333 } 334 335 (void) fcntl(fd, F_SETFD, 1); 336 } 337 338 rewind(fp); 339 fprintf(fp, "%ld\n", (long)getpid()); 340 fflush(fp); 341 (void) ftruncate(fileno(fp), ftell(fp)); 342 343 /* abandon fd and fp even though the file is open. we need to 344 * keep it open and locked, but we don't need the handles elsewhere. 345 */ 346 } 347 348 /* get_char(file) : like getc() but increment LineNumber on newlines 349 */ 350 int 351 get_char(file) 352 FILE *file; 353 { 354 int ch; 355 356 ch = getc(file); 357 if (ch == '\n') 358 Set_LineNum(LineNumber + 1) 359 return (ch); 360 } 361 362 363 /* unget_char(ch, file) : like ungetc but do LineNumber processing 364 */ 365 void 366 unget_char(ch, file) 367 int ch; 368 FILE *file; 369 { 370 ungetc(ch, file); 371 if (ch == '\n') 372 Set_LineNum(LineNumber - 1) 373 } 374 375 376 /* get_string(str, max, file, termstr) : like fgets() but 377 * (1) has terminator string which should include \n 378 * (2) will always leave room for the null 379 * (3) uses get_char() so LineNumber will be accurate 380 * (4) returns EOF or terminating character, whichever 381 */ 382 int 383 get_string(string, size, file, terms) 384 char *string; 385 int size; 386 FILE *file; 387 char *terms; 388 { 389 int ch; 390 391 while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) { 392 if (size > 1) { 393 *string++ = (char) ch; 394 size--; 395 } 396 } 397 398 if (size > 0) 399 *string = '\0'; 400 401 return (ch); 402 } 403 404 405 /* skip_comments(file) : read past comment (if any) 406 */ 407 void 408 skip_comments(file) 409 FILE *file; 410 { 411 int ch; 412 413 while (EOF != (ch = get_char(file))) { 414 /* ch is now the first character of a line. 415 */ 416 417 while (ch == ' ' || ch == '\t') 418 ch = get_char(file); 419 420 if (ch == EOF) 421 break; 422 423 /* ch is now the first non-blank character of a line. 424 */ 425 426 if (ch != '\n' && ch != '#') 427 break; 428 429 /* ch must be a newline or comment as first non-blank 430 * character on a line. 431 */ 432 433 while (ch != '\n' && ch != EOF) 434 ch = get_char(file); 435 436 /* ch is now the newline of a line which we're going to 437 * ignore. 438 */ 439 } 440 if (ch != EOF) 441 unget_char(ch, file); 442 } 443 444 445 /* int in_file(char *string, FILE *file) 446 * return TRUE if one of the lines in file matches string exactly, 447 * FALSE otherwise. 448 */ 449 static int 450 in_file(string, file) 451 char *string; 452 FILE *file; 453 { 454 char line[MAX_TEMPSTR]; 455 456 rewind(file); 457 while (fgets(line, MAX_TEMPSTR, file)) { 458 if (line[0] != '\0') 459 line[strlen(line)-1] = '\0'; 460 if (0 == strcmp(line, string)) 461 return (TRUE); 462 } 463 return (FALSE); 464 } 465 466 467 /* int allowed(char *username) 468 * returns TRUE if (ALLOW_FILE exists and user is listed) 469 * or (DENY_FILE exists and user is NOT listed) 470 * or (neither file exists but user=="root" so it's okay) 471 */ 472 int 473 allowed(username) 474 char *username; 475 { 476 static int init = FALSE; 477 static FILE *allow, *deny; 478 479 if (!init) { 480 init = TRUE; 481 #if defined(ALLOW_FILE) && defined(DENY_FILE) 482 allow = fopen(ALLOW_FILE, "r"); 483 deny = fopen(DENY_FILE, "r"); 484 Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny)) 485 #else 486 allow = NULL; 487 deny = NULL; 488 #endif 489 } 490 491 if (allow) 492 return (in_file(username, allow)); 493 if (deny) 494 return (!in_file(username, deny)); 495 496 #if defined(ALLOW_ONLY_ROOT) 497 return (strcmp(username, ROOT_USER) == 0); 498 #else 499 return (TRUE); 500 #endif 501 } 502 503 504 void 505 log_it(username, xpid, event, detail) 506 const char *username; 507 int xpid; 508 const char *event; 509 const char *detail; 510 { 511 #if defined(LOG_FILE) || DEBUGGING 512 PID_T pid = xpid; 513 #endif 514 #if defined(LOG_FILE) 515 char *msg; 516 size_t msglen; 517 TIME_T now = time((TIME_T) 0); 518 struct tm *t = localtime(&now); 519 #endif /*LOG_FILE*/ 520 521 #if defined(SYSLOG) 522 static int syslog_open = 0; 523 #endif 524 525 #if defined(LOG_FILE) 526 /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation. 527 */ 528 msglen = strlen(username) + strlen(event) + strlen(detail) + 529 MAX_TEMPSTR; 530 if ((msg = malloc(msglen)) == NULL) 531 return; 532 533 if (LogFD < OK) { 534 LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600); 535 if (LogFD < OK) { 536 fprintf(stderr, "%s: can't open log file\n", 537 ProgramName); 538 perror(LOG_FILE); 539 } else { 540 (void) fcntl(LogFD, F_SETFD, 1); 541 } 542 } 543 544 /* we have to snprintf() it because fprintf() doesn't always write 545 * everything out in one chunk and this has to be atomically appended 546 * to the log file. 547 */ 548 snprintf(msg, msglen, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n", 549 username, 550 t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, pid, 551 event, detail); 552 553 /* we have to run strlen() because sprintf() returns (char*) on old BSD 554 */ 555 if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) { 556 if (LogFD >= OK) 557 perror(LOG_FILE); 558 fprintf(stderr, "%s: can't write to log file\n", ProgramName); 559 write(STDERR, msg, strlen(msg)); 560 } 561 562 free(msg); 563 #endif /*LOG_FILE*/ 564 565 #if defined(SYSLOG) 566 if (!syslog_open) { 567 /* we don't use LOG_PID since the pid passed to us by 568 * our client may not be our own. therefore we want to 569 * print the pid ourselves. 570 */ 571 # ifdef LOG_DAEMON 572 openlog(ProgramName, LOG_PID, LOG_CRON); 573 # else 574 openlog(ProgramName, LOG_PID); 575 # endif 576 syslog_open = TRUE; /* assume openlog success */ 577 } 578 579 syslog(LOG_INFO, "(%s) %s (%s)", username, event, detail); 580 581 #endif /*SYSLOG*/ 582 583 #if DEBUGGING 584 if (DebugFlags) { 585 fprintf(stderr, "log_it: (%s %ld) %s (%s)\n", 586 username, (long)pid, event, detail); 587 } 588 #endif 589 } 590 591 592 void 593 log_close() { 594 if (LogFD != ERR) { 595 close(LogFD); 596 LogFD = ERR; 597 } 598 } 599 600 601 /* two warnings: 602 * (1) this routine is fairly slow 603 * (2) it returns a pointer to static storage 604 */ 605 char * 606 first_word(s, t) 607 char *s; /* string we want the first word of */ 608 char *t; /* terminators, implicitly including \0 */ 609 { 610 static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */ 611 static int retsel = 0; 612 char *rb, *rp; 613 614 /* select a return buffer */ 615 retsel = 1-retsel; 616 rb = &retbuf[retsel][0]; 617 rp = rb; 618 619 /* skip any leading terminators */ 620 while (*s && (NULL != strchr(t, *s))) { 621 s++; 622 } 623 624 /* copy until next terminator or full buffer */ 625 while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) { 626 *rp++ = *s++; 627 } 628 629 /* finish the return-string and return it */ 630 *rp = '\0'; 631 return (rb); 632 } 633 634 635 /* warning: 636 * heavily ascii-dependent. 637 */ 638 void 639 mkprint(dst, src, len) 640 char *dst; 641 unsigned char *src; 642 int len; 643 { 644 /* 645 * XXX 646 * We know this routine can't overflow the dst buffer because mkprints() 647 * allocated enough space for the worst case. 648 */ 649 while (len-- > 0) 650 { 651 unsigned char ch = *src++; 652 653 if (ch < ' ') { /* control character */ 654 *dst++ = '^'; 655 *dst++ = ch + '@'; 656 } else if (ch < 0177) { /* printable */ 657 *dst++ = ch; 658 } else if (ch == 0177) { /* delete/rubout */ 659 *dst++ = '^'; 660 *dst++ = '?'; 661 } else { /* parity character */ 662 snprintf(dst, 5, "\\%03o", ch); 663 dst += 4; 664 } 665 } 666 *dst = '\0'; 667 } 668 669 670 /* warning: 671 * returns a pointer to malloc'd storage, you must call free yourself. 672 */ 673 char * 674 mkprints(src, len) 675 unsigned char *src; 676 unsigned int len; 677 { 678 char *dst = malloc(len*4 + 1); 679 680 if (dst) 681 mkprint(dst, src, len); 682 683 return (dst); 684 } 685 686 687 #ifdef MAIL_DATE 688 /* Sat, 27 Feb 1993 11:44:51 -0800 (CST) 689 * 1234567890123456789012345678901234567 690 */ 691 char * 692 arpadate(clock) 693 time_t *clock; 694 { 695 time_t t = clock ? *clock : time(NULL); 696 struct tm *tm = localtime(&t); 697 static char ret[64]; /* zone name might be >3 chars */ 698 char *qmark; 699 size_t len; 700 long gmtoff = get_gmtoff(&t, tm); 701 int hours = gmtoff / 3600; 702 int minutes = (gmtoff - (hours * 3600)) / 60; 703 704 if (minutes < 0) 705 minutes = -minutes; 706 707 /* Defensive coding (almost) never hurts... */ 708 len = strftime(ret, sizeof(ret), "%a, %e %b %Y %T ????? (%Z)", tm); 709 if (len == 0) { 710 ret[0] = '?'; 711 ret[1] = '\0'; 712 return (ret); 713 } 714 qmark = strchr(ret, '?'); 715 if (qmark && len - (qmark - ret) >= 6) { 716 snprintf(qmark, 6, "% .2d%.2d", hours, minutes); 717 qmark[5] = ' '; 718 } 719 return (ret); 720 } 721 #endif /*MAIL_DATE*/ 722 723 #ifdef HAVE_SAVED_UIDS 724 static uid_t save_euid; 725 int swap_uids() { save_euid = geteuid(); return (seteuid(getuid())); } 726 int swap_uids_back() { return (seteuid(save_euid)); } 727 #else /*HAVE_SAVED_UIDS*/ 728 int swap_uids() { return (setreuid(geteuid(), getuid())); } 729 int swap_uids_back() { return (swap_uids()); } 730 #endif /*HAVE_SAVED_UIDS*/ 731 732 /* Return the offset from GMT in seconds (algorithm taken from sendmail). */ 733 #ifndef HAVE_TM_GMTOFF 734 long get_gmtoff(time_t *clock, struct tm *local) 735 { 736 struct tm gmt; 737 long offset; 738 739 gmt = *gmtime(clock); 740 if (local == NULL) 741 local = localtime(clock); 742 743 offset = (local->tm_sec - gmt.tm_sec) + 744 ((local->tm_min - gmt.tm_min) * 60) + 745 ((local->tm_hour - gmt.tm_hour) * 3600); 746 747 /* Timezone may cause year rollover to happen on a different day. */ 748 if (local->tm_year < gmt.tm_year) 749 offset -= 24 * 3600; 750 else if (local->tm_year > gmt.tm_year) 751 offset -= 24 * 3600; 752 else if (local->tm_yday < gmt.tm_yday) 753 offset -= 24 * 3600; 754 else if (local->tm_yday > gmt.tm_yday) 755 offset += 24 * 3600; 756 757 return (offset); 758 } 759 #endif /* HAVE_TM_GMTOFF */ 760