1 /* $OpenBSD: parse.y,v 1.149 2014/11/20 05:51:21 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> 5 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 6 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 7 * Copyright (c) 2001 Markus Friedl. All rights reserved. 8 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 9 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 10 * 11 * Permission to use, copy, modify, and distribute this software for any 12 * purpose with or without fee is hereby granted, provided that the above 13 * copyright notice and this permission notice appear in all copies. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 */ 23 24 %{ 25 #include <sys/types.h> 26 #include <sys/queue.h> 27 #include <sys/tree.h> 28 #include <sys/socket.h> 29 #include <sys/stat.h> 30 #include <sys/ioctl.h> 31 32 #include <net/if.h> 33 #include <netinet/in.h> 34 #include <arpa/inet.h> 35 36 #include <ctype.h> 37 #include <err.h> 38 #include <errno.h> 39 #include <event.h> 40 #include <ifaddrs.h> 41 #include <imsg.h> 42 #include <inttypes.h> 43 #include <netdb.h> 44 #include <paths.h> 45 #include <pwd.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <syslog.h> 50 #include <unistd.h> 51 #include <util.h> 52 53 #include <openssl/ssl.h> 54 55 #include "smtpd.h" 56 #include "ssl.h" 57 #include "log.h" 58 59 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 60 static struct file { 61 TAILQ_ENTRY(file) entry; 62 FILE *stream; 63 char *name; 64 int lineno; 65 int errors; 66 } *file, *topfile; 67 struct file *pushfile(const char *, int); 68 int popfile(void); 69 int check_file_secrecy(int, const char *); 70 int yyparse(void); 71 int yylex(void); 72 int kw_cmp(const void *, const void *); 73 int lookup(char *); 74 int lgetc(int); 75 int lungetc(int); 76 int findeol(void); 77 int yyerror(const char *, ...) 78 __attribute__((__format__ (printf, 1, 2))) 79 __attribute__((__nonnull__ (1))); 80 81 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 82 struct sym { 83 TAILQ_ENTRY(sym) entry; 84 int used; 85 int persist; 86 char *nam; 87 char *val; 88 }; 89 int symset(const char *, const char *, int); 90 char *symget(const char *); 91 92 struct smtpd *conf = NULL; 93 static int errors = 0; 94 95 struct filter_conf *filter = NULL; 96 struct table *table = NULL; 97 struct rule *rule = NULL; 98 struct listener l; 99 struct mta_limits *limits; 100 static struct pki *pki; 101 102 enum listen_options { 103 LO_FAMILY = 0x01, 104 LO_PORT = 0x02, 105 LO_SSL = 0x04, 106 LO_FILTER = 0x08, 107 LO_PKI = 0x10, 108 LO_AUTH = 0x20, 109 LO_TAG = 0x40, 110 LO_HOSTNAME = 0x80, 111 LO_HOSTNAMES = 0x100, 112 LO_MASKSOURCE = 0x200, 113 LO_NODSN = 0x400, 114 }; 115 116 static struct listen_opts { 117 char *ifx; 118 int family; 119 in_port_t port; 120 uint16_t ssl; 121 char *filtername; 122 char *pki; 123 uint16_t auth; 124 struct table *authtable; 125 char *tag; 126 char *hostname; 127 struct table *hostnametable; 128 uint16_t flags; 129 130 uint16_t options; 131 } listen_opts; 132 133 static void create_listener(struct listenerlist *, struct listen_opts *); 134 static void config_listener(struct listener *, struct listen_opts *); 135 136 struct listener *host_v4(const char *, in_port_t); 137 struct listener *host_v6(const char *, in_port_t); 138 int host_dns(struct listenerlist *, struct listen_opts *); 139 int host(struct listenerlist *, struct listen_opts *); 140 int interface(struct listenerlist *, struct listen_opts *); 141 void set_local(const char *); 142 void set_localaddrs(struct table *); 143 int delaytonum(char *); 144 int is_if_in_group(const char *, const char *); 145 146 static struct filter_conf *create_filter_proc(char *, char *); 147 static struct filter_conf *create_filter_chain(char *); 148 static int add_filter_arg(struct filter_conf *, char *); 149 150 typedef struct { 151 union { 152 int64_t number; 153 struct table *table; 154 char *string; 155 struct host *host; 156 struct mailaddr *maddr; 157 } v; 158 int lineno; 159 } YYSTYPE; 160 161 %} 162 163 %token AS QUEUE COMPRESSION ENCRYPTION MAXMESSAGESIZE MAXMTADEFERRED LISTEN ON ANY PORT EXPIRE 164 %token TABLE SECURE SMTPS CERTIFICATE DOMAIN BOUNCEWARN LIMIT INET4 INET6 NODSN 165 %token RELAY BACKUP VIA DELIVER TO LMTP MAILDIR MBOX HOSTNAME HOSTNAMES 166 %token ACCEPT REJECT INCLUDE ERROR MDA FROM FOR SOURCE MTA PKI SCHEDULER 167 %token ARROW AUTH TLS LOCAL VIRTUAL TAG TAGGED ALIAS FILTER KEY CA DHPARAMS 168 %token AUTH_OPTIONAL TLS_REQUIRE USERBASE SENDER MASK_SOURCE VERIFY FORWARDONLY RECIPIENT 169 %token <v.string> STRING 170 %token <v.number> NUMBER 171 %type <v.table> table 172 %type <v.number> size negation 173 %type <v.table> tables tablenew tableref alias virtual userbase 174 %type <v.string> tagged 175 %% 176 177 grammar : /* empty */ 178 | grammar '\n' 179 | grammar include '\n' 180 | grammar varset '\n' 181 | grammar main '\n' 182 | grammar table '\n' 183 | grammar rule '\n' 184 | grammar error '\n' { file->errors++; } 185 ; 186 187 include : INCLUDE STRING { 188 struct file *nfile; 189 190 if ((nfile = pushfile($2, 0)) == NULL) { 191 yyerror("failed to include file %s", $2); 192 free($2); 193 YYERROR; 194 } 195 free($2); 196 197 file = nfile; 198 lungetc('\n'); 199 } 200 ; 201 202 varset : STRING '=' STRING { 203 if (symset($1, $3, 0) == -1) 204 fatal("cannot store variable"); 205 free($1); 206 free($3); 207 } 208 ; 209 210 comma : ',' 211 | nl 212 | /* empty */ 213 ; 214 215 optnl : '\n' optnl 216 | 217 ; 218 219 nl : '\n' optnl 220 ; 221 222 size : NUMBER { 223 if ($1 < 0) { 224 yyerror("invalid size: %" PRId64, $1); 225 YYERROR; 226 } 227 $$ = $1; 228 } 229 | STRING { 230 long long result; 231 232 if (scan_scaled($1, &result) == -1 || result < 0) { 233 yyerror("invalid size: %s", $1); 234 free($1); 235 YYERROR; 236 } 237 free($1); 238 $$ = result; 239 } 240 ; 241 242 tagged : TAGGED negation STRING { 243 if (strlcpy(rule->r_tag, $3, sizeof rule->r_tag) 244 >= sizeof rule->r_tag) { 245 yyerror("tag name too long: %s", $3); 246 free($3); 247 YYERROR; 248 } 249 free($3); 250 rule->r_nottag = $2; 251 } 252 ; 253 254 bouncedelay : STRING { 255 time_t d; 256 int i; 257 258 d = delaytonum($1); 259 if (d < 0) { 260 yyerror("invalid bounce delay: %s", $1); 261 free($1); 262 YYERROR; 263 } 264 free($1); 265 for (i = 0; i < MAX_BOUNCE_WARN; i++) { 266 if (conf->sc_bounce_warn[i] != 0) 267 continue; 268 conf->sc_bounce_warn[i] = d; 269 break; 270 } 271 } 272 273 bouncedelays : bouncedelays ',' bouncedelay 274 | bouncedelay 275 | /* EMPTY */ 276 ; 277 278 opt_limit_mda : STRING NUMBER { 279 if (!strcmp($1, "max-session")) { 280 conf->sc_mda_max_session = $2; 281 } 282 else if (!strcmp($1, "max-session-per-user")) { 283 conf->sc_mda_max_user_session = $2; 284 } 285 else if (!strcmp($1, "task-lowat")) { 286 conf->sc_mda_task_lowat = $2; 287 } 288 else if (!strcmp($1, "task-hiwat")) { 289 conf->sc_mda_task_hiwat = $2; 290 } 291 else if (!strcmp($1, "task-release")) { 292 conf->sc_mda_task_release = $2; 293 } 294 else { 295 yyerror("invalid scheduler limit keyword: %s", $1); 296 free($1); 297 YYERROR; 298 } 299 free($1); 300 } 301 ; 302 303 limits_mda : opt_limit_mda limits_mda 304 | /* empty */ 305 ; 306 307 opt_limit_mta : INET4 { 308 limits->family = AF_INET; 309 } 310 | INET6 { 311 limits->family = AF_INET6; 312 } 313 | STRING NUMBER { 314 if (!limit_mta_set(limits, $1, $2)) { 315 yyerror("invalid mta limit keyword: %s", $1); 316 free($1); 317 YYERROR; 318 } 319 free($1); 320 } 321 ; 322 323 limits_mta : opt_limit_mta limits_mta 324 | /* empty */ 325 ; 326 327 opt_limit_scheduler : STRING NUMBER { 328 if (!strcmp($1, "max-inflight")) { 329 conf->sc_scheduler_max_inflight = $2; 330 } 331 else if (!strcmp($1, "max-evp-batch-size")) { 332 conf->sc_scheduler_max_evp_batch_size = $2; 333 } 334 else if (!strcmp($1, "max-msg-batch-size")) { 335 conf->sc_scheduler_max_msg_batch_size = $2; 336 } 337 else if (!strcmp($1, "max-schedule")) { 338 conf->sc_scheduler_max_schedule = $2; 339 } 340 else { 341 yyerror("invalid scheduler limit keyword: %s", $1); 342 free($1); 343 YYERROR; 344 } 345 free($1); 346 } 347 ; 348 349 limits_scheduler: opt_limit_scheduler limits_scheduler 350 | /* empty */ 351 ; 352 353 opt_pki : CERTIFICATE STRING { 354 pki->pki_cert_file = $2; 355 } 356 | KEY STRING { 357 pki->pki_key_file = $2; 358 } 359 | CA STRING { 360 pki->pki_ca_file = $2; 361 } 362 | DHPARAMS STRING { 363 pki->pki_dhparams_file = $2; 364 } 365 ; 366 367 pki : opt_pki pki 368 | /* empty */ 369 ; 370 371 opt_listen : INET4 { 372 if (listen_opts.options & LO_FAMILY) { 373 yyerror("address family already specified"); 374 YYERROR; 375 } 376 listen_opts.options |= LO_FAMILY; 377 listen_opts.family = AF_INET; 378 } 379 | INET6 { 380 if (listen_opts.options & LO_FAMILY) { 381 yyerror("address family already specified"); 382 YYERROR; 383 } 384 listen_opts.options |= LO_FAMILY; 385 listen_opts.family = AF_INET6; 386 } 387 | PORT STRING { 388 struct servent *servent; 389 390 if (listen_opts.options & LO_PORT) { 391 yyerror("port already specified"); 392 YYERROR; 393 } 394 listen_opts.options |= LO_PORT; 395 396 servent = getservbyname($2, "tcp"); 397 if (servent == NULL) { 398 yyerror("invalid port: %s", $2); 399 free($2); 400 YYERROR; 401 } 402 free($2); 403 listen_opts.port = ntohs(servent->s_port); 404 } 405 | PORT NUMBER { 406 if (listen_opts.options & LO_PORT) { 407 yyerror("port already specified"); 408 YYERROR; 409 } 410 listen_opts.options |= LO_PORT; 411 412 if ($2 <= 0 || $2 >= (int)USHRT_MAX) { 413 yyerror("invalid port: %" PRId64, $2); 414 YYERROR; 415 } 416 listen_opts.port = $2; 417 } 418 | FILTER STRING { 419 if (listen_opts.options & LO_FILTER) { 420 yyerror("filter already specified"); 421 YYERROR; 422 } 423 listen_opts.options |= LO_FILTER; 424 listen_opts.filtername = $2; 425 } 426 | SMTPS { 427 if (listen_opts.options & LO_SSL) { 428 yyerror("TLS mode already specified"); 429 YYERROR; 430 } 431 listen_opts.options |= LO_SSL; 432 listen_opts.ssl = F_SMTPS; 433 } 434 | SMTPS VERIFY { 435 if (listen_opts.options & LO_SSL) { 436 yyerror("TLS mode already specified"); 437 YYERROR; 438 } 439 listen_opts.options |= LO_SSL; 440 listen_opts.ssl = F_SMTPS|F_TLS_VERIFY; 441 } 442 | TLS { 443 if (listen_opts.options & LO_SSL) { 444 yyerror("TLS mode already specified"); 445 YYERROR; 446 } 447 listen_opts.options |= LO_SSL; 448 listen_opts.ssl = F_STARTTLS; 449 } 450 | SECURE { 451 if (listen_opts.options & LO_SSL) { 452 yyerror("TLS mode already specified"); 453 YYERROR; 454 } 455 listen_opts.options |= LO_SSL; 456 listen_opts.ssl = F_SSL; 457 } 458 | TLS_REQUIRE { 459 if (listen_opts.options & LO_SSL) { 460 yyerror("TLS mode already specified"); 461 YYERROR; 462 } 463 listen_opts.options |= LO_SSL; 464 listen_opts.ssl = F_STARTTLS|F_STARTTLS_REQUIRE; 465 } 466 | TLS_REQUIRE VERIFY { 467 if (listen_opts.options & LO_SSL) { 468 yyerror("TLS mode already specified"); 469 YYERROR; 470 } 471 listen_opts.options |= LO_SSL; 472 listen_opts.ssl = F_STARTTLS|F_STARTTLS_REQUIRE|F_TLS_VERIFY; 473 } 474 | PKI STRING { 475 if (listen_opts.options & LO_PKI) { 476 yyerror("pki already specified"); 477 YYERROR; 478 } 479 listen_opts.options |= LO_PKI; 480 listen_opts.pki = $2; 481 } 482 | AUTH { 483 if (listen_opts.options & LO_AUTH) { 484 yyerror("auth already specified"); 485 YYERROR; 486 } 487 listen_opts.options |= LO_AUTH; 488 listen_opts.auth = F_AUTH|F_AUTH_REQUIRE; 489 } 490 | AUTH_OPTIONAL { 491 if (listen_opts.options & LO_AUTH) { 492 yyerror("auth already specified"); 493 YYERROR; 494 } 495 listen_opts.options |= LO_AUTH; 496 listen_opts.auth = F_AUTH; 497 } 498 | AUTH tables { 499 if (listen_opts.options & LO_AUTH) { 500 yyerror("auth already specified"); 501 YYERROR; 502 } 503 listen_opts.options |= LO_AUTH; 504 listen_opts.authtable = $2; 505 listen_opts.auth = F_AUTH|F_AUTH_REQUIRE; 506 } 507 | AUTH_OPTIONAL tables { 508 if (listen_opts.options & LO_AUTH) { 509 yyerror("auth already specified"); 510 YYERROR; 511 } 512 listen_opts.options |= LO_AUTH; 513 listen_opts.authtable = $2; 514 listen_opts.auth = F_AUTH; 515 } 516 | TAG STRING { 517 if (listen_opts.options & LO_TAG) { 518 yyerror("tag already specified"); 519 YYERROR; 520 } 521 listen_opts.options |= LO_TAG; 522 523 if (strlen($2) >= MAX_TAG_SIZE) { 524 yyerror("tag name too long"); 525 free($2); 526 YYERROR; 527 } 528 listen_opts.tag = $2; 529 } 530 | HOSTNAME STRING { 531 if (listen_opts.options & LO_HOSTNAME) { 532 yyerror("hostname already specified"); 533 YYERROR; 534 } 535 listen_opts.options |= LO_HOSTNAME; 536 537 listen_opts.hostname = $2; 538 } 539 | HOSTNAMES tables { 540 struct table *t = $2; 541 542 if (listen_opts.options & LO_HOSTNAMES) { 543 yyerror("hostnames already specified"); 544 YYERROR; 545 } 546 listen_opts.options |= LO_HOSTNAMES; 547 548 if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) { 549 yyerror("invalid use of table \"%s\" as " 550 "HOSTNAMES parameter", t->t_name); 551 YYERROR; 552 } 553 listen_opts.hostnametable = t; 554 } 555 | MASK_SOURCE { 556 if (listen_opts.options & LO_MASKSOURCE) { 557 yyerror("mask-source already specified"); 558 YYERROR; 559 } 560 listen_opts.options |= LO_MASKSOURCE; 561 listen_opts.flags |= F_MASK_SOURCE; 562 } 563 | NODSN { 564 if (listen_opts.options & LO_NODSN) { 565 yyerror("no-dsn already specified"); 566 YYERROR; 567 } 568 listen_opts.options |= LO_NODSN; 569 listen_opts.flags &= ~F_EXT_DSN; 570 } 571 ; 572 573 listen : opt_listen listen 574 | /* empty */ 575 ; 576 577 opt_relay_common: AS STRING { 578 struct mailaddr maddr, *maddrp; 579 580 if (! text_to_mailaddr(&maddr, $2)) { 581 yyerror("invalid parameter to AS: %s", $2); 582 free($2); 583 YYERROR; 584 } 585 free($2); 586 587 if (maddr.user[0] == '\0' && maddr.domain[0] == '\0') { 588 yyerror("invalid empty parameter to AS"); 589 YYERROR; 590 } 591 else if (maddr.domain[0] == '\0') { 592 if (strlcpy(maddr.domain, conf->sc_hostname, 593 sizeof (maddr.domain)) 594 >= sizeof (maddr.domain)) { 595 yyerror("hostname too long for AS parameter: %s", 596 conf->sc_hostname); 597 YYERROR; 598 } 599 } 600 rule->r_as = xmemdup(&maddr, sizeof (*maddrp), "parse relay_as: AS"); 601 } 602 | SOURCE tables { 603 struct table *t = $2; 604 if (! table_check_use(t, T_DYNAMIC|T_LIST, K_SOURCE)) { 605 yyerror("invalid use of table \"%s\" as " 606 "SOURCE parameter", t->t_name); 607 YYERROR; 608 } 609 (void)strlcpy(rule->r_value.relayhost.sourcetable, t->t_name, 610 sizeof rule->r_value.relayhost.sourcetable); 611 } 612 | HOSTNAME STRING { 613 (void)strlcpy(rule->r_value.relayhost.heloname, $2, 614 sizeof rule->r_value.relayhost.heloname); 615 free($2); 616 } 617 | HOSTNAMES tables { 618 struct table *t = $2; 619 if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) { 620 yyerror("invalid use of table \"%s\" as " 621 "HOSTNAMES parameter", t->t_name); 622 YYERROR; 623 } 624 (void)strlcpy(rule->r_value.relayhost.helotable, t->t_name, 625 sizeof rule->r_value.relayhost.helotable); 626 } 627 | PKI STRING { 628 if (! lowercase(rule->r_value.relayhost.pki_name, $2, 629 sizeof(rule->r_value.relayhost.pki_name))) { 630 yyerror("pki name too long: %s", $2); 631 free($2); 632 YYERROR; 633 } 634 if (dict_get(conf->sc_pki_dict, 635 rule->r_value.relayhost.pki_name) == NULL) { 636 log_warnx("pki name not found: %s", $2); 637 free($2); 638 YYERROR; 639 } 640 free($2); 641 } 642 ; 643 644 opt_relay : BACKUP STRING { 645 rule->r_value.relayhost.flags |= F_BACKUP; 646 if (strlcpy(rule->r_value.relayhost.hostname, $2, 647 sizeof (rule->r_value.relayhost.hostname)) 648 >= sizeof (rule->r_value.relayhost.hostname)) { 649 log_warnx("hostname too long: %s", $2); 650 free($2); 651 YYERROR; 652 } 653 free($2); 654 } 655 | BACKUP { 656 rule->r_value.relayhost.flags |= F_BACKUP; 657 (void)strlcpy(rule->r_value.relayhost.hostname, 658 conf->sc_hostname, 659 sizeof (rule->r_value.relayhost.hostname)); 660 } 661 | TLS { 662 rule->r_value.relayhost.flags |= F_STARTTLS; 663 } 664 | TLS VERIFY { 665 rule->r_value.relayhost.flags |= F_STARTTLS|F_TLS_VERIFY; 666 } 667 ; 668 669 relay : opt_relay_common relay 670 | opt_relay relay 671 | /* empty */ 672 ; 673 674 opt_relay_via : AUTH tables { 675 struct table *t = $2; 676 677 if (! table_check_use(t, T_DYNAMIC|T_HASH, K_CREDENTIALS)) { 678 yyerror("invalid use of table \"%s\" as AUTH parameter", 679 t->t_name); 680 YYERROR; 681 } 682 (void)strlcpy(rule->r_value.relayhost.authtable, t->t_name, 683 sizeof(rule->r_value.relayhost.authtable)); 684 } 685 | VERIFY { 686 if (!(rule->r_value.relayhost.flags & F_SSL)) { 687 yyerror("cannot \"verify\" with insecure protocol"); 688 YYERROR; 689 } 690 rule->r_value.relayhost.flags |= F_TLS_VERIFY; 691 } 692 ; 693 694 relay_via : opt_relay_common relay_via 695 | opt_relay_via relay_via 696 | /* empty */ 697 ; 698 699 main : BOUNCEWARN { 700 memset(conf->sc_bounce_warn, 0, sizeof conf->sc_bounce_warn); 701 } bouncedelays 702 | QUEUE COMPRESSION { 703 conf->sc_queue_flags |= QUEUE_COMPRESSION; 704 } 705 | QUEUE ENCRYPTION { 706 conf->sc_queue_flags |= QUEUE_ENCRYPTION; 707 } 708 | QUEUE ENCRYPTION KEY STRING { 709 if (strcasecmp($4, "stdin") == 0 || strcasecmp($4, "-") == 0) { 710 conf->sc_queue_key = "stdin"; 711 free($4); 712 } 713 else 714 conf->sc_queue_key = $4; 715 conf->sc_queue_flags |= QUEUE_ENCRYPTION; 716 } 717 | EXPIRE STRING { 718 conf->sc_qexpire = delaytonum($2); 719 if (conf->sc_qexpire == -1) { 720 yyerror("invalid expire delay: %s", $2); 721 free($2); 722 YYERROR; 723 } 724 free($2); 725 } 726 | MAXMESSAGESIZE size { 727 conf->sc_maxsize = $2; 728 } 729 | MAXMTADEFERRED NUMBER { 730 conf->sc_mta_max_deferred = $2; 731 } 732 | LIMIT MDA limits_mda 733 | LIMIT MTA FOR DOMAIN STRING { 734 struct mta_limits *d; 735 736 limits = dict_get(conf->sc_limits_dict, $5); 737 if (limits == NULL) { 738 limits = xcalloc(1, sizeof(*limits), "mta_limits"); 739 dict_xset(conf->sc_limits_dict, $5, limits); 740 d = dict_xget(conf->sc_limits_dict, "default"); 741 memmove(limits, d, sizeof(*limits)); 742 } 743 free($5); 744 } limits_mta 745 | LIMIT MTA { 746 limits = dict_get(conf->sc_limits_dict, "default"); 747 } limits_mta 748 | LIMIT SCHEDULER limits_scheduler 749 | LISTEN { 750 memset(&l, 0, sizeof l); 751 memset(&listen_opts, 0, sizeof listen_opts); 752 listen_opts.family = AF_UNSPEC; 753 listen_opts.flags |= F_EXT_DSN; 754 } ON STRING listen { 755 listen_opts.ifx = $4; 756 create_listener(conf->sc_listeners, &listen_opts); 757 } 758 | FILTER STRING STRING { 759 if (!strcmp($3, "chain")) { 760 free($3); 761 if ((filter = create_filter_chain($2)) == NULL) { 762 free($2); 763 YYERROR; 764 } 765 } 766 else { 767 if ((filter = create_filter_proc($2, $3)) == NULL) { 768 free($2); 769 free($3); 770 YYERROR; 771 } 772 } 773 } filter_args; 774 | PKI STRING { 775 char buf[MAXHOSTNAMELEN]; 776 xlowercase(buf, $2, sizeof(buf)); 777 free($2); 778 pki = dict_get(conf->sc_pki_dict, buf); 779 if (pki == NULL) { 780 pki = xcalloc(1, sizeof *pki, "parse:pki"); 781 (void)strlcpy(pki->pki_name, buf, sizeof(pki->pki_name)); 782 dict_set(conf->sc_pki_dict, pki->pki_name, pki); 783 } 784 } pki 785 ; 786 787 filter_args : 788 | STRING { 789 if (!add_filter_arg(filter, $1)) { 790 free($1); 791 YYERROR; 792 } 793 } filter_args 794 ; 795 796 table : TABLE STRING STRING { 797 char *p, *backend, *config; 798 799 p = $3; 800 if (*p == '/') { 801 backend = "static"; 802 config = $3; 803 } 804 else { 805 backend = $3; 806 config = NULL; 807 for (p = $3; *p && *p != ':'; p++) 808 ; 809 if (*p == ':') { 810 *p = '\0'; 811 backend = $3; 812 config = p+1; 813 } 814 } 815 if (config != NULL && *config != '/') { 816 yyerror("invalid backend parameter for table: %s", 817 $2); 818 free($2); 819 free($3); 820 YYERROR; 821 } 822 table = table_create(backend, $2, NULL, config); 823 if (!table_config(table)) { 824 yyerror("invalid configuration file %s for table %s", 825 config, table->t_name); 826 free($2); 827 free($3); 828 YYERROR; 829 } 830 free($2); 831 free($3); 832 } 833 | TABLE STRING { 834 table = table_create("static", $2, NULL, NULL); 835 free($2); 836 } '{' tableval_list '}' { 837 table = NULL; 838 } 839 ; 840 841 assign : '=' | ARROW; 842 843 keyval : STRING assign STRING { 844 table->t_type = T_HASH; 845 table_add(table, $1, $3); 846 free($1); 847 free($3); 848 } 849 ; 850 851 keyval_list : keyval 852 | keyval comma keyval_list 853 ; 854 855 stringel : STRING { 856 table->t_type = T_LIST; 857 table_add(table, $1, NULL); 858 free($1); 859 } 860 ; 861 862 string_list : stringel 863 | stringel comma string_list 864 ; 865 866 tableval_list : string_list { } 867 | keyval_list { } 868 ; 869 870 tablenew : STRING { 871 struct table *t; 872 873 t = table_create("static", NULL, NULL, NULL); 874 t->t_type = T_LIST; 875 table_add(t, $1, NULL); 876 free($1); 877 $$ = t; 878 } 879 | '{' { 880 table = table_create("static", NULL, NULL, NULL); 881 } tableval_list '}' { 882 $$ = table; 883 } 884 ; 885 886 tableref : '<' STRING '>' { 887 struct table *t; 888 889 if ((t = table_find($2, NULL)) == NULL) { 890 yyerror("no such table: %s", $2); 891 free($2); 892 YYERROR; 893 } 894 free($2); 895 $$ = t; 896 } 897 ; 898 899 tables : tablenew { $$ = $1; } 900 | tableref { $$ = $1; } 901 ; 902 903 alias : ALIAS tables { 904 struct table *t = $2; 905 906 if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) { 907 yyerror("invalid use of table \"%s\" as ALIAS parameter", 908 t->t_name); 909 YYERROR; 910 } 911 912 $$ = t; 913 } 914 ; 915 916 virtual : VIRTUAL tables { 917 struct table *t = $2; 918 919 if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) { 920 yyerror("invalid use of table \"%s\" as VIRTUAL parameter", 921 t->t_name); 922 YYERROR; 923 } 924 $$ = t; 925 } 926 ; 927 928 usermapping : alias { 929 if (rule->r_mapping) { 930 yyerror("alias specified multiple times"); 931 YYERROR; 932 } 933 rule->r_desttype = DEST_DOM; 934 rule->r_mapping = $1; 935 } 936 | virtual { 937 if (rule->r_mapping) { 938 yyerror("virtual specified multiple times"); 939 YYERROR; 940 } 941 rule->r_desttype = DEST_VDOM; 942 rule->r_mapping = $1; 943 } 944 ; 945 946 userbase : USERBASE tables { 947 struct table *t = $2; 948 949 if (rule->r_userbase) { 950 yyerror("userbase specified multiple times"); 951 YYERROR; 952 } 953 if (! table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) { 954 yyerror("invalid use of table \"%s\" as USERBASE parameter", 955 t->t_name); 956 YYERROR; 957 } 958 rule->r_userbase = t; 959 } 960 ; 961 962 deliver_action : DELIVER TO MAILDIR { 963 rule->r_action = A_MAILDIR; 964 if (strlcpy(rule->r_value.buffer, "~/Maildir", 965 sizeof(rule->r_value.buffer)) >= 966 sizeof(rule->r_value.buffer)) 967 fatal("pathname too long"); 968 } 969 | DELIVER TO MAILDIR STRING { 970 rule->r_action = A_MAILDIR; 971 if (strlcpy(rule->r_value.buffer, $4, 972 sizeof(rule->r_value.buffer)) >= 973 sizeof(rule->r_value.buffer)) 974 fatal("pathname too long"); 975 free($4); 976 } 977 | DELIVER TO MBOX { 978 rule->r_action = A_MBOX; 979 if (strlcpy(rule->r_value.buffer, _PATH_MAILDIR "/%u", 980 sizeof(rule->r_value.buffer)) 981 >= sizeof(rule->r_value.buffer)) 982 fatal("pathname too long"); 983 } 984 | DELIVER TO LMTP STRING { 985 rule->r_action = A_LMTP; 986 if (strchr($4, ':') || $4[0] == '/') { 987 if (strlcpy(rule->r_value.buffer, $4, 988 sizeof(rule->r_value.buffer)) 989 >= sizeof(rule->r_value.buffer)) 990 fatal("lmtp destination too long"); 991 } else 992 fatal("invalid lmtp destination"); 993 free($4); 994 } 995 | DELIVER TO MDA STRING { 996 rule->r_action = A_MDA; 997 if (strlcpy(rule->r_value.buffer, $4, 998 sizeof(rule->r_value.buffer)) 999 >= sizeof(rule->r_value.buffer)) 1000 fatal("command too long"); 1001 free($4); 1002 } 1003 ; 1004 1005 relay_action : RELAY relay { 1006 rule->r_action = A_RELAY; 1007 } 1008 | RELAY VIA STRING { 1009 rule->r_action = A_RELAYVIA; 1010 if (! text_to_relayhost(&rule->r_value.relayhost, $3)) { 1011 yyerror("error: invalid url: %s", $3); 1012 free($3); 1013 YYERROR; 1014 } 1015 free($3); 1016 } relay_via { 1017 /* no worries, F_AUTH cant be set without SSL */ 1018 if (rule->r_value.relayhost.flags & F_AUTH) { 1019 if (rule->r_value.relayhost.authtable[0] == '\0') { 1020 yyerror("error: auth without auth table"); 1021 YYERROR; 1022 } 1023 } 1024 } 1025 ; 1026 1027 negation : '!' { $$ = 1; } 1028 | /* empty */ { $$ = 0; } 1029 ; 1030 1031 from : FROM negation SOURCE tables { 1032 struct table *t = $4; 1033 1034 if (rule->r_sources) { 1035 yyerror("from specified multiple times"); 1036 YYERROR; 1037 } 1038 if (! table_check_use(t, T_DYNAMIC|T_LIST, K_NETADDR)) { 1039 yyerror("invalid use of table \"%s\" as FROM parameter", 1040 t->t_name); 1041 YYERROR; 1042 } 1043 rule->r_notsources = $2; 1044 rule->r_sources = t; 1045 } 1046 | FROM negation ANY { 1047 if (rule->r_sources) { 1048 yyerror("from specified multiple times"); 1049 YYERROR; 1050 } 1051 rule->r_sources = table_find("<anyhost>", NULL); 1052 rule->r_notsources = $2; 1053 } 1054 | FROM negation LOCAL { 1055 if (rule->r_sources) { 1056 yyerror("from specified multiple times"); 1057 YYERROR; 1058 } 1059 rule->r_sources = table_find("<localhost>", NULL); 1060 rule->r_notsources = $2; 1061 } 1062 ; 1063 1064 for : FOR negation DOMAIN tables { 1065 struct table *t = $4; 1066 1067 if (rule->r_destination) { 1068 yyerror("for specified multiple times"); 1069 YYERROR; 1070 } 1071 if (! table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) { 1072 yyerror("invalid use of table \"%s\" as DOMAIN parameter", 1073 t->t_name); 1074 YYERROR; 1075 } 1076 rule->r_notdestination = $2; 1077 rule->r_destination = t; 1078 } 1079 | FOR negation ANY { 1080 if (rule->r_destination) { 1081 yyerror("for specified multiple times"); 1082 YYERROR; 1083 } 1084 rule->r_notdestination = $2; 1085 rule->r_destination = table_find("<anydestination>", NULL); 1086 } 1087 | FOR negation LOCAL { 1088 if (rule->r_destination) { 1089 yyerror("for specified multiple times"); 1090 YYERROR; 1091 } 1092 rule->r_notdestination = $2; 1093 rule->r_destination = table_find("<localnames>", NULL); 1094 } 1095 ; 1096 1097 sender : SENDER negation tables { 1098 struct table *t = $3; 1099 1100 if (rule->r_senders) { 1101 yyerror("sender specified multiple times"); 1102 YYERROR; 1103 } 1104 1105 if (! table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { 1106 yyerror("invalid use of table \"%s\" as SENDER parameter", 1107 t->t_name); 1108 YYERROR; 1109 } 1110 rule->r_notsenders = $2; 1111 rule->r_senders = t; 1112 } 1113 ; 1114 1115 recipient : RECIPIENT negation tables { 1116 struct table *t = $3; 1117 1118 if (rule->r_recipients) { 1119 yyerror("recipient specified multiple times"); 1120 YYERROR; 1121 } 1122 1123 if (! table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { 1124 yyerror("invalid use of table \"%s\" as RECIPIENT parameter", 1125 t->t_name); 1126 YYERROR; 1127 } 1128 rule->r_notrecipients = $2; 1129 rule->r_recipients = t; 1130 } 1131 ; 1132 1133 forwardonly : FORWARDONLY { 1134 if (rule->r_forwardonly) { 1135 yyerror("forward-only specified multiple times"); 1136 YYERROR; 1137 } 1138 rule->r_forwardonly = 1; 1139 } 1140 ; 1141 1142 expire : EXPIRE STRING { 1143 if (rule->r_qexpire != -1) { 1144 yyerror("expire specified multiple times"); 1145 YYERROR; 1146 } 1147 rule->r_qexpire = delaytonum($2); 1148 if (rule->r_qexpire == -1) { 1149 yyerror("invalid expire delay: %s", $2); 1150 free($2); 1151 YYERROR; 1152 } 1153 free($2); 1154 } 1155 ; 1156 1157 opt_decision : sender 1158 | recipient 1159 | from 1160 | for 1161 | tagged 1162 ; 1163 decision : opt_decision decision 1164 | 1165 ; 1166 1167 opt_lookup : userbase 1168 | usermapping 1169 ; 1170 lookup : opt_lookup lookup 1171 | 1172 ; 1173 1174 action : deliver_action 1175 | relay_action 1176 | 1177 ; 1178 1179 opt_accept : expire 1180 | forwardonly 1181 ; 1182 1183 accept_params : opt_accept accept_params 1184 | 1185 ; 1186 1187 rule : ACCEPT { 1188 rule = xcalloc(1, sizeof(*rule), "parse rule: ACCEPT"); 1189 rule->r_action = A_NONE; 1190 rule->r_decision = R_ACCEPT; 1191 rule->r_desttype = DEST_DOM; 1192 rule->r_qexpire = -1; 1193 } decision lookup action accept_params { 1194 if (! rule->r_sources) 1195 rule->r_sources = table_find("<localhost>", NULL); 1196 if (! rule->r_destination) 1197 rule->r_destination = table_find("<localnames>", NULL); 1198 if (! rule->r_userbase) 1199 rule->r_userbase = table_find("<getpwnam>", NULL); 1200 if (rule->r_qexpire == -1) 1201 rule->r_qexpire = conf->sc_qexpire; 1202 if (rule->r_action == A_RELAY || rule->r_action == A_RELAYVIA) { 1203 if (rule->r_userbase != table_find("<getpwnam>", NULL)) { 1204 yyerror("userbase may not be used with a relay rule"); 1205 YYERROR; 1206 } 1207 if (rule->r_mapping) { 1208 yyerror("aliases/virtual may not be used with a relay rule"); 1209 YYERROR; 1210 } 1211 } 1212 if (rule->r_forwardonly && rule->r_action != A_NONE) { 1213 yyerror("forward-only may not be used with a default action"); 1214 YYERROR; 1215 } 1216 TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry); 1217 rule = NULL; 1218 } 1219 | REJECT { 1220 rule = xcalloc(1, sizeof(*rule), "parse rule: REJECT"); 1221 rule->r_decision = R_REJECT; 1222 rule->r_desttype = DEST_DOM; 1223 } decision { 1224 if (! rule->r_sources) 1225 rule->r_sources = table_find("<localhost>", NULL); 1226 if (! rule->r_destination) 1227 rule->r_destination = table_find("<localnames>", NULL); 1228 TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry); 1229 rule = NULL; 1230 } 1231 ; 1232 %% 1233 1234 struct keywords { 1235 const char *k_name; 1236 int k_val; 1237 }; 1238 1239 int 1240 yyerror(const char *fmt, ...) 1241 { 1242 va_list ap; 1243 char *msg; 1244 1245 file->errors++; 1246 va_start(ap, fmt); 1247 if (vasprintf(&msg, fmt, ap) == -1) 1248 fatalx("yyerror vasprintf"); 1249 va_end(ap); 1250 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg); 1251 free(msg); 1252 return (0); 1253 } 1254 1255 int 1256 kw_cmp(const void *k, const void *e) 1257 { 1258 return (strcmp(k, ((const struct keywords *)e)->k_name)); 1259 } 1260 1261 int 1262 lookup(char *s) 1263 { 1264 /* this has to be sorted always */ 1265 static const struct keywords keywords[] = { 1266 { "accept", ACCEPT }, 1267 { "alias", ALIAS }, 1268 { "any", ANY }, 1269 { "as", AS }, 1270 { "auth", AUTH }, 1271 { "auth-optional", AUTH_OPTIONAL }, 1272 { "backup", BACKUP }, 1273 { "bounce-warn", BOUNCEWARN }, 1274 { "ca", CA }, 1275 { "certificate", CERTIFICATE }, 1276 { "compression", COMPRESSION }, 1277 { "deliver", DELIVER }, 1278 { "dhparams", DHPARAMS }, 1279 { "domain", DOMAIN }, 1280 { "encryption", ENCRYPTION }, 1281 { "expire", EXPIRE }, 1282 { "filter", FILTER }, 1283 { "for", FOR }, 1284 { "forward-only", FORWARDONLY }, 1285 { "from", FROM }, 1286 { "hostname", HOSTNAME }, 1287 { "hostnames", HOSTNAMES }, 1288 { "include", INCLUDE }, 1289 { "inet4", INET4 }, 1290 { "inet6", INET6 }, 1291 { "key", KEY }, 1292 { "limit", LIMIT }, 1293 { "listen", LISTEN }, 1294 { "lmtp", LMTP }, 1295 { "local", LOCAL }, 1296 { "maildir", MAILDIR }, 1297 { "mask-source", MASK_SOURCE }, 1298 { "max-message-size", MAXMESSAGESIZE }, 1299 { "max-mta-deferred", MAXMTADEFERRED }, 1300 { "mbox", MBOX }, 1301 { "mda", MDA }, 1302 { "mta", MTA }, 1303 { "no-dsn", NODSN }, 1304 { "on", ON }, 1305 { "pki", PKI }, 1306 { "port", PORT }, 1307 { "queue", QUEUE }, 1308 { "recipient", RECIPIENT }, 1309 { "reject", REJECT }, 1310 { "relay", RELAY }, 1311 { "scheduler", SCHEDULER }, 1312 { "secure", SECURE }, 1313 { "sender", SENDER }, 1314 { "smtps", SMTPS }, 1315 { "source", SOURCE }, 1316 { "table", TABLE }, 1317 { "tag", TAG }, 1318 { "tagged", TAGGED }, 1319 { "tls", TLS }, 1320 { "tls-require", TLS_REQUIRE }, 1321 { "to", TO }, 1322 { "userbase", USERBASE }, 1323 { "verify", VERIFY }, 1324 { "via", VIA }, 1325 { "virtual", VIRTUAL }, 1326 }; 1327 const struct keywords *p; 1328 1329 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 1330 sizeof(keywords[0]), kw_cmp); 1331 1332 if (p) 1333 return (p->k_val); 1334 else 1335 return (STRING); 1336 } 1337 1338 #define MAXPUSHBACK 128 1339 1340 u_char *parsebuf; 1341 int parseindex; 1342 u_char pushback_buffer[MAXPUSHBACK]; 1343 int pushback_index = 0; 1344 1345 int 1346 lgetc(int quotec) 1347 { 1348 int c, next; 1349 1350 if (parsebuf) { 1351 /* Read character from the parsebuffer instead of input. */ 1352 if (parseindex >= 0) { 1353 c = parsebuf[parseindex++]; 1354 if (c != '\0') 1355 return (c); 1356 parsebuf = NULL; 1357 } else 1358 parseindex++; 1359 } 1360 1361 if (pushback_index) 1362 return (pushback_buffer[--pushback_index]); 1363 1364 if (quotec) { 1365 if ((c = getc(file->stream)) == EOF) { 1366 yyerror("reached end of file while parsing " 1367 "quoted string"); 1368 if (file == topfile || popfile() == EOF) 1369 return (EOF); 1370 return (quotec); 1371 } 1372 return (c); 1373 } 1374 1375 while ((c = getc(file->stream)) == '\\') { 1376 next = getc(file->stream); 1377 if (next != '\n') { 1378 c = next; 1379 break; 1380 } 1381 yylval.lineno = file->lineno; 1382 file->lineno++; 1383 } 1384 1385 while (c == EOF) { 1386 if (file == topfile || popfile() == EOF) 1387 return (EOF); 1388 c = getc(file->stream); 1389 } 1390 return (c); 1391 } 1392 1393 int 1394 lungetc(int c) 1395 { 1396 if (c == EOF) 1397 return (EOF); 1398 if (parsebuf) { 1399 parseindex--; 1400 if (parseindex >= 0) 1401 return (c); 1402 } 1403 if (pushback_index < MAXPUSHBACK-1) 1404 return (pushback_buffer[pushback_index++] = c); 1405 else 1406 return (EOF); 1407 } 1408 1409 int 1410 findeol(void) 1411 { 1412 int c; 1413 1414 parsebuf = NULL; 1415 pushback_index = 0; 1416 1417 /* skip to either EOF or the first real EOL */ 1418 while (1) { 1419 c = lgetc(0); 1420 if (c == '\n') { 1421 file->lineno++; 1422 break; 1423 } 1424 if (c == EOF) 1425 break; 1426 } 1427 return (ERROR); 1428 } 1429 1430 int 1431 yylex(void) 1432 { 1433 u_char buf[8096]; 1434 u_char *p, *val; 1435 int quotec, next, c; 1436 int token; 1437 1438 top: 1439 p = buf; 1440 while ((c = lgetc(0)) == ' ' || c == '\t') 1441 ; /* nothing */ 1442 1443 yylval.lineno = file->lineno; 1444 if (c == '#') 1445 while ((c = lgetc(0)) != '\n' && c != EOF) 1446 ; /* nothing */ 1447 if (c == '$' && parsebuf == NULL) { 1448 while (1) { 1449 if ((c = lgetc(0)) == EOF) 1450 return (0); 1451 1452 if (p + 1 >= buf + sizeof(buf) - 1) { 1453 yyerror("string too long"); 1454 return (findeol()); 1455 } 1456 if (isalnum(c) || c == '_') { 1457 *p++ = c; 1458 continue; 1459 } 1460 *p = '\0'; 1461 lungetc(c); 1462 break; 1463 } 1464 val = symget(buf); 1465 if (val == NULL) { 1466 yyerror("macro '%s' not defined", buf); 1467 return (findeol()); 1468 } 1469 parsebuf = val; 1470 parseindex = 0; 1471 goto top; 1472 } 1473 1474 switch (c) { 1475 case '\'': 1476 case '"': 1477 quotec = c; 1478 while (1) { 1479 if ((c = lgetc(quotec)) == EOF) 1480 return (0); 1481 if (c == '\n') { 1482 file->lineno++; 1483 continue; 1484 } else if (c == '\\') { 1485 if ((next = lgetc(quotec)) == EOF) 1486 return (0); 1487 if (next == quotec || c == ' ' || c == '\t') 1488 c = next; 1489 else if (next == '\n') { 1490 file->lineno++; 1491 continue; 1492 } else 1493 lungetc(next); 1494 } else if (c == quotec) { 1495 *p = '\0'; 1496 break; 1497 } else if (c == '\0') { 1498 yyerror("syntax error"); 1499 return (findeol()); 1500 } 1501 if (p + 1 >= buf + sizeof(buf) - 1) { 1502 yyerror("string too long"); 1503 return (findeol()); 1504 } 1505 *p++ = c; 1506 } 1507 yylval.v.string = strdup(buf); 1508 if (yylval.v.string == NULL) 1509 err(1, "yylex: strdup"); 1510 return (STRING); 1511 } 1512 1513 #define allowed_to_end_number(x) \ 1514 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 1515 1516 if (c == '-' || isdigit(c)) { 1517 do { 1518 *p++ = c; 1519 if ((unsigned)(p-buf) >= sizeof(buf)) { 1520 yyerror("string too long"); 1521 return (findeol()); 1522 } 1523 } while ((c = lgetc(0)) != EOF && isdigit(c)); 1524 lungetc(c); 1525 if (p == buf + 1 && buf[0] == '-') 1526 goto nodigits; 1527 if (c == EOF || allowed_to_end_number(c)) { 1528 const char *errstr = NULL; 1529 1530 *p = '\0'; 1531 yylval.v.number = strtonum(buf, LLONG_MIN, 1532 LLONG_MAX, &errstr); 1533 if (errstr) { 1534 yyerror("\"%s\" invalid number: %s", 1535 buf, errstr); 1536 return (findeol()); 1537 } 1538 return (NUMBER); 1539 } else { 1540 nodigits: 1541 while (p > buf + 1) 1542 lungetc(*--p); 1543 c = *--p; 1544 if (c == '-') 1545 return (c); 1546 } 1547 } 1548 1549 if (c == '=') { 1550 if ((c = lgetc(0)) != EOF && c == '>') 1551 return (ARROW); 1552 lungetc(c); 1553 c = '='; 1554 } 1555 1556 #define allowed_in_string(x) \ 1557 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 1558 x != '{' && x != '}' && x != '<' && x != '>' && \ 1559 x != '!' && x != '=' && x != '#' && \ 1560 x != ',')) 1561 1562 if (isalnum(c) || c == ':' || c == '_') { 1563 do { 1564 *p++ = c; 1565 if ((unsigned)(p-buf) >= sizeof(buf)) { 1566 yyerror("string too long"); 1567 return (findeol()); 1568 } 1569 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 1570 lungetc(c); 1571 *p = '\0'; 1572 if ((token = lookup(buf)) == STRING) 1573 if ((yylval.v.string = strdup(buf)) == NULL) 1574 err(1, "yylex: strdup"); 1575 return (token); 1576 } 1577 if (c == '\n') { 1578 yylval.lineno = file->lineno; 1579 file->lineno++; 1580 } 1581 if (c == EOF) 1582 return (0); 1583 return (c); 1584 } 1585 1586 int 1587 check_file_secrecy(int fd, const char *fname) 1588 { 1589 struct stat st; 1590 1591 if (fstat(fd, &st)) { 1592 log_warn("warn: cannot stat %s", fname); 1593 return (-1); 1594 } 1595 if (st.st_uid != 0 && st.st_uid != getuid()) { 1596 log_warnx("warn: %s: owner not root or current user", fname); 1597 return (-1); 1598 } 1599 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { 1600 log_warnx("warn: %s: group/world readable/writeable", fname); 1601 return (-1); 1602 } 1603 return (0); 1604 } 1605 1606 struct file * 1607 pushfile(const char *name, int secret) 1608 { 1609 struct file *nfile; 1610 1611 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 1612 log_warn("warn: malloc"); 1613 return (NULL); 1614 } 1615 if ((nfile->name = strdup(name)) == NULL) { 1616 log_warn("warn: malloc"); 1617 free(nfile); 1618 return (NULL); 1619 } 1620 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 1621 log_warn("warn: %s", nfile->name); 1622 free(nfile->name); 1623 free(nfile); 1624 return (NULL); 1625 } else if (secret && 1626 check_file_secrecy(fileno(nfile->stream), nfile->name)) { 1627 fclose(nfile->stream); 1628 free(nfile->name); 1629 free(nfile); 1630 return (NULL); 1631 } 1632 nfile->lineno = 1; 1633 TAILQ_INSERT_TAIL(&files, nfile, entry); 1634 return (nfile); 1635 } 1636 1637 int 1638 popfile(void) 1639 { 1640 struct file *prev; 1641 1642 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 1643 prev->errors += file->errors; 1644 1645 TAILQ_REMOVE(&files, file, entry); 1646 fclose(file->stream); 1647 free(file->name); 1648 free(file); 1649 file = prev; 1650 return (file ? 0 : EOF); 1651 } 1652 1653 int 1654 parse_config(struct smtpd *x_conf, const char *filename, int opts) 1655 { 1656 struct sym *sym, *next; 1657 struct table *t; 1658 char hostname[SMTPD_MAXHOSTNAMELEN]; 1659 char hostname_copy[SMTPD_MAXHOSTNAMELEN]; 1660 1661 if (! getmailname(hostname, sizeof hostname)) 1662 return (-1); 1663 1664 conf = x_conf; 1665 memset(conf, 0, sizeof(*conf)); 1666 1667 (void)strlcpy(conf->sc_hostname, hostname, sizeof(conf->sc_hostname)); 1668 1669 conf->sc_maxsize = DEFAULT_MAX_BODY_SIZE; 1670 1671 conf->sc_tables_dict = calloc(1, sizeof(*conf->sc_tables_dict)); 1672 conf->sc_rules = calloc(1, sizeof(*conf->sc_rules)); 1673 conf->sc_listeners = calloc(1, sizeof(*conf->sc_listeners)); 1674 conf->sc_pki_dict = calloc(1, sizeof(*conf->sc_pki_dict)); 1675 conf->sc_ssl_dict = calloc(1, sizeof(*conf->sc_ssl_dict)); 1676 conf->sc_limits_dict = calloc(1, sizeof(*conf->sc_limits_dict)); 1677 1678 /* Report mails delayed for more than 4 hours */ 1679 conf->sc_bounce_warn[0] = 3600 * 4; 1680 1681 if (conf->sc_tables_dict == NULL || 1682 conf->sc_rules == NULL || 1683 conf->sc_listeners == NULL || 1684 conf->sc_pki_dict == NULL || 1685 conf->sc_limits_dict == NULL) { 1686 log_warn("warn: cannot allocate memory"); 1687 free(conf->sc_tables_dict); 1688 free(conf->sc_rules); 1689 free(conf->sc_listeners); 1690 free(conf->sc_pki_dict); 1691 free(conf->sc_ssl_dict); 1692 free(conf->sc_limits_dict); 1693 return (-1); 1694 } 1695 1696 errors = 0; 1697 1698 table = NULL; 1699 rule = NULL; 1700 1701 dict_init(&conf->sc_filters); 1702 1703 dict_init(conf->sc_pki_dict); 1704 dict_init(conf->sc_ssl_dict); 1705 dict_init(conf->sc_tables_dict); 1706 1707 dict_init(conf->sc_limits_dict); 1708 limits = xcalloc(1, sizeof(*limits), "mta_limits"); 1709 limit_mta_set_defaults(limits); 1710 dict_xset(conf->sc_limits_dict, "default", limits); 1711 1712 TAILQ_INIT(conf->sc_listeners); 1713 TAILQ_INIT(conf->sc_rules); 1714 1715 conf->sc_qexpire = SMTPD_QUEUE_EXPIRY; 1716 conf->sc_opts = opts; 1717 1718 conf->sc_mta_max_deferred = 100; 1719 conf->sc_scheduler_max_inflight = 5000; 1720 conf->sc_scheduler_max_schedule = 10; 1721 conf->sc_scheduler_max_evp_batch_size = 256; 1722 conf->sc_scheduler_max_msg_batch_size = 1024; 1723 1724 conf->sc_mda_max_session = 50; 1725 conf->sc_mda_max_user_session = 7; 1726 conf->sc_mda_task_hiwat = 50; 1727 conf->sc_mda_task_lowat = 30; 1728 conf->sc_mda_task_release = 10; 1729 1730 if ((file = pushfile(filename, 0)) == NULL) { 1731 purge_config(PURGE_EVERYTHING); 1732 return (-1); 1733 } 1734 topfile = file; 1735 1736 /* 1737 * declare special "localhost", "anyhost" and "localnames" tables 1738 */ 1739 set_local(hostname); 1740 1741 t = table_create("static", "<anydestination>", NULL, NULL); 1742 t->t_type = T_LIST; 1743 table_add(t, "*", NULL); 1744 1745 /* can't truncate here */ 1746 (void)strlcpy(hostname_copy, hostname, sizeof hostname_copy); 1747 1748 hostname_copy[strcspn(hostname_copy, ".")] = '\0'; 1749 if (strcmp(hostname, hostname_copy) != 0) 1750 table_add(t, hostname_copy, NULL); 1751 1752 table_create("getpwnam", "<getpwnam>", NULL, NULL); 1753 1754 /* 1755 * parse configuration 1756 */ 1757 setservent(1); 1758 yyparse(); 1759 errors = file->errors; 1760 popfile(); 1761 endservent(); 1762 1763 /* Free macros and check which have not been used. */ 1764 for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { 1765 next = TAILQ_NEXT(sym, entry); 1766 if ((conf->sc_opts & SMTPD_OPT_VERBOSE) && !sym->used) 1767 fprintf(stderr, "warning: macro '%s' not " 1768 "used\n", sym->nam); 1769 if (!sym->persist) { 1770 free(sym->nam); 1771 free(sym->val); 1772 TAILQ_REMOVE(&symhead, sym, entry); 1773 free(sym); 1774 } 1775 } 1776 1777 if (TAILQ_EMPTY(conf->sc_rules)) { 1778 log_warnx("warn: no rules, nothing to do"); 1779 errors++; 1780 } 1781 1782 if (errors) { 1783 purge_config(PURGE_EVERYTHING); 1784 return (-1); 1785 } 1786 1787 return (0); 1788 } 1789 1790 int 1791 symset(const char *nam, const char *val, int persist) 1792 { 1793 struct sym *sym; 1794 1795 for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); 1796 sym = TAILQ_NEXT(sym, entry)) 1797 ; /* nothing */ 1798 1799 if (sym != NULL) { 1800 if (sym->persist == 1) 1801 return (0); 1802 else { 1803 free(sym->nam); 1804 free(sym->val); 1805 TAILQ_REMOVE(&symhead, sym, entry); 1806 free(sym); 1807 } 1808 } 1809 if ((sym = calloc(1, sizeof(*sym))) == NULL) 1810 return (-1); 1811 1812 sym->nam = strdup(nam); 1813 if (sym->nam == NULL) { 1814 free(sym); 1815 return (-1); 1816 } 1817 sym->val = strdup(val); 1818 if (sym->val == NULL) { 1819 free(sym->nam); 1820 free(sym); 1821 return (-1); 1822 } 1823 sym->used = 0; 1824 sym->persist = persist; 1825 TAILQ_INSERT_TAIL(&symhead, sym, entry); 1826 return (0); 1827 } 1828 1829 int 1830 cmdline_symset(char *s) 1831 { 1832 char *sym, *val; 1833 int ret; 1834 size_t len; 1835 1836 if ((val = strrchr(s, '=')) == NULL) 1837 return (-1); 1838 1839 len = strlen(s) - strlen(val) + 1; 1840 if ((sym = malloc(len)) == NULL) 1841 errx(1, "cmdline_symset: malloc"); 1842 1843 (void)strlcpy(sym, s, len); 1844 1845 ret = symset(sym, val + 1, 1); 1846 free(sym); 1847 1848 return (ret); 1849 } 1850 1851 char * 1852 symget(const char *nam) 1853 { 1854 struct sym *sym; 1855 1856 TAILQ_FOREACH(sym, &symhead, entry) 1857 if (strcmp(nam, sym->nam) == 0) { 1858 sym->used = 1; 1859 return (sym->val); 1860 } 1861 return (NULL); 1862 } 1863 1864 static void 1865 create_listener(struct listenerlist *ll, struct listen_opts *lo) 1866 { 1867 uint16_t flags; 1868 1869 if (lo->port != 0 && lo->ssl == F_SSL) 1870 errx(1, "invalid listen option: tls/smtps on same port"); 1871 1872 if (lo->auth != 0 && !lo->ssl) 1873 errx(1, "invalid listen option: auth requires tls/smtps"); 1874 1875 if (lo->pki && !lo->ssl) 1876 errx(1, "invalid listen option: pki requires tls/smtps"); 1877 1878 flags = lo->flags; 1879 1880 if (lo->port) { 1881 lo->flags = lo->ssl|lo->auth|flags; 1882 lo->port = htons(lo->port); 1883 if (! interface(ll, lo)) 1884 if (host(ll, lo) <= 0) 1885 errx(1, "invalid virtual ip or interface: %s", lo->ifx); 1886 } 1887 else { 1888 if (lo->ssl & F_SMTPS) { 1889 lo->port = htons(465); 1890 lo->flags = F_SMTPS|lo->auth|flags; 1891 if (! interface(ll, lo)) 1892 if (host(ll, lo) <= 0) 1893 errx(1, "invalid virtual ip or interface: %s", lo->ifx); 1894 } 1895 1896 if (! lo->ssl || (lo->ssl & F_STARTTLS)) { 1897 lo->port = htons(25); 1898 lo->flags = lo->auth|flags; 1899 if (lo->ssl & F_STARTTLS) 1900 lo->flags |= F_STARTTLS; 1901 if (! interface(ll, lo)) 1902 if (host(ll, lo) <= 0) 1903 errx(1, "invalid virtual ip or interface: %s", lo->ifx); 1904 } 1905 } 1906 } 1907 1908 static void 1909 config_listener(struct listener *h, struct listen_opts *lo) 1910 { 1911 h->fd = -1; 1912 h->port = lo->port; 1913 h->flags = lo->flags; 1914 1915 if (lo->hostname == NULL) 1916 lo->hostname = conf->sc_hostname; 1917 1918 if (lo->filtername) { 1919 if (dict_get(&conf->sc_filters, lo->filtername) == NULL) { 1920 log_warnx("undefined filter: %s", lo->filtername); 1921 fatalx(NULL); 1922 } 1923 (void)strlcpy(h->filter, lo->filtername, sizeof(h->filter)); 1924 } 1925 1926 h->pki_name[0] = '\0'; 1927 1928 if (lo->authtable != NULL) 1929 (void)strlcpy(h->authtable, lo->authtable->t_name, sizeof(h->authtable)); 1930 if (lo->pki != NULL) { 1931 if (! lowercase(h->pki_name, lo->pki, sizeof(h->pki_name))) { 1932 log_warnx("pki name too long: %s", lo->pki); 1933 fatalx(NULL); 1934 } 1935 if (dict_get(conf->sc_pki_dict, h->pki_name) == NULL) { 1936 log_warnx("pki name not found: %s", lo->pki); 1937 fatalx(NULL); 1938 } 1939 } 1940 if (lo->tag != NULL) 1941 (void)strlcpy(h->tag, lo->tag, sizeof(h->tag)); 1942 1943 (void)strlcpy(h->hostname, lo->hostname, sizeof(h->hostname)); 1944 if (lo->hostnametable) 1945 (void)strlcpy(h->hostnametable, lo->hostnametable->t_name, sizeof(h->hostnametable)); 1946 1947 if (lo->ssl & F_TLS_VERIFY) 1948 h->flags |= F_TLS_VERIFY; 1949 } 1950 1951 struct listener * 1952 host_v4(const char *s, in_port_t port) 1953 { 1954 struct in_addr ina; 1955 struct sockaddr_in *sain; 1956 struct listener *h; 1957 1958 memset(&ina, 0, sizeof(ina)); 1959 if (inet_pton(AF_INET, s, &ina) != 1) 1960 return (NULL); 1961 1962 h = xcalloc(1, sizeof(*h), "host_v4"); 1963 sain = (struct sockaddr_in *)&h->ss; 1964 sain->sin_len = sizeof(struct sockaddr_in); 1965 sain->sin_family = AF_INET; 1966 sain->sin_addr.s_addr = ina.s_addr; 1967 sain->sin_port = port; 1968 1969 return (h); 1970 } 1971 1972 struct listener * 1973 host_v6(const char *s, in_port_t port) 1974 { 1975 struct in6_addr ina6; 1976 struct sockaddr_in6 *sin6; 1977 struct listener *h; 1978 1979 memset(&ina6, 0, sizeof(ina6)); 1980 if (inet_pton(AF_INET6, s, &ina6) != 1) 1981 return (NULL); 1982 1983 h = xcalloc(1, sizeof(*h), "host_v6"); 1984 sin6 = (struct sockaddr_in6 *)&h->ss; 1985 sin6->sin6_len = sizeof(struct sockaddr_in6); 1986 sin6->sin6_family = AF_INET6; 1987 sin6->sin6_port = port; 1988 memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6)); 1989 1990 return (h); 1991 } 1992 1993 int 1994 host_dns(struct listenerlist *al, struct listen_opts *lo) 1995 { 1996 struct addrinfo hints, *res0, *res; 1997 int error, cnt = 0; 1998 struct sockaddr_in *sain; 1999 struct sockaddr_in6 *sin6; 2000 struct listener *h; 2001 2002 memset(&hints, 0, sizeof(hints)); 2003 hints.ai_family = PF_UNSPEC; 2004 hints.ai_socktype = SOCK_STREAM; 2005 error = getaddrinfo(lo->ifx, NULL, &hints, &res0); 2006 if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME) 2007 return (0); 2008 if (error) { 2009 log_warnx("warn: host_dns: could not parse \"%s\": %s", lo->ifx, 2010 gai_strerror(error)); 2011 return (-1); 2012 } 2013 2014 for (res = res0; res; res = res->ai_next) { 2015 if (res->ai_family != AF_INET && 2016 res->ai_family != AF_INET6) 2017 continue; 2018 h = xcalloc(1, sizeof(*h), "host_dns"); 2019 2020 h->ss.ss_family = res->ai_family; 2021 if (res->ai_family == AF_INET) { 2022 sain = (struct sockaddr_in *)&h->ss; 2023 sain->sin_len = sizeof(struct sockaddr_in); 2024 sain->sin_addr.s_addr = ((struct sockaddr_in *) 2025 res->ai_addr)->sin_addr.s_addr; 2026 sain->sin_port = lo->port; 2027 } else { 2028 sin6 = (struct sockaddr_in6 *)&h->ss; 2029 sin6->sin6_len = sizeof(struct sockaddr_in6); 2030 memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *) 2031 res->ai_addr)->sin6_addr, sizeof(struct in6_addr)); 2032 sin6->sin6_port = lo->port; 2033 } 2034 2035 config_listener(h, lo); 2036 2037 TAILQ_INSERT_HEAD(al, h, entry); 2038 cnt++; 2039 } 2040 2041 freeaddrinfo(res0); 2042 return (cnt); 2043 } 2044 2045 int 2046 host(struct listenerlist *al, struct listen_opts *lo) 2047 { 2048 struct listener *h; 2049 2050 h = host_v4(lo->ifx, lo->port); 2051 2052 /* IPv6 address? */ 2053 if (h == NULL) 2054 h = host_v6(lo->ifx, lo->port); 2055 2056 if (h != NULL) { 2057 config_listener(h, lo); 2058 TAILQ_INSERT_HEAD(al, h, entry); 2059 return (1); 2060 } 2061 2062 return (host_dns(al, lo)); 2063 } 2064 2065 int 2066 interface(struct listenerlist *al, struct listen_opts *lo) 2067 { 2068 struct ifaddrs *ifap, *p; 2069 struct sockaddr_in *sain; 2070 struct sockaddr_in6 *sin6; 2071 struct listener *h; 2072 int ret = 0; 2073 2074 if (getifaddrs(&ifap) == -1) 2075 fatal("getifaddrs"); 2076 2077 for (p = ifap; p != NULL; p = p->ifa_next) { 2078 if (p->ifa_addr == NULL) 2079 continue; 2080 if (strcmp(p->ifa_name, lo->ifx) != 0 && 2081 ! is_if_in_group(p->ifa_name, lo->ifx)) 2082 continue; 2083 if (lo->family != AF_UNSPEC && lo->family != p->ifa_addr->sa_family) 2084 continue; 2085 2086 h = xcalloc(1, sizeof(*h), "interface"); 2087 2088 switch (p->ifa_addr->sa_family) { 2089 case AF_INET: 2090 sain = (struct sockaddr_in *)&h->ss; 2091 *sain = *(struct sockaddr_in *)p->ifa_addr; 2092 sain->sin_len = sizeof(struct sockaddr_in); 2093 sain->sin_port = lo->port; 2094 break; 2095 2096 case AF_INET6: 2097 sin6 = (struct sockaddr_in6 *)&h->ss; 2098 *sin6 = *(struct sockaddr_in6 *)p->ifa_addr; 2099 sin6->sin6_len = sizeof(struct sockaddr_in6); 2100 sin6->sin6_port = lo->port; 2101 break; 2102 2103 default: 2104 free(h); 2105 continue; 2106 } 2107 2108 config_listener(h, lo); 2109 ret = 1; 2110 TAILQ_INSERT_HEAD(al, h, entry); 2111 } 2112 2113 freeifaddrs(ifap); 2114 2115 return ret; 2116 } 2117 2118 void 2119 set_local(const char *hostname) 2120 { 2121 struct table *t; 2122 2123 t = table_create("static", "<localnames>", NULL, NULL); 2124 t->t_type = T_LIST; 2125 table_add(t, "localhost", NULL); 2126 table_add(t, hostname, NULL); 2127 2128 set_localaddrs(t); 2129 } 2130 2131 void 2132 set_localaddrs(struct table *localnames) 2133 { 2134 struct ifaddrs *ifap, *p; 2135 struct sockaddr_storage ss; 2136 struct sockaddr_in *sain; 2137 struct sockaddr_in6 *sin6; 2138 struct table *t; 2139 char buf[NI_MAXHOST + 5]; 2140 2141 t = table_create("static", "<anyhost>", NULL, NULL); 2142 table_add(t, "local", NULL); 2143 table_add(t, "0.0.0.0/0", NULL); 2144 table_add(t, "::/0", NULL); 2145 2146 if (getifaddrs(&ifap) == -1) 2147 fatal("getifaddrs"); 2148 2149 t = table_create("static", "<localhost>", NULL, NULL); 2150 table_add(t, "local", NULL); 2151 2152 for (p = ifap; p != NULL; p = p->ifa_next) { 2153 if (p->ifa_addr == NULL) 2154 continue; 2155 switch (p->ifa_addr->sa_family) { 2156 case AF_INET: 2157 sain = (struct sockaddr_in *)&ss; 2158 *sain = *(struct sockaddr_in *)p->ifa_addr; 2159 sain->sin_len = sizeof(struct sockaddr_in); 2160 table_add(t, ss_to_text(&ss), NULL); 2161 table_add(localnames, ss_to_text(&ss), NULL); 2162 (void)snprintf(buf, sizeof buf, "[%s]", ss_to_text(&ss)); 2163 table_add(localnames, buf, NULL); 2164 break; 2165 2166 case AF_INET6: 2167 sin6 = (struct sockaddr_in6 *)&ss; 2168 *sin6 = *(struct sockaddr_in6 *)p->ifa_addr; 2169 sin6->sin6_len = sizeof(struct sockaddr_in6); 2170 table_add(t, ss_to_text(&ss), NULL); 2171 table_add(localnames, ss_to_text(&ss), NULL); 2172 (void)snprintf(buf, sizeof buf, "[%s]", ss_to_text(&ss)); 2173 table_add(localnames, buf, NULL); 2174 (void)snprintf(buf, sizeof buf, "[ipv6:%s]", ss_to_text(&ss)); 2175 table_add(localnames, buf, NULL); 2176 break; 2177 } 2178 } 2179 2180 freeifaddrs(ifap); 2181 } 2182 2183 int 2184 delaytonum(char *str) 2185 { 2186 unsigned int factor; 2187 size_t len; 2188 const char *errstr = NULL; 2189 int delay; 2190 2191 /* we need at least 1 digit and 1 unit */ 2192 len = strlen(str); 2193 if (len < 2) 2194 goto bad; 2195 2196 switch(str[len - 1]) { 2197 2198 case 's': 2199 factor = 1; 2200 break; 2201 2202 case 'm': 2203 factor = 60; 2204 break; 2205 2206 case 'h': 2207 factor = 60 * 60; 2208 break; 2209 2210 case 'd': 2211 factor = 24 * 60 * 60; 2212 break; 2213 2214 default: 2215 goto bad; 2216 } 2217 2218 str[len - 1] = '\0'; 2219 delay = strtonum(str, 1, INT_MAX / factor, &errstr); 2220 if (errstr) 2221 goto bad; 2222 2223 return (delay * factor); 2224 2225 bad: 2226 return (-1); 2227 } 2228 2229 int 2230 is_if_in_group(const char *ifname, const char *groupname) 2231 { 2232 unsigned int len; 2233 struct ifgroupreq ifgr; 2234 struct ifg_req *ifg; 2235 int s; 2236 int ret = 0; 2237 2238 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 2239 err(1, "socket"); 2240 2241 memset(&ifgr, 0, sizeof(ifgr)); 2242 if (strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ) >= IFNAMSIZ) 2243 errx(1, "interface name too large"); 2244 2245 if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) { 2246 if (errno == EINVAL || errno == ENOTTY) 2247 goto end; 2248 err(1, "SIOCGIFGROUP"); 2249 } 2250 2251 len = ifgr.ifgr_len; 2252 ifgr.ifgr_groups = 2253 (struct ifg_req *)xcalloc(len/sizeof(struct ifg_req), 2254 sizeof(struct ifg_req), "is_if_in_group"); 2255 if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) 2256 err(1, "SIOCGIFGROUP"); 2257 2258 ifg = ifgr.ifgr_groups; 2259 for (; ifg && len >= sizeof(struct ifg_req); ifg++) { 2260 len -= sizeof(struct ifg_req); 2261 if (strcmp(ifg->ifgrq_group, groupname) == 0) { 2262 ret = 1; 2263 break; 2264 } 2265 } 2266 free(ifgr.ifgr_groups); 2267 2268 end: 2269 close(s); 2270 return ret; 2271 } 2272 2273 static struct filter_conf * 2274 create_filter_proc(char *name, char *prog) 2275 { 2276 struct filter_conf *f; 2277 char *path; 2278 2279 if (dict_get(&conf->sc_filters, name)) { 2280 yyerror("filter \"%s\" already defined", name); 2281 return (NULL); 2282 } 2283 2284 if (asprintf(&path, "%s/filter-%s", PATH_LIBEXEC, prog) == -1) { 2285 yyerror("filter \"%s\" asprintf failed", name); 2286 return (0); 2287 } 2288 2289 f = xcalloc(1, sizeof(*f), "create_filter"); 2290 f->path = path; 2291 f->name = name; 2292 f->argv[f->argc++] = name; 2293 2294 dict_xset(&conf->sc_filters, name, f); 2295 2296 return (f); 2297 } 2298 2299 static struct filter_conf * 2300 create_filter_chain(char *name) 2301 { 2302 struct filter_conf *f; 2303 2304 if (dict_get(&conf->sc_filters, name)) { 2305 yyerror("filter \"%s\" already defined", name); 2306 return (NULL); 2307 } 2308 2309 f = xcalloc(1, sizeof(*f), "create_filter_chain"); 2310 f->chain = 1; 2311 f->name = name; 2312 2313 dict_xset(&conf->sc_filters, name, f); 2314 2315 return (f); 2316 } 2317 2318 static int 2319 add_filter_arg(struct filter_conf *f, char *arg) 2320 { 2321 if (f->argc == MAX_FILTER_ARGS) { 2322 yyerror("filter \"%s\" is full", f->name); 2323 return (0); 2324 } 2325 2326 if (f->chain) { 2327 if (dict_get(&conf->sc_filters, arg) == NULL) { 2328 yyerror("undefined filter \"%s\"", arg); 2329 return (0); 2330 } 2331 if (dict_get(&conf->sc_filters, arg) == f) { 2332 yyerror("filter chain cannot contain itself"); 2333 return (0); 2334 } 2335 } 2336 2337 f->argv[f->argc++] = arg; 2338 2339 return (1); 2340 } 2341