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