xref: /openbsd/usr.sbin/smtpd/expand.c (revision 3cab2bb3)
1 /*	$OpenBSD: expand.c,v 1.31 2018/05/31 21:06:12 gilles Exp $	*/
2 
3 /*
4  * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
5  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/queue.h>
22 #include <sys/tree.h>
23 #include <sys/socket.h>
24 
25 #include <ctype.h>
26 #include <event.h>
27 #include <imsg.h>
28 #include <stdio.h>
29 #include <limits.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "smtpd.h"
34 #include "log.h"
35 
36 static const char *expandnode_info(struct expandnode *);
37 
38 struct expandnode *
39 expand_lookup(struct expand *expand, struct expandnode *key)
40 {
41 	return RB_FIND(expandtree, &expand->tree, key);
42 }
43 
44 int
45 expand_to_text(struct expand *expand, char *buf, size_t sz)
46 {
47 	struct expandnode *xn;
48 
49 	buf[0] = '\0';
50 
51 	RB_FOREACH(xn, expandtree, &expand->tree) {
52 		if (buf[0])
53 			(void)strlcat(buf, ", ", sz);
54 		if (strlcat(buf, expandnode_to_text(xn), sz) >= sz)
55 			return 0;
56 	}
57 
58 	return 1;
59 }
60 
61 void
62 expand_insert(struct expand *expand, struct expandnode *node)
63 {
64 	struct expandnode *xn;
65 
66 	node->rule = expand->rule;
67 	node->parent = expand->parent;
68 
69 	log_trace(TRACE_EXPAND, "expand: %p: expand_insert() called for %s",
70 	    expand, expandnode_info(node));
71 	if (node->type == EXPAND_USERNAME &&
72 	    expand->parent &&
73 	    expand->parent->type == EXPAND_USERNAME &&
74 	    !strcmp(expand->parent->u.user, node->u.user)) {
75 		log_trace(TRACE_EXPAND, "expand: %p: setting sameuser = 1",
76 		    expand);
77 		node->sameuser = 1;
78 	}
79 
80 	if (expand_lookup(expand, node)) {
81 		log_trace(TRACE_EXPAND, "expand: %p: node found, discarding",
82 			expand);
83 		return;
84 	}
85 
86 	xn = xmemdup(node, sizeof *xn);
87 	xn->rule = expand->rule;
88 	xn->parent = expand->parent;
89 	if (xn->parent)
90 		xn->depth = xn->parent->depth + 1;
91 	else
92 		xn->depth = 0;
93 	RB_INSERT(expandtree, &expand->tree, xn);
94 	if (expand->queue)
95 		TAILQ_INSERT_TAIL(expand->queue, xn, tq_entry);
96 	expand->nb_nodes++;
97 	log_trace(TRACE_EXPAND, "expand: %p: inserted node %p", expand, xn);
98 }
99 
100 void
101 expand_clear(struct expand *expand)
102 {
103 	struct expandnode *xn;
104 
105 	log_trace(TRACE_EXPAND, "expand: %p: clearing expand tree", expand);
106 	if (expand->queue)
107 		while ((xn = TAILQ_FIRST(expand->queue)))
108 			TAILQ_REMOVE(expand->queue, xn, tq_entry);
109 
110 	while ((xn = RB_ROOT(&expand->tree)) != NULL) {
111 		RB_REMOVE(expandtree, &expand->tree, xn);
112 		free(xn);
113 	}
114 }
115 
116 void
117 expand_free(struct expand *expand)
118 {
119 	expand_clear(expand);
120 
121 	log_trace(TRACE_EXPAND, "expand: %p: freeing expand tree", expand);
122 	free(expand);
123 }
124 
125 int
126 expand_cmp(struct expandnode *e1, struct expandnode *e2)
127 {
128 	struct expandnode *p1, *p2;
129 	int		   r;
130 
131 	if (e1->type < e2->type)
132 		return -1;
133 	if (e1->type > e2->type)
134 		return 1;
135 	if (e1->sameuser < e2->sameuser)
136 		return -1;
137 	if (e1->sameuser > e2->sameuser)
138 		return 1;
139 	if (e1->realuser < e2->realuser)
140 		return -1;
141 	if (e1->realuser > e2->realuser)
142 		return 1;
143 
144 	r = memcmp(&e1->u, &e2->u, sizeof(e1->u));
145 	if (r)
146 		return (r);
147 
148 	if (e1->parent == e2->parent)
149 		return (0);
150 
151 	if (e1->parent == NULL)
152 		return (-1);
153 	if (e2->parent == NULL)
154 		return (1);
155 
156 	/*
157 	 * The same node can be expanded in for different dest context.
158 	 * Wen need to distinguish between those.
159 	 */
160 	for(p1 = e1->parent; p1->type != EXPAND_ADDRESS; p1 = p1->parent)
161 		;
162 	for(p2 = e2->parent; p2->type != EXPAND_ADDRESS; p2 = p2->parent)
163 		;
164 	if (p1 < p2)
165 		return (-1);
166 	if (p1 > p2)
167 		return (1);
168 
169 	if (e1->type != EXPAND_FILENAME && e1->type != EXPAND_FILTER)
170 		return (0);
171 
172 	/*
173 	 * For external delivery, we need to distinguish between users.
174 	 * If we can't find a username, we assume it is _smtpd.
175 	 */
176 	for(p1 = e1->parent; p1 && p1->type != EXPAND_USERNAME; p1 = p1->parent)
177 		;
178 	for(p2 = e2->parent; p2 && p2->type != EXPAND_USERNAME; p2 = p2->parent)
179 		;
180 	if (p1 < p2)
181 		return (-1);
182 	if (p1 > p2)
183 		return (1);
184 
185 	return (0);
186 }
187 
188 static int
189 expand_line_split(char **line, char **ret)
190 {
191 	static char	buffer[LINE_MAX];
192 	int		esc, dq, sq;
193 	size_t		i;
194 	char	       *s;
195 
196 	memset(buffer, 0, sizeof buffer);
197 	esc = dq = sq = 0;
198 	i = 0;
199 	for (s = *line; (*s) && (i < sizeof(buffer)); ++s) {
200 		if (esc) {
201 			buffer[i++] = *s;
202 			esc = 0;
203 			continue;
204 		}
205 		if (*s == '\\') {
206 			esc = 1;
207 			continue;
208 		}
209 		if (*s == ',' && !dq && !sq) {
210 			*ret = buffer;
211 			*line = s+1;
212 			return (1);
213 		}
214 
215 		buffer[i++] = *s;
216 		esc = 0;
217 
218 		if (*s == '"' && !sq)
219 			dq ^= 1;
220 		if (*s == '\'' && !dq)
221 			sq ^= 1;
222 	}
223 
224 	if (esc || dq || sq || i == sizeof(buffer))
225 		return (-1);
226 
227 	*ret = buffer;
228 	*line = s;
229 	return (i ? 1 : 0);
230 }
231 
232 int
233 expand_line(struct expand *expand, const char *s, int do_includes)
234 {
235 	struct expandnode	xn;
236 	char			buffer[LINE_MAX];
237 	char		       *p, *subrcpt;
238 	int			ret;
239 
240 	memset(buffer, 0, sizeof buffer);
241 	if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer)
242 		return 0;
243 
244 	p = buffer;
245 	while ((ret = expand_line_split(&p, &subrcpt)) > 0) {
246 		subrcpt = strip(subrcpt);
247 		if (subrcpt[0] == '\0')
248 			continue;
249 		if (!text_to_expandnode(&xn, subrcpt))
250 			return 0;
251 		if (!do_includes)
252 			if (xn.type == EXPAND_INCLUDE)
253 				continue;
254 		expand_insert(expand, &xn);
255 	}
256 
257 	if (ret >= 0)
258 		return 1;
259 
260 	/* expand_line_split() returned < 0 */
261 	return 0;
262 }
263 
264 static const char *
265 expandnode_info(struct expandnode *e)
266 {
267 	static char	buffer[1024];
268 	const char     *type = NULL;
269 	const char     *value = NULL;
270 	char		tmp[64];
271 
272 	switch (e->type) {
273 	case EXPAND_FILTER:
274 		type = "filter";
275 		break;
276 	case EXPAND_FILENAME:
277 		type = "filename";
278 		break;
279 	case EXPAND_INCLUDE:
280 		type = "include";
281 		break;
282 	case EXPAND_USERNAME:
283 		type = "username";
284 		break;
285 	case EXPAND_ADDRESS:
286 		type = "address";
287 		break;
288 	case EXPAND_ERROR:
289 		type = "error";
290 		break;
291 	case EXPAND_INVALID:
292 	default:
293 		return NULL;
294 	}
295 
296 	if ((value = expandnode_to_text(e)) == NULL)
297 		return NULL;
298 
299 	(void)strlcpy(buffer, type, sizeof buffer);
300 	(void)strlcat(buffer, ":", sizeof buffer);
301 	if (strlcat(buffer, value, sizeof buffer) >= sizeof buffer)
302 		return NULL;
303 
304 	(void)snprintf(tmp, sizeof(tmp), "[parent=%p", e->parent);
305 	if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
306 		return NULL;
307 
308 	(void)snprintf(tmp, sizeof(tmp), ", rule=%p", e->rule);
309 	if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
310 		return NULL;
311 
312 	if (e->rule) {
313 		(void)snprintf(tmp, sizeof(tmp), ", dispatcher=%p", e->rule->dispatcher);
314 		if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
315 			return NULL;
316 	}
317 
318 	if (strlcat(buffer, "]", sizeof buffer) >= sizeof buffer)
319 		return NULL;
320 
321 	return buffer;
322 }
323 
324 RB_GENERATE(expandtree, expandnode, entry, expand_cmp);
325