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 18 #if !defined(lint) && !defined(LINT) 19 static const char rcsid[] = 20 "$FreeBSD$"; 21 #endif 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 <err.h> 37 #include <errno.h> 38 #include <string.h> 39 #include <fcntl.h> 40 #if defined(SYSLOG) 41 # include <syslog.h> 42 #endif 43 44 45 #if defined(LOG_DAEMON) && !defined(LOG_CRON) 46 #define LOG_CRON LOG_DAEMON 47 #endif 48 49 50 static int LogFD = ERR; 51 52 53 int 54 strcmp_until(char *left, char *right, int until) 55 { 56 register int diff; 57 58 while (*left && *left != until && *left == *right) { 59 left++; 60 right++; 61 } 62 63 if ((*left=='\0' || *left == until) && 64 (*right=='\0' || *right == until)) { 65 diff = 0; 66 } else { 67 diff = *left - *right; 68 } 69 70 return diff; 71 } 72 73 74 /* strdtb(s) - delete trailing blanks in string 's' and return new length 75 */ 76 int 77 strdtb(char *s) 78 { 79 char *x = s; 80 81 /* scan forward to the null 82 */ 83 while (*x) 84 x++; 85 86 /* scan backward to either the first character before the string, 87 * or the last non-blank in the string, whichever comes first. 88 */ 89 do {x--;} 90 while (x >= s && isspace(*x)); 91 92 /* one character beyond where we stopped above is where the null 93 * goes. 94 */ 95 *++x = '\0'; 96 97 /* the difference between the position of the null character and 98 * the position of the first character of the string is the length. 99 */ 100 return x - s; 101 } 102 103 104 int 105 set_debug_flags(char *flags) 106 { 107 /* debug flags are of the form flag[,flag ...] 108 * 109 * if an error occurs, print a message to stdout and return FALSE. 110 * otherwise return TRUE after setting ERROR_FLAGS. 111 */ 112 113 #if !DEBUGGING 114 115 printf("this program was compiled without debugging enabled\n"); 116 return FALSE; 117 118 #else /* DEBUGGING */ 119 120 char *pc = flags; 121 122 DebugFlags = 0; 123 124 while (*pc) { 125 char **test; 126 int mask; 127 128 /* try to find debug flag name in our list. 129 */ 130 for ( test = DebugFlagNames, mask = 1; 131 *test && strcmp_until(*test, pc, ','); 132 test++, mask <<= 1 133 ) 134 ; 135 136 if (!*test) { 137 fprintf(stderr, 138 "unrecognized debug flag <%s> <%s>\n", 139 flags, pc); 140 return FALSE; 141 } 142 143 DebugFlags |= mask; 144 145 /* skip to the next flag 146 */ 147 while (*pc && *pc != ',') 148 pc++; 149 if (*pc == ',') 150 pc++; 151 } 152 153 if (DebugFlags) { 154 int flag; 155 156 fprintf(stderr, "debug flags enabled:"); 157 158 for (flag = 0; DebugFlagNames[flag]; flag++) 159 if (DebugFlags & (1 << flag)) 160 fprintf(stderr, " %s", DebugFlagNames[flag]); 161 fprintf(stderr, "\n"); 162 } 163 164 return TRUE; 165 166 #endif /* DEBUGGING */ 167 } 168 169 170 void 171 set_cron_uid(void) 172 { 173 #if defined(BSD) || defined(POSIX) 174 if (seteuid(ROOT_UID) < OK) 175 err(ERROR_EXIT, "seteuid"); 176 #else 177 if (setuid(ROOT_UID) < OK) 178 err(ERROR_EXIT, "setuid"); 179 #endif 180 } 181 182 183 void 184 set_cron_cwd(void) 185 { 186 struct stat sb; 187 188 /* first check for CRONDIR ("/var/cron" or some such) 189 */ 190 if (stat(CRONDIR, &sb) < OK && errno == ENOENT) { 191 warn("%s", CRONDIR); 192 if (OK == mkdir(CRONDIR, 0700)) { 193 warnx("%s: created", CRONDIR); 194 stat(CRONDIR, &sb); 195 } else { 196 err(ERROR_EXIT, "%s: mkdir", CRONDIR); 197 } 198 } 199 if (!(sb.st_mode & S_IFDIR)) 200 err(ERROR_EXIT, "'%s' is not a directory, bailing out", CRONDIR); 201 if (chdir(CRONDIR) < OK) 202 err(ERROR_EXIT, "cannot chdir(%s), bailing out", CRONDIR); 203 204 /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such) 205 */ 206 if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) { 207 warn("%s", SPOOL_DIR); 208 if (OK == mkdir(SPOOL_DIR, 0700)) { 209 warnx("%s: created", SPOOL_DIR); 210 stat(SPOOL_DIR, &sb); 211 } else { 212 err(ERROR_EXIT, "%s: mkdir", SPOOL_DIR); 213 } 214 } 215 if (!(sb.st_mode & S_IFDIR)) 216 err(ERROR_EXIT, "'%s' is not a directory, bailing out", SPOOL_DIR); 217 } 218 219 220 /* get_char(file) : like getc() but increment LineNumber on newlines 221 */ 222 int 223 get_char(FILE *file) 224 { 225 int ch; 226 227 ch = getc(file); 228 if (ch == '\n') 229 Set_LineNum(LineNumber + 1) 230 return ch; 231 } 232 233 234 /* unget_char(ch, file) : like ungetc but do LineNumber processing 235 */ 236 void 237 unget_char(int ch, FILE *file) 238 { 239 ungetc(ch, file); 240 if (ch == '\n') 241 Set_LineNum(LineNumber - 1) 242 } 243 244 245 /* get_string(str, max, file, termstr) : like fgets() but 246 * (1) has terminator string which should include \n 247 * (2) will always leave room for the null 248 * (3) uses get_char() so LineNumber will be accurate 249 * (4) returns EOF or terminating character, whichever 250 */ 251 int 252 get_string(char *string, int size, FILE *file, char *terms) 253 { 254 int ch; 255 256 while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) { 257 if (size > 1) { 258 *string++ = (char) ch; 259 size--; 260 } 261 } 262 263 if (size > 0) 264 *string = '\0'; 265 266 return ch; 267 } 268 269 270 /* skip_comments(file) : read past comment (if any) 271 */ 272 void 273 skip_comments(FILE *file) 274 { 275 int ch; 276 277 while (EOF != (ch = get_char(file))) { 278 /* ch is now the first character of a line. 279 */ 280 281 while (ch == ' ' || ch == '\t') 282 ch = get_char(file); 283 284 if (ch == EOF) 285 break; 286 287 /* ch is now the first non-blank character of a line. 288 */ 289 290 if (ch != '\n' && ch != '#') 291 break; 292 293 /* ch must be a newline or comment as first non-blank 294 * character on a line. 295 */ 296 297 while (ch != '\n' && ch != EOF) 298 ch = get_char(file); 299 300 /* ch is now the newline of a line which we're going to 301 * ignore. 302 */ 303 } 304 if (ch != EOF) 305 unget_char(ch, file); 306 } 307 308 309 /* int in_file(char *string, FILE *file) 310 * return TRUE if one of the lines in file matches string exactly, 311 * FALSE otherwise. 312 */ 313 static int 314 in_file(char *string, FILE *file) 315 { 316 char line[MAX_TEMPSTR]; 317 318 rewind(file); 319 while (fgets(line, MAX_TEMPSTR, file)) { 320 if (line[0] != '\0') 321 if (line[strlen(line)-1] == '\n') 322 line[strlen(line)-1] = '\0'; 323 if (0 == strcmp(line, string)) 324 return TRUE; 325 } 326 return FALSE; 327 } 328 329 330 /* int allowed(char *username) 331 * returns TRUE if (ALLOW_FILE exists and user is listed) 332 * or (DENY_FILE exists and user is NOT listed) 333 * or (neither file exists but user=="root" so it's okay) 334 */ 335 int 336 allowed(char *username) 337 { 338 FILE *allow, *deny; 339 int isallowed; 340 341 isallowed = FALSE; 342 343 deny = NULL; 344 #if defined(ALLOW_FILE) && defined(DENY_FILE) 345 if ((allow = fopen(ALLOW_FILE, "r")) == NULL && errno != ENOENT) 346 goto out; 347 if ((deny = fopen(DENY_FILE, "r")) == NULL && errno != ENOENT) 348 goto out; 349 Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny)) 350 #else 351 allow = NULL; 352 #endif 353 354 if (allow) 355 isallowed = in_file(username, allow); 356 else if (deny) 357 isallowed = !in_file(username, deny); 358 else { 359 #if defined(ALLOW_ONLY_ROOT) 360 isallowed = (strcmp(username, ROOT_USER) == 0); 361 #else 362 isallowed = TRUE; 363 #endif 364 } 365 out: if (allow) 366 fclose(allow); 367 if (deny) 368 fclose(deny); 369 return (isallowed); 370 } 371 372 373 void 374 log_it(char *username, int xpid, char *event, const char *detail) 375 { 376 #if defined(LOG_FILE) || DEBUGGING 377 PID_T pid = xpid; 378 #endif 379 #if defined(LOG_FILE) 380 char *msg; 381 TIME_T now = time((TIME_T) 0); 382 register struct tm *t = localtime(&now); 383 #endif /*LOG_FILE*/ 384 385 #if defined(SYSLOG) 386 static int syslog_open = 0; 387 #endif 388 389 #if defined(LOG_FILE) 390 /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation. 391 */ 392 msg = malloc(strlen(username) 393 + strlen(event) 394 + strlen(detail) 395 + MAX_TEMPSTR); 396 397 if (msg == NULL) 398 warnx("failed to allocate memory for log message"); 399 else { 400 if (LogFD < OK) { 401 LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600); 402 if (LogFD < OK) { 403 warn("can't open log file %s", LOG_FILE); 404 } else { 405 (void) fcntl(LogFD, F_SETFD, 1); 406 } 407 } 408 409 /* we have to sprintf() it because fprintf() doesn't always 410 * write everything out in one chunk and this has to be 411 * atomically appended to the log file. 412 */ 413 sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n", 414 username, 415 t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, 416 t->tm_sec, pid, event, detail); 417 418 /* we have to run strlen() because sprintf() returns (char*) 419 * on old BSD. 420 */ 421 if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) { 422 if (LogFD >= OK) 423 warn("%s", LOG_FILE); 424 warnx("can't write to log file"); 425 write(STDERR, msg, strlen(msg)); 426 } 427 428 free(msg); 429 } 430 #endif /*LOG_FILE*/ 431 432 #if defined(SYSLOG) 433 if (!syslog_open) { 434 /* we don't use LOG_PID since the pid passed to us by 435 * our client may not be our own. therefore we want to 436 * print the pid ourselves. 437 */ 438 # ifdef LOG_DAEMON 439 openlog(ProgramName, LOG_PID, LOG_CRON); 440 # else 441 openlog(ProgramName, LOG_PID); 442 # endif 443 syslog_open = TRUE; /* assume openlog success */ 444 } 445 446 syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail); 447 448 #endif /*SYSLOG*/ 449 450 #if DEBUGGING 451 if (DebugFlags) { 452 fprintf(stderr, "log_it: (%s %d) %s (%s)\n", 453 username, pid, event, detail); 454 } 455 #endif 456 } 457 458 459 void 460 log_close(void) 461 { 462 if (LogFD != ERR) { 463 close(LogFD); 464 LogFD = ERR; 465 } 466 } 467 468 469 /* two warnings: 470 * (1) this routine is fairly slow 471 * (2) it returns a pointer to static storage 472 */ 473 char * 474 first_word(char *s, char *t) 475 { 476 static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */ 477 static int retsel = 0; 478 register char *rb, *rp; 479 480 /* select a return buffer */ 481 retsel = 1-retsel; 482 rb = &retbuf[retsel][0]; 483 rp = rb; 484 485 /* skip any leading terminators */ 486 while (*s && (NULL != strchr(t, *s))) { 487 s++; 488 } 489 490 /* copy until next terminator or full buffer */ 491 while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) { 492 *rp++ = *s++; 493 } 494 495 /* finish the return-string and return it */ 496 *rp = '\0'; 497 return rb; 498 } 499 500 501 /* warning: 502 * heavily ascii-dependent. 503 */ 504 static void 505 mkprint(register char *dst, register unsigned char *src, register int len) 506 { 507 while (len-- > 0) 508 { 509 register unsigned char ch = *src++; 510 511 if (ch < ' ') { /* control character */ 512 *dst++ = '^'; 513 *dst++ = ch + '@'; 514 } else if (ch < 0177) { /* printable */ 515 *dst++ = ch; 516 } else if (ch == 0177) { /* delete/rubout */ 517 *dst++ = '^'; 518 *dst++ = '?'; 519 } else { /* parity character */ 520 sprintf(dst, "\\%03o", ch); 521 dst += 4; 522 } 523 } 524 *dst = '\0'; 525 } 526 527 528 /* warning: 529 * returns a pointer to malloc'd storage, you must call free yourself. 530 */ 531 char * 532 mkprints(unsigned char *src, unsigned int len) 533 { 534 register char *dst = malloc(len*4 + 1); 535 536 if (dst != NULL) 537 mkprint(dst, src, len); 538 539 return dst; 540 } 541 542 543 #ifdef MAIL_DATE 544 /* Sat, 27 Feb 93 11:44:51 CST 545 * 123456789012345678901234567 546 */ 547 char * 548 arpadate(time_t *clock) 549 { 550 time_t t = clock ?*clock :time(0L); 551 struct tm *tm = localtime(&t); 552 static char ret[32]; /* zone name might be >3 chars */ 553 554 if (tm->tm_year >= 100) 555 tm->tm_year += 1900; 556 557 (void) snprintf(ret, sizeof(ret), "%s, %2d %s %d %02d:%02d:%02d %s", 558 DowNames[tm->tm_wday], 559 tm->tm_mday, 560 MonthNames[tm->tm_mon], 561 tm->tm_year, 562 tm->tm_hour, 563 tm->tm_min, 564 tm->tm_sec, 565 TZONE(*tm)); 566 return ret; 567 } 568 #endif /*MAIL_DATE*/ 569 570 571 #ifdef HAVE_SAVED_UIDS 572 static int save_euid; 573 int swap_uids(void) { save_euid = geteuid(); return seteuid(getuid()); } 574 int swap_uids_back(void) { return seteuid(save_euid); } 575 #else /*HAVE_SAVED_UIDS*/ 576 int swap_uids(void) { return setreuid(geteuid(), getuid()); } 577 int swap_uids_back(void) { return swap_uids(); } 578 #endif /*HAVE_SAVED_UIDS*/ 579