1 /* $OpenBSD: util.c,v 1.110 2014/05/25 10:55:36 espie 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 <resolv.h> 43 #include <stdarg.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <time.h> 48 #include <unistd.h> 49 50 #include "smtpd.h" 51 #include "log.h" 52 53 const char *log_in6addr(const struct in6_addr *); 54 const char *log_sockaddr(struct sockaddr *); 55 56 void * 57 xmalloc(size_t size, const char *where) 58 { 59 void *r; 60 61 if ((r = malloc(size)) == NULL) { 62 log_warnx("%s: malloc(%zu)", where, size); 63 fatalx("exiting"); 64 } 65 66 return (r); 67 } 68 69 void * 70 xcalloc(size_t nmemb, size_t size, const char *where) 71 { 72 void *r; 73 74 if ((r = calloc(nmemb, size)) == NULL) { 75 log_warnx("%s: calloc(%zu, %zu)", where, nmemb, size); 76 fatalx("exiting"); 77 } 78 79 return (r); 80 } 81 82 char * 83 xstrdup(const char *str, const char *where) 84 { 85 char *r; 86 87 if ((r = strdup(str)) == NULL) { 88 log_warnx("%s: strdup(%p)", where, str); 89 fatalx("exiting"); 90 } 91 92 return (r); 93 } 94 95 void * 96 xmemdup(const void *ptr, size_t size, const char *where) 97 { 98 void *r; 99 100 if ((r = malloc(size)) == NULL) { 101 log_warnx("%s: malloc(%zu)", where, size); 102 fatalx("exiting"); 103 } 104 memmove(r, ptr, size); 105 106 return (r); 107 } 108 109 #if !defined(NO_IO) 110 void 111 iobuf_xinit(struct iobuf *io, size_t size, size_t max, const char *where) 112 { 113 if (iobuf_init(io, size, max) == -1) { 114 log_warnx("%s: iobuf_init(%p, %zu, %zu)", where, io, size, max); 115 fatalx("exiting"); 116 } 117 } 118 119 void 120 iobuf_xfqueue(struct iobuf *io, const char *where, const char *fmt, ...) 121 { 122 va_list ap; 123 int len; 124 125 va_start(ap, fmt); 126 len = iobuf_vfqueue(io, fmt, ap); 127 va_end(ap); 128 129 if (len == -1) { 130 log_warnx("%s: iobuf_xfqueue(%p, %s, ...)", where, io, fmt); 131 fatalx("exiting"); 132 } 133 } 134 #endif 135 136 char * 137 strip(char *s) 138 { 139 size_t l; 140 141 while (*s == ' ' || *s == '\t') 142 s++; 143 144 for (l = strlen(s); l; l--) { 145 if (s[l-1] != ' ' && s[l-1] != '\t') 146 break; 147 s[l-1] = '\0'; 148 } 149 150 return (s); 151 } 152 153 int 154 bsnprintf(char *str, size_t size, const char *format, ...) 155 { 156 int ret; 157 va_list ap; 158 159 va_start(ap, format); 160 ret = vsnprintf(str, size, format, ap); 161 va_end(ap); 162 if (ret == -1 || ret >= (int)size) 163 return 0; 164 165 return 1; 166 } 167 168 169 static int 170 mkdirs_component(char *path, mode_t mode) 171 { 172 struct stat sb; 173 174 if (stat(path, &sb) == -1) { 175 if (errno != ENOENT) 176 return 0; 177 if (mkdir(path, mode | S_IWUSR | S_IXUSR) == -1) 178 return 0; 179 } 180 else if (! S_ISDIR(sb.st_mode)) 181 return 0; 182 183 return 1; 184 } 185 186 int 187 mkdirs(char *path, mode_t mode) 188 { 189 char buf[SMTPD_MAXPATHLEN]; 190 int i = 0; 191 int done = 0; 192 char *p; 193 194 /* absolute path required */ 195 if (*path != '/') 196 return 0; 197 198 /* make sure we don't exceed SMTPD_MAXPATHLEN */ 199 if (strlen(path) >= sizeof buf) 200 return 0; 201 202 memset(buf, 0, sizeof buf); 203 for (p = path; *p; p++) { 204 if (*p == '/') { 205 if (buf[0] != '\0') 206 if (! mkdirs_component(buf, mode)) 207 return 0; 208 while (*p == '/') 209 p++; 210 buf[i++] = '/'; 211 buf[i++] = *p; 212 if (*p == '\0' && ++done) 213 break; 214 continue; 215 } 216 buf[i++] = *p; 217 } 218 if (! done) 219 if (! mkdirs_component(buf, mode)) 220 return 0; 221 222 if (chmod(path, mode) == -1) 223 return 0; 224 225 return 1; 226 } 227 228 229 int 230 ckdir(const char *path, mode_t mode, uid_t owner, gid_t group, int create) 231 { 232 char mode_str[12]; 233 int ret; 234 struct stat sb; 235 236 if (stat(path, &sb) == -1) { 237 if (errno != ENOENT || create == 0) { 238 log_warn("stat: %s", path); 239 return (0); 240 } 241 242 /* chmod is deferred to avoid umask effect */ 243 if (mkdir(path, 0) == -1) { 244 log_warn("mkdir: %s", path); 245 return (0); 246 } 247 248 if (chown(path, owner, group) == -1) { 249 log_warn("chown: %s", path); 250 return (0); 251 } 252 253 if (chmod(path, mode) == -1) { 254 log_warn("chmod: %s", path); 255 return (0); 256 } 257 258 if (stat(path, &sb) == -1) { 259 log_warn("stat: %s", path); 260 return (0); 261 } 262 } 263 264 ret = 1; 265 266 /* check if it's a directory */ 267 if (!S_ISDIR(sb.st_mode)) { 268 ret = 0; 269 log_warnx("%s is not a directory", path); 270 } 271 272 /* check that it is owned by owner/group */ 273 if (sb.st_uid != owner) { 274 ret = 0; 275 log_warnx("%s is not owned by uid %d", path, owner); 276 } 277 if (sb.st_gid != group) { 278 ret = 0; 279 log_warnx("%s is not owned by gid %d", path, group); 280 } 281 282 /* check permission */ 283 if ((sb.st_mode & 07777) != mode) { 284 ret = 0; 285 strmode(mode, mode_str); 286 mode_str[10] = '\0'; 287 log_warnx("%s must be %s (%o)", path, mode_str + 1, mode); 288 } 289 290 return ret; 291 } 292 293 int 294 rmtree(char *path, int keepdir) 295 { 296 char *path_argv[2]; 297 FTS *fts; 298 FTSENT *e; 299 int ret, depth; 300 301 path_argv[0] = path; 302 path_argv[1] = NULL; 303 ret = 0; 304 depth = 0; 305 306 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 307 if (fts == NULL) { 308 log_warn("fts_open: %s", path); 309 return (-1); 310 } 311 312 while ((e = fts_read(fts)) != NULL) { 313 switch (e->fts_info) { 314 case FTS_D: 315 depth++; 316 break; 317 case FTS_DP: 318 case FTS_DNR: 319 depth--; 320 if (keepdir && depth == 0) 321 continue; 322 if (rmdir(e->fts_path) == -1) { 323 log_warn("rmdir: %s", e->fts_path); 324 ret = -1; 325 } 326 break; 327 328 case FTS_F: 329 if (unlink(e->fts_path) == -1) { 330 log_warn("unlink: %s", e->fts_path); 331 ret = -1; 332 } 333 } 334 } 335 336 fts_close(fts); 337 338 return (ret); 339 } 340 341 int 342 mvpurge(char *from, char *to) 343 { 344 size_t n; 345 int retry; 346 const char *sep; 347 char buf[SMTPD_MAXPATHLEN]; 348 349 if ((n = strlen(to)) == 0) 350 fatalx("to is empty"); 351 352 sep = (to[n - 1] == '/') ? "" : "/"; 353 retry = 0; 354 355 again: 356 (void)snprintf(buf, sizeof buf, "%s%s%u", to, sep, arc4random()); 357 if (rename(from, buf) == -1) { 358 /* ENOTDIR has actually 2 meanings, and incorrect input 359 * could lead to an infinite loop. Consider that after 360 * 20 tries something is hopelessly wrong. 361 */ 362 if (errno == ENOTEMPTY || errno == EISDIR || errno == ENOTDIR) { 363 if ((retry++) >= 20) 364 return (-1); 365 goto again; 366 } 367 return -1; 368 } 369 370 return 0; 371 } 372 373 374 int 375 mktmpfile(void) 376 { 377 char path[SMTPD_MAXPATHLEN]; 378 int fd; 379 mode_t omode; 380 381 if (! bsnprintf(path, sizeof(path), "%s/smtpd.XXXXXXXXXX", 382 PATH_TEMPORARY)) { 383 log_warn("snprintf"); 384 fatal("exiting"); 385 } 386 387 omode = umask(7077); 388 if ((fd = mkstemp(path)) == -1) { 389 log_warn("cannot create temporary file %s", path); 390 fatal("exiting"); 391 } 392 umask(omode); 393 unlink(path); 394 return (fd); 395 } 396 397 398 /* Close file, signifying temporary error condition (if any) to the caller. */ 399 int 400 safe_fclose(FILE *fp) 401 { 402 if (ferror(fp)) { 403 fclose(fp); 404 return 0; 405 } 406 if (fflush(fp)) { 407 fclose(fp); 408 if (errno == ENOSPC) 409 return 0; 410 fatal("safe_fclose: fflush"); 411 } 412 if (fsync(fileno(fp))) 413 fatal("safe_fclose: fsync"); 414 if (fclose(fp)) 415 fatal("safe_fclose: fclose"); 416 417 return 1; 418 } 419 420 int 421 hostname_match(const char *hostname, const char *pattern) 422 { 423 while (*pattern != '\0' && *hostname != '\0') { 424 if (*pattern == '*') { 425 while (*pattern == '*') 426 pattern++; 427 while (*hostname != '\0' && 428 tolower((unsigned char)*hostname) != 429 tolower((unsigned char)*pattern)) 430 hostname++; 431 continue; 432 } 433 434 if (tolower((unsigned char)*pattern) != 435 tolower((unsigned char)*hostname)) 436 return 0; 437 pattern++; 438 hostname++; 439 } 440 441 return (*hostname == '\0' && *pattern == '\0'); 442 } 443 444 int 445 valid_localpart(const char *s) 446 { 447 #define IS_ATEXT(c) (isalnum((unsigned char)(c)) || strchr(MAILADDR_ALLOWED, (c))) 448 nextatom: 449 if (! IS_ATEXT(*s) || *s == '\0') 450 return 0; 451 while (*(++s) != '\0') { 452 if (*s == '.') 453 break; 454 if (IS_ATEXT(*s)) 455 continue; 456 return 0; 457 } 458 if (*s == '.') { 459 s++; 460 goto nextatom; 461 } 462 return 1; 463 } 464 465 int 466 valid_domainpart(const char *s) 467 { 468 struct in_addr ina; 469 struct in6_addr ina6; 470 char *c, domain[SMTPD_MAXDOMAINPARTSIZE]; 471 const char *p; 472 473 if (*s == '[') { 474 if (strncasecmp("[IPv6:", s, 6) == 0) 475 p = s + 6; 476 else 477 p = s + 1; 478 479 if (strlcpy(domain, p, sizeof domain) >= sizeof domain) 480 return 0; 481 482 c = strchr(domain, (int)']'); 483 if (!c || c[1] != '\0') 484 return 0; 485 486 *c = '\0'; 487 488 if (inet_pton(AF_INET6, domain, &ina6) == 1) 489 return 1; 490 if (inet_pton(AF_INET, domain, &ina) == 1) 491 return 1; 492 493 return 0; 494 } 495 496 nextsub: 497 if (!isalnum((unsigned char)*s)) 498 return 0; 499 while (*(++s) != '\0') { 500 if (*s == '.') 501 break; 502 if (isalnum((unsigned char)*s) || *s == '-') 503 continue; 504 return 0; 505 } 506 if (s[-1] == '-') 507 return 0; 508 if (*s == '.') { 509 s++; 510 goto nextsub; 511 } 512 return 1; 513 } 514 515 /* 516 * Check file for security. Based on usr.bin/ssh/auth.c. 517 */ 518 int 519 secure_file(int fd, char *path, char *userdir, uid_t uid, int mayread) 520 { 521 char buf[PATH_MAX]; 522 char homedir[PATH_MAX]; 523 struct stat st; 524 char *cp; 525 526 if (realpath(path, buf) == NULL) 527 return 0; 528 529 if (realpath(userdir, homedir) == NULL) 530 homedir[0] = '\0'; 531 532 /* Check the open file to avoid races. */ 533 if (fstat(fd, &st) < 0 || 534 !S_ISREG(st.st_mode) || 535 (st.st_uid != 0 && st.st_uid != uid) || 536 (st.st_mode & (mayread ? 022 : 066)) != 0) 537 return 0; 538 539 /* For each component of the canonical path, walking upwards. */ 540 for (;;) { 541 if ((cp = dirname(buf)) == NULL) 542 return 0; 543 (void)strlcpy(buf, cp, sizeof(buf)); 544 545 if (stat(buf, &st) < 0 || 546 (st.st_uid != 0 && st.st_uid != uid) || 547 (st.st_mode & 022) != 0) 548 return 0; 549 550 /* We can stop checking after reaching homedir level. */ 551 if (strcmp(homedir, buf) == 0) 552 break; 553 554 /* 555 * dirname should always complete with a "/" path, 556 * but we can be paranoid and check for "." too 557 */ 558 if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) 559 break; 560 } 561 562 return 1; 563 } 564 565 void 566 addargs(arglist *args, char *fmt, ...) 567 { 568 va_list ap; 569 char *cp; 570 uint nalloc; 571 int r; 572 573 va_start(ap, fmt); 574 r = vasprintf(&cp, fmt, ap); 575 va_end(ap); 576 if (r == -1) 577 fatal("addargs: argument too long"); 578 579 nalloc = args->nalloc; 580 if (args->list == NULL) { 581 nalloc = 32; 582 args->num = 0; 583 } else if (args->num+2 >= nalloc) 584 nalloc *= 2; 585 586 args->list = reallocarray(args->list, nalloc, sizeof(char *)); 587 if (args->list == NULL) 588 fatal("addargs: reallocarray"); 589 args->nalloc = nalloc; 590 args->list[args->num++] = cp; 591 args->list[args->num] = NULL; 592 } 593 594 int 595 lowercase(char *buf, const char *s, size_t len) 596 { 597 if (len == 0) 598 return 0; 599 600 if (strlcpy(buf, s, len) >= len) 601 return 0; 602 603 while (*buf != '\0') { 604 *buf = tolower((unsigned char)*buf); 605 buf++; 606 } 607 608 return 1; 609 } 610 611 int 612 uppercase(char *buf, const char *s, size_t len) 613 { 614 if (len == 0) 615 return 0; 616 617 if (strlcpy(buf, s, len) >= len) 618 return 0; 619 620 while (*buf != '\0') { 621 *buf = toupper((unsigned char)*buf); 622 buf++; 623 } 624 625 return 1; 626 } 627 628 void 629 xlowercase(char *buf, const char *s, size_t len) 630 { 631 if (len == 0) 632 fatalx("lowercase: len == 0"); 633 634 if (! lowercase(buf, s, len)) 635 fatalx("lowercase: truncation"); 636 } 637 638 uint64_t 639 generate_uid(void) 640 { 641 static uint32_t id; 642 static uint8_t inited; 643 uint64_t uid; 644 645 if (!inited) { 646 id = arc4random(); 647 inited = 1; 648 } 649 while ((uid = ((uint64_t)(id++) << 32 | arc4random())) == 0) 650 ; 651 652 return (uid); 653 } 654 655 void 656 session_socket_blockmode(int fd, enum blockmodes bm) 657 { 658 int flags; 659 660 if ((flags = fcntl(fd, F_GETFL, 0)) == -1) 661 fatal("fcntl F_GETFL"); 662 663 if (bm == BM_NONBLOCK) 664 flags |= O_NONBLOCK; 665 else 666 flags &= ~O_NONBLOCK; 667 668 if ((flags = fcntl(fd, F_SETFL, flags)) == -1) 669 fatal("fcntl F_SETFL"); 670 } 671 672 void 673 session_socket_no_linger(int fd) 674 { 675 struct linger lng; 676 677 memset(&lng, 0, sizeof(lng)); 678 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1) 679 fatal("session_socket_no_linger"); 680 } 681 682 int 683 session_socket_error(int fd) 684 { 685 int error; 686 socklen_t len; 687 688 len = sizeof(error); 689 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) 690 fatal("session_socket_error: getsockopt"); 691 692 return (error); 693 } 694 695 const char * 696 parse_smtp_response(char *line, size_t len, char **msg, int *cont) 697 { 698 size_t i; 699 700 if (len >= SMTPD_MAXLINESIZE) 701 return "line too long"; 702 703 if (len > 3) { 704 if (msg) 705 *msg = line + 4; 706 if (cont) 707 *cont = (line[3] == '-'); 708 } else if (len == 3) { 709 if (msg) 710 *msg = line + 3; 711 if (cont) 712 *cont = 0; 713 } else 714 return "line too short"; 715 716 /* validate reply code */ 717 if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) || 718 !isdigit((unsigned char)line[2])) 719 return "reply code out of range"; 720 721 /* validate reply message */ 722 for (i = 0; i < len; i++) 723 if (!isprint((unsigned char)line[i])) 724 return "non-printable character in reply"; 725 726 return NULL; 727 } 728 729 int 730 getmailname(char *hostname, size_t len) 731 { 732 struct addrinfo hints, *res = NULL; 733 FILE *fp; 734 char *buf, *lbuf = NULL; 735 size_t buflen; 736 int error, ret = 0; 737 738 /* First, check if we have MAILNAME_FILE */ 739 if ((fp = fopen(MAILNAME_FILE, "r")) == NULL) 740 goto nomailname; 741 742 if ((buf = fgetln(fp, &buflen)) == NULL) 743 goto end; 744 745 if (buf[buflen-1] == '\n') 746 buf[buflen - 1] = '\0'; 747 else { 748 if ((lbuf = calloc(buflen + 1, 1)) == NULL) { 749 log_warn("calloc"); 750 fatal("exiting"); 751 } 752 memcpy(lbuf, buf, buflen); 753 } 754 755 if (strlcpy(hostname, buf, len) >= len) 756 fprintf(stderr, MAILNAME_FILE " entry too long"); 757 else { 758 ret = 1; 759 goto end; 760 } 761 762 nomailname: 763 if (gethostname(hostname, len) == -1) { 764 fprintf(stderr, "invalid hostname: gethostname() failed\n"); 765 goto end; 766 } 767 768 if (strchr(hostname, '.') == NULL) { 769 memset(&hints, 0, sizeof hints); 770 hints.ai_family = PF_UNSPEC; 771 hints.ai_socktype = SOCK_STREAM; 772 hints.ai_protocol = IPPROTO_TCP; 773 hints.ai_flags = AI_CANONNAME; 774 error = getaddrinfo(hostname, NULL, &hints, &res); 775 if (error) { 776 fprintf(stderr, "invalid hostname: getaddrinfo() failed: %s\n", 777 gai_strerror(error)); 778 goto end; 779 } 780 781 if (strlcpy(hostname, res->ai_canonname, len) >= len) { 782 fprintf(stderr, "hostname too long"); 783 goto end; 784 } 785 } 786 787 ret = 1; 788 789 end: 790 free(lbuf); 791 if (res) 792 freeaddrinfo(res); 793 if (fp) 794 fclose(fp); 795 return ret; 796 } 797 798 int 799 base64_encode(unsigned char const *src, size_t srclen, 800 char *dest, size_t destsize) 801 { 802 return __b64_ntop(src, srclen, dest, destsize); 803 } 804 805 int 806 base64_decode(char const *src, unsigned char *dest, size_t destsize) 807 { 808 return __b64_pton(src, dest, destsize); 809 } 810