1 /* Copyright 1988,1990,1993,1994 by Paul Vixie 2 * All rights reserved 3 */ 4 5 /* 6 * Copyright (c) 1997 by Internet Software Consortium 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 13 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 14 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 15 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 18 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 19 * SOFTWARE. 20 */ 21 22 23 /* vix 26jan87 [RCS has the rest of the log] 24 * vix 30dec86 [written] 25 */ 26 27 28 #include "cron.h" 29 #if SYS_TIME_H 30 # include <sys/time.h> 31 #else 32 # include <time.h> 33 #endif 34 #include <sys/file.h> 35 #include <sys/stat.h> 36 #include <errno.h> 37 #include <string.h> 38 #include <fcntl.h> 39 #if defined(SYSLOG) 40 # include <syslog.h> 41 #endif 42 43 44 #if defined(LOG_CRON) && defined(LOG_FILE) 45 # undef LOG_FILE 46 #endif 47 48 #if defined(LOG_DAEMON) && !defined(LOG_CRON) 49 # define LOG_CRON LOG_DAEMON 50 #endif 51 52 53 static int LogFD = ERR; 54 55 56 int 57 strcmp_until(const char *left, const char *right, int until) 58 { 59 while (*left && *left != until && *left == *right) { 60 left++; 61 right++; 62 } 63 64 if ((*left=='\0' || *left == until) && 65 (*right=='\0' || *right == until)) { 66 return (0); 67 } 68 return (*left - *right); 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 const 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 != NULL && 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 /* get_char(file) : like getc() but increment LineNumber on newlines 219 */ 220 int 221 get_char(FILE *file) 222 { 223 int ch; 224 225 ch = getc(file); 226 if (ch == '\n') 227 Set_LineNum(LineNumber + 1) 228 return ch; 229 } 230 231 232 /* unget_char(ch, file) : like ungetc but do LineNumber processing 233 */ 234 void 235 unget_char(int ch, FILE *file) 236 { 237 ungetc(ch, file); 238 if (ch == '\n') 239 Set_LineNum(LineNumber - 1) 240 } 241 242 243 /* get_string(str, max, file, termstr) : like fgets() but 244 * (1) has terminator string which should include \n 245 * (2) will always leave room for the null 246 * (3) uses get_char() so LineNumber will be accurate 247 * (4) returns EOF or terminating character, whichever 248 */ 249 int 250 get_string(char *string, int size, FILE *file, char *terms) 251 { 252 int ch; 253 254 while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) { 255 if (size > 1) { 256 *string++ = (char) ch; 257 size--; 258 } 259 } 260 261 if (size > 0) 262 *string = '\0'; 263 264 return ch; 265 } 266 267 268 /* skip_comments(file) : read past comment (if any) 269 */ 270 void 271 skip_comments(FILE *file) 272 { 273 int ch; 274 275 while (EOF != (ch = get_char(file))) { 276 /* ch is now the first character of a line. 277 */ 278 279 while (ch == ' ' || ch == '\t') 280 ch = get_char(file); 281 282 if (ch == EOF) 283 break; 284 285 /* ch is now the first non-blank character of a line. 286 */ 287 288 if (ch != '\n' && ch != '#') 289 break; 290 291 /* ch must be a newline or comment as first non-blank 292 * character on a line. 293 */ 294 295 while (ch != '\n' && ch != EOF) 296 ch = get_char(file); 297 298 /* ch is now the newline of a line which we're going to 299 * ignore. 300 */ 301 } 302 if (ch != EOF) 303 unget_char(ch, file); 304 } 305 306 307 /* int in_file(char *string, FILE *file) 308 * return TRUE if one of the lines in file matches string exactly, 309 * FALSE otherwise. 310 */ 311 static int 312 in_file(char *string, FILE *file) 313 { 314 char line[MAX_TEMPSTR]; 315 316 rewind(file); 317 while (fgets(line, MAX_TEMPSTR, file)) { 318 if (line[0] != '\0') 319 if (line[strlen(line)-1] == '\n') 320 line[strlen(line)-1] = '\0'; 321 if (0 == strcmp(line, string)) 322 return TRUE; 323 } 324 return FALSE; 325 } 326 327 328 /* int allowed(char *username) 329 * returns TRUE if (ALLOW_FILE exists and user is listed) 330 * or (DENY_FILE exists and user is NOT listed) 331 * or (neither file exists but user=="root" so it's okay) 332 */ 333 int 334 allowed(char *username) 335 { 336 FILE *allow, *deny; 337 int isallowed; 338 339 isallowed = FALSE; 340 341 deny = NULL; 342 #if defined(ALLOW_FILE) && defined(DENY_FILE) 343 if ((allow = fopen(ALLOW_FILE, "r")) == NULL && errno != ENOENT) 344 goto out; 345 if ((deny = fopen(DENY_FILE, "r")) == NULL && errno != ENOENT) 346 goto out; 347 Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny)) 348 #else 349 allow = NULL; 350 #endif 351 352 if (allow) 353 isallowed = in_file(username, allow); 354 else if (deny) 355 isallowed = !in_file(username, deny); 356 else { 357 #if defined(ALLOW_ONLY_ROOT) 358 isallowed = (strcmp(username, ROOT_USER) == 0); 359 #else 360 isallowed = TRUE; 361 #endif 362 } 363 out: if (allow) 364 fclose(allow); 365 if (deny) 366 fclose(deny); 367 return (isallowed); 368 } 369 370 371 void 372 log_it(const char *username, int xpid, const char *event, const char *detail) 373 { 374 #if defined(LOG_FILE) || DEBUGGING 375 PID_T pid = xpid; 376 #endif 377 #if defined(LOG_FILE) 378 char *msg; 379 TIME_T now = time((TIME_T) 0); 380 struct tm *t = localtime(&now); 381 #endif /*LOG_FILE*/ 382 383 #if defined(SYSLOG) 384 static int syslog_open = 0; 385 #endif 386 387 #if defined(LOG_FILE) 388 /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation. 389 */ 390 msg = malloc(strlen(username) 391 + strlen(event) 392 + strlen(detail) 393 + MAX_TEMPSTR); 394 395 if (msg == NULL) 396 warnx("failed to allocate memory for log message"); 397 else { 398 if (LogFD < OK) { 399 LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600); 400 if (LogFD < OK) { 401 warn("can't open log file %s", LOG_FILE); 402 } else { 403 (void) fcntl(LogFD, F_SETFD, 1); 404 } 405 } 406 407 /* we have to sprintf() it because fprintf() doesn't always 408 * write everything out in one chunk and this has to be 409 * atomically appended to the log file. 410 */ 411 sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n", 412 username, 413 t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, 414 t->tm_sec, pid, event, detail); 415 416 /* we have to run strlen() because sprintf() returns (char*) 417 * on old BSD. 418 */ 419 if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) { 420 if (LogFD >= OK) 421 warn("%s", LOG_FILE); 422 warnx("can't write to log file"); 423 write(STDERR, msg, strlen(msg)); 424 } 425 426 free(msg); 427 } 428 #endif /*LOG_FILE*/ 429 430 #if defined(SYSLOG) 431 if (!syslog_open) { 432 /* we don't use LOG_PID since the pid passed to us by 433 * our client may not be our own. therefore we want to 434 * print the pid ourselves. 435 */ 436 # ifdef LOG_DAEMON 437 openlog(ProgramName, LOG_PID, LOG_CRON); 438 # else 439 openlog(ProgramName, LOG_PID); 440 # endif 441 syslog_open = TRUE; /* assume openlog success */ 442 } 443 444 syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail); 445 446 #endif /*SYSLOG*/ 447 448 #if DEBUGGING 449 if (DebugFlags) { 450 fprintf(stderr, "log_it: (%s %d) %s (%s)\n", 451 username, pid, event, detail); 452 } 453 #endif 454 } 455 456 457 void 458 log_close(void) 459 { 460 if (LogFD != ERR) { 461 close(LogFD); 462 LogFD = ERR; 463 } 464 } 465 466 467 /* two warnings: 468 * (1) this routine is fairly slow 469 * (2) it returns a pointer to static storage 470 * parameters: 471 * s: string we want the first word of 472 * t: terminators, implicitly including \0 473 */ 474 char * 475 first_word(char *s, char *t) 476 { 477 static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */ 478 static int retsel = 0; 479 char *rb, *rp; 480 481 /* select a return buffer */ 482 retsel = 1-retsel; 483 rb = &retbuf[retsel][0]; 484 rp = rb; 485 486 /* skip any leading terminators */ 487 while (*s && (NULL != strchr(t, *s))) { 488 s++; 489 } 490 491 /* copy until next terminator or full buffer */ 492 while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) { 493 *rp++ = *s++; 494 } 495 496 /* finish the return-string and return it */ 497 *rp = '\0'; 498 return rb; 499 } 500 501 502 /* warning: 503 * heavily ascii-dependent. 504 */ 505 static void 506 mkprint(char *dst, unsigned char *src, int len) 507 { 508 /* 509 * XXX 510 * We know this routine can't overflow the dst buffer because mkprints() 511 * allocated enough space for the worst case. 512 */ 513 while (len-- > 0) 514 { 515 unsigned char ch = *src++; 516 517 if (ch < ' ') { /* control character */ 518 *dst++ = '^'; 519 *dst++ = ch + '@'; 520 } else if (ch < 0177) { /* printable */ 521 *dst++ = ch; 522 } else if (ch == 0177) { /* delete/rubout */ 523 *dst++ = '^'; 524 *dst++ = '?'; 525 } else { /* parity character */ 526 sprintf(dst, "\\%03o", ch); 527 dst += 4; 528 } 529 } 530 *dst = '\0'; 531 } 532 533 534 /* warning: 535 * returns a pointer to malloc'd storage, you must call free yourself. 536 */ 537 char * 538 mkprints(unsigned char *src, unsigned int len) 539 { 540 char *dst = malloc(len*4 + 1); 541 542 if (dst != NULL) 543 mkprint(dst, src, len); 544 545 return dst; 546 } 547 548 549 #ifdef MAIL_DATE 550 /* Sat, 27 Feb 93 11:44:51 CST 551 * 123456789012345678901234567 552 */ 553 char * 554 arpadate(time_t *clock) 555 { 556 time_t t = clock ?*clock :time(0L); 557 struct tm *tm = localtime(&t); 558 static char ret[60]; /* zone name might be >3 chars */ 559 560 if (tm->tm_year >= 100) 561 tm->tm_year += 1900; 562 563 (void) snprintf(ret, sizeof(ret), "%s, %2d %s %d %02d:%02d:%02d %s", 564 DowNames[tm->tm_wday], 565 tm->tm_mday, 566 MonthNames[tm->tm_mon], 567 tm->tm_year, 568 tm->tm_hour, 569 tm->tm_min, 570 tm->tm_sec, 571 TZONE(*tm)); 572 return ret; 573 } 574 #endif /*MAIL_DATE*/ 575 576 577 #ifdef HAVE_SAVED_UIDS 578 static int save_euid; 579 int swap_uids(void) { save_euid = geteuid(); return seteuid(getuid()); } 580 int swap_uids_back(void) { return seteuid(save_euid); } 581 #else /*HAVE_SAVED_UIDS*/ 582 int swap_uids(void) { return setreuid(geteuid(), getuid()); } 583 int swap_uids_back(void) { return swap_uids(); } 584 #endif /*HAVE_SAVED_UIDS*/ 585