xref: /openbsd-src/usr.sbin/smtpd/expand.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: expand.c,v 1.27 2014/05/09 21:30:11 tedu 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 <stdlib.h>
30 #include <string.h>
31 
32 #include "smtpd.h"
33 #include "log.h"
34 
35 static const char *expandnode_info(struct expandnode *);
36 
37 struct expandnode *
38 expand_lookup(struct expand *expand, struct expandnode *key)
39 {
40 	return RB_FIND(expandtree, &expand->tree, key);
41 }
42 
43 int
44 expand_to_text(struct expand *expand, char *buf, size_t sz)
45 {
46 	struct expandnode *xn;
47 
48 	buf[0] = '\0';
49 
50 	RB_FOREACH(xn, expandtree, &expand->tree) {
51 		if (buf[0])
52 			(void)strlcat(buf, ", ", sz);
53 		if (strlcat(buf, expandnode_to_text(xn), sz) >= sz)
54 			return 0;
55 	}
56 
57 	return 1;
58 }
59 
60 void
61 expand_insert(struct expand *expand, struct expandnode *node)
62 {
63 	struct expandnode *xn;
64 
65 	node->parent = expand->parent;
66 
67 	log_trace(TRACE_EXPAND, "expand: %p: expand_insert() called for %s",
68 	    expand, expandnode_info(node));
69 	if (node->type == EXPAND_USERNAME &&
70 	    expand->parent &&
71 	    expand->parent->type == EXPAND_USERNAME &&
72 	    !strcmp(expand->parent->u.user, node->u.user)) {
73 		log_trace(TRACE_EXPAND, "expand: %p: setting sameuser = 1",
74 		    expand);
75 		node->sameuser = 1;
76 	}
77 
78 	if (expand_lookup(expand, node)) {
79 		log_trace(TRACE_EXPAND, "expand: %p: node found, discarding",
80 			expand);
81 		return;
82 	}
83 
84 	xn = xmemdup(node, sizeof *xn, "expand_insert");
85 	xn->rule = expand->rule;
86 	xn->parent = expand->parent;
87 	xn->alias = expand->alias;
88 	if (xn->parent)
89 		xn->depth = xn->parent->depth + 1;
90 	else
91 		xn->depth = 0;
92 	RB_INSERT(expandtree, &expand->tree, xn);
93 	if (expand->queue)
94 		TAILQ_INSERT_TAIL(expand->queue, xn, tq_entry);
95 	expand->nb_nodes++;
96 	log_trace(TRACE_EXPAND, "expand: %p: inserted node %p", expand, xn);
97 }
98 
99 void
100 expand_clear(struct expand *expand)
101 {
102 	struct expandnode *xn;
103 
104 	log_trace(TRACE_EXPAND, "expand: %p: clearing expand tree", expand);
105 	if (expand->queue)
106 		while ((xn = TAILQ_FIRST(expand->queue)))
107 			TAILQ_REMOVE(expand->queue, xn, tq_entry);
108 
109 	while ((xn = RB_ROOT(&expand->tree)) != NULL) {
110 		RB_REMOVE(expandtree, &expand->tree, xn);
111 		free(xn);
112 	}
113 }
114 
115 void
116 expand_free(struct expand *expand)
117 {
118 	expand_clear(expand);
119 
120 	log_trace(TRACE_EXPAND, "expand: %p: freeing expand tree", expand);
121 	free(expand);
122 }
123 
124 int
125 expand_cmp(struct expandnode *e1, struct expandnode *e2)
126 {
127 	struct expandnode *p1, *p2;
128 	int		   r;
129 
130 	if (e1->type < e2->type)
131 		return -1;
132 	if (e1->type > e2->type)
133 		return 1;
134 	if (e1->sameuser < e2->sameuser)
135 		return -1;
136 	if (e1->sameuser > e2->sameuser)
137 		return 1;
138 	if (e1->mapping < e2->mapping)
139 		return -1;
140 	if (e1->mapping > e2->mapping)
141 		return 1;
142 	if (e1->userbase < e2->userbase)
143 		return -1;
144 	if (e1->userbase > e2->userbase)
145 		return 1;
146 
147 	r = memcmp(&e1->u, &e2->u, sizeof(e1->u));
148 	if (r)
149 		return (r);
150 
151 
152 	if (e1->parent == e2->parent)
153 		return (0);
154 
155 	if (e1->parent == NULL)
156 		return (-1);
157 	if (e2->parent == NULL)
158 		return (1);
159 
160 	/*
161 	 * The same node can be expanded in for different dest context.
162 	 * Wen need to distinguish between those.
163 	 */
164 	for(p1 = e1->parent; p1->type != EXPAND_ADDRESS; p1 = p1->parent)
165 		;
166 	for(p2 = e2->parent; p2->type != EXPAND_ADDRESS; p2 = p2->parent)
167 		;
168 	if (p1 < p2)
169 		return (-1);
170 	if (p1 > p2)
171 		return (1);
172 
173 	if (e1->type != EXPAND_FILENAME && e1->type != EXPAND_FILTER)
174 		return (0);
175 
176 	/*
177 	 * For external delivery, we need to distinguish between users.
178 	 * If we can't find a username, we assume it is _smtpd.
179 	 */
180 	for(p1 = e1->parent; p1 && p1->type != EXPAND_USERNAME; p1 = p1->parent)
181 		;
182 	for(p2 = e2->parent; p2 && p2->type != EXPAND_USERNAME; p2 = p2->parent)
183 		;
184 	if (p1 < p2)
185 		return (-1);
186 	if (p1 > p2)
187 		return (1);
188 
189 	return (0);
190 }
191 
192 static int
193 expand_line_split(char **line, char **ret)
194 {
195 	static char	buffer[SMTPD_MAXLINESIZE];
196 	int		esc, dq, sq;
197 	size_t		i;
198 	char	       *s;
199 
200 	memset(buffer, 0, sizeof buffer);
201 	esc = dq = sq = 0;
202 	i = 0;
203 	for (s = *line; (*s) && (i < sizeof(buffer)); ++s) {
204 		if (esc) {
205 			buffer[i++] = *s;
206 			esc = 0;
207 			continue;
208 		}
209 		if (*s == '\\') {
210 			esc = 1;
211 			continue;
212 		}
213 		if (*s == ',' && !dq && !sq) {
214 			*ret = buffer;
215 			*line = s+1;
216 			return (1);
217 		}
218 
219 		buffer[i++] = *s;
220 		esc = 0;
221 
222 		if (*s == '"' && !sq)
223 			dq ^= 1;
224 		if (*s == '\'' && !dq)
225 			sq ^= 1;
226 	}
227 
228 	if (esc || dq || sq || i == sizeof(buffer))
229 		return (-1);
230 
231 	*ret = buffer;
232 	*line = s;
233 	return (i ? 1 : 0);
234 }
235 
236 int
237 expand_line(struct expand *expand, const char *s, int do_includes)
238 {
239 	struct expandnode	xn;
240 	char			buffer[SMTPD_MAXLINESIZE];
241 	char		       *p, *subrcpt;
242 	int			ret;
243 
244 	memset(buffer, 0, sizeof buffer);
245 	if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer)
246 		return 0;
247 
248 	p = buffer;
249 	while ((ret = expand_line_split(&p, &subrcpt)) > 0) {
250 		subrcpt = strip(subrcpt);
251 		if (subrcpt[0] == '\0')
252 			continue;
253 		if (! text_to_expandnode(&xn, subrcpt))
254 			return 0;
255 		if (! do_includes)
256 			if (xn.type == EXPAND_INCLUDE)
257 				continue;
258 		expand_insert(expand, &xn);
259 	}
260 
261 	if (ret >= 0)
262 		return 1;
263 
264 	/* expand_line_split() returned < 0 */
265 	return 0;
266 }
267 
268 static const char *
269 expandnode_info(struct expandnode *e)
270 {
271 	static char	buffer[1024];
272 	const char     *type = NULL;
273 	const char     *value = NULL;
274 	char		tmp[64];
275 
276 	switch (e->type) {
277 	case EXPAND_FILTER:
278 		type = "filter";
279 		break;
280 	case EXPAND_FILENAME:
281 		type = "filename";
282 		break;
283 	case EXPAND_INCLUDE:
284 		type = "include";
285 		break;
286 	case EXPAND_USERNAME:
287 		type = "username";
288 		break;
289 	case EXPAND_ADDRESS:
290 		type = "address";
291 		break;
292 	case EXPAND_ERROR:
293 		type = "error";
294 		break;
295 	case EXPAND_INVALID:
296 	default:
297 		return NULL;
298 	}
299 
300 	if ((value = expandnode_to_text(e)) == NULL)
301 		return NULL;
302 
303 	(void)strlcpy(buffer, type, sizeof buffer);
304 	(void)strlcat(buffer, ":", sizeof buffer);
305 	if (strlcat(buffer, value, sizeof buffer) >= sizeof buffer)
306 		return NULL;
307 
308 	(void)snprintf(tmp, sizeof(tmp), "[parent=%p", e->parent);
309 	if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
310 		return NULL;
311 
312 	if (e->mapping) {
313 		(void)strlcat(buffer, ", mapping=", sizeof buffer);
314 		(void)strlcat(buffer, e->mapping->t_name, sizeof buffer);
315 	}
316 
317 	if (e->userbase) {
318 		(void)strlcat(buffer, ", userbase=", sizeof buffer);
319 		(void)strlcat(buffer, e->userbase->t_name, sizeof buffer);
320 	}
321 
322 	if (strlcat(buffer, "]", sizeof buffer) >= sizeof buffer)
323 		return NULL;
324 
325 	return buffer;
326 }
327 
328 RB_GENERATE(expandtree, expandnode, entry, expand_cmp);
329