xref: /openbsd/usr.sbin/smtpd/aliases.c (revision cca36db2)
1 /*	$OpenBSD: aliases.c,v 1.47 2012/04/21 12:45:05 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_alias *map_alias;
47 	char buf[MAX_LOCALPART_SIZE];
48 
49 	lowercase(buf, username, sizeof(buf));
50 	map_alias = map_lookup(mapid, buf, K_ALIAS);
51 	if (map_alias == NULL)
52 		return 0;
53 
54 	/* XXX - for now the map API always allocate */
55 	log_debug("aliases_exist: '%s' exists with %zd expansion nodes",
56 	    username, map_alias->nbnodes);
57 
58 	expandtree_free_nodes(&map_alias->expandtree);
59 	free(map_alias);
60 
61 	return 1;
62 }
63 
64 int
65 aliases_get(objid_t mapid, struct expandtree *expandtree, char *username)
66 {
67 	struct map_alias *map_alias;
68 	struct expandnode *expnode;
69 	char buf[MAX_LOCALPART_SIZE];
70 	size_t nbaliases;
71 
72 	lowercase(buf, username, sizeof(buf));
73 	map_alias = map_lookup(mapid, buf, K_ALIAS);
74 	if (map_alias == NULL)
75 		return 0;
76 
77 	/* foreach node in map_alias expandtree, we merge */
78 	nbaliases = 0;
79 	RB_FOREACH(expnode, expandtree, &map_alias->expandtree) {
80 		strlcpy(expnode->as_user, SMTPD_USER, sizeof (expnode->as_user));
81 		if (expnode->type == EXPAND_INCLUDE)
82 			nbaliases += aliases_expand_include(expandtree, expnode->u.buffer);
83 		else {
84 			expandtree_increment_node(expandtree, expnode);
85 			nbaliases++;
86 		}
87 	}
88 
89 	expandtree_free_nodes(&map_alias->expandtree);
90 	free(map_alias);
91 
92 	log_debug("aliases_get: returned %zd aliases", nbaliases);
93 	return nbaliases;
94 }
95 
96 int
97 aliases_vdomain_exists(objid_t mapid, char *hostname)
98 {
99 	struct map_virtual *map_virtual;
100 	char buf[MAXHOSTNAMELEN];
101 
102 	lowercase(buf, hostname, sizeof(buf));
103 	map_virtual = map_lookup(mapid, buf, K_VIRTUAL);
104 	if (map_virtual == NULL)
105 		return 0;
106 
107 	/* XXX - for now the map API always allocate */
108 	log_debug("aliases_vdomain_exist: '%s' exists", hostname);
109 	expandtree_free_nodes(&map_virtual->expandtree);
110 	free(map_virtual);
111 
112 	return 1;
113 }
114 
115 int
116 aliases_virtual_exist(objid_t mapid, struct mailaddr *maddr)
117 {
118 	struct map_virtual *map_virtual;
119 	char buf[MAX_LINE_SIZE];
120 	char *pbuf = buf;
121 
122 	if (! bsnprintf(buf, sizeof(buf), "%s@%s", maddr->user,
123 		maddr->domain))
124 		return 0;
125 	lowercase(buf, buf, sizeof(buf));
126 
127 	map_virtual = map_lookup(mapid, buf, K_VIRTUAL);
128 	if (map_virtual == NULL) {
129 		pbuf = strchr(buf, '@');
130 		map_virtual = map_lookup(mapid, pbuf, K_VIRTUAL);
131 	}
132 	if (map_virtual == NULL)
133 		return 0;
134 
135 	log_debug("aliases_virtual_exist: '%s' exists", pbuf);
136 	expandtree_free_nodes(&map_virtual->expandtree);
137 	free(map_virtual);
138 
139 	return 1;
140 }
141 
142 int
143 aliases_virtual_get(objid_t mapid, struct expandtree *expandtree,
144     struct mailaddr *maddr)
145 {
146 	struct map_virtual *map_virtual;
147 	struct expandnode *expnode;
148 	char buf[MAX_LINE_SIZE];
149 	char *pbuf = buf;
150 	int nbaliases;
151 
152 	if (! bsnprintf(buf, sizeof(buf), "%s@%s", maddr->user,
153 		maddr->domain))
154 		return 0;
155 	lowercase(buf, buf, sizeof(buf));
156 
157 	map_virtual = map_lookup(mapid, buf, K_VIRTUAL);
158 	if (map_virtual == NULL) {
159 		pbuf = strchr(buf, '@');
160 		map_virtual = map_lookup(mapid, pbuf, K_VIRTUAL);
161 	}
162 	if (map_virtual == NULL)
163 		return 0;
164 
165 	/* foreach node in map_virtual expandtree, we merge */
166 	nbaliases = 0;
167 	RB_FOREACH(expnode, expandtree, &map_virtual->expandtree) {
168 		strlcpy(expnode->as_user, SMTPD_USER, sizeof (expnode->as_user));
169 		if (expnode->type == EXPAND_INCLUDE)
170 			nbaliases += aliases_expand_include(expandtree, expnode->u.buffer);
171 		else {
172 			expandtree_increment_node(expandtree, expnode);
173 			nbaliases++;
174 		}
175 	}
176 
177 	expandtree_free_nodes(&map_virtual->expandtree);
178 	free(map_virtual);
179 	log_debug("aliases_virtual_get: '%s' resolved to %d nodes", pbuf, nbaliases);
180 
181 	return nbaliases;
182 }
183 
184 int
185 aliases_expand_include(struct expandtree *expandtree, char *filename)
186 {
187 	FILE *fp;
188 	char *line;
189 	size_t len;
190 	size_t lineno = 0;
191 	char delim[] = { '\\', '#' };
192 	struct expandnode expnode;
193 
194 	fp = fopen(filename, "r");
195 	if (fp == NULL) {
196 		log_warn("failed to open include file \"%s\".", filename);
197 		return 0;
198 	}
199 
200 	while ((line = fparseln(fp, &len, &lineno, delim, 0)) != NULL) {
201 		if (len == 0) {
202 			free(line);
203 			continue;
204 		}
205 
206 		bzero(&expnode, sizeof(struct expandnode));
207 		if (! alias_parse(&expnode, line)) {
208 			log_warnx("could not parse include entry \"%s\".", line);
209 		}
210 
211 		if (expnode.type == EXPAND_INCLUDE)
212 			log_warnx("nested inclusion is not supported.");
213 		else
214 			expandtree_increment_node(expandtree, &expnode);
215 
216 		free(line);
217 	}
218 
219 	fclose(fp);
220 	return 1;
221 }
222 
223 int
224 alias_parse(struct expandnode *alias, char *line)
225 {
226 	size_t i;
227 	int (*f[])(struct expandnode *, char *, size_t) = {
228 		alias_is_include,
229 		alias_is_filter,
230 		alias_is_filename,
231 		alias_is_address,
232 		alias_is_username
233 	};
234 	char *wsp;
235 
236 	/* remove ending whitespaces */
237 	wsp = line + strlen(line);
238 	while (wsp != line) {
239 		if (*wsp != '\0' && !isspace((int)*wsp))
240 			break;
241 		*wsp-- = '\0';
242 	}
243 
244 	for (i = 0; i < sizeof(f) / sizeof(void *); ++i) {
245 		bzero(alias, sizeof(struct expandnode));
246 		if (f[i](alias, line, strlen(line)))
247 			break;
248 	}
249 	if (i == sizeof(f) / sizeof(void *))
250 		return 0;
251 
252 	return 1;
253 }
254 
255 
256 int
257 alias_is_filter(struct expandnode *alias, char *line, size_t len)
258 {
259 	if (*line == '|') {
260 		if (strlcpy(alias->u.buffer, line + 1,
261 			sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer))
262 			return 0;
263 		alias->type = EXPAND_FILTER;
264 		return 1;
265 	}
266 	return 0;
267 }
268 
269 int
270 alias_is_username(struct expandnode *alias, char *line, size_t len)
271 {
272 	if (strlcpy(alias->u.user, line,
273 	    sizeof(alias->u.user)) >= sizeof(alias->u.user))
274 		return 0;
275 
276 	while (*line) {
277 		if (!isalnum((int)*line) &&
278 		    *line != '_' && *line != '.' && *line != '-')
279 			return 0;
280 		++line;
281 	}
282 
283 	alias->type = EXPAND_USERNAME;
284 	return 1;
285 }
286 
287 int
288 alias_is_address(struct expandnode *alias, char *line, size_t len)
289 {
290 	char *domain;
291 
292 	if (len < 3)	/* x@y */
293 		return 0;
294 
295 	domain = strchr(line, '@');
296 	if (domain == NULL)
297 		return 0;
298 
299 	/* @ cannot start or end an address */
300 	if (domain == line || domain == line + len - 1)
301 		return 0;
302 
303 	/* scan pre @ for disallowed chars */
304 	*domain++ = '\0';
305 	strlcpy(alias->u.mailaddr.user, line, sizeof(alias->u.mailaddr.user));
306 	strlcpy(alias->u.mailaddr.domain, domain, sizeof(alias->u.mailaddr.domain));
307 
308 	while (*line) {
309 		char allowedset[] = "!#$%*/?|^{}`~&'+-=_.";
310 		if (!isalnum((int)*line) &&
311 		    strchr(allowedset, *line) == NULL)
312 			return 0;
313 		++line;
314 	}
315 
316 	while (*domain) {
317 		char allowedset[] = "-.";
318 		if (!isalnum((int)*domain) &&
319 		    strchr(allowedset, *domain) == NULL)
320 			return 0;
321 		++domain;
322 	}
323 
324 	alias->type = EXPAND_ADDRESS;
325 	return 1;
326 }
327 
328 int
329 alias_is_filename(struct expandnode *alias, char *line, size_t len)
330 {
331 	if (*line != '/')
332 		return 0;
333 
334 	if (strlcpy(alias->u.buffer, line,
335 	    sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer))
336 		return 0;
337 	alias->type = EXPAND_FILENAME;
338 	return 1;
339 }
340 
341 int
342 alias_is_include(struct expandnode *alias, char *line, size_t len)
343 {
344 	size_t skip;
345 
346 	if (strncasecmp(":include:", line, 9) == 0)
347 		skip = 9;
348 	else if (strncasecmp("include:", line, 8) == 0)
349 		skip = 8;
350 	else
351 		return 0;
352 
353 	if (! alias_is_filename(alias, line + skip, len - skip))
354 		return 0;
355 
356 	alias->type = EXPAND_INCLUDE;
357 	return 1;
358 }
359