1 /* $OpenBSD: util.c,v 1.150 2019/10/03 04:49:12 gilles Exp $ */ 2 3 /* 4 * Copyright (c) 2000,2001 Markus Friedl. All rights reserved. 5 * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> 6 * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> 7 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> 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 THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 #include <sys/types.h> 23 #include <sys/queue.h> 24 #include <sys/tree.h> 25 #include <sys/socket.h> 26 #include <sys/stat.h> 27 #include <sys/resource.h> 28 29 #include <netinet/in.h> 30 #include <arpa/inet.h> 31 32 #include <ctype.h> 33 #include <errno.h> 34 #include <event.h> 35 #include <fcntl.h> 36 #include <fts.h> 37 #include <imsg.h> 38 #include <inttypes.h> 39 #include <libgen.h> 40 #include <netdb.h> 41 #include <pwd.h> 42 #include <limits.h> 43 #include <resolv.h> 44 #include <stdarg.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <syslog.h> 49 #include <time.h> 50 #include <unistd.h> 51 52 #include "smtpd.h" 53 #include "log.h" 54 55 const char *log_in6addr(const struct in6_addr *); 56 const char *log_sockaddr(struct sockaddr *); 57 static int parse_mailname_file(char *, size_t); 58 59 int tracing = 0; 60 int foreground_log = 0; 61 62 void * 63 xmalloc(size_t size) 64 { 65 void *r; 66 67 if ((r = malloc(size)) == NULL) 68 fatal("malloc"); 69 70 return (r); 71 } 72 73 void * 74 xcalloc(size_t nmemb, size_t size) 75 { 76 void *r; 77 78 if ((r = calloc(nmemb, size)) == NULL) 79 fatal("calloc"); 80 81 return (r); 82 } 83 84 char * 85 xstrdup(const char *str) 86 { 87 char *r; 88 89 if ((r = strdup(str)) == NULL) 90 fatal("strdup"); 91 92 return (r); 93 } 94 95 void * 96 xmemdup(const void *ptr, size_t size) 97 { 98 void *r; 99 100 if ((r = malloc(size)) == NULL) 101 fatal("malloc"); 102 103 memmove(r, ptr, size); 104 105 return (r); 106 } 107 108 int 109 xasprintf(char **ret, const char *format, ...) 110 { 111 int r; 112 va_list ap; 113 114 va_start(ap, format); 115 r = vasprintf(ret, format, ap); 116 va_end(ap); 117 if (r == -1) 118 fatal("vasprintf"); 119 120 return (r); 121 } 122 123 124 #if !defined(NO_IO) 125 int 126 io_xprintf(struct io *io, const char *fmt, ...) 127 { 128 va_list ap; 129 int len; 130 131 va_start(ap, fmt); 132 len = io_vprintf(io, fmt, ap); 133 va_end(ap); 134 if (len == -1) 135 fatal("io_xprintf(%p, %s, ...)", io, fmt); 136 137 return len; 138 } 139 140 int 141 io_xprint(struct io *io, const char *str) 142 { 143 int len; 144 145 len = io_print(io, str); 146 if (len == -1) 147 fatal("io_xprint(%p, %s, ...)", io, str); 148 149 return len; 150 } 151 #endif 152 153 char * 154 strip(char *s) 155 { 156 size_t l; 157 158 while (isspace((unsigned char)*s)) 159 s++; 160 161 for (l = strlen(s); l; l--) { 162 if (!isspace((unsigned char)s[l-1])) 163 break; 164 s[l-1] = '\0'; 165 } 166 167 return (s); 168 } 169 170 int 171 bsnprintf(char *str, size_t size, const char *format, ...) 172 { 173 int ret; 174 va_list ap; 175 176 va_start(ap, format); 177 ret = vsnprintf(str, size, format, ap); 178 va_end(ap); 179 if (ret < 0 || ret >= (int)size) 180 return 0; 181 182 return 1; 183 } 184 185 186 int 187 ckdir(const char *path, mode_t mode, uid_t owner, gid_t group, int create) 188 { 189 char mode_str[12]; 190 int ret; 191 struct stat sb; 192 193 if (stat(path, &sb) == -1) { 194 if (errno != ENOENT || create == 0) { 195 log_warn("stat: %s", path); 196 return (0); 197 } 198 199 /* chmod is deferred to avoid umask effect */ 200 if (mkdir(path, 0) == -1) { 201 log_warn("mkdir: %s", path); 202 return (0); 203 } 204 205 if (chown(path, owner, group) == -1) { 206 log_warn("chown: %s", path); 207 return (0); 208 } 209 210 if (chmod(path, mode) == -1) { 211 log_warn("chmod: %s", path); 212 return (0); 213 } 214 215 if (stat(path, &sb) == -1) { 216 log_warn("stat: %s", path); 217 return (0); 218 } 219 } 220 221 ret = 1; 222 223 /* check if it's a directory */ 224 if (!S_ISDIR(sb.st_mode)) { 225 ret = 0; 226 log_warnx("%s is not a directory", path); 227 } 228 229 /* check that it is owned by owner/group */ 230 if (sb.st_uid != owner) { 231 ret = 0; 232 log_warnx("%s is not owned by uid %d", path, owner); 233 } 234 if (sb.st_gid != group) { 235 ret = 0; 236 log_warnx("%s is not owned by gid %d", path, group); 237 } 238 239 /* check permission */ 240 if ((sb.st_mode & 07777) != mode) { 241 ret = 0; 242 strmode(mode, mode_str); 243 mode_str[10] = '\0'; 244 log_warnx("%s must be %s (%o)", path, mode_str + 1, mode); 245 } 246 247 return ret; 248 } 249 250 int 251 rmtree(char *path, int keepdir) 252 { 253 char *path_argv[2]; 254 FTS *fts; 255 FTSENT *e; 256 int ret, depth; 257 258 path_argv[0] = path; 259 path_argv[1] = NULL; 260 ret = 0; 261 depth = 0; 262 263 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 264 if (fts == NULL) { 265 log_warn("fts_open: %s", path); 266 return (-1); 267 } 268 269 while ((e = fts_read(fts)) != NULL) { 270 switch (e->fts_info) { 271 case FTS_D: 272 depth++; 273 break; 274 case FTS_DP: 275 case FTS_DNR: 276 depth--; 277 if (keepdir && depth == 0) 278 continue; 279 if (rmdir(e->fts_path) == -1) { 280 log_warn("rmdir: %s", e->fts_path); 281 ret = -1; 282 } 283 break; 284 285 case FTS_F: 286 if (unlink(e->fts_path) == -1) { 287 log_warn("unlink: %s", e->fts_path); 288 ret = -1; 289 } 290 } 291 } 292 293 fts_close(fts); 294 295 return (ret); 296 } 297 298 int 299 mvpurge(char *from, char *to) 300 { 301 size_t n; 302 int retry; 303 const char *sep; 304 char buf[PATH_MAX]; 305 306 if ((n = strlen(to)) == 0) 307 fatalx("to is empty"); 308 309 sep = (to[n - 1] == '/') ? "" : "/"; 310 retry = 0; 311 312 again: 313 (void)snprintf(buf, sizeof buf, "%s%s%u", to, sep, arc4random()); 314 if (rename(from, buf) == -1) { 315 /* ENOTDIR has actually 2 meanings, and incorrect input 316 * could lead to an infinite loop. Consider that after 317 * 20 tries something is hopelessly wrong. 318 */ 319 if (errno == ENOTEMPTY || errno == EISDIR || errno == ENOTDIR) { 320 if ((retry++) >= 20) 321 return (-1); 322 goto again; 323 } 324 return -1; 325 } 326 327 return 0; 328 } 329 330 331 int 332 mktmpfile(void) 333 { 334 char path[PATH_MAX]; 335 int fd; 336 337 if (!bsnprintf(path, sizeof(path), "%s/smtpd.XXXXXXXXXX", 338 PATH_TEMPORARY)) { 339 log_warn("snprintf"); 340 fatal("exiting"); 341 } 342 343 if ((fd = mkstemp(path)) == -1) { 344 log_warn("cannot create temporary file %s", path); 345 fatal("exiting"); 346 } 347 unlink(path); 348 return (fd); 349 } 350 351 352 /* Close file, signifying temporary error condition (if any) to the caller. */ 353 int 354 safe_fclose(FILE *fp) 355 { 356 if (ferror(fp)) { 357 fclose(fp); 358 return 0; 359 } 360 if (fflush(fp)) { 361 fclose(fp); 362 if (errno == ENOSPC) 363 return 0; 364 fatal("safe_fclose: fflush"); 365 } 366 if (fsync(fileno(fp))) 367 fatal("safe_fclose: fsync"); 368 if (fclose(fp)) 369 fatal("safe_fclose: fclose"); 370 371 return 1; 372 } 373 374 int 375 hostname_match(const char *hostname, const char *pattern) 376 { 377 while (*pattern != '\0' && *hostname != '\0') { 378 if (*pattern == '*') { 379 while (*pattern == '*') 380 pattern++; 381 while (*hostname != '\0' && 382 tolower((unsigned char)*hostname) != 383 tolower((unsigned char)*pattern)) 384 hostname++; 385 continue; 386 } 387 388 if (tolower((unsigned char)*pattern) != 389 tolower((unsigned char)*hostname)) 390 return 0; 391 pattern++; 392 hostname++; 393 } 394 395 return (*hostname == '\0' && *pattern == '\0'); 396 } 397 398 int 399 mailaddr_match(const struct mailaddr *maddr1, const struct mailaddr *maddr2) 400 { 401 struct mailaddr m1 = *maddr1; 402 struct mailaddr m2 = *maddr2; 403 char *p; 404 405 /* catchall */ 406 if (m2.user[0] == '\0' && m2.domain[0] == '\0') 407 return 1; 408 409 if (m2.domain[0] && !hostname_match(m1.domain, m2.domain)) 410 return 0; 411 412 if (m2.user[0]) { 413 /* if address from table has a tag, we must respect it */ 414 if (strchr(m2.user, *env->sc_subaddressing_delim) == NULL) { 415 /* otherwise, strip tag from session address if any */ 416 p = strchr(m1.user, *env->sc_subaddressing_delim); 417 if (p) 418 *p = '\0'; 419 } 420 if (strcasecmp(m1.user, m2.user)) 421 return 0; 422 } 423 return 1; 424 } 425 426 int 427 valid_localpart(const char *s) 428 { 429 #define IS_ATEXT(c) (isalnum((unsigned char)(c)) || strchr(MAILADDR_ALLOWED, (c))) 430 nextatom: 431 if (!IS_ATEXT(*s) || *s == '\0') 432 return 0; 433 while (*(++s) != '\0') { 434 if (*s == '.') 435 break; 436 if (IS_ATEXT(*s)) 437 continue; 438 return 0; 439 } 440 if (*s == '.') { 441 s++; 442 goto nextatom; 443 } 444 return 1; 445 } 446 447 int 448 valid_domainpart(const char *s) 449 { 450 struct in_addr ina; 451 struct in6_addr ina6; 452 char *c, domain[SMTPD_MAXDOMAINPARTSIZE]; 453 const char *p; 454 size_t dlen; 455 456 if (*s == '[') { 457 if (strncasecmp("[IPv6:", s, 6) == 0) 458 p = s + 6; 459 else 460 p = s + 1; 461 462 if (strlcpy(domain, p, sizeof domain) >= sizeof domain) 463 return 0; 464 465 c = strchr(domain, (int)']'); 466 if (!c || c[1] != '\0') 467 return 0; 468 469 *c = '\0'; 470 471 if (inet_pton(AF_INET6, domain, &ina6) == 1) 472 return 1; 473 if (inet_pton(AF_INET, domain, &ina) == 1) 474 return 1; 475 476 return 0; 477 } 478 479 if (*s == '\0') 480 return 0; 481 482 dlen = strlen(s); 483 if (dlen >= sizeof domain) 484 return 0; 485 486 if (s[dlen - 1] == '.') 487 return 0; 488 489 return res_hnok(s); 490 } 491 492 #define LABELCHR(c) ((c) == '-' || (c) == '_' || isalpha((int)(c)) || isdigit((int)(c))) 493 #define LABELMAX 63 494 #define DNAMEMAX 253 495 496 int 497 valid_domainname(const char *str) 498 { 499 const char *label, *s; 500 501 /* 502 * Expect a sequence of dot-separated labels, possibly with a trailing 503 * dot. The empty string is rejected, as well a single dot. 504 */ 505 for (s = str; *s; s++) { 506 507 /* Start of a new label. */ 508 label = s; 509 while (LABELCHR(*s)) 510 s++; 511 512 /* Must have at least one char and at most LABELMAX. */ 513 if (s == label || s - label > LABELMAX) 514 return 0; 515 516 /* If last label, stop here. */ 517 if (*s == '\0') 518 break; 519 520 /* Expect a dot as label separator or last char. */ 521 if (*s != '.') 522 return 0; 523 } 524 525 /* Must have at leat one label and no more than DNAMEMAX chars. */ 526 if (s == str || s - str > DNAMEMAX) 527 return 0; 528 529 return 1; 530 } 531 532 int 533 valid_smtp_response(const char *s) 534 { 535 if (strlen(s) < 5) 536 return 0; 537 538 if ((s[0] < '2' || s[0] > '5') || 539 (s[1] < '0' || s[1] > '9') || 540 (s[2] < '0' || s[2] > '9') || 541 (s[3] != ' ')) 542 return 0; 543 544 return 1; 545 } 546 547 int 548 secure_file(int fd, char *path, char *userdir, uid_t uid, int mayread) 549 { 550 char buf[PATH_MAX]; 551 char homedir[PATH_MAX]; 552 struct stat st; 553 char *cp; 554 555 if (realpath(path, buf) == NULL) 556 return 0; 557 558 if (realpath(userdir, homedir) == NULL) 559 homedir[0] = '\0'; 560 561 /* Check the open file to avoid races. */ 562 if (fstat(fd, &st) == -1 || 563 !S_ISREG(st.st_mode) || 564 st.st_uid != uid || 565 (st.st_mode & (mayread ? 022 : 066)) != 0) 566 return 0; 567 568 /* For each component of the canonical path, walking upwards. */ 569 for (;;) { 570 if ((cp = dirname(buf)) == NULL) 571 return 0; 572 (void)strlcpy(buf, cp, sizeof(buf)); 573 574 if (stat(buf, &st) == -1 || 575 (st.st_uid != 0 && st.st_uid != uid) || 576 (st.st_mode & 022) != 0) 577 return 0; 578 579 /* We can stop checking after reaching homedir level. */ 580 if (strcmp(homedir, buf) == 0) 581 break; 582 583 /* 584 * dirname should always complete with a "/" path, 585 * but we can be paranoid and check for "." too 586 */ 587 if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) 588 break; 589 } 590 591 return 1; 592 } 593 594 void 595 addargs(arglist *args, char *fmt, ...) 596 { 597 va_list ap; 598 char *cp; 599 uint nalloc; 600 int r; 601 char **tmp; 602 603 va_start(ap, fmt); 604 r = vasprintf(&cp, fmt, ap); 605 va_end(ap); 606 if (r == -1) 607 fatal("addargs: argument too long"); 608 609 nalloc = args->nalloc; 610 if (args->list == NULL) { 611 nalloc = 32; 612 args->num = 0; 613 } else if (args->num+2 >= nalloc) 614 nalloc *= 2; 615 616 tmp = reallocarray(args->list, nalloc, sizeof(char *)); 617 if (tmp == NULL) 618 fatal("addargs: reallocarray"); 619 args->list = tmp; 620 args->nalloc = nalloc; 621 args->list[args->num++] = cp; 622 args->list[args->num] = NULL; 623 } 624 625 int 626 lowercase(char *buf, const char *s, size_t len) 627 { 628 if (len == 0) 629 return 0; 630 631 if (strlcpy(buf, s, len) >= len) 632 return 0; 633 634 while (*buf != '\0') { 635 *buf = tolower((unsigned char)*buf); 636 buf++; 637 } 638 639 return 1; 640 } 641 642 int 643 uppercase(char *buf, const char *s, size_t len) 644 { 645 if (len == 0) 646 return 0; 647 648 if (strlcpy(buf, s, len) >= len) 649 return 0; 650 651 while (*buf != '\0') { 652 *buf = toupper((unsigned char)*buf); 653 buf++; 654 } 655 656 return 1; 657 } 658 659 void 660 xlowercase(char *buf, const char *s, size_t len) 661 { 662 if (len == 0) 663 fatalx("lowercase: len == 0"); 664 665 if (!lowercase(buf, s, len)) 666 fatalx("lowercase: truncation"); 667 } 668 669 uint64_t 670 generate_uid(void) 671 { 672 static uint32_t id; 673 static uint8_t inited; 674 uint64_t uid; 675 676 if (!inited) { 677 id = arc4random(); 678 inited = 1; 679 } 680 while ((uid = ((uint64_t)(id++) << 32 | arc4random())) == 0) 681 ; 682 683 return (uid); 684 } 685 686 int 687 session_socket_error(int fd) 688 { 689 int error; 690 socklen_t len; 691 692 len = sizeof(error); 693 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) 694 fatal("session_socket_error: getsockopt"); 695 696 return (error); 697 } 698 699 const char * 700 parse_smtp_response(char *line, size_t len, char **msg, int *cont) 701 { 702 if (len >= LINE_MAX) 703 return "line too long"; 704 705 if (len > 3) { 706 if (msg) 707 *msg = line + 4; 708 if (cont) 709 *cont = (line[3] == '-'); 710 } else if (len == 3) { 711 if (msg) 712 *msg = line + 3; 713 if (cont) 714 *cont = 0; 715 } else 716 return "line too short"; 717 718 /* validate reply code */ 719 if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) || 720 !isdigit((unsigned char)line[2])) 721 return "reply code out of range"; 722 723 return NULL; 724 } 725 726 static int 727 parse_mailname_file(char *hostname, size_t len) 728 { 729 FILE *fp; 730 char *buf = NULL; 731 size_t bufsz = 0; 732 ssize_t buflen; 733 734 if ((fp = fopen(MAILNAME_FILE, "r")) == NULL) 735 return 1; 736 737 if ((buflen = getline(&buf, &bufsz, fp)) == -1) 738 goto error; 739 740 if (buf[buflen - 1] == '\n') 741 buf[buflen - 1] = '\0'; 742 743 if (strlcpy(hostname, buf, len) >= len) { 744 fprintf(stderr, MAILNAME_FILE " entry too long"); 745 goto error; 746 } 747 748 return 0; 749 error: 750 fclose(fp); 751 free(buf); 752 return 1; 753 } 754 755 int 756 getmailname(char *hostname, size_t len) 757 { 758 struct addrinfo hints, *res = NULL; 759 int error; 760 761 /* Try MAILNAME_FILE first */ 762 if (parse_mailname_file(hostname, len) == 0) 763 return 0; 764 765 /* Next, gethostname(3) */ 766 if (gethostname(hostname, len) == -1) { 767 fprintf(stderr, "getmailname: gethostname() failed\n"); 768 return -1; 769 } 770 771 if (strchr(hostname, '.') != NULL) 772 return 0; 773 774 /* Canonicalize if domain part is missing */ 775 memset(&hints, 0, sizeof hints); 776 hints.ai_family = PF_UNSPEC; 777 hints.ai_flags = AI_CANONNAME; 778 error = getaddrinfo(hostname, NULL, &hints, &res); 779 if (error) 780 return 0; /* Continue with non-canon hostname */ 781 782 if (strlcpy(hostname, res->ai_canonname, len) >= len) { 783 fprintf(stderr, "hostname too long"); 784 freeaddrinfo(res); 785 return -1; 786 } 787 788 freeaddrinfo(res); 789 return 0; 790 } 791 792 int 793 base64_encode(unsigned char const *src, size_t srclen, 794 char *dest, size_t destsize) 795 { 796 return __b64_ntop(src, srclen, dest, destsize); 797 } 798 799 int 800 base64_decode(char const *src, unsigned char *dest, size_t destsize) 801 { 802 return __b64_pton(src, dest, destsize); 803 } 804 805 int 806 base64_encode_rfc3548(unsigned char const *src, size_t srclen, 807 char *dest, size_t destsize) 808 { 809 size_t i; 810 int ret; 811 812 if ((ret = base64_encode(src, srclen, dest, destsize)) == -1) 813 return -1; 814 815 for (i = 0; i < destsize; ++i) { 816 if (dest[i] == '/') 817 dest[i] = '_'; 818 else if (dest[i] == '+') 819 dest[i] = '-'; 820 } 821 822 return ret; 823 } 824 825 void 826 log_trace(int mask, const char *emsg, ...) 827 { 828 va_list ap; 829 830 if (tracing & mask) { 831 va_start(ap, emsg); 832 vlog(LOG_DEBUG, emsg, ap); 833 va_end(ap); 834 } 835 } 836 837 void 838 log_trace_verbose(int v) 839 { 840 tracing = v; 841 842 /* Set debug logging in log.c */ 843 log_setverbose(v & TRACE_DEBUG ? 2 : foreground_log); 844 } 845