xref: /openbsd-src/usr.sbin/smtpd/expand.c (revision d3140113bef2b86d3af61dd20c05a8630ff966c2)
1*d3140113Seric /*	$OpenBSD: expand.c,v 1.32 2021/06/14 17:58:15 eric Exp $	*/
2b85aba96Sgilles 
3b85aba96Sgilles /*
465c4fdfbSgilles  * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
55312a512Seric  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
6b85aba96Sgilles  *
7b85aba96Sgilles  * Permission to use, copy, modify, and distribute this software for any
8b85aba96Sgilles  * purpose with or without fee is hereby granted, provided that the above
9b85aba96Sgilles  * copyright notice and this permission notice appear in all copies.
10b85aba96Sgilles  *
11b85aba96Sgilles  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12b85aba96Sgilles  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13b85aba96Sgilles  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14b85aba96Sgilles  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15b85aba96Sgilles  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16b85aba96Sgilles  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17b85aba96Sgilles  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18b85aba96Sgilles  */
19b85aba96Sgilles 
20b85aba96Sgilles #include <stdlib.h>
21b85aba96Sgilles #include <string.h>
22b85aba96Sgilles 
23b85aba96Sgilles #include "smtpd.h"
24b85aba96Sgilles 
2559a46edcSgilles static const char *expandnode_info(struct expandnode *);
2659a46edcSgilles 
277b8073ddSgilles struct expandnode *
expand_lookup(struct expand * expand,struct expandnode * key)28b5283b1cSeric expand_lookup(struct expand *expand, struct expandnode *key)
29b85aba96Sgilles {
30b5283b1cSeric 	return RB_FIND(expandtree, &expand->tree, key);
31b85aba96Sgilles }
32b85aba96Sgilles 
33299c4efeSeric int
expand_to_text(struct expand * expand,char * buf,size_t sz)34299c4efeSeric expand_to_text(struct expand *expand, char *buf, size_t sz)
35299c4efeSeric {
36299c4efeSeric 	struct expandnode *xn;
37299c4efeSeric 
38299c4efeSeric 	buf[0] = '\0';
39299c4efeSeric 
40299c4efeSeric 	RB_FOREACH(xn, expandtree, &expand->tree) {
41299c4efeSeric 		if (buf[0])
4254d5b618Sgilles 			(void)strlcat(buf, ", ", sz);
4354d5b618Sgilles 		if (strlcat(buf, expandnode_to_text(xn), sz) >= sz)
4454d5b618Sgilles 			return 0;
45299c4efeSeric 	}
46299c4efeSeric 
47299c4efeSeric 	return 1;
48299c4efeSeric }
49299c4efeSeric 
50b85aba96Sgilles void
expand_insert(struct expand * expand,struct expandnode * node)51b5283b1cSeric expand_insert(struct expand *expand, struct expandnode *node)
52b85aba96Sgilles {
5392620fa8Seric 	struct expandnode *xn;
5456ea0ff8Sgilles 
55a8e22235Sgilles 	node->rule = expand->rule;
56a2b6b1eaSeric 	node->parent = expand->parent;
57a2b6b1eaSeric 
5859a46edcSgilles 	log_trace(TRACE_EXPAND, "expand: %p: expand_insert() called for %s",
5959a46edcSgilles 	    expand, expandnode_info(node));
605312a512Seric 	if (node->type == EXPAND_USERNAME &&
615312a512Seric 	    expand->parent &&
625312a512Seric 	    expand->parent->type == EXPAND_USERNAME &&
6359a46edcSgilles 	    !strcmp(expand->parent->u.user, node->u.user)) {
6459a46edcSgilles 		log_trace(TRACE_EXPAND, "expand: %p: setting sameuser = 1",
6559a46edcSgilles 		    expand);
665312a512Seric 		node->sameuser = 1;
6759a46edcSgilles 	}
685312a512Seric 
6959a46edcSgilles 	if (expand_lookup(expand, node)) {
7059a46edcSgilles 		log_trace(TRACE_EXPAND, "expand: %p: node found, discarding",
7159a46edcSgilles 			expand);
72faa7039fSeric 		return;
7359a46edcSgilles 	}
74faa7039fSeric 
75118c16f3Sgilles 	xn = xmemdup(node, sizeof *xn);
765312a512Seric 	xn->rule = expand->rule;
775312a512Seric 	xn->parent = expand->parent;
785312a512Seric 	if (xn->parent)
795312a512Seric 		xn->depth = xn->parent->depth + 1;
805312a512Seric 	else
815312a512Seric 		xn->depth = 0;
82b5283b1cSeric 	RB_INSERT(expandtree, &expand->tree, xn);
835312a512Seric 	if (expand->queue)
845312a512Seric 		TAILQ_INSERT_TAIL(expand->queue, xn, tq_entry);
8565c4fdfbSgilles 	expand->nb_nodes++;
86a2b6b1eaSeric 	log_trace(TRACE_EXPAND, "expand: %p: inserted node %p", expand, xn);
87b85aba96Sgilles }
88b85aba96Sgilles 
89b85aba96Sgilles void
expand_clear(struct expand * expand)901c6ac251Seric expand_clear(struct expand *expand)
917b8073ddSgilles {
924bbf671bSeric 	struct expandnode *xn;
937b8073ddSgilles 
9459a46edcSgilles 	log_trace(TRACE_EXPAND, "expand: %p: clearing expand tree", expand);
955312a512Seric 	if (expand->queue)
965312a512Seric 		while ((xn = TAILQ_FIRST(expand->queue)))
975312a512Seric 			TAILQ_REMOVE(expand->queue, xn, tq_entry);
985312a512Seric 
99b5283b1cSeric 	while ((xn = RB_ROOT(&expand->tree)) != NULL) {
100b5283b1cSeric 		RB_REMOVE(expandtree, &expand->tree, xn);
1014bbf671bSeric 		free(xn);
1027b8073ddSgilles 	}
1037b8073ddSgilles }
1047b8073ddSgilles 
1051c6ac251Seric void
expand_free(struct expand * expand)1061c6ac251Seric expand_free(struct expand *expand)
1071c6ac251Seric {
1081c6ac251Seric 	expand_clear(expand);
10959a46edcSgilles 
11059a46edcSgilles 	log_trace(TRACE_EXPAND, "expand: %p: freeing expand tree", expand);
1111c6ac251Seric 	free(expand);
1121c6ac251Seric }
1131c6ac251Seric 
114b85aba96Sgilles int
expand_cmp(struct expandnode * e1,struct expandnode * e2)1157b8073ddSgilles expand_cmp(struct expandnode *e1, struct expandnode *e2)
116b85aba96Sgilles {
117a2b6b1eaSeric 	struct expandnode *p1, *p2;
118a2b6b1eaSeric 	int		   r;
119a2b6b1eaSeric 
12056ea0ff8Sgilles 	if (e1->type < e2->type)
121b85aba96Sgilles 		return -1;
12256ea0ff8Sgilles 	if (e1->type > e2->type)
123b85aba96Sgilles 		return 1;
1245312a512Seric 	if (e1->sameuser < e2->sameuser)
1255312a512Seric 		return -1;
1265312a512Seric 	if (e1->sameuser > e2->sameuser)
1275312a512Seric 		return 1;
128a8e22235Sgilles 	if (e1->realuser < e2->realuser)
12959a46edcSgilles 		return -1;
130a8e22235Sgilles 	if (e1->realuser > e2->realuser)
13159a46edcSgilles 		return 1;
132b85aba96Sgilles 
133a2b6b1eaSeric 	r = memcmp(&e1->u, &e2->u, sizeof(e1->u));
134a2b6b1eaSeric 	if (r)
135a2b6b1eaSeric 		return (r);
136a2b6b1eaSeric 
137a2b6b1eaSeric 	if (e1->parent == e2->parent)
138a2b6b1eaSeric 		return (0);
139a2b6b1eaSeric 
140a2b6b1eaSeric 	if (e1->parent == NULL)
141a2b6b1eaSeric 		return (-1);
142a2b6b1eaSeric 	if (e2->parent == NULL)
143a2b6b1eaSeric 		return (1);
144a2b6b1eaSeric 
145a2b6b1eaSeric 	/*
146a2b6b1eaSeric 	 * The same node can be expanded in for different dest context.
147a2b6b1eaSeric 	 * Wen need to distinguish between those.
148a2b6b1eaSeric 	 */
149a2b6b1eaSeric 	for(p1 = e1->parent; p1->type != EXPAND_ADDRESS; p1 = p1->parent)
150a2b6b1eaSeric 		;
151a2b6b1eaSeric 	for(p2 = e2->parent; p2->type != EXPAND_ADDRESS; p2 = p2->parent)
152a2b6b1eaSeric 		;
153a2b6b1eaSeric 	if (p1 < p2)
154a2b6b1eaSeric 		return (-1);
155a2b6b1eaSeric 	if (p1 > p2)
156a2b6b1eaSeric 		return (1);
157a2b6b1eaSeric 
158a2b6b1eaSeric 	if (e1->type != EXPAND_FILENAME && e1->type != EXPAND_FILTER)
159a2b6b1eaSeric 		return (0);
160a2b6b1eaSeric 
161a2b6b1eaSeric 	/*
162a2b6b1eaSeric 	 * For external delivery, we need to distinguish between users.
163a2b6b1eaSeric 	 * If we can't find a username, we assume it is _smtpd.
164a2b6b1eaSeric 	 */
165a2b6b1eaSeric 	for(p1 = e1->parent; p1 && p1->type != EXPAND_USERNAME; p1 = p1->parent)
166a2b6b1eaSeric 		;
167a2b6b1eaSeric 	for(p2 = e2->parent; p2 && p2->type != EXPAND_USERNAME; p2 = p2->parent)
168a2b6b1eaSeric 		;
169a2b6b1eaSeric 	if (p1 < p2)
170a2b6b1eaSeric 		return (-1);
171a2b6b1eaSeric 	if (p1 > p2)
172a2b6b1eaSeric 		return (1);
173a2b6b1eaSeric 
174a2b6b1eaSeric 	return (0);
175b85aba96Sgilles }
176b85aba96Sgilles 
17765c4fdfbSgilles static int
expand_line_split(char ** line,char ** ret)17865c4fdfbSgilles expand_line_split(char **line, char **ret)
17965c4fdfbSgilles {
180953aae25Sderaadt 	static char	buffer[LINE_MAX];
181da5e82e8Stedu 	int		esc, dq, sq;
182da5e82e8Stedu 	size_t		i;
18365c4fdfbSgilles 	char	       *s;
18465c4fdfbSgilles 
185c1392a69Seric 	memset(buffer, 0, sizeof buffer);
186da5e82e8Stedu 	esc = dq = sq = 0;
187da5e82e8Stedu 	i = 0;
188da5e82e8Stedu 	for (s = *line; (*s) && (i < sizeof(buffer)); ++s) {
18965c4fdfbSgilles 		if (esc) {
19065c4fdfbSgilles 			buffer[i++] = *s;
19165c4fdfbSgilles 			esc = 0;
19265c4fdfbSgilles 			continue;
19365c4fdfbSgilles 		}
19465c4fdfbSgilles 		if (*s == '\\') {
19565c4fdfbSgilles 			esc = 1;
19665c4fdfbSgilles 			continue;
19765c4fdfbSgilles 		}
19865c4fdfbSgilles 		if (*s == ',' && !dq && !sq) {
19965c4fdfbSgilles 			*ret = buffer;
20065c4fdfbSgilles 			*line = s+1;
20165c4fdfbSgilles 			return (1);
20265c4fdfbSgilles 		}
20365c4fdfbSgilles 
20465c4fdfbSgilles 		buffer[i++] = *s;
20565c4fdfbSgilles 		esc = 0;
20665c4fdfbSgilles 
20765c4fdfbSgilles 		if (*s == '"' && !sq)
20865c4fdfbSgilles 			dq ^= 1;
20965c4fdfbSgilles 		if (*s == '\'' && !dq)
21065c4fdfbSgilles 			sq ^= 1;
21165c4fdfbSgilles 	}
21265c4fdfbSgilles 
21365c4fdfbSgilles 	if (esc || dq || sq || i == sizeof(buffer))
21465c4fdfbSgilles 		return (-1);
21565c4fdfbSgilles 
21665c4fdfbSgilles 	*ret = buffer;
21765c4fdfbSgilles 	*line = s;
21865c4fdfbSgilles 	return (i ? 1 : 0);
21965c4fdfbSgilles }
22065c4fdfbSgilles 
22165c4fdfbSgilles int
expand_line(struct expand * expand,const char * s,int do_includes)22265c4fdfbSgilles expand_line(struct expand *expand, const char *s, int do_includes)
22365c4fdfbSgilles {
22465c4fdfbSgilles 	struct expandnode	xn;
225953aae25Sderaadt 	char			buffer[LINE_MAX];
22665c4fdfbSgilles 	char		       *p, *subrcpt;
22765c4fdfbSgilles 	int			ret;
22865c4fdfbSgilles 
229c1392a69Seric 	memset(buffer, 0, sizeof buffer);
23065c4fdfbSgilles 	if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer)
23165c4fdfbSgilles 		return 0;
23265c4fdfbSgilles 
23365c4fdfbSgilles 	p = buffer;
23465c4fdfbSgilles 	while ((ret = expand_line_split(&p, &subrcpt)) > 0) {
23565c4fdfbSgilles 		subrcpt = strip(subrcpt);
23665c4fdfbSgilles 		if (subrcpt[0] == '\0')
23765c4fdfbSgilles 			continue;
23865c4fdfbSgilles 		if (!text_to_expandnode(&xn, subrcpt))
23965c4fdfbSgilles 			return 0;
24065c4fdfbSgilles 		if (!do_includes)
24165c4fdfbSgilles 			if (xn.type == EXPAND_INCLUDE)
24265c4fdfbSgilles 				continue;
24365c4fdfbSgilles 		expand_insert(expand, &xn);
24465c4fdfbSgilles 	}
24565c4fdfbSgilles 
24665c4fdfbSgilles 	if (ret >= 0)
24765c4fdfbSgilles 		return 1;
24865c4fdfbSgilles 
24965c4fdfbSgilles 	/* expand_line_split() returned < 0 */
25065c4fdfbSgilles 	return 0;
25165c4fdfbSgilles }
25265c4fdfbSgilles 
25359a46edcSgilles static const char *
expandnode_info(struct expandnode * e)25459a46edcSgilles expandnode_info(struct expandnode *e)
25559a46edcSgilles {
25659a46edcSgilles 	static char	buffer[1024];
25759a46edcSgilles 	const char     *type = NULL;
25859a46edcSgilles 	const char     *value = NULL;
259a2b6b1eaSeric 	char		tmp[64];
26059a46edcSgilles 
26159a46edcSgilles 	switch (e->type) {
26259a46edcSgilles 	case EXPAND_FILTER:
26359a46edcSgilles 		type = "filter";
26459a46edcSgilles 		break;
26559a46edcSgilles 	case EXPAND_FILENAME:
26659a46edcSgilles 		type = "filename";
26759a46edcSgilles 		break;
26859a46edcSgilles 	case EXPAND_INCLUDE:
26959a46edcSgilles 		type = "include";
27059a46edcSgilles 		break;
27159a46edcSgilles 	case EXPAND_USERNAME:
27259a46edcSgilles 		type = "username";
27359a46edcSgilles 		break;
27459a46edcSgilles 	case EXPAND_ADDRESS:
27559a46edcSgilles 		type = "address";
27659a46edcSgilles 		break;
277299c4efeSeric 	case EXPAND_ERROR:
278299c4efeSeric 		type = "error";
279299c4efeSeric 		break;
28059a46edcSgilles 	case EXPAND_INVALID:
28159a46edcSgilles 	default:
28259a46edcSgilles 		return NULL;
28359a46edcSgilles 	}
28459a46edcSgilles 
28559a46edcSgilles 	if ((value = expandnode_to_text(e)) == NULL)
28659a46edcSgilles 		return NULL;
28759a46edcSgilles 
28854d5b618Sgilles 	(void)strlcpy(buffer, type, sizeof buffer);
28954d5b618Sgilles 	(void)strlcat(buffer, ":", sizeof buffer);
29059a46edcSgilles 	if (strlcat(buffer, value, sizeof buffer) >= sizeof buffer)
29159a46edcSgilles 		return NULL;
29259a46edcSgilles 
29354d5b618Sgilles 	(void)snprintf(tmp, sizeof(tmp), "[parent=%p", e->parent);
294a2b6b1eaSeric 	if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
295a2b6b1eaSeric 		return NULL;
296a2b6b1eaSeric 
297a8e22235Sgilles 	(void)snprintf(tmp, sizeof(tmp), ", rule=%p", e->rule);
298a8e22235Sgilles 	if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
299a8e22235Sgilles 		return NULL;
300a2b6b1eaSeric 
301a8e22235Sgilles 	if (e->rule) {
302a8e22235Sgilles 		(void)snprintf(tmp, sizeof(tmp), ", dispatcher=%p", e->rule->dispatcher);
303a8e22235Sgilles 		if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
304a8e22235Sgilles 			return NULL;
30559a46edcSgilles 	}
306a2b6b1eaSeric 
30759a46edcSgilles 	if (strlcat(buffer, "]", sizeof buffer) >= sizeof buffer)
30859a46edcSgilles 		return NULL;
309a2b6b1eaSeric 
31059a46edcSgilles 	return buffer;
31159a46edcSgilles }
31259a46edcSgilles 
3137b8073ddSgilles RB_GENERATE(expandtree, expandnode, entry, expand_cmp);
314