xref: /openbsd/usr.sbin/smtpd/aliases.c (revision 8932bfb7)
1 /*	$OpenBSD: aliases.c,v 1.43 2011/05/16 21:05:51 gilles Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/tree.h>
22 #include <sys/param.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 #include <util.h>
32 
33 #include "smtpd.h"
34 #include "log.h"
35 
36 static int aliases_expand_include(struct expandtree *, char *);
37 static int alias_is_filter(struct expandnode *, char *, size_t);
38 static int alias_is_username(struct expandnode *, char *, size_t);
39 static int alias_is_address(struct expandnode *, char *, size_t);
40 static int alias_is_filename(struct expandnode *, char *, size_t);
41 static int alias_is_include(struct expandnode *, char *, size_t);
42 
43 int
44 aliases_exist(objid_t mapid, char *username)
45 {
46 	struct map *map;
47 	struct map_alias *map_alias;
48 	char buf[MAX_LOCALPART_SIZE];
49 
50 	map = map_find(mapid);
51 	if (map == NULL)
52 		return 0;
53 
54 	lowercase(buf, username, sizeof(buf));
55 	map_alias = map_lookup(mapid, buf, K_ALIAS);
56 	if (map_alias == NULL)
57 		return 0;
58 
59 	/* XXX - for now the map API always allocate */
60 	log_debug("aliases_exist: '%s' exists with %zd expansion nodes",
61 	    username, map_alias->nbnodes);
62 
63 	expandtree_free_nodes(&map_alias->expandtree);
64 	free(map_alias);
65 
66 	return 1;
67 }
68 
69 int
70 aliases_get(objid_t mapid, struct expandtree *expandtree, char *username)
71 {
72 	struct map *map;
73 	struct map_alias *map_alias;
74 	struct expandnode *expnode;
75 	char buf[MAX_LOCALPART_SIZE];
76 	size_t nbaliases;
77 
78 	map = map_find(mapid);
79 	if (map == NULL)
80 		return 0;
81 
82 	lowercase(buf, username, sizeof(buf));
83 	map_alias = map_lookup(mapid, buf, K_ALIAS);
84 	if (map_alias == NULL)
85 		return 0;
86 
87 	/* foreach node in map_alias expandtree, we merge */
88 	nbaliases = 0;
89 	RB_FOREACH(expnode, expandtree, &map_alias->expandtree) {
90 		if (expnode->type == EXPAND_INCLUDE)
91 			nbaliases += aliases_expand_include(expandtree, expnode->u.buffer);
92 		else {
93 			expandtree_increment_node(expandtree, expnode);
94 			nbaliases++;
95 		}
96 	}
97 
98 	expandtree_free_nodes(&map_alias->expandtree);
99 	free(map_alias);
100 
101 	log_debug("aliases_get: returned %zd aliases", nbaliases);
102 	return nbaliases;
103 }
104 
105 int
106 aliases_vdomain_exists(objid_t mapid, char *hostname)
107 {
108 	struct map *map;
109 	struct map_virtual *map_virtual;
110 	char buf[MAXHOSTNAMELEN];
111 
112 	map = map_find(mapid);
113 	if (map == NULL)
114 		return 0;
115 
116 	lowercase(buf, hostname, sizeof(buf));
117 	map_virtual = map_lookup(mapid, buf, K_VIRTUAL);
118 	if (map_virtual == NULL)
119 		return 0;
120 
121 	/* XXX - for now the map API always allocate */
122 	log_debug("aliases_vdomain_exist: '%s' exists", hostname);
123 	expandtree_free_nodes(&map_virtual->expandtree);
124 	free(map_virtual);
125 
126 
127 	return 1;
128 }
129 
130 int
131 aliases_virtual_exist(objid_t mapid, struct mailaddr *maddr)
132 {
133 	struct map *map;
134 	struct map_virtual *map_virtual;
135 	char buf[MAX_LINE_SIZE];
136 	char *pbuf = buf;
137 
138 	map = map_find(mapid);
139 	if (map == NULL)
140 		return 0;
141 
142 	if (! bsnprintf(buf, sizeof(buf), "%s@%s", maddr->user,
143 		maddr->domain))
144 		return 0;
145 	lowercase(buf, buf, sizeof(buf));
146 
147 	map_virtual = map_lookup(mapid, buf, K_VIRTUAL);
148 	if (map_virtual == NULL) {
149 		pbuf = strchr(buf, '@');
150 		map_virtual = map_lookup(mapid, pbuf, K_VIRTUAL);
151 	}
152 	if (map_virtual == NULL)
153 		return 0;
154 
155 	log_debug("aliases_virtual_exist: '%s' exists", pbuf);
156 	expandtree_free_nodes(&map_virtual->expandtree);
157 	free(map_virtual);
158 
159 	return 1;
160 }
161 
162 int
163 aliases_virtual_get(objid_t mapid, struct expandtree *expandtree,
164     struct mailaddr *maddr)
165 {
166 	struct map *map;
167 	struct map_virtual *map_virtual;
168 	struct expandnode *expnode;
169 	char buf[MAX_LINE_SIZE];
170 	char *pbuf = buf;
171 	int nbaliases;
172 
173 	map = map_find(mapid);
174 	if (map == NULL)
175 		return 0;
176 
177 	if (! bsnprintf(buf, sizeof(buf), "%s@%s", maddr->user,
178 		maddr->domain))
179 		return 0;
180 	lowercase(buf, buf, sizeof(buf));
181 
182 	map_virtual = map_lookup(mapid, buf, K_VIRTUAL);
183 	if (map_virtual == NULL) {
184 		pbuf = strchr(buf, '@');
185 		map_virtual = map_lookup(mapid, pbuf, K_VIRTUAL);
186 	}
187 	if (map_virtual == NULL)
188 		return 0;
189 
190 	/* foreach node in map_virtual expandtree, we merge */
191 	nbaliases = 0;
192 	RB_FOREACH(expnode, expandtree, &map_virtual->expandtree) {
193 		if (expnode->type == EXPAND_INCLUDE)
194 			nbaliases += aliases_expand_include(expandtree, expnode->u.buffer);
195 		else {
196 			expandtree_increment_node(expandtree, expnode);
197 			nbaliases++;
198 		}
199 	}
200 
201 	expandtree_free_nodes(&map_virtual->expandtree);
202 	free(map_virtual);
203 	log_debug("aliases_virtual_get: '%s' resolved to %d nodes", pbuf, nbaliases);
204 
205 	return nbaliases;
206 }
207 
208 int
209 aliases_expand_include(struct expandtree *expandtree, char *filename)
210 {
211 	FILE *fp;
212 	char *line;
213 	size_t len;
214 	size_t lineno = 0;
215 	char delim[] = { '\\', '#' };
216 	struct expandnode expnode;
217 
218 	fp = fopen(filename, "r");
219 	if (fp == NULL) {
220 		log_warn("failed to open include file \"%s\".", filename);
221 		return 0;
222 	}
223 
224 	while ((line = fparseln(fp, &len, &lineno, delim, 0)) != NULL) {
225 		if (len == 0) {
226 			free(line);
227 			continue;
228 		}
229 
230 		bzero(&expnode, sizeof(struct expandnode));
231 		if (! alias_parse(&expnode, line)) {
232 			log_warnx("could not parse include entry \"%s\".", line);
233 		}
234 
235 		if (expnode.type == EXPAND_INCLUDE)
236 			log_warnx("nested inclusion is not supported.");
237 		else
238 			expandtree_increment_node(expandtree, &expnode);
239 
240 		free(line);
241 	}
242 
243 	fclose(fp);
244 	return 1;
245 }
246 
247 int
248 alias_parse(struct expandnode *alias, char *line)
249 {
250 	size_t i;
251 	int (*f[])(struct expandnode *, char *, size_t) = {
252 		alias_is_include,
253 		alias_is_filter,
254 		alias_is_filename,
255 		alias_is_address,
256 		alias_is_username
257 	};
258 	char *wsp;
259 
260 	/* remove ending whitespaces */
261 	wsp = line + strlen(line);
262 	while (wsp != line) {
263 		if (*wsp != '\0' && !isspace((int)*wsp))
264 			break;
265 		*wsp-- = '\0';
266 	}
267 
268 	for (i = 0; i < sizeof(f) / sizeof(void *); ++i) {
269 		bzero(alias, sizeof(struct expandnode));
270 		if (f[i](alias, line, strlen(line)))
271 			break;
272 	}
273 	if (i == sizeof(f) / sizeof(void *))
274 		return 0;
275 
276 	return 1;
277 }
278 
279 
280 int
281 alias_is_filter(struct expandnode *alias, char *line, size_t len)
282 {
283 	if (*line == '|') {
284 		if (strlcpy(alias->u.buffer, line + 1,
285 			sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer))
286 			return 0;
287 		alias->type = EXPAND_FILTER;
288 		return 1;
289 	}
290 	return 0;
291 }
292 
293 int
294 alias_is_username(struct expandnode *alias, char *line, size_t len)
295 {
296 	if (strlcpy(alias->u.user, line,
297 	    sizeof(alias->u.user)) >= sizeof(alias->u.user))
298 		return 0;
299 
300 	while (*line) {
301 		if (!isalnum((int)*line) &&
302 		    *line != '_' && *line != '.' && *line != '-')
303 			return 0;
304 		++line;
305 	}
306 
307 	alias->type = EXPAND_USERNAME;
308 	return 1;
309 }
310 
311 int
312 alias_is_address(struct expandnode *alias, char *line, size_t len)
313 {
314 	char *domain;
315 
316 	if (len < 3)	/* x@y */
317 		return 0;
318 
319 	domain = strchr(line, '@');
320 	if (domain == NULL)
321 		return 0;
322 
323 	/* @ cannot start or end an address */
324 	if (domain == line || domain == line + len - 1)
325 		return 0;
326 
327 	/* scan pre @ for disallowed chars */
328 	*domain++ = '\0';
329 	strlcpy(alias->u.mailaddr.user, line, sizeof(alias->u.mailaddr.user));
330 	strlcpy(alias->u.mailaddr.domain, domain, sizeof(alias->u.mailaddr.domain));
331 
332 	while (*line) {
333 		char allowedset[] = "!#$%*/?|^{}`~&'+-=_.";
334 		if (!isalnum((int)*line) &&
335 		    strchr(allowedset, *line) == NULL)
336 			return 0;
337 		++line;
338 	}
339 
340 	while (*domain) {
341 		char allowedset[] = "-.";
342 		if (!isalnum((int)*domain) &&
343 		    strchr(allowedset, *domain) == NULL)
344 			return 0;
345 		++domain;
346 	}
347 
348 	alias->type = EXPAND_ADDRESS;
349 	return 1;
350 }
351 
352 int
353 alias_is_filename(struct expandnode *alias, char *line, size_t len)
354 {
355 	if (*line != '/')
356 		return 0;
357 
358 	if (strlcpy(alias->u.buffer, line,
359 	    sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer))
360 		return 0;
361 	alias->type = EXPAND_FILENAME;
362 	return 1;
363 }
364 
365 int
366 alias_is_include(struct expandnode *alias, char *line, size_t len)
367 {
368 	if (strncasecmp(":include:", line, 9) != 0)
369 		return 0;
370 
371 	if (! alias_is_filename(alias, line + 9, len - 9))
372 		return 0;
373 
374 	alias->type = EXPAND_INCLUDE;
375 	return 1;
376 }
377