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 |
|
|
} |