xref: /openbsd/usr.sbin/smtpd/expand.c (revision cecf84d4)
1 /*	$OpenBSD: expand.c,v 1.28 2015/01/20 17:37:54 deraadt 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->parent = expand->parent;
67 
68 	log_trace(TRACE_EXPAND, "expand: %p: expand_insert() called for %s",
69 	    expand, expandnode_info(node));
70 	if (node->type == EXPAND_USERNAME &&
71 	    expand->parent &&
72 	    expand->parent->type == EXPAND_USERNAME &&
73 	    !strcmp(expand->parent->u.user, node->u.user)) {
74 		log_trace(TRACE_EXPAND, "expand: %p: setting sameuser = 1",
75 		    expand);
76 		node->sameuser = 1;
77 	}
78 
79 	if (expand_lookup(expand, node)) {
80 		log_trace(TRACE_EXPAND, "expand: %p: node found, discarding",
81 			expand);
82 		return;
83 	}
84 
85 	xn = xmemdup(node, sizeof *xn, "expand_insert");
86 	xn->rule = expand->rule;
87 	xn->parent = expand->parent;
88 	xn->alias = expand->alias;
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->mapping < e2->mapping)
140 		return -1;
141 	if (e1->mapping > e2->mapping)
142 		return 1;
143 	if (e1->userbase < e2->userbase)
144 		return -1;
145 	if (e1->userbase > e2->userbase)
146 		return 1;
147 
148 	r = memcmp(&e1->u, &e2->u, sizeof(e1->u));
149 	if (r)
150 		return (r);
151 
152 
153 	if (e1->parent == e2->parent)
154 		return (0);
155 
156 	if (e1->parent == NULL)
157 		return (-1);
158 	if (e2->parent == NULL)
159 		return (1);
160 
161 	/*
162 	 * The same node can be expanded in for different dest context.
163 	 * Wen need to distinguish between those.
164 	 */
165 	for(p1 = e1->parent; p1->type != EXPAND_ADDRESS; p1 = p1->parent)
166 		;
167 	for(p2 = e2->parent; p2->type != EXPAND_ADDRESS; p2 = p2->parent)
168 		;
169 	if (p1 < p2)
170 		return (-1);
171 	if (p1 > p2)
172 		return (1);
173 
174 	if (e1->type != EXPAND_FILENAME && e1->type != EXPAND_FILTER)
175 		return (0);
176 
177 	/*
178 	 * For external delivery, we need to distinguish between users.
179 	 * If we can't find a username, we assume it is _smtpd.
180 	 */
181 	for(p1 = e1->parent; p1 && p1->type != EXPAND_USERNAME; p1 = p1->parent)
182 		;
183 	for(p2 = e2->parent; p2 && p2->type != EXPAND_USERNAME; p2 = p2->parent)
184 		;
185 	if (p1 < p2)
186 		return (-1);
187 	if (p1 > p2)
188 		return (1);
189 
190 	return (0);
191 }
192 
193 static int
194 expand_line_split(char **line, char **ret)
195 {
196 	static char	buffer[LINE_MAX];
197 	int		esc, dq, sq;
198 	size_t		i;
199 	char	       *s;
200 
201 	memset(buffer, 0, sizeof buffer);
202 	esc = dq = sq = 0;
203 	i = 0;
204 	for (s = *line; (*s) && (i < sizeof(buffer)); ++s) {
205 		if (esc) {
206 			buffer[i++] = *s;
207 			esc = 0;
208 			continue;
209 		}
210 		if (*s == '\\') {
211 			esc = 1;
212 			continue;
213 		}
214 		if (*s == ',' && !dq && !sq) {
215 			*ret = buffer;
216 			*line = s+1;
217 			return (1);
218 		}
219 
220 		buffer[i++] = *s;
221 		esc = 0;
222 
223 		if (*s == '"' && !sq)
224 			dq ^= 1;
225 		if (*s == '\'' && !dq)
226 			sq ^= 1;
227 	}
228 
229 	if (esc || dq || sq || i == sizeof(buffer))
230 		return (-1);
231 
232 	*ret = buffer;
233 	*line = s;
234 	return (i ? 1 : 0);
235 }
236 
237 int
238 expand_line(struct expand *expand, const char *s, int do_includes)
239 {
240 	struct expandnode	xn;
241 	char			buffer[LINE_MAX];
242 	char		       *p, *subrcpt;
243 	int			ret;
244 
245 	memset(buffer, 0, sizeof buffer);
246 	if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer)
247 		return 0;
248 
249 	p = buffer;
250 	while ((ret = expand_line_split(&p, &subrcpt)) > 0) {
251 		subrcpt = strip(subrcpt);
252 		if (subrcpt[0] == '\0')
253 			continue;
254 		if (! text_to_expandnode(&xn, subrcpt))
255 			return 0;
256 		if (! do_includes)
257 			if (xn.type == EXPAND_INCLUDE)
258 				continue;
259 		expand_insert(expand, &xn);
260 	}
261 
262 	if (ret >= 0)
263 		return 1;
264 
265 	/* expand_line_split() returned < 0 */
266 	return 0;
267 }
268 
269 static const char *
270 expandnode_info(struct expandnode *e)
271 {
272 	static char	buffer[1024];
273 	const char     *type = NULL;
274 	const char     *value = NULL;
275 	char		tmp[64];
276 
277 	switch (e->type) {
278 	case EXPAND_FILTER:
279 		type = "filter";
280 		break;
281 	case EXPAND_FILENAME:
282 		type = "filename";
283 		break;
284 	case EXPAND_INCLUDE:
285 		type = "include";
286 		break;
287 	case EXPAND_USERNAME:
288 		type = "username";
289 		break;
290 	case EXPAND_ADDRESS:
291 		type = "address";
292 		break;
293 	case EXPAND_ERROR:
294 		type = "error";
295 		break;
296 	case EXPAND_INVALID:
297 	default:
298 		return NULL;
299 	}
300 
301 	if ((value = expandnode_to_text(e)) == NULL)
302 		return NULL;
303 
304 	(void)strlcpy(buffer, type, sizeof buffer);
305 	(void)strlcat(buffer, ":", sizeof buffer);
306 	if (strlcat(buffer, value, sizeof buffer) >= sizeof buffer)
307 		return NULL;
308 
309 	(void)snprintf(tmp, sizeof(tmp), "[parent=%p", e->parent);
310 	if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
311 		return NULL;
312 
313 	if (e->mapping) {
314 		(void)strlcat(buffer, ", mapping=", sizeof buffer);
315 		(void)strlcat(buffer, e->mapping->t_name, sizeof buffer);
316 	}
317 
318 	if (e->userbase) {
319 		(void)strlcat(buffer, ", userbase=", sizeof buffer);
320 		(void)strlcat(buffer, e->userbase->t_name, sizeof buffer);
321 	}
322 
323 	if (strlcat(buffer, "]", sizeof buffer) >= sizeof buffer)
324 		return NULL;
325 
326 	return buffer;
327 }
328 
329 RB_GENERATE(expandtree, expandnode, entry, expand_cmp);
330