Hi, Here is the updated diff, which removes table_proc and adds table_procexec as the default backend when no backend name matches.
With this diff, I have the following configuration for smtpd: # $OpenBSD: smtpd.conf,v 1.14 2019/11/26 20:14:38 gilles Exp $ # This is the smtpd server system-wide configuration file. # See smtpd.conf(5) for more information. table aliases aliases:root.t...@bsd.ac listen on socket # To accept external mail, replace with: listen on all # listen on lo0 action "local_mail" mbox alias <aliases> action "outbound" relay action "bsd.ac" relay host smtp://10.7.0.1 # Uncomment the following to accept external mail for domain "example.org" # # match from any for domain "example.org" action "local_mail" match from local for local action "local_mail" match from local for domain "bsd.ac" action "bsd.ac" match from local for any action "outbound" where my /usr/local/libexec/smtpd/table-aliases contains: #!/bin/ksh user="${1:-r...@bsd.ac}" while read line do reqid="$(echo $line | awk -F'|' '{ print $5; }')" reply="TABLE-RESULT|$reqid|FOUND|$user" echo $reply done < /dev/stdin exit 0 This should hopefully satisfy the requirements for transparency and sanity. I will work on the opensmtpd-extras and make a PR in the github separately, if that sounds fine. Cheers, Aisha diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y index 011e306ac61..1b0ee5ad38f 100644 --- a/usr.sbin/smtpd/parse.y +++ b/usr.sbin/smtpd/parse.y @@ -2557,13 +2557,6 @@ table : TABLE STRING STRING { config = p+1; } } - if (config != NULL && *config != '/') { - yyerror("invalid backend parameter for table: %s", - $2); - free($2); - free($3); - YYERROR; - } table = table_create(conf, backend, $2, config); if (!table_config(table)) { yyerror("invalid configuration file %s for table %s", diff --git a/usr.sbin/smtpd/smtpctl/Makefile b/usr.sbin/smtpd/smtpctl/Makefile index ef8148be8c9..46831d647dc 100644 --- a/usr.sbin/smtpd/smtpctl/Makefile +++ b/usr.sbin/smtpd/smtpctl/Makefile @@ -47,7 +47,7 @@ SRCS+= table.c SRCS+= table_static.c SRCS+= table_db.c SRCS+= table_getpwnam.c -SRCS+= table_proc.c +SRCS+= table_procexec.c SRCS+= unpack_dns.c SRCS+= spfwalk.c diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index be934112103..221f24fbdc4 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1656,6 +1656,7 @@ int table_regex_match(const char *, const char *); void table_open_all(struct smtpd *); void table_dump_all(struct smtpd *); void table_close_all(struct smtpd *); +const char *table_service_name(enum table_service ); /* to.c */ diff --git a/usr.sbin/smtpd/smtpd/Makefile b/usr.sbin/smtpd/smtpd/Makefile index b31d4e42224..64e73c3bb70 100644 --- a/usr.sbin/smtpd/smtpd/Makefile +++ b/usr.sbin/smtpd/smtpd/Makefile @@ -62,7 +62,7 @@ SRCS+= compress_gzip.c SRCS+= table_db.c SRCS+= table_getpwnam.c -SRCS+= table_proc.c +SRCS+= table_procexec.c SRCS+= table_static.c SRCS+= queue_fs.c diff --git a/usr.sbin/smtpd/table.c b/usr.sbin/smtpd/table.c index 1d82d88b81a..a09229ca174 100644 --- a/usr.sbin/smtpd/table.c +++ b/usr.sbin/smtpd/table.c @@ -45,9 +45,8 @@ struct table_backend *table_backend_lookup(const char *); extern struct table_backend table_backend_static; extern struct table_backend table_backend_db; extern struct table_backend table_backend_getpwnam; -extern struct table_backend table_backend_proc; +extern struct table_backend table_backend_procexec; -static const char * table_service_name(enum table_service); static int table_parse_lookup(enum table_service, const char *, const char *, union lookup *); static int parse_sockaddr(struct sockaddr *, int, const char *); @@ -58,7 +57,7 @@ static struct table_backend *backends[] = { &table_backend_static, &table_backend_db, &table_backend_getpwnam, - &table_backend_proc, + &table_backend_procexec, NULL }; @@ -77,7 +76,7 @@ table_backend_lookup(const char *backend) return NULL; } -static const char * +const char * table_service_name(enum table_service s) { switch (s) { @@ -208,10 +207,9 @@ table_create(struct smtpd *conf, const char *backend, const char *name, PATH_LIBEXEC"/table-%s\"", backend); } if (stat(path, &sb) == 0) { - tb = table_backend_lookup("proc"); - (void)strlcpy(path, backend, sizeof(path)); + tb = table_backend_lookup("proc-exec"); if (config) { - (void)strlcat(path, ":", sizeof(path)); + (void)strlcat(path, " ", sizeof(path)); if (strlcat(path, config, sizeof(path)) >= sizeof(path)) fatalx("table_create: config file path too long"); diff --git a/usr.sbin/smtpd/table_proc.c b/usr.sbin/smtpd/table_proc.c deleted file mode 100644 index dad041a47c3..00000000000 --- a/usr.sbin/smtpd/table_proc.c +++ /dev/null @@ -1,279 +0,0 @@ -/* $OpenBSD: table_proc.c,v 1.16 2019/10/03 04:51:15 gilles Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <e...@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <paths.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -struct table_proc_priv { - pid_t pid; - struct imsgbuf ibuf; -}; - -static struct imsg imsg; -static size_t rlen; -static char *rdata; - -extern char **environ; - -static void -table_proc_call(struct table_proc_priv *p) -{ - ssize_t n; - - if (imsg_flush(&p->ibuf) == -1) { - log_warn("warn: table-proc: imsg_flush"); - fatalx("table-proc: exiting"); - } - - while (1) { - if ((n = imsg_get(&p->ibuf, &imsg)) == -1) { - log_warn("warn: table-proc: imsg_get"); - break; - } - if (n) { - rlen = imsg.hdr.len - IMSG_HEADER_SIZE; - rdata = imsg.data; - - if (imsg.hdr.type != PROC_TABLE_OK) { - log_warnx("warn: table-proc: bad response"); - break; - } - return; - } - - if ((n = imsg_read(&p->ibuf)) == -1 && errno != EAGAIN) { - log_warn("warn: table-proc: imsg_read"); - break; - } - - if (n == 0) { - log_warnx("warn: table-proc: pipe closed"); - break; - } - } - - fatalx("table-proc: exiting"); -} - -static void -table_proc_read(void *dst, size_t len) -{ - if (len > rlen) { - log_warnx("warn: table-proc: bad msg len"); - fatalx("table-proc: exiting"); - } - - if (dst) - memmove(dst, rdata, len); - - rlen -= len; - rdata += len; -} - -static void -table_proc_end(void) -{ - if (rlen) { - log_warnx("warn: table-proc: bogus data"); - fatalx("table-proc: exiting"); - } - imsg_free(&imsg); -} - -/* - * API - */ - -static int -table_proc_open(struct table *table) -{ - struct table_proc_priv *priv; - struct table_open_params op; - int fd; - - priv = xcalloc(1, sizeof(*priv)); - - fd = fork_proc_backend("table", table->t_config, table->t_name); - if (fd == -1) - fatalx("table-proc: exiting"); - - imsg_init(&priv->ibuf, fd); - - memset(&op, 0, sizeof op); - op.version = PROC_TABLE_API_VERSION; - (void)strlcpy(op.name, table->t_name, sizeof op.name); - imsg_compose(&priv->ibuf, PROC_TABLE_OPEN, 0, 0, -1, &op, sizeof op); - - table_proc_call(priv); - table_proc_end(); - - table->t_handle = priv; - - return (1); -} - -static int -table_proc_update(struct table *table) -{ - struct table_proc_priv *priv = table->t_handle; - int r; - - imsg_compose(&priv->ibuf, PROC_TABLE_UPDATE, 0, 0, -1, NULL, 0); - - table_proc_call(priv); - table_proc_read(&r, sizeof(r)); - table_proc_end(); - - return (r); -} - -static void -table_proc_close(struct table *table) -{ - struct table_proc_priv *priv = table->t_handle; - - imsg_compose(&priv->ibuf, PROC_TABLE_CLOSE, 0, 0, -1, NULL, 0); - if (imsg_flush(&priv->ibuf) == -1) - fatal("imsg_flush"); - - table->t_handle = NULL; -} - -static int -imsg_add_params(struct ibuf *buf) -{ - size_t count = 0; - - if (imsg_add(buf, &count, sizeof(count)) == -1) - return (-1); - - return (0); -} - -static int -table_proc_lookup(struct table *table, enum table_service s, const char *k, char **dst) -{ - struct table_proc_priv *priv = table->t_handle; - struct ibuf *buf; - int r; - - buf = imsg_create(&priv->ibuf, - dst ? PROC_TABLE_LOOKUP : PROC_TABLE_CHECK, 0, 0, - sizeof(s) + strlen(k) + 1); - - if (buf == NULL) - return (-1); - if (imsg_add(buf, &s, sizeof(s)) == -1) - return (-1); - if (imsg_add_params(buf) == -1) - return (-1); - if (imsg_add(buf, k, strlen(k) + 1) == -1) - return (-1); - imsg_close(&priv->ibuf, buf); - - table_proc_call(priv); - table_proc_read(&r, sizeof(r)); - - if (r == 1 && dst) { - if (rlen == 0) { - log_warnx("warn: table-proc: empty response"); - fatalx("table-proc: exiting"); - } - if (rdata[rlen - 1] != '\0') { - log_warnx("warn: table-proc: not NUL-terminated"); - fatalx("table-proc: exiting"); - } - *dst = strdup(rdata); - if (*dst == NULL) - r = -1; - table_proc_read(NULL, rlen); - } - - table_proc_end(); - - return (r); -} - -static int -table_proc_fetch(struct table *table, enum table_service s, char **dst) -{ - struct table_proc_priv *priv = table->t_handle; - struct ibuf *buf; - int r; - - buf = imsg_create(&priv->ibuf, PROC_TABLE_FETCH, 0, 0, sizeof(s)); - if (buf == NULL) - return (-1); - if (imsg_add(buf, &s, sizeof(s)) == -1) - return (-1); - if (imsg_add_params(buf) == -1) - return (-1); - imsg_close(&priv->ibuf, buf); - - table_proc_call(priv); - table_proc_read(&r, sizeof(r)); - - if (r == 1) { - if (rlen == 0) { - log_warnx("warn: table-proc: empty response"); - fatalx("table-proc: exiting"); - } - if (rdata[rlen - 1] != '\0') { - log_warnx("warn: table-proc: not NUL-terminated"); - fatalx("table-proc: exiting"); - } - *dst = strdup(rdata); - if (*dst == NULL) - r = -1; - table_proc_read(NULL, rlen); - } - - table_proc_end(); - - return (r); -} - -struct table_backend table_backend_proc = { - "proc", - K_ANY, - NULL, - NULL, - NULL, - table_proc_open, - table_proc_update, - table_proc_close, - table_proc_lookup, - table_proc_fetch, -}; diff --git a/usr.sbin/smtpd/table_procexec.c b/usr.sbin/smtpd/table_procexec.c new file mode 100644 index 00000000000..66f1d50ca26 --- /dev/null +++ b/usr.sbin/smtpd/table_procexec.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2020 Gilles Chehade <gil...@poolp.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/tree.h> +#include <sys/socket.h> + +#include <ctype.h> +#include <errno.h> +#include <event.h> +#include <fcntl.h> +#include <imsg.h> +#include <paths.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <unistd.h> + +#include <err.h> +#include <inttypes.h> + +#include "smtpd.h" +#include "log.h" + +#define PROTOCOL_VERSION "1" + +static int table_procexec_open(struct table *); +static int table_procexec_update(struct table *); +static void table_procexec_close(struct table *); +static int table_procexec_lookup(struct table *, enum table_service, const char *, char **); +static int table_procexec_fetch(struct table *, enum table_service, char **); + +struct table_backend table_backend_procexec = { + "proc-exec", + K_ANY, + NULL, + NULL, + NULL, + table_procexec_open, + table_procexec_update, + table_procexec_close, + table_procexec_lookup, + table_procexec_fetch, +}; + +struct procexec_handle { + FILE *backend_w; + FILE *backend_r; + pid_t pid; +}; + +static int +table_procexec_open(struct table *t) { + struct procexec_handle *pe_handle; + pid_t pid; + int sp[2]; + int execr; + char exec[_POSIX_ARG_MAX]; + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1){ + fatalx("procexec - socket pair: %s", t->t_name); + } + + pe_handle = xcalloc(1, sizeof(*pe_handle)); + + if ((pid = fork()) == -1) { + fatalx("procexec - fork: %s", t->t_name); + } + + if (pid > 0) { + close(sp[0]); + FILE *backend_w, *backend_r; + if ((backend_w = fdopen(sp[1], "w")) == NULL) + fatalx("procexec - backend_w: %s", t->t_name); + + if ((backend_r = fdopen(sp[1], "r")) == NULL) + fatalx("procexec - backend_r: %s", t->t_name); + + pe_handle->pid = pid; + pe_handle->backend_w = backend_w; + pe_handle->backend_r = backend_r; + t->t_handle = pe_handle; + return 1; + } + else { + close(sp[1]); + dup2(sp[0], STDIN_FILENO); + dup2(sp[0], STDOUT_FILENO); + + execr = snprintf(exec, sizeof(exec), "exec %s" , t->t_config); + if (execr >= (int) sizeof(exec)) + fatalx("procexec - execr: %s", t->t_name); + execl("/bin/sh", "/bin/sh", "-c", exec, (char *)NULL); + fatalx("procexec: %s", t->t_name); + } +} + +static void +table_procexec_close(struct table *t){ + if(t->t_handle == NULL) + return; + kill(((struct procexec_handle *)t->t_handle)->pid, SIGTERM); + free(t->t_handle); + t->t_handle = NULL; +} + +static int +table_procexec_update(struct table *t) +{ + struct timeval tv; + uint64_t reqid; + char *line = NULL; + size_t linecap = 0; + ssize_t linelen; + uint64_t reqid_res; + char *qid = NULL; + char *ep = NULL; + + reqid = generate_uid(); + gettimeofday(&tv, NULL); + + FILE* backend_w = ((struct procexec_handle *)t->t_handle)->backend_w; + FILE* backend_r = ((struct procexec_handle *)t->t_handle)->backend_r; + + fprintf(backend_w, "TABLE|%s|%lld.%06ld|UPDATE|%016"PRIx64"\n", + PROTOCOL_VERSION, + tv.tv_sec, tv.tv_usec, reqid); + fflush(backend_w); + + linelen = getline(&line, &linecap, backend_r); + if (linelen == 0) + return 0; + line[strcspn(line, "\n")] = '\0'; + + if (strncmp(line, "TABLE-RESULT|", 13) != 0) + return -1; + line += 13; + + qid = line; + reqid_res = strtoull(qid, &ep, 16); + if (qid[0] == '\0' || *ep != '|') + return -1; + if (errno == ERANGE && reqid_res == ULLONG_MAX) + return -1; + if (reqid != reqid_res) + return -1; + + line = ep+1; + + return strcmp(line, "UPDATED") == 0; +} + +/* +static int +table_procexec_check(int service, struct dict *params, const char *key) +{ + struct timeval tv; + uint64_t reqid; + char *line = NULL; + size_t linecap = 0; + ssize_t linelen; + uint64_t reqid_res; + char *qid = NULL; + char *ep = NULL; + + reqid = generate_uid(); + gettimeofday(&tv, NULL); + + fprintf(backend_w, "TABLE|%s|%lld.%06ld|CHECK|%016"PRIx64"|%s|%s\n", + PROTOCOL_VERSION, + tv.tv_sec, tv.tv_usec, reqid, service_to_name(service), key); + fflush(backend_w); + + linelen = getline(&line, &linecap, backend_r); + if (linelen == 0) + return 0; + line[strcspn(line, "\n")] = '\0'; + + if (strncmp(line, "TABLE-RESULT|", 13) != 0) + return -1; + line += 13; + + qid = line; + reqid_res = strtoull(qid, &ep, 16); + if (qid[0] == '\0' || *ep != '|') + return -1; + if (errno == ERANGE && reqid_res == ULLONG_MAX) + return -1; + if (reqid != reqid_res) + return -1; + + line = ep+1; + + if (strcmp(line, "FAILURE") == 0) + return -1; + + if (strcmp(line, "NOT-FOUND") == 0) + return 0; + + if (strcmp(line, "FOUND") == 0) + return 1; + + return -1; +} +*/ + +static int +table_procexec_lookup(struct table *t, enum table_service service, const char *key, char **dst) { + struct timeval tv; + uint64_t reqid; + char *line = NULL; + size_t linecap = 0; + ssize_t linelen; + uint64_t reqid_res; + char *qid = NULL; + char *ep = NULL; + size_t sz = 0; + + FILE* backend_w = ((struct procexec_handle *)t->t_handle)->backend_w; + FILE* backend_r = ((struct procexec_handle *)t->t_handle)->backend_r; + + reqid = generate_uid(); + gettimeofday(&tv, NULL); + + fprintf(backend_w, "TABLE|%s|%lld.%06ld|LOOKUP|%016"PRIx64"|%s|%s\n", + PROTOCOL_VERSION, + tv.tv_sec, tv.tv_usec, reqid, table_service_name(service), key); + fflush(backend_w); + + linelen = getline(&line, &linecap, backend_r); + if (linelen == 0) + return 0; + line[strcspn(line, "\n")] = '\0'; + + if (strncmp(line, "TABLE-RESULT|", 13) != 0) + return -1; + line += 13; + + qid = line; + reqid_res = strtoull(qid, &ep, 16); + if (qid[0] == '\0' || *ep != '|') + return -1; + if (errno == ERANGE && reqid_res == ULLONG_MAX) + return -1; + if (reqid != reqid_res) + return -1; + + line = ep+1; + + if (strcmp(line, "FAILURE") == 0) + return -1; + + if (strcmp(line, "NOT-FOUND") == 0) + return 0; + + if (strncmp(line, "FOUND|", 6) == 0) { + line = line + 6; + sz = strlen(line) + 1; + *dst = xcalloc(sz, sizeof(*dst)); + if (strlcpy(*dst, line, sz) >= sz){ + free(*dst); + return -1; + } + return 1; + } + return -1; +} + +static int +table_procexec_fetch(struct table *t, enum table_service service, char **dst){ + struct timeval tv; + uint64_t reqid; + char *line = NULL; + size_t linecap = 0; + ssize_t linelen; + uint64_t reqid_res; + char *qid = NULL; + char *ep = NULL; + size_t sz = 0; + + FILE* backend_w = ((struct procexec_handle *)t->t_handle)->backend_w; + FILE* backend_r = ((struct procexec_handle *)t->t_handle)->backend_r; + + reqid = generate_uid(); + gettimeofday(&tv, NULL); + + fprintf(backend_w, "TABLE|%s|%lld.%06ld|FETCH|%016"PRIx64"|%s\n", + PROTOCOL_VERSION, + tv.tv_sec, tv.tv_usec, reqid, table_service_name(service)); + fflush(backend_w); + + linelen = getline(&line, &linecap, backend_r); + if (linelen == 0) + return 0; + line[strcspn(line, "\n")] = '\0'; + + if (strncmp(line, "TABLE-RESULT|", 13) != 0) + return -1; + line += 13; + + qid = line; + reqid_res = strtoull(qid, &ep, 16); + if (qid[0] == '\0' || *ep != '|') + return -1; + if (errno == ERANGE && reqid_res == ULLONG_MAX) + return -1; + if (reqid != reqid_res) + return -1; + + line = ep+1; + + if (strcmp(line, "FAILURE") == 0) + return -1; + + if (strcmp(line, "NOT-FOUND") == 0) + return 0; + + if (strncmp(line, "FOUND|", 6) == 0) { + sz = strlen(line) + 1; + *dst = xcalloc(sz, sizeof(*dst)); + if (strlcpy(*dst, line+6, sz) >= sz){ + free(*dst); + return -1; + } + return 1; + } + return -1; +}