1 /* $OpenBSD: to.c,v 1.44 2019/11/12 20:21:46 gilles Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> 5 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> 6 * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> 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 THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/types.h> 22 #include <sys/queue.h> 23 #include <sys/tree.h> 24 #include <sys/socket.h> 25 #include <sys/stat.h> 26 #include <sys/resource.h> 27 28 #include <netinet/in.h> 29 #include <arpa/inet.h> 30 31 #include <ctype.h> 32 #include <err.h> 33 #include <errno.h> 34 #include <event.h> 35 #include <fcntl.h> 36 #include <imsg.h> 37 #include <limits.h> 38 #include <inttypes.h> 39 #include <netdb.h> 40 #include <pwd.h> 41 #include <stdarg.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <time.h> 46 #include <unistd.h> 47 48 #include "smtpd.h" 49 #include "log.h" 50 51 static const char *in6addr_to_text(const struct in6_addr *); 52 static int alias_is_filter(struct expandnode *, const char *, size_t); 53 static int alias_is_username(struct expandnode *, const char *, size_t); 54 static int alias_is_address(struct expandnode *, const char *, size_t); 55 static int alias_is_filename(struct expandnode *, const char *, size_t); 56 static int alias_is_include(struct expandnode *, const char *, size_t); 57 static int alias_is_error(struct expandnode *, const char *, size_t); 58 59 const char * 60 sockaddr_to_text(struct sockaddr *sa) 61 { 62 static char buf[NI_MAXHOST]; 63 64 if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0, 65 NI_NUMERICHOST)) 66 return ("(unknown)"); 67 else 68 return (buf); 69 } 70 71 static const char * 72 in6addr_to_text(const struct in6_addr *addr) 73 { 74 struct sockaddr_in6 sa_in6; 75 uint16_t tmp16; 76 77 memset(&sa_in6, 0, sizeof(sa_in6)); 78 sa_in6.sin6_len = sizeof(sa_in6); 79 sa_in6.sin6_family = AF_INET6; 80 memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr)); 81 82 /* XXX thanks, KAME, for this ugliness... adopted from route/show.c */ 83 if (IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) || 84 IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr)) { 85 memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16)); 86 sa_in6.sin6_scope_id = ntohs(tmp16); 87 sa_in6.sin6_addr.s6_addr[2] = 0; 88 sa_in6.sin6_addr.s6_addr[3] = 0; 89 } 90 91 return (sockaddr_to_text((struct sockaddr *)&sa_in6)); 92 } 93 94 int 95 text_to_mailaddr(struct mailaddr *maddr, const char *email) 96 { 97 char *username; 98 char *hostname; 99 char buffer[LINE_MAX]; 100 101 if (strlcpy(buffer, email, sizeof buffer) >= sizeof buffer) 102 return 0; 103 104 memset(maddr, 0, sizeof *maddr); 105 106 username = buffer; 107 hostname = strrchr(username, '@'); 108 109 if (hostname == NULL) { 110 if (strlcpy(maddr->user, username, sizeof maddr->user) 111 >= sizeof maddr->user) 112 return 0; 113 } 114 else if (username == hostname) { 115 *hostname++ = '\0'; 116 if (strlcpy(maddr->domain, hostname, sizeof maddr->domain) 117 >= sizeof maddr->domain) 118 return 0; 119 } 120 else { 121 *hostname++ = '\0'; 122 if (strlcpy(maddr->user, username, sizeof maddr->user) 123 >= sizeof maddr->user) 124 return 0; 125 if (strlcpy(maddr->domain, hostname, sizeof maddr->domain) 126 >= sizeof maddr->domain) 127 return 0; 128 } 129 130 return 1; 131 } 132 133 const char * 134 mailaddr_to_text(const struct mailaddr *maddr) 135 { 136 static char buffer[LINE_MAX]; 137 138 (void)strlcpy(buffer, maddr->user, sizeof buffer); 139 (void)strlcat(buffer, "@", sizeof buffer); 140 if (strlcat(buffer, maddr->domain, sizeof buffer) >= sizeof buffer) 141 return NULL; 142 143 return buffer; 144 } 145 146 147 const char * 148 sa_to_text(const struct sockaddr *sa) 149 { 150 static char buf[NI_MAXHOST + 5]; 151 char *p; 152 153 buf[0] = '\0'; 154 p = buf; 155 156 if (sa->sa_family == AF_LOCAL) 157 (void)strlcpy(buf, "local", sizeof buf); 158 else if (sa->sa_family == AF_INET) { 159 in_addr_t addr; 160 161 addr = ((const struct sockaddr_in *)sa)->sin_addr.s_addr; 162 addr = ntohl(addr); 163 (void)bsnprintf(p, NI_MAXHOST, "%d.%d.%d.%d", 164 (addr >> 24) & 0xff, (addr >> 16) & 0xff, 165 (addr >> 8) & 0xff, addr & 0xff); 166 } 167 else if (sa->sa_family == AF_INET6) { 168 const struct sockaddr_in6 *in6; 169 const struct in6_addr *in6_addr; 170 171 in6 = (const struct sockaddr_in6 *)sa; 172 p = buf; 173 in6_addr = &in6->sin6_addr; 174 (void)bsnprintf(p, NI_MAXHOST, "[%s]", in6addr_to_text(in6_addr)); 175 } 176 177 return (buf); 178 } 179 180 const char * 181 ss_to_text(const struct sockaddr_storage *ss) 182 { 183 return (sa_to_text((const struct sockaddr*)ss)); 184 } 185 186 const char * 187 time_to_text(time_t when) 188 { 189 struct tm *lt; 190 static char buf[40]; 191 char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 192 char *month[] = {"Jan","Feb","Mar","Apr","May","Jun", 193 "Jul","Aug","Sep","Oct","Nov","Dec"}; 194 char *tz; 195 long offset; 196 197 lt = localtime(&when); 198 if (lt == NULL || when == 0) 199 fatalx("time_to_text: localtime"); 200 201 offset = lt->tm_gmtoff; 202 tz = lt->tm_zone; 203 204 /* We do not use strftime because it is subject to locale substitution*/ 205 if (!bsnprintf(buf, sizeof(buf), 206 "%s, %d %s %d %02d:%02d:%02d %c%02d%02d (%s)", 207 day[lt->tm_wday], lt->tm_mday, month[lt->tm_mon], 208 lt->tm_year + 1900, 209 lt->tm_hour, lt->tm_min, lt->tm_sec, 210 offset >= 0 ? '+' : '-', 211 abs((int)offset / 3600), 212 abs((int)offset % 3600) / 60, 213 tz)) 214 fatalx("time_to_text: bsnprintf"); 215 216 return buf; 217 } 218 219 const char * 220 duration_to_text(time_t t) 221 { 222 static char dst[64]; 223 char buf[64]; 224 int h, m, s; 225 long long d; 226 227 if (t == 0) { 228 (void)strlcpy(dst, "0s", sizeof dst); 229 return (dst); 230 } 231 232 dst[0] = '\0'; 233 if (t < 0) { 234 (void)strlcpy(dst, "-", sizeof dst); 235 t = -t; 236 } 237 238 s = t % 60; 239 t /= 60; 240 m = t % 60; 241 t /= 60; 242 h = t % 24; 243 d = t / 24; 244 245 if (d) { 246 (void)snprintf(buf, sizeof buf, "%lldd", d); 247 (void)strlcat(dst, buf, sizeof dst); 248 } 249 if (h) { 250 (void)snprintf(buf, sizeof buf, "%dh", h); 251 (void)strlcat(dst, buf, sizeof dst); 252 } 253 if (m) { 254 (void)snprintf(buf, sizeof buf, "%dm", m); 255 (void)strlcat(dst, buf, sizeof dst); 256 } 257 if (s) { 258 (void)snprintf(buf, sizeof buf, "%ds", s); 259 (void)strlcat(dst, buf, sizeof dst); 260 } 261 262 return (dst); 263 } 264 265 int 266 text_to_netaddr(struct netaddr *netaddr, const char *s) 267 { 268 struct sockaddr_storage ss; 269 struct sockaddr_in ssin; 270 struct sockaddr_in6 ssin6; 271 int bits; 272 char buf[NI_MAXHOST]; 273 size_t len; 274 275 memset(&ssin, 0, sizeof(struct sockaddr_in)); 276 memset(&ssin6, 0, sizeof(struct sockaddr_in6)); 277 278 if (strncasecmp("IPv6:", s, 5) == 0) 279 s += 5; 280 281 bits = inet_net_pton(AF_INET, s, &ssin.sin_addr, 282 sizeof(struct in_addr)); 283 if (bits != -1) { 284 ssin.sin_family = AF_INET; 285 memcpy(&ss, &ssin, sizeof(ssin)); 286 ss.ss_len = sizeof(struct sockaddr_in); 287 } else { 288 if (s[0] != '[') { 289 if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf) 290 return 0; 291 } 292 else { 293 s++; 294 if (strncasecmp("IPv6:", s, 5) == 0) 295 s += 5; 296 if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf) 297 return 0; 298 if (buf[len-1] != ']') 299 return 0; 300 buf[len-1] = 0; 301 } 302 bits = inet_net_pton(AF_INET6, buf, &ssin6.sin6_addr, 303 sizeof(struct in6_addr)); 304 if (bits == -1) 305 return 0; 306 ssin6.sin6_family = AF_INET6; 307 memcpy(&ss, &ssin6, sizeof(ssin6)); 308 ss.ss_len = sizeof(struct sockaddr_in6); 309 } 310 311 netaddr->ss = ss; 312 netaddr->bits = bits; 313 return 1; 314 } 315 316 int 317 text_to_relayhost(struct relayhost *relay, const char *s) 318 { 319 static const struct schema { 320 const char *name; 321 int tls; 322 uint16_t flags; 323 uint16_t port; 324 } schemas [] = { 325 /* 326 * new schemas should be *appended* otherwise the default 327 * schema index needs to be updated later in this function. 328 */ 329 { "smtp://", RELAY_TLS_OPPORTUNISTIC, 0, 25 }, 330 { "smtp+tls://", RELAY_TLS_STARTTLS, 0, 25 }, 331 { "smtp+notls://", RELAY_TLS_NO, 0, 25 }, 332 /* need to specify an explicit port for LMTP */ 333 { "lmtp://", RELAY_TLS_NO, RELAY_LMTP, 0 }, 334 { "smtps://", RELAY_TLS_SMTPS, 0, 465 } 335 }; 336 const char *errstr = NULL; 337 char *p, *q; 338 char buffer[1024]; 339 char *beg, *end; 340 size_t i; 341 size_t len; 342 343 memset(buffer, 0, sizeof buffer); 344 if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) 345 return 0; 346 347 for (i = 0; i < nitems(schemas); ++i) 348 if (strncasecmp(schemas[i].name, s, 349 strlen(schemas[i].name)) == 0) 350 break; 351 352 if (i == nitems(schemas)) { 353 /* there is a schema, but it's not recognized */ 354 if (strstr(buffer, "://")) 355 return 0; 356 357 /* no schema, default to smtp:// */ 358 i = 0; 359 p = buffer; 360 } 361 else 362 p = buffer + strlen(schemas[i].name); 363 364 relay->tls = schemas[i].tls; 365 relay->flags = schemas[i].flags; 366 relay->port = schemas[i].port; 367 368 /* first, we extract the label if any */ 369 if ((q = strchr(p, '@')) != NULL) { 370 *q = 0; 371 if (strlcpy(relay->authlabel, p, sizeof (relay->authlabel)) 372 >= sizeof (relay->authlabel)) 373 return 0; 374 p = q + 1; 375 } 376 377 /* then, we extract the mail exchanger */ 378 beg = end = p; 379 if (*beg == '[') { 380 if ((end = strchr(beg, ']')) == NULL) 381 return 0; 382 /* skip ']', it has to be included in the relay hostname */ 383 ++end; 384 len = end - beg; 385 } 386 else { 387 for (end = beg; *end; ++end) 388 if (!isalnum((unsigned char)*end) && 389 *end != '_' && *end != '.' && *end != '-') 390 break; 391 len = end - beg; 392 } 393 if (len >= sizeof relay->hostname) 394 return 0; 395 for (i = 0; i < len; ++i) 396 relay->hostname[i] = beg[i]; 397 relay->hostname[i] = 0; 398 399 /* finally, we extract the port */ 400 p = beg + len; 401 if (*p == ':') { 402 relay->port = strtonum(p+1, 1, IPPORT_HILASTAUTO, &errstr); 403 if (errstr) 404 return 0; 405 } 406 407 if (!valid_domainpart(relay->hostname)) 408 return 0; 409 if ((relay->flags & RELAY_LMTP) && (relay->port == 0)) 410 return 0; 411 if (relay->authlabel[0]) { 412 /* disallow auth on non-tls scheme. */ 413 if (relay->tls != RELAY_TLS_STARTTLS && 414 relay->tls != RELAY_TLS_SMTPS) 415 return 0; 416 relay->flags |= RELAY_AUTH; 417 } 418 419 return 1; 420 } 421 422 uint64_t 423 text_to_evpid(const char *s) 424 { 425 uint64_t ulval; 426 char *ep; 427 428 errno = 0; 429 ulval = strtoull(s, &ep, 16); 430 if (s[0] == '\0' || *ep != '\0') 431 return 0; 432 if (errno == ERANGE && ulval == ULLONG_MAX) 433 return 0; 434 if (ulval == 0) 435 return 0; 436 return (ulval); 437 } 438 439 uint32_t 440 text_to_msgid(const char *s) 441 { 442 uint64_t ulval; 443 char *ep; 444 445 errno = 0; 446 ulval = strtoull(s, &ep, 16); 447 if (s[0] == '\0' || *ep != '\0') 448 return 0; 449 if (errno == ERANGE && ulval == ULLONG_MAX) 450 return 0; 451 if (ulval == 0) 452 return 0; 453 if (ulval > 0xffffffff) 454 return 0; 455 return (ulval & 0xffffffff); 456 } 457 458 const char * 459 rule_to_text(struct rule *r) 460 { 461 static char buf[4096]; 462 463 memset(buf, 0, sizeof buf); 464 (void)strlcpy(buf, "match", sizeof buf); 465 if (r->flag_tag) { 466 if (r->flag_tag < 0) 467 (void)strlcat(buf, " !", sizeof buf); 468 (void)strlcat(buf, " tag ", sizeof buf); 469 (void)strlcat(buf, r->table_tag, sizeof buf); 470 } 471 472 if (r->flag_from) { 473 if (r->flag_from < 0) 474 (void)strlcat(buf, " !", sizeof buf); 475 if (r->flag_from_socket) 476 (void)strlcat(buf, " from socket", sizeof buf); 477 else if (r->flag_from_rdns) { 478 (void)strlcat(buf, " from rdns", sizeof buf); 479 if (r->table_from) { 480 (void)strlcat(buf, " ", sizeof buf); 481 (void)strlcat(buf, r->table_from, sizeof buf); 482 } 483 } 484 else if (strcmp(r->table_from, "<anyhost>") == 0) 485 (void)strlcat(buf, " from any", sizeof buf); 486 else if (strcmp(r->table_from, "<localhost>") == 0) 487 (void)strlcat(buf, " from local", sizeof buf); 488 else { 489 (void)strlcat(buf, " from src ", sizeof buf); 490 (void)strlcat(buf, r->table_from, sizeof buf); 491 } 492 } 493 494 if (r->flag_for) { 495 if (r->flag_for < 0) 496 (void)strlcat(buf, " !", sizeof buf); 497 if (strcmp(r->table_for, "<anydestination>") == 0) 498 (void)strlcat(buf, " for any", sizeof buf); 499 else if (strcmp(r->table_for, "<localnames>") == 0) 500 (void)strlcat(buf, " for local", sizeof buf); 501 else { 502 (void)strlcat(buf, " for domain ", sizeof buf); 503 (void)strlcat(buf, r->table_for, sizeof buf); 504 } 505 } 506 507 if (r->flag_smtp_helo) { 508 if (r->flag_smtp_helo < 0) 509 (void)strlcat(buf, " !", sizeof buf); 510 (void)strlcat(buf, " helo ", sizeof buf); 511 (void)strlcat(buf, r->table_smtp_helo, sizeof buf); 512 } 513 514 if (r->flag_smtp_auth) { 515 if (r->flag_smtp_auth < 0) 516 (void)strlcat(buf, " !", sizeof buf); 517 (void)strlcat(buf, " auth", sizeof buf); 518 if (r->table_smtp_auth) { 519 (void)strlcat(buf, " ", sizeof buf); 520 (void)strlcat(buf, r->table_smtp_auth, sizeof buf); 521 } 522 } 523 524 if (r->flag_smtp_starttls) { 525 if (r->flag_smtp_starttls < 0) 526 (void)strlcat(buf, " !", sizeof buf); 527 (void)strlcat(buf, " tls", sizeof buf); 528 } 529 530 if (r->flag_smtp_mail_from) { 531 if (r->flag_smtp_mail_from < 0) 532 (void)strlcat(buf, " !", sizeof buf); 533 (void)strlcat(buf, " mail-from ", sizeof buf); 534 (void)strlcat(buf, r->table_smtp_mail_from, sizeof buf); 535 } 536 537 if (r->flag_smtp_rcpt_to) { 538 if (r->flag_smtp_rcpt_to < 0) 539 (void)strlcat(buf, " !", sizeof buf); 540 (void)strlcat(buf, " rcpt-to ", sizeof buf); 541 (void)strlcat(buf, r->table_smtp_rcpt_to, sizeof buf); 542 } 543 (void)strlcat(buf, " action ", sizeof buf); 544 if (r->reject) 545 (void)strlcat(buf, "reject", sizeof buf); 546 else 547 (void)strlcat(buf, r->dispatcher, sizeof buf); 548 return buf; 549 } 550 551 552 int 553 text_to_userinfo(struct userinfo *userinfo, const char *s) 554 { 555 char buf[PATH_MAX]; 556 char *p; 557 const char *errstr; 558 559 memset(buf, 0, sizeof buf); 560 p = buf; 561 while (*s && *s != ':') 562 *p++ = *s++; 563 if (*s++ != ':') 564 goto error; 565 566 if (strlcpy(userinfo->username, buf, 567 sizeof userinfo->username) >= sizeof userinfo->username) 568 goto error; 569 570 memset(buf, 0, sizeof buf); 571 p = buf; 572 while (*s && *s != ':') 573 *p++ = *s++; 574 if (*s++ != ':') 575 goto error; 576 userinfo->uid = strtonum(buf, 0, UID_MAX, &errstr); 577 if (errstr) 578 goto error; 579 580 memset(buf, 0, sizeof buf); 581 p = buf; 582 while (*s && *s != ':') 583 *p++ = *s++; 584 if (*s++ != ':') 585 goto error; 586 userinfo->gid = strtonum(buf, 0, GID_MAX, &errstr); 587 if (errstr) 588 goto error; 589 590 if (strlcpy(userinfo->directory, s, 591 sizeof userinfo->directory) >= sizeof userinfo->directory) 592 goto error; 593 594 return 1; 595 596 error: 597 return 0; 598 } 599 600 int 601 text_to_credentials(struct credentials *creds, const char *s) 602 { 603 char *p; 604 char buffer[LINE_MAX]; 605 size_t offset; 606 607 p = strchr(s, ':'); 608 if (p == NULL) { 609 creds->username[0] = '\0'; 610 if (strlcpy(creds->password, s, sizeof creds->password) 611 >= sizeof creds->password) 612 return 0; 613 return 1; 614 } 615 616 offset = p - s; 617 618 memset(buffer, 0, sizeof buffer); 619 if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) 620 return 0; 621 p = buffer + offset; 622 *p = '\0'; 623 624 if (strlcpy(creds->username, buffer, sizeof creds->username) 625 >= sizeof creds->username) 626 return 0; 627 if (strlcpy(creds->password, p+1, sizeof creds->password) 628 >= sizeof creds->password) 629 return 0; 630 631 return 1; 632 } 633 634 int 635 text_to_expandnode(struct expandnode *expandnode, const char *s) 636 { 637 size_t l; 638 639 l = strlen(s); 640 if (alias_is_error(expandnode, s, l) || 641 alias_is_include(expandnode, s, l) || 642 alias_is_filter(expandnode, s, l) || 643 alias_is_filename(expandnode, s, l) || 644 alias_is_address(expandnode, s, l) || 645 alias_is_username(expandnode, s, l)) 646 return (1); 647 648 return (0); 649 } 650 651 const char * 652 expandnode_to_text(struct expandnode *expandnode) 653 { 654 switch (expandnode->type) { 655 case EXPAND_FILTER: 656 case EXPAND_FILENAME: 657 case EXPAND_INCLUDE: 658 case EXPAND_ERROR: 659 case EXPAND_USERNAME: 660 return expandnode->u.user; 661 case EXPAND_ADDRESS: 662 return mailaddr_to_text(&expandnode->u.mailaddr); 663 case EXPAND_INVALID: 664 break; 665 } 666 667 return NULL; 668 } 669 670 /******/ 671 static int 672 alias_is_filter(struct expandnode *alias, const char *line, size_t len) 673 { 674 int v = 0; 675 676 if (*line == '"') 677 v = 1; 678 if (*(line+v) == '|') { 679 if (strlcpy(alias->u.buffer, line + v + 1, 680 sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) 681 return 0; 682 if (v) { 683 v = strlen(alias->u.buffer); 684 if (v == 0) 685 return (0); 686 if (alias->u.buffer[v-1] != '"') 687 return (0); 688 alias->u.buffer[v-1] = '\0'; 689 } 690 alias->type = EXPAND_FILTER; 691 return (1); 692 } 693 return (0); 694 } 695 696 static int 697 alias_is_username(struct expandnode *alias, const char *line, size_t len) 698 { 699 memset(alias, 0, sizeof *alias); 700 701 if (strlcpy(alias->u.user, line, 702 sizeof(alias->u.user)) >= sizeof(alias->u.user)) 703 return 0; 704 705 while (*line) { 706 if (!isalnum((unsigned char)*line) && 707 *line != '_' && *line != '.' && *line != '-' && *line != '+') 708 return 0; 709 ++line; 710 } 711 712 alias->type = EXPAND_USERNAME; 713 return 1; 714 } 715 716 static int 717 alias_is_address(struct expandnode *alias, const char *line, size_t len) 718 { 719 char *domain; 720 721 memset(alias, 0, sizeof *alias); 722 723 if (len < 3) /* x@y */ 724 return 0; 725 726 domain = strchr(line, '@'); 727 if (domain == NULL) 728 return 0; 729 730 /* @ cannot start or end an address */ 731 if (domain == line || domain == line + len - 1) 732 return 0; 733 734 /* scan pre @ for disallowed chars */ 735 *domain++ = '\0'; 736 (void)strlcpy(alias->u.mailaddr.user, line, sizeof(alias->u.mailaddr.user)); 737 (void)strlcpy(alias->u.mailaddr.domain, domain, 738 sizeof(alias->u.mailaddr.domain)); 739 740 while (*line) { 741 char allowedset[] = "!#$%*/?|^{}`~&'+-=_."; 742 if (!isalnum((unsigned char)*line) && 743 strchr(allowedset, *line) == NULL) 744 return 0; 745 ++line; 746 } 747 748 while (*domain) { 749 char allowedset[] = "-."; 750 if (!isalnum((unsigned char)*domain) && 751 strchr(allowedset, *domain) == NULL) 752 return 0; 753 ++domain; 754 } 755 756 alias->type = EXPAND_ADDRESS; 757 return 1; 758 } 759 760 static int 761 alias_is_filename(struct expandnode *alias, const char *line, size_t len) 762 { 763 memset(alias, 0, sizeof *alias); 764 765 if (*line != '/') 766 return 0; 767 768 if (strlcpy(alias->u.buffer, line, 769 sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) 770 return 0; 771 alias->type = EXPAND_FILENAME; 772 return 1; 773 } 774 775 static int 776 alias_is_include(struct expandnode *alias, const char *line, size_t len) 777 { 778 size_t skip; 779 780 memset(alias, 0, sizeof *alias); 781 782 if (strncasecmp(":include:", line, 9) == 0) 783 skip = 9; 784 else if (strncasecmp("include:", line, 8) == 0) 785 skip = 8; 786 else 787 return 0; 788 789 if (!alias_is_filename(alias, line + skip, len - skip)) 790 return 0; 791 792 alias->type = EXPAND_INCLUDE; 793 return 1; 794 } 795 796 static int 797 alias_is_error(struct expandnode *alias, const char *line, size_t len) 798 { 799 size_t skip; 800 801 memset(alias, 0, sizeof *alias); 802 803 if (strncasecmp(":error:", line, 7) == 0) 804 skip = 7; 805 else if (strncasecmp("error:", line, 6) == 0) 806 skip = 6; 807 else 808 return 0; 809 810 if (strlcpy(alias->u.buffer, line + skip, 811 sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) 812 return 0; 813 814 if (strlen(alias->u.buffer) < 5) 815 return 0; 816 817 /* [45][0-9]{2} [a-zA-Z0-9].* */ 818 if (alias->u.buffer[3] != ' ' || 819 !isalnum((unsigned char)alias->u.buffer[4]) || 820 (alias->u.buffer[0] != '4' && alias->u.buffer[0] != '5') || 821 !isdigit((unsigned char)alias->u.buffer[1]) || 822 !isdigit((unsigned char)alias->u.buffer[2])) 823 return 0; 824 825 alias->type = EXPAND_ERROR; 826 return 1; 827 } 828