aboutsummaryrefslogtreecommitdiffstats
path: root/smtpd/expand.c
diff options
context:
space:
mode:
Diffstat (limited to 'smtpd/expand.c')
-rw-r--r--smtpd/expand.c332
1 files changed, 332 insertions, 0 deletions
diff --git a/smtpd/expand.c b/smtpd/expand.c
new file mode 100644
index 00000000..a4306fc0
--- /dev/null
+++ b/smtpd/expand.c
@@ -0,0 +1,332 @@
+/* $OpenBSD: expand.c,v 1.31 2018/05/31 21:06:12 gilles Exp $ */
+
+/*
+ * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
+ * Copyright (c) 2012 Eric Faurot <eric@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 "includes.h"
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/socket.h>
+
+#include <ctype.h>
+#include <event.h>
+#include <imsg.h>
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+
+#include "smtpd.h"
+#include "log.h"
+
+static const char *expandnode_info(struct expandnode *);
+
+struct expandnode *
+expand_lookup(struct expand *expand, struct expandnode *key)
+{
+ return RB_FIND(expandtree, &expand->tree, key);
+}
+
+int
+expand_to_text(struct expand *expand, char *buf, size_t sz)
+{
+ struct expandnode *xn;
+
+ buf[0] = '\0';
+
+ RB_FOREACH(xn, expandtree, &expand->tree) {
+ if (buf[0])
+ (void)strlcat(buf, ", ", sz);
+ if (strlcat(buf, expandnode_to_text(xn), sz) >= sz)
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+expand_insert(struct expand *expand, struct expandnode *node)
+{
+ struct expandnode *xn;
+
+ node->rule = expand->rule;
+ node->parent = expand->parent;
+
+ log_trace(TRACE_EXPAND, "expand: %p: expand_insert() called for %s",
+ expand, expandnode_info(node));
+ if (node->type == EXPAND_USERNAME &&
+ expand->parent &&
+ expand->parent->type == EXPAND_USERNAME &&
+ !strcmp(expand->parent->u.user, node->u.user)) {
+ log_trace(TRACE_EXPAND, "expand: %p: setting sameuser = 1",
+ expand);
+ node->sameuser = 1;
+ }
+
+ if (expand_lookup(expand, node)) {
+ log_trace(TRACE_EXPAND, "expand: %p: node found, discarding",
+ expand);
+ return;
+ }
+
+ xn = xmemdup(node, sizeof *xn);
+ xn->rule = expand->rule;
+ xn->parent = expand->parent;
+ if (xn->parent)
+ xn->depth = xn->parent->depth + 1;
+ else
+ xn->depth = 0;
+ RB_INSERT(expandtree, &expand->tree, xn);
+ if (expand->queue)
+ TAILQ_INSERT_TAIL(expand->queue, xn, tq_entry);
+ expand->nb_nodes++;
+ log_trace(TRACE_EXPAND, "expand: %p: inserted node %p", expand, xn);
+}
+
+void
+expand_clear(struct expand *expand)
+{
+ struct expandnode *xn;
+
+ log_trace(TRACE_EXPAND, "expand: %p: clearing expand tree", expand);
+ if (expand->queue)
+ while ((xn = TAILQ_FIRST(expand->queue)))
+ TAILQ_REMOVE(expand->queue, xn, tq_entry);
+
+ while ((xn = RB_ROOT(&expand->tree)) != NULL) {
+ RB_REMOVE(expandtree, &expand->tree, xn);
+ free(xn);
+ }
+}
+
+void
+expand_free(struct expand *expand)
+{
+ expand_clear(expand);
+
+ log_trace(TRACE_EXPAND, "expand: %p: freeing expand tree", expand);
+ free(expand);
+}
+
+int
+expand_cmp(struct expandnode *e1, struct expandnode *e2)
+{
+ struct expandnode *p1, *p2;
+ int r;
+
+ if (e1->type < e2->type)
+ return -1;
+ if (e1->type > e2->type)
+ return 1;
+ if (e1->sameuser < e2->sameuser)
+ return -1;
+ if (e1->sameuser > e2->sameuser)
+ return 1;
+ if (e1->realuser < e2->realuser)
+ return -1;
+ if (e1->realuser > e2->realuser)
+ return 1;
+
+ r = memcmp(&e1->u, &e2->u, sizeof(e1->u));
+ if (r)
+ return (r);
+
+ if (e1->parent == e2->parent)
+ return (0);
+
+ if (e1->parent == NULL)
+ return (-1);
+ if (e2->parent == NULL)
+ return (1);
+
+ /*
+ * The same node can be expanded in for different dest context.
+ * Wen need to distinguish between those.
+ */
+ for(p1 = e1->parent; p1->type != EXPAND_ADDRESS; p1 = p1->parent)
+ ;
+ for(p2 = e2->parent; p2->type != EXPAND_ADDRESS; p2 = p2->parent)
+ ;
+ if (p1 < p2)
+ return (-1);
+ if (p1 > p2)
+ return (1);
+
+ if (e1->type != EXPAND_FILENAME && e1->type != EXPAND_FILTER)
+ return (0);
+
+ /*
+ * For external delivery, we need to distinguish between users.
+ * If we can't find a username, we assume it is _smtpd.
+ */
+ for(p1 = e1->parent; p1 && p1->type != EXPAND_USERNAME; p1 = p1->parent)
+ ;
+ for(p2 = e2->parent; p2 && p2->type != EXPAND_USERNAME; p2 = p2->parent)
+ ;
+ if (p1 < p2)
+ return (-1);
+ if (p1 > p2)
+ return (1);
+
+ return (0);
+}
+
+static int
+expand_line_split(char **line, char **ret)
+{
+ static char buffer[LINE_MAX];
+ int esc, dq, sq;
+ size_t i;
+ char *s;
+
+ memset(buffer, 0, sizeof buffer);
+ esc = dq = sq = 0;
+ i = 0;
+ for (s = *line; (*s) && (i < sizeof(buffer)); ++s) {
+ if (esc) {
+ buffer[i++] = *s;
+ esc = 0;
+ continue;
+ }
+ if (*s == '\\') {
+ esc = 1;
+ continue;
+ }
+ if (*s == ',' && !dq && !sq) {
+ *ret = buffer;
+ *line = s+1;
+ return (1);
+ }
+
+ buffer[i++] = *s;
+ esc = 0;
+
+ if (*s == '"' && !sq)
+ dq ^= 1;
+ if (*s == '\'' && !dq)
+ sq ^= 1;
+ }
+
+ if (esc || dq || sq || i == sizeof(buffer))
+ return (-1);
+
+ *ret = buffer;
+ *line = s;
+ return (i ? 1 : 0);
+}
+
+int
+expand_line(struct expand *expand, const char *s, int do_includes)
+{
+ struct expandnode xn;
+ char buffer[LINE_MAX];
+ char *p, *subrcpt;
+ int ret;
+
+ memset(buffer, 0, sizeof buffer);
+ if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer)
+ return 0;
+
+ p = buffer;
+ while ((ret = expand_line_split(&p, &subrcpt)) > 0) {
+ subrcpt = strip(subrcpt);
+ if (subrcpt[0] == '\0')
+ continue;
+ if (!text_to_expandnode(&xn, subrcpt))
+ return 0;
+ if (!do_includes)
+ if (xn.type == EXPAND_INCLUDE)
+ continue;
+ expand_insert(expand, &xn);
+ }
+
+ if (ret >= 0)
+ return 1;
+
+ /* expand_line_split() returned < 0 */
+ return 0;
+}
+
+static const char *
+expandnode_info(struct expandnode *e)
+{
+ static char buffer[1024];
+ const char *type = NULL;
+ const char *value = NULL;
+ char tmp[64];
+
+ switch (e->type) {
+ case EXPAND_FILTER:
+ type = "filter";
+ break;
+ case EXPAND_FILENAME:
+ type = "filename";
+ break;
+ case EXPAND_INCLUDE:
+ type = "include";
+ break;
+ case EXPAND_USERNAME:
+ type = "username";
+ break;
+ case EXPAND_ADDRESS:
+ type = "address";
+ break;
+ case EXPAND_ERROR:
+ type = "error";
+ break;
+ case EXPAND_INVALID:
+ default:
+ return NULL;
+ }
+
+ if ((value = expandnode_to_text(e)) == NULL)
+ return NULL;
+
+ (void)strlcpy(buffer, type, sizeof buffer);
+ (void)strlcat(buffer, ":", sizeof buffer);
+ if (strlcat(buffer, value, sizeof buffer) >= sizeof buffer)
+ return NULL;
+
+ (void)snprintf(tmp, sizeof(tmp), "[parent=%p", e->parent);
+ if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
+ return NULL;
+
+ (void)snprintf(tmp, sizeof(tmp), ", rule=%p", e->rule);
+ if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
+ return NULL;
+
+ if (e->rule) {
+ (void)snprintf(tmp, sizeof(tmp), ", dispatcher=%p", e->rule->dispatcher);
+ if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
+ return NULL;
+ }
+
+ if (strlcat(buffer, "]", sizeof buffer) >= sizeof buffer)
+ return NULL;
+
+ return buffer;
+}
+
+RB_GENERATE(expandtree, expandnode, entry, expand_cmp);