diff options
author | Gilles Chehade <gilles@cvs.openbsd.org> | 2018-05-24 11:38:25 +0000 |
---|---|---|
committer | Gilles Chehade <gilles@cvs.openbsd.org> | 2018-05-24 11:38:25 +0000 |
commit | ace804cafcf33d9452774666bb47840ac1899bd0 (patch) | |
tree | 1b3ee00426c097326c9b50cc3bc062f20c89b0f9 /usr.sbin/smtpd/parse.y | |
parent | b22354e5b6362967535adf6c2e0d2c0d978e6ee0 (diff) |
switch smtpd to new grammar
ok eric@
Diffstat (limited to 'usr.sbin/smtpd/parse.y')
-rw-r--r-- | usr.sbin/smtpd/parse.y | 1229 |
1 files changed, 642 insertions, 587 deletions
diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y index 2ebfe459fa9..2275e6a88df 100644 --- a/usr.sbin/smtpd/parse.y +++ b/usr.sbin/smtpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.200 2018/04/26 14:12:19 krw Exp $ */ +/* $OpenBSD: parse.y,v 1.201 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -93,15 +93,17 @@ char *symget(const char *); struct smtpd *conf = NULL; static int errors = 0; -static uint64_t ruleid = 0; struct filter_conf *filter = NULL; struct table *table = NULL; -struct rule *rule = NULL; struct mta_limits *limits; static struct pki *pki; static struct ca *sca; +struct dispatcher *dispatcher; +struct rule *rule; + + enum listen_options { LO_FAMILY = 0x000001, LO_PORT = 0x000002, @@ -168,28 +170,46 @@ typedef struct { %} -%token AS QUEUE COMPRESSION ENCRYPTION MAXMESSAGESIZE MAXMTADEFERRED LISTEN ON ANY PORT EXPIRE -%token TABLE SMTPS CERTIFICATE DOMAIN BOUNCEWARN LIMIT INET4 INET6 NODSN SESSION -%token RELAY BACKUP VIA DELIVER TO LMTP MAILDIR MBOX RCPTTO HOSTNAME HOSTNAMES -%token ACCEPT REJECT INCLUDE ERROR MDA FROM FOR SOURCE MTA PKI SCHEDULER -%token ARROW AUTH TLS LOCAL VIRTUAL TAG TAGGED ALIAS FILTER KEY CA DHE -%token AUTH_OPTIONAL TLS_REQUIRE USERBASE SENDER SENDERS MASK_SOURCE VERIFY FORWARDONLY RECIPIENT -%token CIPHERS RECEIVEDAUTH MASQUERADE SOCKET SUBADDRESSING_DELIM AUTHENTICATED +%token ACTION ALIAS ANY ARROW AUTH AUTH_OPTIONAL +%token BACKUP BOUNCE +%token CA CERT CIPHERS COMPRESSION +%token DHE DOMAIN +%token ENCRYPTION ERROR EXPAND_ONLY +%token FILTER FOR FORWARD_ONLY FROM +%token HELO HELO_SRC HOST HOSTNAME HOSTNAMES +%token INCLUDE INET4 INET6 +%token KEY +%token LIMIT LISTEN LOCAL +%token MAIL_FROM MAILDIR MASK_SRC MASQUERADE MATCH MAX_MESSAGE_SIZE MAX_DEFERRED MBOX MDA MTA MX +%token NODSN +%token ON +%token PKI PORT +%token QUEUE +%token RCPT_TO RECIPIENT RECEIVEDAUTH RELAY REJECT +%token SCHEDULER SENDER SENDERS SET SMTP SMTPS SOCKET SRC SUB_ADDR_DELIM +%token TABLE TAG TAGGED TLS TLS_REQUIRE TO TTL +%token USER USERBASE +%token VERIFY VIRTUAL +%token WARN_INTERVAL + %token <v.string> STRING %token <v.number> NUMBER %type <v.table> table %type <v.number> size negation -%type <v.table> tables tablenew tableref alias virtual userbase -%type <v.string> tagged +%type <v.table> tables tablenew tableref %% grammar : /* empty */ | grammar '\n' | grammar include '\n' | grammar varset '\n' - | grammar main '\n' + | grammar limit '\n' + | grammar listen '\n' + | grammar set '\n' + | grammar pkica '\n' | grammar table '\n' - | grammar rule '\n' + | grammar dispatcher '\n' + | grammar match '\n' | grammar error '\n' { file->errors++; } ; @@ -238,6 +258,532 @@ optnl : '\n' optnl nl : '\n' optnl ; + +dispatcher_local_option: +USER STRING { + if (dispatcher->u.local.requires_root) { + yyerror("user may not be specified for this dispatcher"); + YYERROR; + } + + if (dispatcher->u.local.forward_only) { + yyerror("user may not be specified for forward-only"); + YYERROR; + } + + if (dispatcher->u.local.expand_only) { + yyerror("user may not be specified for expand-only"); + YYERROR; + } + + if (dispatcher->u.local.user) { + yyerror("user already specified for this dispatcher"); + YYERROR; + } + + dispatcher->u.local.user = $2; +} +| ALIAS tables { + struct table *t = $2; + + if (dispatcher->u.local.table_alias) { + yyerror("alias mapping already specified for this dispatcher"); + YYERROR; + } + + if (dispatcher->u.local.table_virtual) { + yyerror("virtual mapping already specified for this dispatcher"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) { + yyerror("table \"%s\" may not be used for alias lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.local.table_alias = strdup(t->t_name); +} +| VIRTUAL tables { + struct table *t = $2; + + if (dispatcher->u.local.table_virtual) { + yyerror("virtual mapping already specified for this dispatcher"); + YYERROR; + } + + if (dispatcher->u.local.table_alias) { + yyerror("alias mapping already specified for this dispatcher"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) { + yyerror("table \"%s\" may not be used for virtual lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.local.table_virtual = strdup(t->t_name); +} +| USERBASE tables { + struct table *t = $2; + + if (dispatcher->u.local.table_userbase) { + yyerror("userbase mapping already specified for this dispatcher"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) { + yyerror("table \"%s\" may not be used for userbase lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.local.table_userbase = strdup(t->t_name); +} +; + +dispatcher_local_options: +dispatcher_local_option dispatcher_local_options +| /* empty */ +; + +dispatcher_local: +MBOX { + dispatcher->u.local.requires_root = 1; + dispatcher->u.local.user = xstrdup("root", "dispatcher_mda"); + asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.local -f %%{sender} %%{user.username}"); +} dispatcher_local_options +| MAILDIR { + asprintf(&dispatcher->u.local.command, + "/usr/libexec/mail.maildir -p %%{user.directory}/Maildir -r %%{dest.user}"); +} dispatcher_local_options +| MAILDIR STRING { + if (strncmp($2, "~/", 2) == 0) + asprintf(&dispatcher->u.local.command, + "/usr/libexec/mail.maildir -p %%{user.directory}/%s -r %%{dest.user}}", $2+2); + else + asprintf(&dispatcher->u.local.command, + "/usr/libexec/mail.maildir -p %s -r %%{dest.user}}", $2); +} dispatcher_local_options +| MDA STRING { + asprintf(&dispatcher->u.local.command, + "/usr/libexec/mail.mda \"%s\"", $2); +} dispatcher_local_options +| FORWARD_ONLY { + dispatcher->u.local.forward_only = 1; +} dispatcher_local_options +| EXPAND_ONLY { + dispatcher->u.local.expand_only = 1; +} dispatcher_local_options + +; + +dispatcher_remote_option: +HELO STRING { + if (dispatcher->u.remote.helo) { + yyerror("helo already specified for this dispatcher"); + YYERROR; + } + + dispatcher->u.remote.helo = $2; +} +| HELO_SRC tables { + struct table *t = $2; + + if (dispatcher->u.remote.helo_source) { + yyerror("helo-source mapping already specified for this dispatcher"); + YYERROR; + } + if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) { + yyerror("table \"%s\" may not be used for helo-source lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.remote.helo_source = strdup(t->t_name); +} +| PKI STRING { + if (dispatcher->u.remote.pki) { + yyerror("pki already specified for this dispatcher"); + YYERROR; + } + + dispatcher->u.remote.pki = $2; +} +| CA STRING { + if (dispatcher->u.remote.ca) { + yyerror("ca already specified for this dispatcher"); + YYERROR; + } + + dispatcher->u.remote.ca = $2; +} +| SRC tables { + struct table *t = $2; + + if (dispatcher->u.remote.source) { + yyerror("source mapping already specified for this dispatcher"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_SOURCE)) { + yyerror("table \"%s\" may not be used for source lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.remote.source = strdup(t->t_name); +} +| MAIL_FROM STRING { + if (dispatcher->u.remote.mail_from) { + yyerror("mail-from already specified for this dispatcher"); + YYERROR; + } + + dispatcher->u.remote.mail_from = $2; +} +| BACKUP MX STRING { + if (dispatcher->u.remote.backup) { + yyerror("backup already specified for this dispatcher"); + YYERROR; + } + if (dispatcher->u.remote.smarthost) { + yyerror("backup and host are mutually exclusive"); + YYERROR; + } + + dispatcher->u.remote.backup = 1; + dispatcher->u.remote.backupmx = $3; +} +| BACKUP { + if (dispatcher->u.remote.backup) { + yyerror("backup already specified for this dispatcher"); + YYERROR; + } + if (dispatcher->u.remote.smarthost) { + yyerror("backup and host are mutually exclusive"); + YYERROR; + } + + dispatcher->u.remote.backup = 1; +} +| HOST tables { + struct table *t = $2; + + if (dispatcher->u.remote.smarthost) { + yyerror("host mapping already specified for this dispatcher"); + YYERROR; + } + if (dispatcher->u.remote.backup) { + yyerror("backup and host are mutually exclusive"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_RELAYHOST)) { + yyerror("table \"%s\" may not be used for host lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.remote.smarthost = strdup(t->t_name); +} +| AUTH tables { + struct table *t = $2; + + if (dispatcher->u.remote.smarthost == NULL) { + yyerror("auth may not be specified without host on a dispatcher"); + YYERROR; + } + + if (dispatcher->u.remote.auth) { + yyerror("auth mapping already specified for this dispatcher"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_HASH, K_CREDENTIALS)) { + yyerror("table \"%s\" may not be used for auth lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.remote.auth = strdup(t->t_name); +} +; + +dispatcher_remote_options: +dispatcher_remote_option dispatcher_remote_options +| /* empty */ +; + +dispatcher_remote : +RELAY dispatcher_remote_options +; + +dispatcher_type: +dispatcher_local { + dispatcher->type = DISPATCHER_LOCAL; +} +| dispatcher_remote { + dispatcher->type = DISPATCHER_REMOTE; +} +; + +dispatcher_option: +TTL STRING { + if (dispatcher->ttl) { + yyerror("ttl already specified for this dispatcher"); + YYERROR; + } + + dispatcher->ttl = delaytonum($2); + if (dispatcher->ttl == -1) { + yyerror("ttl delay \"%s\" is invalid", $2); + free($2); + YYERROR; + } + free($2); +} +; + +dispatcher_options: +dispatcher_option dispatcher_options +| /* empty */ +; + +dispatcher: +ACTION STRING { + if (dict_get(conf->sc_dispatchers, $2)) { + yyerror("dispatcher already declared with that name: %s", $2); + YYERROR; + } + dispatcher = xcalloc(1, sizeof *dispatcher, "dispatcher"); +} dispatcher_type dispatcher_options { + if (dispatcher->type == DISPATCHER_LOCAL) + if (dispatcher->u.local.table_userbase == NULL) + dispatcher->u.local.table_userbase = "<getpwnam>"; + dict_set(conf->sc_dispatchers, $2, dispatcher); + dispatcher = NULL; +} +; + +match_option: +negation TAG tables { + struct table *t = $3; + + if (rule->flag_tag) { + yyerror("tag already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING)) { + yyerror("table \"%s\" may not be used for tag lookups", + t->t_name); + YYERROR; + } + + rule->flag_tag = $1 ? -1 : 1; + rule->table_tag = strdup(t->t_name); +} +| negation HELO tables { + struct table *t = $3; + + if (rule->flag_smtp_helo) { + yyerror("mail-helo already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) { + yyerror("table \"%s\" may not be used for helo lookups", + t->t_name); + YYERROR; + } + + rule->flag_smtp_helo = $1 ? -1 : 1; + rule->table_smtp_helo = strdup(t->t_name); +} +| negation TLS { + if (rule->flag_smtp_starttls) { + yyerror("tls already specified for this rule"); + YYERROR; + } + rule->flag_smtp_starttls = $1 ? -1 : 1; +} +| negation AUTH { + if (rule->flag_smtp_auth) { + yyerror("auth already specified for this rule"); + YYERROR; + } + rule->flag_smtp_auth = $1 ? -1 : 1; +} +| negation AUTH tables { + struct table *t = $3; + + if (rule->flag_smtp_auth) { + yyerror("auth already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_CREDENTIALS)) { + yyerror("table \"%s\" may not be used for auth lookups", + t->t_name); + YYERROR; + } + + rule->flag_smtp_auth = $1 ? -1 : 1; + rule->table_smtp_auth = strdup(t->t_name); +} +| negation MAIL_FROM tables { + struct table *t = $3; + + if (rule->flag_smtp_mail_from) { + yyerror("mail-from already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { + yyerror("table \"%s\" may not be used for mail-from lookups", + t->t_name); + YYERROR; + } + + rule->flag_smtp_mail_from = $1 ? -1 : 1; + rule->table_smtp_mail_from = strdup(t->t_name); +} +| negation RCPT_TO tables { + struct table *t = $3; + + if (rule->flag_smtp_rcpt_to) { + yyerror("rcpt-to already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { + yyerror("table \"%s\" may not be used for rcpt-to lookups", + t->t_name); + YYERROR; + } + + rule->flag_smtp_rcpt_to = $1 ? -1 : 1; + rule->table_smtp_rcpt_to = strdup(t->t_name); +} + +| negation FROM SOCKET { + if (rule->flag_from) { + yyerror("from already specified for this rule"); + YYERROR; + } + rule->flag_from = $1 ? -1 : 1; + rule->flag_from_socket = 1; +} +| negation FROM LOCAL { + struct table *t = table_find("<localhost>", NULL); + + if (rule->flag_from) { + yyerror("from already specified for this rule"); + YYERROR; + } + rule->flag_from = $1 ? -1 : 1; + rule->table_from = strdup(t->t_name); +} +| negation FROM ANY { + struct table *t = table_find("<anyhost>", NULL); + + if (rule->flag_from) { + yyerror("from already specified for this rule"); + YYERROR; + } + rule->flag_from = $1 ? -1 : 1; + rule->table_from = strdup(t->t_name); +} +| negation FROM SRC tables { + struct table *t = $4; + + if (rule->flag_from) { + yyerror("from already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_NETADDR)) { + yyerror("table \"%s\" may not be used for from lookups", + t->t_name); + YYERROR; + } + + rule->flag_from = $1 ? -1 : 1; + rule->table_from = strdup(t->t_name); +} + +| negation FOR LOCAL { + struct table *t = table_find("<localnames>", NULL); + + if (rule->flag_for) { + yyerror("for already specified for this rule"); + YYERROR; + } + rule->flag_for = $1 ? -1 : 1; + rule->table_for = strdup(t->t_name); +} +| negation FOR ANY { + struct table *t = table_find("<anydestination>", NULL); + + if (rule->flag_for) { + yyerror("for already specified for this rule"); + YYERROR; + } + rule->flag_for = $1 ? -1 : 1; + rule->table_for = strdup(t->t_name); +} +| negation FOR DOMAIN tables { + struct table *t = $4; + + if (rule->flag_for) { + yyerror("for already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) { + yyerror("table \"%s\" may not be used for 'for' lookups", + t->t_name); + YYERROR; + } + + rule->flag_for = $1 ? -1 : 1; + rule->table_for = strdup(t->t_name); +} +; + +match_options: +match_option match_options +| /* empty */ +; + +match_dispatcher: +STRING { + if (dict_get(conf->sc_dispatchers, $1) == NULL) { + yyerror("no such dispatcher: %s", $1); + YYERROR; + } + rule->dispatcher = $1; +} +; + +action: +REJECT { + rule->reject = 1; +} +| ACTION match_dispatcher +; + +match: +MATCH { + rule = xcalloc(1, sizeof *rule, "rule"); +} match_options action { + TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry); + rule = NULL; +} +; + size : NUMBER { if ($1 < 0) { yyerror("invalid size: %" PRId64, $1); @@ -258,24 +804,6 @@ size : NUMBER { } ; -tagged : TAGGED negation STRING { - if (strlcpy(rule->r_tag, $3, sizeof rule->r_tag) - >= sizeof rule->r_tag) { - yyerror("tag name too long: %s", $3); - free($3); - YYERROR; - } - free($3); - rule->r_nottag = $2; - } - ; - -authenticated : negation AUTHENTICATED { - rule->r_wantauth = 1; - rule->r_negwantauth = $1; - } - ; - bouncedelay : STRING { time_t d; int i; @@ -294,10 +822,10 @@ bouncedelay : STRING { break; } } + ; bouncedelays : bouncedelays ',' bouncedelay | bouncedelay - | /* EMPTY */ ; opt_limit_mda : STRING NUMBER { @@ -325,11 +853,11 @@ opt_limit_mda : STRING NUMBER { } ; -limits_session : opt_limit_session limits_session +limits_smtp : opt_limit_smtp limits_smtp | /* empty */ ; -opt_limit_session : STRING NUMBER { +opt_limit_smtp : STRING NUMBER { if (!strcmp($1, "max-rcpt")) { conf->sc_session_max_rcpt = $2; } @@ -395,7 +923,7 @@ limits_scheduler: opt_limit_scheduler limits_scheduler | /* empty */ ; -opt_ca : CERTIFICATE STRING { +opt_ca : CERT STRING { sca->ca_cert_file = $2; } ; @@ -403,7 +931,7 @@ opt_ca : CERTIFICATE STRING { ca : opt_ca ; -opt_pki : CERTIFICATE STRING { +opt_pki : CERT STRING { pki->pki_cert_file = $2; } | KEY STRING { @@ -434,7 +962,7 @@ opt_sock_listen : FILTER STRING { YYERROR; } } - | MASK_SOURCE { + | MASK_SRC { if (config_lo_mask_source(&listen_opts)) { YYERROR; } @@ -622,7 +1150,7 @@ opt_if_listen : INET4 { } listen_opts.hostnametable = t; } - | MASK_SOURCE { + | MASK_SRC { if (config_lo_mask_source(&listen_opts)) { YYERROR; } @@ -704,168 +1232,19 @@ if_listen : opt_if_listen if_listen | /* empty */ ; -opt_relay_common: AS STRING { - struct mailaddr maddr, *maddrp; - - if (!text_to_mailaddr(&maddr, $2)) { - yyerror("invalid parameter to AS: %s", $2); - free($2); - YYERROR; - } - free($2); - - if (maddr.user[0] == '\0' && maddr.domain[0] == '\0') { - yyerror("invalid empty parameter to AS"); - YYERROR; - } - else if (maddr.domain[0] == '\0') { - if (strlcpy(maddr.domain, conf->sc_hostname, - sizeof (maddr.domain)) - >= sizeof (maddr.domain)) { - yyerror("hostname too long for AS parameter: %s", - conf->sc_hostname); - YYERROR; - } - } - rule->r_as = xmemdup(&maddr, sizeof (*maddrp), "parse relay_as: AS"); - } - | SOURCE tables { - struct table *t = $2; - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_SOURCE)) { - yyerror("invalid use of table \"%s\" as " - "SOURCE parameter", t->t_name); - YYERROR; - } - (void)strlcpy(rule->r_value.relayhost.sourcetable, t->t_name, - sizeof rule->r_value.relayhost.sourcetable); - } - | HOSTNAME STRING { - (void)strlcpy(rule->r_value.relayhost.heloname, $2, - sizeof rule->r_value.relayhost.heloname); - free($2); - } - | HOSTNAMES tables { - struct table *t = $2; - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) { - yyerror("invalid use of table \"%s\" as " - "HOSTNAMES parameter", t->t_name); - YYERROR; - } - (void)strlcpy(rule->r_value.relayhost.helotable, t->t_name, - sizeof rule->r_value.relayhost.helotable); - } - | PKI STRING { - if (!lowercase(rule->r_value.relayhost.pki_name, $2, - sizeof(rule->r_value.relayhost.pki_name))) { - yyerror("pki name too long: %s", $2); - free($2); - YYERROR; - } - if (dict_get(conf->sc_pki_dict, - rule->r_value.relayhost.pki_name) == NULL) { - log_warnx("pki name not found: %s", $2); - free($2); - YYERROR; - } - free($2); - } - | CA STRING { - if (!lowercase(rule->r_value.relayhost.ca_name, $2, - sizeof(rule->r_value.relayhost.ca_name))) { - yyerror("ca name too long: %s", $2); - free($2); - YYERROR; - } - if (dict_get(conf->sc_ca_dict, - rule->r_value.relayhost.ca_name) == NULL) { - log_warnx("ca name not found: %s", $2); - free($2); - YYERROR; - } - free($2); - } - ; - -opt_relay : BACKUP STRING { - rule->r_value.relayhost.flags |= F_BACKUP; - if (strlcpy(rule->r_value.relayhost.hostname, $2, - sizeof (rule->r_value.relayhost.hostname)) - >= sizeof (rule->r_value.relayhost.hostname)) { - log_warnx("hostname too long: %s", $2); - free($2); - YYERROR; - } - free($2); - } - | BACKUP { - rule->r_value.relayhost.flags |= F_BACKUP; - (void)strlcpy(rule->r_value.relayhost.hostname, - conf->sc_hostname, - sizeof (rule->r_value.relayhost.hostname)); - } - | TLS { - rule->r_value.relayhost.flags |= F_STARTTLS; - } - | TLS VERIFY { - rule->r_value.relayhost.flags |= F_STARTTLS|F_TLS_VERIFY; - } - ; - -relay : opt_relay_common relay - | opt_relay relay - | /* empty */ - ; - -opt_relay_via : AUTH tables { - struct table *t = $2; - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_CREDENTIALS)) { - yyerror("invalid use of table \"%s\" as AUTH parameter", - t->t_name); - YYERROR; - } - (void)strlcpy(rule->r_value.relayhost.authtable, t->t_name, - sizeof(rule->r_value.relayhost.authtable)); - } - | VERIFY { - if (!(rule->r_value.relayhost.flags & F_SSL)) { - yyerror("cannot \"verify\" with insecure protocol"); - YYERROR; - } - rule->r_value.relayhost.flags |= F_TLS_VERIFY; - } - ; - -relay_via : opt_relay_common relay_via - | opt_relay_via relay_via - | /* empty */ - ; - -main : BOUNCEWARN { +set : SET BOUNCE WARN_INTERVAL { memset(conf->sc_bounce_warn, 0, sizeof conf->sc_bounce_warn); } bouncedelays - | SUBADDRESSING_DELIM STRING { - if (strlen($2) != 1) { - yyerror("subaddressing-delimiter must be one character"); - free($2); - YYERROR; - } - - if (isspace((int)*$2) || !isprint((int)*$2) || *$2== '@') { - yyerror("subaddressing-delimiter uses invalid character"); - free($2); - YYERROR; - } - - conf->sc_subaddressing_delim = $2; + | SET MTA MAX_DEFERRED NUMBER { + conf->sc_mta_max_deferred = $4; } - | QUEUE COMPRESSION { + | SET QUEUE COMPRESSION { conf->sc_queue_flags |= QUEUE_COMPRESSION; } - | QUEUE ENCRYPTION { + | SET QUEUE ENCRYPTION { conf->sc_queue_flags |= QUEUE_ENCRYPTION; } - | QUEUE ENCRYPTION KEY STRING { + | SET QUEUE ENCRYPTION STRING { if (strcasecmp($4, "stdin") == 0 || strcasecmp($4, "-") == 0) { conf->sc_queue_key = "stdin"; free($4); @@ -874,22 +1253,37 @@ main : BOUNCEWARN { conf->sc_queue_key = $4; conf->sc_queue_flags |= QUEUE_ENCRYPTION; } - | EXPIRE STRING { - conf->sc_qexpire = delaytonum($2); - if (conf->sc_qexpire == -1) { - yyerror("invalid expire delay: %s", $2); - free($2); + | SET QUEUE TTL STRING { + conf->sc_ttl = delaytonum($4); + if (conf->sc_ttl == -1) { + yyerror("invalid ttl delay: %s", $4); + free($4); YYERROR; } - free($2); + free($4); + } + | SET SMTP CIPHERS STRING { + conf->sc_tls_ciphers = $4; } - | MAXMESSAGESIZE size { - conf->sc_maxsize = $2; + | SET SMTP MAX_MESSAGE_SIZE size { + conf->sc_maxsize = $4; } - | MAXMTADEFERRED NUMBER { - conf->sc_mta_max_deferred = $2; + | SET SMTP SUB_ADDR_DELIM STRING { + if (strlen($4) != 1) { + yyerror("subaddressing-delimiter must be one character"); + free($4); + YYERROR; + } + if (isspace((int)*$4) || !isprint((int)*$4) || *$4== '@') { + yyerror("sub-addr-delim uses invalid character"); + free($4); + YYERROR; + } + conf->sc_subaddressing_delim = $4; } - | LIMIT SESSION limits_session + ; + +limit : LIMIT SMTP limits_smtp | LIMIT MDA limits_mda | LIMIT MTA FOR DOMAIN STRING { struct mta_limits *d; @@ -907,12 +1301,9 @@ main : BOUNCEWARN { limits = dict_get(conf->sc_limits_dict, "default"); } limits_mta | LIMIT SCHEDULER limits_scheduler - | LISTEN { - memset(&listen_opts, 0, sizeof listen_opts); - listen_opts.family = AF_UNSPEC; - listen_opts.flags |= F_EXT_DSN; - } ON listener_type - | PKI STRING { + ; + +pkica : PKI STRING { char buf[HOST_NAME_MAX+1]; /* if not catchall, check that it is a valid domain */ @@ -952,9 +1343,13 @@ main : BOUNCEWARN { dict_set(conf->sc_ca_dict, sca->ca_name, sca); } } ca - | CIPHERS STRING { - conf->sc_tls_ciphers = $2; - } + ; + +listen : LISTEN { + memset(&listen_opts, 0, sizeof listen_opts); + listen_opts.family = AF_UNSPEC; + listen_opts.flags |= F_EXT_DSN; + } ON listener_type ; table : TABLE STRING STRING { @@ -1064,363 +1459,10 @@ tables : tablenew { $$ = $1; } | tableref { $$ = $1; } ; -alias : ALIAS tables { - struct table *t = $2; - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) { - yyerror("invalid use of table \"%s\" as ALIAS parameter", - t->t_name); - YYERROR; - } - - $$ = t; - } - ; - -virtual : VIRTUAL tables { - struct table *t = $2; - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) { - yyerror("invalid use of table \"%s\" as VIRTUAL parameter", - t->t_name); - YYERROR; - } - $$ = t; - } - ; - -usermapping : alias { - if (rule->r_mapping) { - yyerror("alias specified multiple times"); - YYERROR; - } - rule->r_desttype = DEST_DOM; - rule->r_mapping = $1; - } - | virtual { - if (rule->r_mapping) { - yyerror("virtual specified multiple times"); - YYERROR; - } - rule->r_desttype = DEST_VDOM; - rule->r_mapping = $1; - } - ; - -userbase : USERBASE tables { - struct table *t = $2; - - if (rule->r_userbase) { - yyerror("userbase specified multiple times"); - YYERROR; - } - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) { - yyerror("invalid use of table \"%s\" as USERBASE parameter", - t->t_name); - YYERROR; - } - rule->r_userbase = t; - } - ; - -deliver_as : AS STRING { - if (strlcpy(rule->r_delivery_user, $2, - sizeof(rule->r_delivery_user)) - >= sizeof(rule->r_delivery_user)) - fatal("username too long"); - free($2); - } - | /* empty */ {} - ; - -deliver_action : DELIVER TO MAILDIR { - rule->r_action = A_MAILDIR; - if (strlcpy(rule->r_value.buffer, "~/Maildir", - sizeof(rule->r_value.buffer)) >= - sizeof(rule->r_value.buffer)) - fatal("pathname too long"); - } - | DELIVER TO MAILDIR STRING { - rule->r_action = A_MAILDIR; - if (strlcpy(rule->r_value.buffer, $4, - sizeof(rule->r_value.buffer)) >= - sizeof(rule->r_value.buffer)) - fatal("pathname too long"); - free($4); - } - | DELIVER TO MBOX { - rule->r_action = A_MBOX; - if (strlcpy(rule->r_value.buffer, _PATH_MAILDIR "/%u", - sizeof(rule->r_value.buffer)) - >= sizeof(rule->r_value.buffer)) - fatal("pathname too long"); - } - | DELIVER TO LMTP STRING deliver_as { - rule->r_action = A_LMTP; - if (strchr($4, ':') || $4[0] == '/') { - if (strlcpy(rule->r_value.buffer, $4, - sizeof(rule->r_value.buffer)) - >= sizeof(rule->r_value.buffer)) - fatal("lmtp destination too long"); - } else - fatal("invalid lmtp destination"); - free($4); - } - | DELIVER TO LMTP STRING RCPTTO deliver_as { - rule->r_action = A_LMTP; - if (strchr($4, ':') || $4[0] == '/') { - if (strlcpy(rule->r_value.buffer, $4, - sizeof(rule->r_value.buffer)) - >= sizeof(rule->r_value.buffer)) - fatal("lmtp destination too long"); - if (strlcat(rule->r_value.buffer, " rcpt-to", - sizeof(rule->r_value.buffer)) - >= sizeof(rule->r_value.buffer)) - fatal("lmtp recipient too long"); - } else - fatal("invalid lmtp destination"); - free($4); - } - | DELIVER TO MDA STRING deliver_as { - rule->r_action = A_MDA; - if (strlcpy(rule->r_value.buffer, $4, - sizeof(rule->r_value.buffer)) - >= sizeof(rule->r_value.buffer)) - fatal("command too long"); - free($4); - } - ; - -relay_action : RELAY relay { - rule->r_action = A_RELAY; - } - | RELAY VIA STRING { - rule->r_action = A_RELAYVIA; - if (!text_to_relayhost(&rule->r_value.relayhost, $3)) { - yyerror("error: invalid url: %s", $3); - free($3); - YYERROR; - } - free($3); - } relay_via { - /* no worries, F_AUTH cant be set without SSL */ - if (rule->r_value.relayhost.flags & F_AUTH) { - if (rule->r_value.relayhost.authtable[0] == '\0') { - yyerror("error: auth without auth table"); - YYERROR; - } - } - } - ; - negation : '!' { $$ = 1; } | /* empty */ { $$ = 0; } ; -from : FROM negation SOURCE tables { - struct table *t = $4; - - if (rule->r_sources) { - yyerror("from specified multiple times"); - YYERROR; - } - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_NETADDR)) { - yyerror("invalid use of table \"%s\" as FROM parameter", - t->t_name); - YYERROR; - } - rule->r_notsources = $2; - rule->r_sources = t; - } - | FROM negation ANY { - if (rule->r_sources) { - yyerror("from specified multiple times"); - YYERROR; - } - rule->r_sources = table_find("<anyhost>", NULL); - rule->r_notsources = $2; - } - | FROM negation LOCAL { - if (rule->r_sources) { - yyerror("from specified multiple times"); - YYERROR; - } - rule->r_sources = table_find("<localhost>", NULL); - rule->r_notsources = $2; - } - ; - -for : FOR negation DOMAIN tables { - struct table *t = $4; - - if (rule->r_destination) { - yyerror("for specified multiple times"); - YYERROR; - } - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) { - yyerror("invalid use of table \"%s\" as DOMAIN parameter", - t->t_name); - YYERROR; - } - rule->r_notdestination = $2; - rule->r_destination = t; - } - | FOR negation ANY { - if (rule->r_destination) { - yyerror("for specified multiple times"); - YYERROR; - } - rule->r_notdestination = $2; - rule->r_destination = table_find("<anydestination>", NULL); - } - | FOR negation LOCAL { - if (rule->r_destination) { - yyerror("for specified multiple times"); - YYERROR; - } - rule->r_notdestination = $2; - rule->r_destination = table_find("<localnames>", NULL); - } - ; - -sender : SENDER negation tables { - struct table *t = $3; - - if (rule->r_senders) { - yyerror("sender specified multiple times"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { - yyerror("invalid use of table \"%s\" as SENDER parameter", - t->t_name); - YYERROR; - } - rule->r_notsenders = $2; - rule->r_senders = t; - } - ; - -recipient : RECIPIENT negation tables { - struct table *t = $3; - - if (rule->r_recipients) { - yyerror("recipient specified multiple times"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { - yyerror("invalid use of table \"%s\" as RECIPIENT parameter", - t->t_name); - YYERROR; - } - rule->r_notrecipients = $2; - rule->r_recipients = t; - } - ; - -forwardonly : FORWARDONLY { - if (rule->r_forwardonly) { - yyerror("forward-only specified multiple times"); - YYERROR; - } - rule->r_forwardonly = 1; - } - ; - -expire : EXPIRE STRING { - if (rule->r_qexpire != -1) { - yyerror("expire specified multiple times"); - YYERROR; - } - rule->r_qexpire = delaytonum($2); - if (rule->r_qexpire == -1) { - yyerror("invalid expire delay: %s", $2); - free($2); - YYERROR; - } - free($2); - } - ; - -opt_decision : sender - | recipient - | from - | for - | tagged - | authenticated - ; -decision : opt_decision decision - | - ; - -opt_lookup : userbase - | usermapping - ; -lookup : opt_lookup lookup - | - ; - -action : deliver_action - | relay_action - | - ; - -opt_accept : expire - | forwardonly - ; - -accept_params : opt_accept accept_params - | - ; - -rule : ACCEPT { - rule = xcalloc(1, sizeof(*rule), "parse rule: ACCEPT"); - rule->r_id = ++ruleid; - rule->r_action = A_NONE; - rule->r_decision = R_ACCEPT; - rule->r_desttype = DEST_DOM; - rule->r_qexpire = -1; - } decision lookup action accept_params { - if (!rule->r_sources) - rule->r_sources = table_find("<localhost>", NULL); - if (!rule->r_destination) - rule->r_destination = table_find("<localnames>", NULL); - if (!rule->r_userbase) - rule->r_userbase = table_find("<getpwnam>", NULL); - if (rule->r_qexpire == -1) - rule->r_qexpire = conf->sc_qexpire; - if (rule->r_action == A_RELAY || rule->r_action == A_RELAYVIA) { - if (rule->r_userbase != table_find("<getpwnam>", NULL)) { - yyerror("userbase may not be used with a relay rule"); - YYERROR; - } - if (rule->r_mapping) { - yyerror("aliases/virtual may not be used with a relay rule"); - YYERROR; - } - } - if (rule->r_forwardonly && rule->r_action != A_NONE) { - yyerror("forward-only may not be used with a default action"); - YYERROR; - } - TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry); - rule = NULL; - } - | REJECT { - rule = xcalloc(1, sizeof(*rule), "parse rule: REJECT"); - rule->r_id = ++ruleid; - rule->r_decision = R_REJECT; - rule->r_desttype = DEST_DOM; - } decision { - if (!rule->r_sources) - rule->r_sources = table_find("<localhost>", NULL); - if (!rule->r_destination) - rule->r_destination = table_find("<localnames>", NULL); - TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry); - rule = NULL; - } - ; %% struct keywords { @@ -1455,28 +1497,28 @@ lookup(char *s) { /* this has to be sorted always */ static const struct keywords keywords[] = { - { "accept", ACCEPT }, + { "action", ACTION }, { "alias", ALIAS }, { "any", ANY }, - { "as", AS }, { "auth", AUTH }, { "auth-optional", AUTH_OPTIONAL }, - { "authenticated", AUTHENTICATED }, { "backup", BACKUP }, - { "bounce-warn", BOUNCEWARN }, + { "bounce", BOUNCE }, { "ca", CA }, - { "certificate", CERTIFICATE }, + { "cert", CERT }, { "ciphers", CIPHERS }, { "compression", COMPRESSION }, - { "deliver", DELIVER }, { "dhe", DHE }, { "domain", DOMAIN }, { "encryption", ENCRYPTION }, - { "expire", EXPIRE }, + { "expand-only", EXPAND_ONLY }, { "filter", FILTER }, { "for", FOR }, - { "forward-only", FORWARDONLY }, + { "forward-only", FORWARD_ONLY }, { "from", FROM }, + { "helo", HELO }, + { "helo-src", HELO_SRC }, + { "host", HOST }, { "hostname", HOSTNAME }, { "hostnames", HOSTNAMES }, { "include", INCLUDE }, @@ -1485,44 +1527,48 @@ lookup(char *s) { "key", KEY }, { "limit", LIMIT }, { "listen", LISTEN }, - { "lmtp", LMTP }, { "local", LOCAL }, + { "mail-from", MAIL_FROM }, { "maildir", MAILDIR }, - { "mask-source", MASK_SOURCE }, + { "mask-src", MASK_SRC }, { "masquerade", MASQUERADE }, - { "max-message-size", MAXMESSAGESIZE }, - { "max-mta-deferred", MAXMTADEFERRED }, + { "match", MATCH }, + { "max-deferred", MAX_DEFERRED }, + { "max-message-size", MAX_MESSAGE_SIZE }, { "mbox", MBOX }, { "mda", MDA }, { "mta", MTA }, + { "mx", MX }, { "no-dsn", NODSN }, { "on", ON }, { "pki", PKI }, { "port", PORT }, { "queue", QUEUE }, - { "rcpt-to", RCPTTO }, + { "rcpt-to", RCPT_TO }, { "received-auth", RECEIVEDAUTH }, { "recipient", RECIPIENT }, { "reject", REJECT }, { "relay", RELAY }, { "scheduler", SCHEDULER }, - { "sender", SENDER }, { "senders", SENDERS }, - { "session", SESSION }, + { "set", SET }, + { "smtp", SMTP }, { "smtps", SMTPS }, { "socket", SOCKET }, - { "source", SOURCE }, - { "subaddressing-delimiter", SUBADDRESSING_DELIM }, + { "src", SRC }, + { "sub-addr-delim", SUB_ADDR_DELIM }, { "table", TABLE }, { "tag", TAG }, { "tagged", TAGGED }, { "tls", TLS }, { "tls-require", TLS_REQUIRE }, { "to", TO }, + { "ttl", TTL }, + { "user", USER }, { "userbase", USERBASE }, { "verify", VERIFY }, - { "via", VIA }, { "virtual", VIRTUAL }, + { "warn-interval", WARN_INTERVAL }, }; const struct keywords *p; @@ -1871,6 +1917,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) conf->sc_tables_dict = calloc(1, sizeof(*conf->sc_tables_dict)); conf->sc_rules = calloc(1, sizeof(*conf->sc_rules)); + conf->sc_dispatchers = calloc(1, sizeof(*conf->sc_dispatchers)); conf->sc_listeners = calloc(1, sizeof(*conf->sc_listeners)); conf->sc_ca_dict = calloc(1, sizeof(*conf->sc_ca_dict)); conf->sc_pki_dict = calloc(1, sizeof(*conf->sc_pki_dict)); @@ -1882,6 +1929,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) if (conf->sc_tables_dict == NULL || conf->sc_rules == NULL || + conf->sc_dispatchers == NULL || conf->sc_listeners == NULL || conf->sc_ca_dict == NULL || conf->sc_pki_dict == NULL || @@ -1890,6 +1938,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) log_warn("warn: cannot allocate memory"); free(conf->sc_tables_dict); free(conf->sc_rules); + free(conf->sc_dispatchers); free(conf->sc_listeners); free(conf->sc_ca_dict); free(conf->sc_pki_dict); @@ -1901,8 +1950,8 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) errors = 0; table = NULL; - rule = NULL; + dict_init(conf->sc_dispatchers); dict_init(conf->sc_ca_dict); dict_init(conf->sc_pki_dict); dict_init(conf->sc_ssl_dict); @@ -1916,7 +1965,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) TAILQ_INIT(conf->sc_listeners); TAILQ_INIT(conf->sc_rules); - conf->sc_qexpire = SMTPD_QUEUE_EXPIRY; + conf->sc_ttl = SMTPD_QUEUE_EXPIRY; conf->sc_opts = opts; conf->sc_mta_max_deferred = 100; @@ -1958,6 +2007,12 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) table_create("getpwnam", "<getpwnam>", NULL, NULL); + /* bounce dispatcher */ + dispatcher = xcalloc(1, sizeof *dispatcher, "dispatcher"); + dispatcher->type = DISPATCHER_BOUNCE; + conf->sc_dispatcher_bounce = dispatcher; + dispatcher = NULL; + /* * parse configuration */ |