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