1 /* Copyright 1988,1990,1993,1994 by Paul Vixie 2 * All rights reserved 3 * 4 * Distribute freely, except: don't remove my name from the source or 5 * documentation (don't take credit for my work), mark your changes (don't 6 * get me blamed for your possible bugs), don't alter or remove this 7 * notice. May be sold if buildable source is provided to buyer. No 8 * warrantee of any kind, express or implied, is included with this 9 * software; use at your own risk, responsibility for damages (if any) to 10 * anyone resulting from the use of this software rests entirely with the 11 * user. 12 * 13 * Send bug reports, bug fixes, enhancements, requests, flames, etc., and 14 * I'll try to keep a version up to date. I can be reached as follows: 15 * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul 16 * 17 * $FreeBSD: src/usr.sbin/cron/lib/misc.c,v 1.8.2.2 2002/04/28 22:45:53 dwmalone Exp $ 18 * $DragonFly: src/usr.sbin/cron/lib/misc.c,v 1.5 2004/12/18 22:48:03 swildner Exp $ 19 */ 20 21 /* vix 26jan87 [RCS has the rest of the log] 22 * vix 30dec86 [written] 23 */ 24 25 26 #include "cron.h" 27 #if SYS_TIME_H 28 # include <sys/time.h> 29 #else 30 # include <time.h> 31 #endif 32 #include <sys/file.h> 33 #include <sys/stat.h> 34 #include <err.h> 35 #include <errno.h> 36 #include <string.h> 37 #include <fcntl.h> 38 #if defined(SYSLOG) 39 # include <syslog.h> 40 #endif 41 42 43 #if defined(LOG_DAEMON) && !defined(LOG_CRON) 44 #define LOG_CRON LOG_DAEMON 45 #endif 46 47 48 static int LogFD = ERR; 49 50 51 int 52 strcmp_until(char *left, char *right, int until) 53 { 54 int diff; 55 56 while (*left && *left != until && *left == *right) { 57 left++; 58 right++; 59 } 60 61 if ((*left=='\0' || *left == until) && 62 (*right=='\0' || *right == until)) { 63 diff = 0; 64 } else { 65 diff = *left - *right; 66 } 67 68 return diff; 69 } 70 71 72 /* strdtb(s) - delete trailing blanks in string 's' and return new length 73 */ 74 int 75 strdtb(char *s) 76 { 77 char *x = s; 78 79 /* scan forward to the null 80 */ 81 while (*x) 82 x++; 83 84 /* scan backward to either the first character before the string, 85 * or the last non-blank in the string, whichever comes first. 86 */ 87 do {x--;} 88 while (x >= s && isspace(*x)); 89 90 /* one character beyond where we stopped above is where the null 91 * goes. 92 */ 93 *++x = '\0'; 94 95 /* the difference between the position of the null character and 96 * the position of the first character of the string is the length. 97 */ 98 return x - s; 99 } 100 101 102 int 103 set_debug_flags(char *flags) 104 { 105 /* debug flags are of the form flag[,flag ...] 106 * 107 * if an error occurs, print a message to stdout and return FALSE. 108 * otherwise return TRUE after setting ERROR_FLAGS. 109 */ 110 111 #if !DEBUGGING 112 113 printf("this program was compiled without debugging enabled\n"); 114 return FALSE; 115 116 #else /* DEBUGGING */ 117 118 char *pc = flags; 119 120 DebugFlags = 0; 121 122 while (*pc) { 123 char **test; 124 int mask; 125 126 /* try to find debug flag name in our list. 127 */ 128 for ( test = DebugFlagNames, mask = 1; 129 *test && strcmp_until(*test, pc, ','); 130 test++, mask <<= 1 131 ) 132 ; 133 134 if (!*test) { 135 fprintf(stderr, 136 "unrecognized debug flag <%s> <%s>\n", 137 flags, pc); 138 return FALSE; 139 } 140 141 DebugFlags |= mask; 142 143 /* skip to the next flag 144 */ 145 while (*pc && *pc != ',') 146 pc++; 147 if (*pc == ',') 148 pc++; 149 } 150 151 if (DebugFlags) { 152 int flag; 153 154 fprintf(stderr, "debug flags enabled:"); 155 156 for (flag = 0; DebugFlagNames[flag]; flag++) 157 if (DebugFlags & (1 << flag)) 158 fprintf(stderr, " %s", DebugFlagNames[flag]); 159 fprintf(stderr, "\n"); 160 } 161 162 return TRUE; 163 164 #endif /* DEBUGGING */ 165 } 166 167 168 void 169 set_cron_uid(void) 170 { 171 #if defined(BSD) || defined(POSIX) 172 if (seteuid(ROOT_UID) < OK) 173 err(ERROR_EXIT, "seteuid"); 174 #else 175 if (setuid(ROOT_UID) < OK) 176 err(ERROR_EXIT, "setuid"); 177 #endif 178 } 179 180 181 void 182 set_cron_cwd(void) 183 { 184 struct stat sb; 185 186 /* first check for CRONDIR ("/var/cron" or some such) 187 */ 188 if (stat(CRONDIR, &sb) < OK && errno == ENOENT) { 189 warn("%s", CRONDIR); 190 if (OK == mkdir(CRONDIR, 0700)) { 191 warnx("%s: created", CRONDIR); 192 stat(CRONDIR, &sb); 193 } else { 194 err(ERROR_EXIT, "%s: mkdir", CRONDIR); 195 } 196 } 197 if (!(sb.st_mode & S_IFDIR)) 198 err(ERROR_EXIT, "'%s' is not a directory, bailing out", CRONDIR); 199 if (chdir(CRONDIR) < OK) 200 err(ERROR_EXIT, "cannot chdir(%s), bailing out", CRONDIR); 201 202 /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such) 203 */ 204 if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) { 205 warn("%s", SPOOL_DIR); 206 if (OK == mkdir(SPOOL_DIR, 0700)) { 207 warnx("%s: created", SPOOL_DIR); 208 stat(SPOOL_DIR, &sb); 209 } else { 210 err(ERROR_EXIT, "%s: mkdir", SPOOL_DIR); 211 } 212 } 213 if (!(sb.st_mode & S_IFDIR)) 214 err(ERROR_EXIT, "'%s' is not a directory, bailing out", SPOOL_DIR); 215 } 216 217 218 /* acquire_daemonlock() - write our PID into /etc/cron.pid, unless 219 * another daemon is already running, which we detect here. 220 * 221 * note: main() calls us twice; once before forking, once after. 222 * we maintain static storage of the file pointer so that we 223 * can rewrite our PID into the PIDFILE after the fork. 224 * 225 * it would be great if fflush() disassociated the file buffer. 226 */ 227 void 228 acquire_daemonlock(int closeflag) 229 { 230 static FILE *fp = NULL; 231 232 if (closeflag && fp) { 233 fclose(fp); 234 fp = NULL; 235 return; 236 } 237 238 if (!fp) { 239 char pidfile[MAX_FNAME]; 240 char buf[MAX_TEMPSTR]; 241 int fd, otherpid; 242 243 sprintf(pidfile, PIDFILE, PIDDIR); 244 if ((-1 == (fd = open(pidfile, O_RDWR|O_CREAT, 0644))) 245 || (NULL == (fp = fdopen(fd, "r+"))) 246 ) { 247 sprintf(buf, "can't open or create %s: %s", 248 pidfile, strerror(errno)); 249 log_it("CRON", getpid(), "DEATH", buf); 250 errx(ERROR_EXIT, "%s", buf); 251 } 252 253 if (flock(fd, LOCK_EX|LOCK_NB) < OK) { 254 int save_errno = errno; 255 256 fscanf(fp, "%d", &otherpid); 257 sprintf(buf, "can't lock %s, otherpid may be %d: %s", 258 pidfile, otherpid, strerror(save_errno)); 259 log_it("CRON", getpid(), "DEATH", buf); 260 errx(ERROR_EXIT, "%s", buf); 261 } 262 263 fcntl(fd, F_SETFD, 1); 264 } 265 266 rewind(fp); 267 fprintf(fp, "%d\n", getpid()); 268 fflush(fp); 269 ftruncate(fileno(fp), ftell(fp)); 270 271 /* abandon fd and fp even though the file is open. we need to 272 * keep it open and locked, but we don't need the handles elsewhere. 273 */ 274 } 275 276 /* get_char(file) : like getc() but increment LineNumber on newlines 277 */ 278 int 279 get_char(FILE *file) 280 { 281 int ch; 282 283 ch = getc(file); 284 if (ch == '\n') 285 Set_LineNum(LineNumber + 1) 286 return ch; 287 } 288 289 290 /* unget_char(ch, file) : like ungetc but do LineNumber processing 291 */ 292 void 293 unget_char(int ch, FILE *file) 294 { 295 ungetc(ch, file); 296 if (ch == '\n') 297 Set_LineNum(LineNumber - 1) 298 } 299 300 301 /* get_string(str, max, file, termstr) : like fgets() but 302 * (1) has terminator string which should include \n 303 * (2) will always leave room for the null 304 * (3) uses get_char() so LineNumber will be accurate 305 * (4) returns EOF or terminating character, whichever 306 */ 307 int 308 get_string(char *string, int size, FILE *file, char *terms) 309 { 310 int ch; 311 312 while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) { 313 if (size > 1) { 314 *string++ = (char) ch; 315 size--; 316 } 317 } 318 319 if (size > 0) 320 *string = '\0'; 321 322 return ch; 323 } 324 325 326 /* skip_comments(file) : read past comment (if any) 327 */ 328 void 329 skip_comments(FILE *file) 330 { 331 int ch; 332 333 while (EOF != (ch = get_char(file))) { 334 /* ch is now the first character of a line. 335 */ 336 337 while (ch == ' ' || ch == '\t') 338 ch = get_char(file); 339 340 if (ch == EOF) 341 break; 342 343 /* ch is now the first non-blank character of a line. 344 */ 345 346 if (ch != '\n' && ch != '#') 347 break; 348 349 /* ch must be a newline or comment as first non-blank 350 * character on a line. 351 */ 352 353 while (ch != '\n' && ch != EOF) 354 ch = get_char(file); 355 356 /* ch is now the newline of a line which we're going to 357 * ignore. 358 */ 359 } 360 if (ch != EOF) 361 unget_char(ch, file); 362 } 363 364 365 /* int in_file(char *string, FILE *file) 366 * return TRUE if one of the lines in file matches string exactly, 367 * FALSE otherwise. 368 */ 369 static int 370 in_file(char *string, FILE *file) 371 { 372 char line[MAX_TEMPSTR]; 373 374 rewind(file); 375 while (fgets(line, MAX_TEMPSTR, file)) { 376 if (line[0] != '\0') 377 if (line[strlen(line)-1] == '\n') 378 line[strlen(line)-1] = '\0'; 379 if (0 == strcmp(line, string)) 380 return TRUE; 381 } 382 return FALSE; 383 } 384 385 386 /* int allowed(char *username) 387 * returns TRUE if (ALLOW_FILE exists and user is listed) 388 * or (DENY_FILE exists and user is NOT listed) 389 * or (neither file exists but user=="root" so it's okay) 390 */ 391 int 392 allowed(char *username) 393 { 394 static int init = FALSE; 395 static FILE *allow, *deny; 396 397 if (!init) { 398 init = TRUE; 399 #if defined(ALLOW_FILE) && defined(DENY_FILE) 400 allow = fopen(ALLOW_FILE, "r"); 401 deny = fopen(DENY_FILE, "r"); 402 Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny)) 403 #else 404 allow = NULL; 405 deny = NULL; 406 #endif 407 } 408 409 if (allow) 410 return (in_file(username, allow)); 411 if (deny) 412 return (!in_file(username, deny)); 413 414 #if defined(ALLOW_ONLY_ROOT) 415 return (strcmp(username, ROOT_USER) == 0); 416 #else 417 return TRUE; 418 #endif 419 } 420 421 422 void 423 log_it(char *username, int xpid, char *event, char *detail) 424 { 425 PID_T pid = xpid; 426 #if defined(LOG_FILE) 427 char *msg; 428 TIME_T now; 429 struct tm *t; 430 #endif /*LOG_FILE*/ 431 432 #if defined(SYSLOG) 433 static int syslog_open = 0; 434 #endif 435 436 #if defined(LOG_FILE) 437 now = time((TIME_T)0); 438 t = localtime(&now); 439 /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation. 440 */ 441 msg = malloc(strlen(username) 442 + strlen(event) 443 + strlen(detail) 444 + MAX_TEMPSTR); 445 446 if (msg == NULL) 447 warnx("failed to allocate memory for log message"); 448 else { 449 if (LogFD < OK) { 450 LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600); 451 if (LogFD < OK) { 452 warn("can't open log file %s", LOG_FILE); 453 } else { 454 fcntl(LogFD, F_SETFD, 1); 455 } 456 } 457 458 /* we have to sprintf() it because fprintf() doesn't always 459 * write everything out in one chunk and this has to be 460 * atomically appended to the log file. 461 */ 462 sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n", 463 username, 464 t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, 465 t->tm_sec, pid, event, detail); 466 467 /* we have to run strlen() because sprintf() returns (char*) 468 * on old BSD. 469 */ 470 if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) { 471 if (LogFD >= OK) 472 warn("%s", LOG_FILE); 473 warnx("can't write to log file"); 474 write(STDERR, msg, strlen(msg)); 475 } 476 477 free(msg); 478 } 479 #endif /*LOG_FILE*/ 480 481 #if defined(SYSLOG) 482 if (!syslog_open) { 483 /* we don't use LOG_PID since the pid passed to us by 484 * our client may not be our own. therefore we want to 485 * print the pid ourselves. 486 */ 487 # ifdef LOG_DAEMON 488 openlog(ProgramName, LOG_PID, LOG_CRON); 489 # else 490 openlog(ProgramName, LOG_PID); 491 # endif 492 syslog_open = TRUE; /* assume openlog success */ 493 } 494 495 syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail); 496 497 #endif /*SYSLOG*/ 498 499 #if DEBUGGING 500 if (DebugFlags) { 501 fprintf(stderr, "log_it: (%s %d) %s (%s)\n", 502 username, pid, event, detail); 503 } 504 #endif 505 } 506 507 508 void 509 log_close(void) { 510 if (LogFD != ERR) { 511 close(LogFD); 512 LogFD = ERR; 513 } 514 } 515 516 517 /* two warnings: 518 * (1) this routine is fairly slow 519 * (2) it returns a pointer to static storage 520 * 521 * s string we want the first word of 522 * t terminators, implicitly including \0 523 */ 524 char * 525 first_word(char *s, char *t) 526 { 527 static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */ 528 static int retsel = 0; 529 char *rb, *rp; 530 531 /* select a return buffer */ 532 retsel = 1-retsel; 533 rb = &retbuf[retsel][0]; 534 rp = rb; 535 536 /* skip any leading terminators */ 537 while (*s && (NULL != strchr(t, *s))) { 538 s++; 539 } 540 541 /* copy until next terminator or full buffer */ 542 while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) { 543 *rp++ = *s++; 544 } 545 546 /* finish the return-string and return it */ 547 *rp = '\0'; 548 return rb; 549 } 550 551 552 /* warning: 553 * heavily ascii-dependent. 554 */ 555 void 556 mkprint(char *dst, unsigned char *src, int len) 557 { 558 while (len-- > 0) 559 { 560 unsigned char ch = *src++; 561 562 if (ch < ' ') { /* control character */ 563 *dst++ = '^'; 564 *dst++ = ch + '@'; 565 } else if (ch < 0177) { /* printable */ 566 *dst++ = ch; 567 } else if (ch == 0177) { /* delete/rubout */ 568 *dst++ = '^'; 569 *dst++ = '?'; 570 } else { /* parity character */ 571 sprintf(dst, "\\%03o", ch); 572 dst += 4; 573 } 574 } 575 *dst = '\0'; 576 } 577 578 579 /* warning: 580 * returns a pointer to malloc'd storage, you must call free yourself. 581 */ 582 char * 583 mkprints(unsigned char *src, unsigned int len) 584 { 585 char *dst; 586 587 dst = malloc(len * 4 + 1); 588 if (dst != NULL) 589 mkprint(dst, src, len); 590 591 return dst; 592 } 593 594 595 #ifdef MAIL_DATE 596 /* Sat, 27 Feb 93 11:44:51 CST 597 * 123456789012345678901234567 598 */ 599 char * 600 arpadate(time_t *clock) 601 { 602 time_t t = clock ?*clock :time(0L); 603 struct tm *tm = localtime(&t); 604 static char ret[32]; /* zone name might be >3 chars */ 605 606 if (tm->tm_year >= 100) 607 tm->tm_year += 1900; 608 609 snprintf(ret, sizeof(ret), "%s, %2d %s %d %02d:%02d:%02d %s", 610 DowNames[tm->tm_wday], 611 tm->tm_mday, 612 MonthNames[tm->tm_mon], 613 tm->tm_year, 614 tm->tm_hour, 615 tm->tm_min, 616 tm->tm_sec, 617 TZONE(*tm)); 618 return ret; 619 } 620 #endif /*MAIL_DATE*/ 621 622 623 #ifdef HAVE_SAVED_UIDS 624 static int save_euid; 625 int 626 swap_uids(void) 627 { 628 save_euid = geteuid(); 629 return seteuid(getuid()); 630 } 631 int 632 swap_uids_back(void) 633 { 634 635 return seteuid(save_euid); 636 } 637 #else /*HAVE_SAVED_UIDS*/ 638 int 639 swap_uids(void) 640 { 641 return setreuid(geteuid(), getuid()); 642 } 643 int 644 swap_uids_back(void) 645 { 646 return swap_uids(); 647 } 648 #endif /*HAVE_SAVED_UIDS*/ 649