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