summaryrefslogtreecommitdiff
path: root/usr.sbin/smtpd
diff options
context:
space:
mode:
authorGilles Chehade <gilles@cvs.openbsd.org>2018-06-04 15:57:47 +0000
committerGilles Chehade <gilles@cvs.openbsd.org>2018-06-04 15:57:47 +0000
commitbe093b72b9386d86877019e872d095e349ac27d4 (patch)
tree7d416c00695b66af5477cb80a910c21161a5f0a9 /usr.sbin/smtpd
parent2f00ae092b312b1ac6fd3a6c8f430368c84ae10d (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.c29
-rw-r--r--usr.sbin/smtpd/mda_variables.c16
-rw-r--r--usr.sbin/smtpd/parse.y146
-rw-r--r--usr.sbin/smtpd/smtpd.conf.513
-rw-r--r--usr.sbin/smtpd/smtpd.h6
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 */