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