diff options
author | Gilles Chehade <gilles@cvs.openbsd.org> | 2018-06-04 15:57:47 +0000 |
---|---|---|
committer | Gilles Chehade <gilles@cvs.openbsd.org> | 2018-06-04 15:57:47 +0000 |
commit | be093b72b9386d86877019e872d095e349ac27d4 (patch) | |
tree | 7d416c00695b66af5477cb80a910c21161a5f0a9 /usr.sbin/smtpd | |
parent | 2f00ae092b312b1ac6fd3a6c8f430368c84ae10d (diff) |
add support for mda wrappers allowing postmaster to define command wrappers
that will be executed (with recipient privileges) before calling the users'
mail delivery agent
ok eric@
Diffstat (limited to 'usr.sbin/smtpd')
-rw-r--r-- | usr.sbin/smtpd/mda_unpriv.c | 29 | ||||
-rw-r--r-- | usr.sbin/smtpd/mda_variables.c | 16 | ||||
-rw-r--r-- | usr.sbin/smtpd/parse.y | 146 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.conf.5 | 13 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.h | 6 |
5 files changed, 150 insertions, 60 deletions
diff --git a/usr.sbin/smtpd/mda_unpriv.c b/usr.sbin/smtpd/mda_unpriv.c index fa47184fe63..641034f7734 100644 --- a/usr.sbin/smtpd/mda_unpriv.c +++ b/usr.sbin/smtpd/mda_unpriv.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mda_unpriv.c,v 1.1 2018/06/03 14:04:06 gilles Exp $ */ +/* $OpenBSD: mda_unpriv.c,v 1.2 2018/06/04 15:57:46 gilles Exp $ */ /* * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org> @@ -42,7 +42,9 @@ mda_unpriv(struct dispatcher *dsp, struct deliver *deliver, int idx; char *mda_environ[10]; char mda_exec[LINE_MAX]; + char mda_wrapper[LINE_MAX]; const char *mda_command; + const char *mda_command_wrap; const char *extension; if (deliver->mda_exec[0]) @@ -55,9 +57,11 @@ mda_unpriv(struct dispatcher *dsp, struct deliver *deliver, err(1, "mda command line too long"); if (! mda_expand_format(mda_exec, sizeof mda_exec, deliver, - &deliver->userinfo)) + &deliver->userinfo, NULL)) err(1, "mda command line could not be expanded"); + mda_command = mda_exec; + /* setup environment similar to other MTA */ idx = 0; xasprintf(&mda_environ[idx++], "PATH=%s", _PATH_DEFPATH); @@ -75,8 +79,25 @@ mda_unpriv(struct dispatcher *dsp, struct deliver *deliver, mda_environ[idx++] = (char *)NULL; - execle("/bin/sh", "/bin/sh", "-c", mda_exec, (char *)NULL, - mda_environ); + if (dsp->u.local.mda_wrapper) { + mda_command_wrap = dict_get(env->sc_mda_wrappers, + dsp->u.local.mda_wrapper); + if (mda_command_wrap == NULL) + err(1, "could not find wrapper %s", + dsp->u.local.mda_wrapper); + + if (strlcpy(mda_wrapper, mda_command_wrap, sizeof (mda_wrapper)) + >= sizeof (mda_wrapper)) + err(1, "mda command line too long"); + + if (! mda_expand_format(mda_wrapper, sizeof mda_wrapper, deliver, + &deliver->userinfo, mda_command)) + err(1, "mda command line could not be expanded"); + mda_command = mda_wrapper; + } + execle("/bin/sh", "/bin/sh", "-c", mda_command, (char *)NULL, + mda_environ); + perror("execle"); _exit(1); } diff --git a/usr.sbin/smtpd/mda_variables.c b/usr.sbin/smtpd/mda_variables.c index 630e6096187..8a82b38ea35 100644 --- a/usr.sbin/smtpd/mda_variables.c +++ b/usr.sbin/smtpd/mda_variables.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mda_variables.c,v 1.2 2018/05/24 11:38:24 gilles Exp $ */ +/* $OpenBSD: mda_variables.c,v 1.3 2018/06/04 15:57:46 gilles Exp $ */ /* * Copyright (c) 2011-2017 Gilles Chehade <gilles@poolp.org> @@ -37,9 +37,9 @@ #define EXPAND_DEPTH 10 size_t mda_expand_format(char *, size_t, const struct deliver *, - const struct userinfo *); + const struct userinfo *, const char *); static size_t mda_expand_token(char *, size_t, const char *, - const struct deliver *, const struct userinfo *); + const struct deliver *, const struct userinfo *, const char *); static int mod_lowercase(char *, size_t); static int mod_uppercase(char *, size_t); static int mod_strip(char *, size_t); @@ -58,7 +58,7 @@ static struct modifiers { static size_t mda_expand_token(char *dest, size_t len, const char *token, - const struct deliver *dlv, const struct userinfo *ui) + const struct deliver *dlv, const struct userinfo *ui, const char *mda_command) { char rtoken[MAXTOKENLEN]; char tmp[EXPAND_BUFFER]; @@ -148,6 +148,10 @@ mda_expand_token(char *dest, size_t len, const char *token, string = dlv->dest.user; else if (!strcasecmp("dest.domain", rtoken)) string = dlv->dest.domain; + else if (!strcasecmp("mda", rtoken)) { + string = mda_command; + replace = 0; + } else return 0; @@ -228,7 +232,7 @@ mda_expand_token(char *dest, size_t len, const char *token, size_t mda_expand_format(char *buf, size_t len, const struct deliver *dlv, - const struct userinfo *ui) + const struct userinfo *ui, const char *mda_command) { char tmpbuf[EXPAND_BUFFER], *ptmp, *pbuf, *ebuf; char exptok[EXPAND_BUFFER]; @@ -289,7 +293,7 @@ mda_expand_format(char *buf, size_t len, const struct deliver *dlv, *strchr(token, '}') = '\0'; exptoklen = mda_expand_token(exptok, sizeof exptok, token, dlv, - ui); + ui, mda_command); if (exptoklen == 0) return 0; diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y index 957aaba1d22..4e98a6343da 100644 --- a/usr.sbin/smtpd/parse.y +++ b/usr.sbin/smtpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.210 2018/06/01 20:31:33 gilles Exp $ */ +/* $OpenBSD: parse.y,v 1.211 2018/06/04 15:57:46 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -63,6 +63,10 @@ static struct file { TAILQ_ENTRY(file) entry; FILE *stream; char *name; + size_t ungetpos; + size_t ungetsize; + u_char *ungetbuf; + int eof_reached; int lineno; int errors; } *file, *topfile; @@ -73,8 +77,9 @@ int yyparse(void); int yylex(void); int kw_cmp(const void *, const void *); int lookup(char *); +int igetc(void); int lgetc(int); -int lungetc(int); +void lungetc(int); int findeol(void); int yyerror(const char *, ...) __attribute__((__format__ (printf, 1, 2))) @@ -191,7 +196,7 @@ typedef struct { %token TABLE TAG TAGGED TLS TLS_REQUIRE TO TTL %token USER USERBASE %token VERIFY VIRTUAL -%token WARN_INTERVAL +%token WARN_INTERVAL WRAPPER %token <v.string> STRING %token <v.number> NUMBER @@ -342,6 +347,13 @@ ca_params_opt mda: MDA LIMIT limits_mda +| MDA WRAPPER STRING STRING { + if (dict_get(conf->sc_mda_wrappers, $3)) { + yyerror("mda wrapper already declared with that name: %s", $3); + YYERROR; + } + dict_set(conf->sc_mda_wrappers, $3, $4); +} ; @@ -559,6 +571,13 @@ USER STRING { dispatcher->u.local.table_userbase = strdup(t->t_name); } +| WRAPPER STRING { + if (! dict_get(conf->sc_mda_wrappers, $2)) { + yyerror("no mda wrapper with that name: %s", $2); + YYERROR; + } + dispatcher->u.local.mda_wrapper = $2; +} ; dispatcher_local_options: @@ -1651,6 +1670,7 @@ lookup(char *s) { "verify", VERIFY }, { "virtual", VIRTUAL }, { "warn-interval", WARN_INTERVAL }, + { "wrapper", WRAPPER }, }; const struct keywords *p; @@ -1663,34 +1683,39 @@ lookup(char *s) return (STRING); } -#define MAXPUSHBACK 128 +#define START_EXPAND 1 +#define DONE_EXPAND 2 -unsigned char *parsebuf; -int parseindex; -unsigned char pushback_buffer[MAXPUSHBACK]; -int pushback_index = 0; +static int expanding; int -lgetc(int quotec) +igetc(void) { - int c, next; + int c; - if (parsebuf) { - /* Read character from the parsebuffer instead of input. */ - if (parseindex >= 0) { - c = parsebuf[parseindex++]; - if (c != '\0') - return (c); - parsebuf = NULL; - } else - parseindex++; + while (1) { + if (file->ungetpos > 0) + c = file->ungetbuf[--file->ungetpos]; + else + c = getc(file->stream); + + if (c == START_EXPAND) + expanding = 1; + else if (c == DONE_EXPAND) + expanding = 0; + else + break; } + return (c); +} - if (pushback_index) - return (pushback_buffer[--pushback_index]); +int +lgetc(int quotec) +{ + int c, next; if (quotec) { - if ((c = getc(file->stream)) == EOF) { + if ((c = igetc()) == EOF) { yyerror("reached end of file while parsing " "quoted string"); if (file == topfile || popfile() == EOF) @@ -1700,8 +1725,8 @@ lgetc(int quotec) return (c); } - while ((c = getc(file->stream)) == '\\') { - next = getc(file->stream); + while ((c = igetc()) == '\\') { + next = igetc(); if (next != '\n') { c = next; break; @@ -1710,28 +1735,39 @@ lgetc(int quotec) file->lineno++; } - while (c == EOF) { - if (file == topfile || popfile() == EOF) - return (EOF); - c = getc(file->stream); + if (c == EOF) { + /* + * Fake EOL when hit EOF for the first time. This gets line + * count right if last line in included file is syntactically + * invalid and has no newline. + */ + if (file->eof_reached == 0) { + file->eof_reached = 1; + return ('\n'); + } + while (c == EOF) { + if (file == topfile || popfile() == EOF) + return (EOF); + c = igetc(); + } } return (c); } -int +void lungetc(int c) { if (c == EOF) - return (EOF); - if (parsebuf) { - parseindex--; - if (parseindex >= 0) - return (c); - } - if (pushback_index < MAXPUSHBACK-1) - return (pushback_buffer[pushback_index++] = c); - else - return (EOF); + return; + + if (file->ungetpos >= file->ungetsize) { + void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); + if (p == NULL) + err(1, "lungetc"); + file->ungetbuf = p; + file->ungetsize *= 2; + } + file->ungetbuf[file->ungetpos++] = c; } int @@ -1739,9 +1775,6 @@ findeol(void) { int c; - parsebuf = NULL; - pushback_index = 0; - /* skip to either EOF or the first real EOL */ while (1) { c = lgetc(0); @@ -1772,7 +1805,7 @@ top: if (c == '#') while ((c = lgetc(0)) != '\n' && c != EOF) ; /* nothing */ - if (c == '$' && parsebuf == NULL) { + if (c == '$' && !expanding) { while (1) { if ((c = lgetc(0)) == EOF) return (0); @@ -1794,8 +1827,13 @@ top: yyerror("macro '%s' not defined", buf); return (findeol()); } - parsebuf = val; - parseindex = 0; + p = val + strlen(val) - 1; + lungetc(DONE_EXPAND); + while (p >= val) { + lungetc(*p); + p--; + } + lungetc(START_EXPAND); goto top; } @@ -1957,7 +1995,16 @@ pushfile(const char *name, int secret) free(nfile); return (NULL); } - nfile->lineno = 1; + nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; + nfile->ungetsize = 16; + nfile->ungetbuf = malloc(nfile->ungetsize); + if (nfile->ungetbuf == NULL) { + log_warn("warn: malloc"); + fclose(nfile->stream); + free(nfile->name); + free(nfile); + return (NULL); + } TAILQ_INSERT_TAIL(&files, nfile, entry); return (nfile); } @@ -1973,6 +2020,7 @@ popfile(void) TAILQ_REMOVE(&files, file, entry); fclose(file->stream); free(file->name); + free(file->ungetbuf); free(file); file = prev; return (file ? 0 : EOF); @@ -2005,6 +2053,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) conf->sc_pki_dict = calloc(1, sizeof(*conf->sc_pki_dict)); conf->sc_ssl_dict = calloc(1, sizeof(*conf->sc_ssl_dict)); conf->sc_limits_dict = calloc(1, sizeof(*conf->sc_limits_dict)); + conf->sc_mda_wrappers = calloc(1, sizeof(*conf->sc_mda_wrappers)); /* Report mails delayed for more than 4 hours */ conf->sc_bounce_warn[0] = 3600 * 4; @@ -2016,7 +2065,8 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) conf->sc_ca_dict == NULL || conf->sc_pki_dict == NULL || conf->sc_ssl_dict == NULL || - conf->sc_limits_dict == NULL) { + conf->sc_limits_dict == NULL || + conf->sc_mda_wrappers == NULL) { log_warn("warn: cannot allocate memory"); free(conf->sc_tables_dict); free(conf->sc_rules); @@ -2026,6 +2076,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) free(conf->sc_pki_dict); free(conf->sc_ssl_dict); free(conf->sc_limits_dict); + free(conf->sc_mda_wrappers); return (-1); } @@ -2034,6 +2085,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) table = NULL; dict_init(conf->sc_dispatchers); + dict_init(conf->sc_mda_wrappers); dict_init(conf->sc_ca_dict); dict_init(conf->sc_pki_dict); dict_init(conf->sc_ssl_dict); diff --git a/usr.sbin/smtpd/smtpd.conf.5 b/usr.sbin/smtpd/smtpd.conf.5 index aee0ace7a66..78cfc6dc2d9 100644 --- a/usr.sbin/smtpd/smtpd.conf.5 +++ b/usr.sbin/smtpd/smtpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: smtpd.conf.5,v 1.189 2018/06/04 12:15:10 jmc Exp $ +.\" $OpenBSD: smtpd.conf.5,v 1.190 2018/06/04 15:57:46 gilles Exp $ .\" .\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org> .\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> @@ -473,6 +473,16 @@ The same are supported as for the .Ic match Cm action directive. +.It Ic mda Cm wrapper Ar name Ar command +Associate +.Ar command +to the mail delivery agent wrapper named +.Ar name . +When a local delivery specifies a wrapper, the +.Ar command +associated to the wrapper will be executed instead. +The command may contain format specifiers +.Pq see Sx FORMAT SPECIFIERS . .It Ic mta Cm max\-deferred Ar number When delivery to a given host is suspended due to temporary failures, cache at most @@ -626,6 +636,7 @@ The following formats are currently supported: .It %{dest.domain} Ta domain part after expansion .It %{user.username} Ta local user .It %{user.directory} Ta home directory of the local user +.It %{mda} Ta mda command, only available for mda wrappers .El .Pp Expansion formats also support partial expansion using the optional diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index 735c42f2393..4c60441ac71 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.548 2018/06/03 14:04:06 gilles Exp $ */ +/* $OpenBSD: smtpd.h,v 1.549 2018/06/04 15:57:46 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -531,6 +531,7 @@ struct smtpd { size_t sc_session_max_rcpt; size_t sc_session_max_mails; + struct dict *sc_mda_wrappers; size_t sc_mda_max_session; size_t sc_mda_max_user_session; size_t sc_mda_task_hiwat; @@ -1046,6 +1047,7 @@ struct dispatcher_local { uint8_t expand_only; uint8_t forward_only; + char *mda_wrapper; char *command; char *table_alias; @@ -1245,7 +1247,7 @@ void mda_unpriv(struct dispatcher *, struct deliver *, const char *, const char /* mda_variables.c */ size_t mda_expand_format(char *, size_t, const struct deliver *, - const struct userinfo *); + const struct userinfo *, const char *); /* makemap.c */ |