xref: /openbsd/usr.sbin/smtpd/aliases.c (revision fc61954a)
1 /*	$OpenBSD: aliases.c,v 1.71 2016/08/31 10:18:08 gilles Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.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/socket.h>
23 
24 #include <ctype.h>
25 #include <errno.h>
26 #include <event.h>
27 #include <imsg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <limits.h>
32 #include <util.h>
33 
34 #include "smtpd.h"
35 #include "log.h"
36 
37 static int aliases_expand_include(struct expand *, const char *);
38 
39 int
40 aliases_get(struct expand *expand, const char *username)
41 {
42 	struct expandnode      *xn;
43 	char			buf[SMTPD_MAXLOCALPARTSIZE];
44 	size_t			nbaliases;
45 	int			ret;
46 	union lookup		lk;
47 	struct table	       *mapping = NULL;
48 	struct table	       *userbase = NULL;
49 	char		       *pbuf;
50 
51 	mapping = expand->rule->r_mapping;
52 	userbase = expand->rule->r_userbase;
53 
54 	xlowercase(buf, username, sizeof(buf));
55 
56 	/* first, check if entry has a user-part tag */
57 	pbuf = strchr(buf, *env->sc_subaddressing_delim);
58 	if (pbuf) {
59 		ret = table_lookup(mapping, NULL, buf, K_ALIAS, &lk);
60 		if (ret < 0)
61 			return (-1);
62 		if (ret)
63 			goto expand;
64 		*pbuf = '\0';
65 	}
66 
67 	/* no user-part tag, try looking up user */
68 	ret = table_lookup(mapping, NULL, buf, K_ALIAS, &lk);
69 	if (ret <= 0)
70 		return ret;
71 
72 expand:
73 	/* foreach node in table_alias expandtree, we merge */
74 	nbaliases = 0;
75 	RB_FOREACH(xn, expandtree, &lk.expand->tree) {
76 		if (xn->type == EXPAND_INCLUDE)
77 			nbaliases += aliases_expand_include(expand,
78 			    xn->u.buffer);
79 		else {
80 			xn->mapping = mapping;
81 			xn->userbase = userbase;
82 			expand_insert(expand, xn);
83 			nbaliases++;
84 		}
85 	}
86 
87 	expand_free(lk.expand);
88 
89 	log_debug("debug: aliases_get: returned %zd aliases", nbaliases);
90 	return nbaliases;
91 }
92 
93 int
94 aliases_virtual_get(struct expand *expand, const struct mailaddr *maddr)
95 {
96 	struct expandnode      *xn;
97 	union lookup		lk;
98 	char			buf[LINE_MAX];
99 	char			user[LINE_MAX];
100 	char			tag[LINE_MAX];
101 	char			domain[LINE_MAX];
102 	char		       *pbuf;
103 	int			nbaliases;
104 	int			ret;
105 	struct table	       *mapping = NULL;
106 	struct table	       *userbase = NULL;
107 
108 	mapping = expand->rule->r_mapping;
109 	userbase = expand->rule->r_userbase;
110 
111 	if (!bsnprintf(user, sizeof(user), "%s", maddr->user))
112 		return 0;
113 	if (!bsnprintf(domain, sizeof(domain), "%s", maddr->domain))
114 		return 0;
115 	xlowercase(user, user, sizeof(user));
116 	xlowercase(domain, domain, sizeof(domain));
117 
118 	memset(tag, '\0', sizeof tag);
119 	pbuf = strchr(user, *env->sc_subaddressing_delim);
120 	if (pbuf) {
121 		if (!bsnprintf(tag, sizeof(tag), "%s", pbuf + 1))
122 			return 0;
123 		xlowercase(tag, tag, sizeof(tag));
124 		*pbuf = '\0';
125 	}
126 
127 	/* first, check if entry has a user-part tag */
128 	if (tag[0]) {
129 		if (!bsnprintf(buf, sizeof(buf), "%s%c%s@%s",
130 			user, *env->sc_subaddressing_delim, tag, domain))
131 			return 0;
132 		ret = table_lookup(mapping, NULL, buf, K_ALIAS, &lk);
133 		if (ret < 0)
134 			return (-1);
135 		if (ret)
136 			goto expand;
137 	}
138 
139 	/* then, check if entry exists without user-part tag */
140 	if (!bsnprintf(buf, sizeof(buf), "%s@%s", user, domain))
141 		return 0;
142 	ret = table_lookup(mapping, NULL, buf, K_ALIAS, &lk);
143 	if (ret < 0)
144 		return (-1);
145 	if (ret)
146 		goto expand;
147 
148 	if (tag[0]) {
149 		/* Failed ? We lookup for username + user-part tag */
150 		if (!bsnprintf(buf, sizeof(buf), "%s%c%s",
151 			user, *env->sc_subaddressing_delim, tag))
152 			return 0;
153 		ret = table_lookup(mapping, NULL, buf, K_ALIAS, &lk);
154 		if (ret < 0)
155 			return (-1);
156 		if (ret)
157 			goto expand;
158 	}
159 
160 	/* Failed ? We lookup for username only */
161 	if (!bsnprintf(buf, sizeof(buf), "%s", user))
162 		return 0;
163 	ret = table_lookup(mapping, NULL, buf, K_ALIAS, &lk);
164 	if (ret < 0)
165 		return (-1);
166 	if (ret)
167 		goto expand;
168 
169 	if (!bsnprintf(buf, sizeof(buf), "@%s", domain))
170 		return 0;
171 	/* Failed ? We lookup for catch all for virtual domain */
172 	ret = table_lookup(mapping, NULL, buf, K_ALIAS, &lk);
173 	if (ret < 0)
174 		return (-1);
175 	if (ret)
176 		goto expand;
177 
178 	/* Failed ? We lookup for a *global* catch all */
179 	ret = table_lookup(mapping, NULL, "@", K_ALIAS, &lk);
180 	if (ret <= 0)
181 		return (ret);
182 
183 expand:
184 	/* foreach node in table_virtual expand, we merge */
185 	nbaliases = 0;
186 	RB_FOREACH(xn, expandtree, &lk.expand->tree) {
187 		if (xn->type == EXPAND_INCLUDE)
188 			nbaliases += aliases_expand_include(expand,
189 			    xn->u.buffer);
190 		else {
191 			xn->mapping = mapping;
192 			xn->userbase = userbase;
193 			expand_insert(expand, xn);
194 			nbaliases++;
195 		}
196 	}
197 
198 	expand_free(lk.expand);
199 
200 	log_debug("debug: aliases_virtual_get: '%s' resolved to %d nodes",
201 	    buf, nbaliases);
202 
203 	return nbaliases;
204 }
205 
206 static int
207 aliases_expand_include(struct expand *expand, const char *filename)
208 {
209 	FILE *fp;
210 	char *line;
211 	size_t len, lineno = 0;
212 	char delim[3] = { '\\', '#', '\0' };
213 
214 	fp = fopen(filename, "r");
215 	if (fp == NULL) {
216 		log_warn("warn: failed to open include file \"%s\".", filename);
217 		return 0;
218 	}
219 
220 	while ((line = fparseln(fp, &len, &lineno, delim, 0)) != NULL) {
221 		expand_line(expand, line, 0);
222 		free(line);
223 	}
224 
225 	fclose(fp);
226 	return 1;
227 }
228