diff options
Diffstat (limited to 'smtpd/expand.c')
-rw-r--r-- | smtpd/expand.c | 332 |
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); |