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