File: | src/usr.sbin/smtpd/smtpd/../table.c |
Warning: | line 653, column 7 Dereference of null pointer (loaded from variable 'str') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: table.c,v 1.51 2024/01/04 09:34:03 op Exp $ */ | |||
2 | ||||
3 | /* | |||
4 | * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> | |||
5 | * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> | |||
6 | * | |||
7 | * Permission to use, copy, modify, and distribute this software for any | |||
8 | * purpose with or without fee is hereby granted, provided that the above | |||
9 | * copyright notice and this permission notice appear in all copies. | |||
10 | * | |||
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
18 | */ | |||
19 | ||||
20 | #include <sys/stat.h> | |||
21 | ||||
22 | #include <net/if.h> | |||
23 | ||||
24 | #include <arpa/inet.h> | |||
25 | #include <errno(*__errno()).h> | |||
26 | #include <regex.h> | |||
27 | #include <stdlib.h> | |||
28 | #include <string.h> | |||
29 | ||||
30 | #include "smtpd.h" | |||
31 | #include "log.h" | |||
32 | ||||
33 | struct table_backend *table_backend_lookup(const char *); | |||
34 | ||||
35 | extern struct table_backend table_backend_static; | |||
36 | extern struct table_backend table_backend_db; | |||
37 | extern struct table_backend table_backend_getpwnam; | |||
38 | extern struct table_backend table_backend_proc; | |||
39 | ||||
40 | static const char * table_service_name(enum table_service); | |||
41 | static int table_parse_lookup(enum table_service, const char *, const char *, | |||
42 | union lookup *); | |||
43 | static int parse_sockaddr(struct sockaddr *, int, const char *); | |||
44 | ||||
45 | static unsigned int last_table_id = 0; | |||
46 | ||||
47 | static struct table_backend *backends[] = { | |||
48 | &table_backend_static, | |||
49 | &table_backend_db, | |||
50 | &table_backend_getpwnam, | |||
51 | &table_backend_proc, | |||
52 | NULL((void *)0) | |||
53 | }; | |||
54 | ||||
55 | struct table_backend * | |||
56 | table_backend_lookup(const char *backend) | |||
57 | { | |||
58 | int i; | |||
59 | ||||
60 | if (!strcmp(backend, "file")) | |||
61 | backend = "static"; | |||
62 | ||||
63 | for (i = 0; backends[i]; i++) | |||
64 | if (!strcmp(backends[i]->name, backend)) | |||
65 | return (backends[i]); | |||
66 | ||||
67 | return NULL((void *)0); | |||
68 | } | |||
69 | ||||
70 | static const char * | |||
71 | table_service_name(enum table_service s) | |||
72 | { | |||
73 | switch (s) { | |||
74 | case K_NONE: return "NONE"; | |||
75 | case K_ALIAS: return "ALIAS"; | |||
76 | case K_DOMAIN: return "DOMAIN"; | |||
77 | case K_CREDENTIALS: return "CREDENTIALS"; | |||
78 | case K_NETADDR: return "NETADDR"; | |||
79 | case K_USERINFO: return "USERINFO"; | |||
80 | case K_SOURCE: return "SOURCE"; | |||
81 | case K_MAILADDR: return "MAILADDR"; | |||
82 | case K_ADDRNAME: return "ADDRNAME"; | |||
83 | case K_MAILADDRMAP: return "MAILADDRMAP"; | |||
84 | case K_RELAYHOST: return "RELAYHOST"; | |||
85 | case K_STRING: return "STRING"; | |||
86 | case K_REGEX: return "REGEX"; | |||
87 | } | |||
88 | return "???"; | |||
89 | } | |||
90 | ||||
91 | struct table * | |||
92 | table_find(struct smtpd *conf, const char *name) | |||
93 | { | |||
94 | return dict_get(conf->sc_tables_dict, name); | |||
95 | } | |||
96 | ||||
97 | int | |||
98 | table_match(struct table *table, enum table_service kind, const char *key) | |||
99 | { | |||
100 | return table_lookup(table, kind, key, NULL((void *)0)); | |||
101 | } | |||
102 | ||||
103 | int | |||
104 | table_lookup(struct table *table, enum table_service kind, const char *key, | |||
105 | union lookup *lk) | |||
106 | { | |||
107 | char lkey[1024], *buf = NULL((void *)0); | |||
108 | int r; | |||
109 | ||||
110 | r = -1; | |||
111 | if (table->t_backend->lookup == NULL((void *)0)) | |||
112 | errno(*__errno()) = ENOTSUP91; | |||
113 | else if (!lowercase(lkey, key, sizeof lkey)) { | |||
114 | log_warnx("warn: lookup key too long: %s", key); | |||
115 | errno(*__errno()) = EINVAL22; | |||
116 | } | |||
117 | else | |||
118 | r = table->t_backend->lookup(table, kind, lkey, lk ? &buf : NULL((void *)0)); | |||
119 | ||||
120 | if (r == 1) { | |||
121 | log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s",do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
122 | lk ? "lookup" : "match",do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
123 | key,do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
124 | table_service_name(kind),do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
125 | table->t_backend->name,do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
126 | table->t_name,do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
127 | lk ? "\"" : "",do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
128 | lk ? buf : "true",do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
129 | lk ? "\"" : "")do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0); | |||
130 | if (buf) | |||
131 | r = table_parse_lookup(kind, lkey, buf, lk); | |||
132 | } | |||
133 | else | |||
134 | log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s",do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
135 | lk ? "lookup" : "match",do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
136 | key,do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
137 | table_service_name(kind),do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
138 | table->t_backend->name,do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
139 | table->t_name,do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
140 | (r == -1) ? "error: " : (lk ? "none" : "false"),do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
141 | (r == -1) ? strerror(errno) : "")do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0); | |||
142 | ||||
143 | free(buf); | |||
144 | ||||
145 | return (r); | |||
146 | } | |||
147 | ||||
148 | int | |||
149 | table_fetch(struct table *table, enum table_service kind, union lookup *lk) | |||
150 | { | |||
151 | char *buf = NULL((void *)0); | |||
152 | int r; | |||
153 | ||||
154 | r = -1; | |||
155 | if (table->t_backend->fetch == NULL((void *)0)) | |||
| ||||
156 | errno(*__errno()) = ENOTSUP91; | |||
157 | else | |||
158 | r = table->t_backend->fetch(table, kind, &buf); | |||
159 | ||||
160 | if (r == 1) { | |||
161 | log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> \"%s\"",do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> \"%s\"" , table_service_name(kind), table->t_backend->name, table ->t_name, buf); } while (0) | |||
162 | table_service_name(kind),do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> \"%s\"" , table_service_name(kind), table->t_backend->name, table ->t_name, buf); } while (0) | |||
163 | table->t_backend->name,do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> \"%s\"" , table_service_name(kind), table->t_backend->name, table ->t_name, buf); } while (0) | |||
164 | table->t_name,do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> \"%s\"" , table_service_name(kind), table->t_backend->name, table ->t_name, buf); } while (0) | |||
165 | buf)do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> \"%s\"" , table_service_name(kind), table->t_backend->name, table ->t_name, buf); } while (0); | |||
166 | r = table_parse_lookup(kind, NULL((void *)0), buf, lk); | |||
167 | } | |||
168 | else | |||
169 | log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> %s%s",do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> %s%s" , table_service_name(kind), table->t_backend->name, table ->t_name, (r == -1) ? "error: " : "none", (r == -1) ? strerror ((*__errno())) : ""); } while (0) | |||
170 | table_service_name(kind),do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> %s%s" , table_service_name(kind), table->t_backend->name, table ->t_name, (r == -1) ? "error: " : "none", (r == -1) ? strerror ((*__errno())) : ""); } while (0) | |||
171 | table->t_backend->name,do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> %s%s" , table_service_name(kind), table->t_backend->name, table ->t_name, (r == -1) ? "error: " : "none", (r == -1) ? strerror ((*__errno())) : ""); } while (0) | |||
172 | table->t_name,do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> %s%s" , table_service_name(kind), table->t_backend->name, table ->t_name, (r == -1) ? "error: " : "none", (r == -1) ? strerror ((*__errno())) : ""); } while (0) | |||
173 | (r == -1) ? "error: " : "none",do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> %s%s" , table_service_name(kind), table->t_backend->name, table ->t_name, (r == -1) ? "error: " : "none", (r == -1) ? strerror ((*__errno())) : ""); } while (0) | |||
174 | (r == -1) ? strerror(errno) : "")do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> %s%s" , table_service_name(kind), table->t_backend->name, table ->t_name, (r == -1) ? "error: " : "none", (r == -1) ? strerror ((*__errno())) : ""); } while (0); | |||
175 | ||||
176 | free(buf); | |||
177 | ||||
178 | return (r); | |||
179 | } | |||
180 | ||||
181 | struct table * | |||
182 | table_create(struct smtpd *conf, const char *backend, const char *name, | |||
183 | const char *config) | |||
184 | { | |||
185 | struct table *t; | |||
186 | struct table_backend *tb; | |||
187 | char path[LINE_MAX2048]; | |||
188 | size_t n; | |||
189 | struct stat sb; | |||
190 | ||||
191 | if (name && table_find(conf, name)) | |||
192 | fatalx("table_create: table \"%s\" already defined", name); | |||
193 | ||||
194 | if ((tb = table_backend_lookup(backend)) == NULL((void *)0)) { | |||
195 | if ((size_t)snprintf(path, sizeof(path), PATH_LIBEXEC"/usr/local/libexec/smtpd""/table-%s", | |||
196 | backend) >= sizeof(path)) { | |||
197 | fatalx("table_create: path too long \"" | |||
198 | PATH_LIBEXEC"/usr/local/libexec/smtpd""/table-%s\"", backend); | |||
199 | } | |||
200 | if (stat(path, &sb) == 0) { | |||
201 | tb = table_backend_lookup("proc"); | |||
202 | (void)strlcpy(path, backend, sizeof(path)); | |||
203 | if (config) { | |||
204 | (void)strlcat(path, ":", sizeof(path)); | |||
205 | if (strlcat(path, config, sizeof(path)) | |||
206 | >= sizeof(path)) | |||
207 | fatalx("table_create: config file path too long"); | |||
208 | } | |||
209 | config = path; | |||
210 | } | |||
211 | } | |||
212 | ||||
213 | if (tb == NULL((void *)0)) | |||
214 | fatalx("table_create: backend \"%s\" does not exist", backend); | |||
215 | ||||
216 | t = xcalloc(1, sizeof(*t)); | |||
217 | t->t_backend = tb; | |||
218 | ||||
219 | if (config) { | |||
220 | if (strlcpy(t->t_config, config, sizeof t->t_config) | |||
221 | >= sizeof t->t_config) | |||
222 | fatalx("table_create: table config \"%s\" too large", | |||
223 | t->t_config); | |||
224 | } | |||
225 | ||||
226 | if (strcmp(tb->name, "static") != 0) | |||
227 | t->t_type = T_DYNAMIC; | |||
228 | ||||
229 | if (name == NULL((void *)0)) | |||
230 | (void)snprintf(t->t_name, sizeof(t->t_name), "<dynamic:%u>", | |||
231 | last_table_id++); | |||
232 | else { | |||
233 | n = strlcpy(t->t_name, name, sizeof(t->t_name)); | |||
234 | if (n >= sizeof(t->t_name)) | |||
235 | fatalx("table_create: table name too long"); | |||
236 | } | |||
237 | ||||
238 | dict_set(conf->sc_tables_dict, t->t_name, t); | |||
239 | ||||
240 | return (t); | |||
241 | } | |||
242 | ||||
243 | void | |||
244 | table_destroy(struct smtpd *conf, struct table *t) | |||
245 | { | |||
246 | dict_xpop(conf->sc_tables_dict, t->t_name); | |||
247 | free(t); | |||
248 | } | |||
249 | ||||
250 | int | |||
251 | table_config(struct table *t) | |||
252 | { | |||
253 | if (t->t_backend->config == NULL((void *)0)) | |||
254 | return (1); | |||
255 | return (t->t_backend->config(t)); | |||
256 | } | |||
257 | ||||
258 | void | |||
259 | table_add(struct table *t, const char *key, const char *val) | |||
260 | { | |||
261 | if (t->t_backend->add == NULL((void *)0)) | |||
262 | fatalx("table_add: cannot add to table"); | |||
263 | ||||
264 | if (t->t_backend->add(t, key, val) == 0) | |||
265 | log_warnx("warn: failed to add \"%s\" in table \"%s\"", key, t->t_name); | |||
266 | } | |||
267 | ||||
268 | void | |||
269 | table_dump(struct table *t) | |||
270 | { | |||
271 | const char *type; | |||
272 | char buf[LINE_MAX2048]; | |||
273 | ||||
274 | switch(t->t_type) { | |||
275 | case T_NONE: | |||
276 | type = "NONE"; | |||
277 | break; | |||
278 | case T_DYNAMIC: | |||
279 | type = "DYNAMIC"; | |||
280 | break; | |||
281 | case T_LIST: | |||
282 | type = "LIST"; | |||
283 | break; | |||
284 | case T_HASH: | |||
285 | type = "HASH"; | |||
286 | break; | |||
287 | default: | |||
288 | type = "???"; | |||
289 | break; | |||
290 | } | |||
291 | ||||
292 | if (t->t_config[0]) | |||
293 | snprintf(buf, sizeof(buf), " config=\"%s\"", t->t_config); | |||
294 | else | |||
295 | buf[0] = '\0'; | |||
296 | ||||
297 | log_debug("TABLE \"%s\" backend=%s type=%s%s", t->t_name, | |||
298 | t->t_backend->name, type, buf); | |||
299 | ||||
300 | if (t->t_backend->dump) | |||
301 | t->t_backend->dump(t); | |||
302 | } | |||
303 | ||||
304 | int | |||
305 | table_check_type(struct table *t, uint32_t mask) | |||
306 | { | |||
307 | return t->t_type & mask; | |||
308 | } | |||
309 | ||||
310 | int | |||
311 | table_check_service(struct table *t, uint32_t mask) | |||
312 | { | |||
313 | return t->t_backend->services & mask; | |||
314 | } | |||
315 | ||||
316 | int | |||
317 | table_check_use(struct table *t, uint32_t tmask, uint32_t smask) | |||
318 | { | |||
319 | return table_check_type(t, tmask) && table_check_service(t, smask); | |||
320 | } | |||
321 | ||||
322 | int | |||
323 | table_open(struct table *t) | |||
324 | { | |||
325 | if (t->t_backend->open == NULL((void *)0)) | |||
326 | return (1); | |||
327 | return (t->t_backend->open(t)); | |||
328 | } | |||
329 | ||||
330 | void | |||
331 | table_close(struct table *t) | |||
332 | { | |||
333 | if (t->t_backend->close) | |||
334 | t->t_backend->close(t); | |||
335 | } | |||
336 | ||||
337 | int | |||
338 | table_update(struct table *t) | |||
339 | { | |||
340 | if (t->t_backend->update == NULL((void *)0)) | |||
341 | return (1); | |||
342 | return (t->t_backend->update(t)); | |||
343 | } | |||
344 | ||||
345 | ||||
346 | /* | |||
347 | * quick reminder: | |||
348 | * in *_match() s1 comes from session, s2 comes from table | |||
349 | */ | |||
350 | ||||
351 | int | |||
352 | table_domain_match(const char *s1, const char *s2) | |||
353 | { | |||
354 | return hostname_match(s1, s2); | |||
355 | } | |||
356 | ||||
357 | int | |||
358 | table_mailaddr_match(const char *s1, const char *s2) | |||
359 | { | |||
360 | struct mailaddr m1; | |||
361 | struct mailaddr m2; | |||
362 | ||||
363 | if (!text_to_mailaddr(&m1, s1)) | |||
364 | return 0; | |||
365 | if (!text_to_mailaddr(&m2, s2)) | |||
366 | return 0; | |||
367 | return mailaddr_match(&m1, &m2); | |||
368 | } | |||
369 | ||||
370 | static int table_match_mask(struct sockaddr_storage *, struct netaddr *); | |||
371 | static int table_inet4_match(struct sockaddr_in *, struct netaddr *); | |||
372 | static int table_inet6_match(struct sockaddr_in6 *, struct netaddr *); | |||
373 | ||||
374 | int | |||
375 | table_netaddr_match(const char *s1, const char *s2) | |||
376 | { | |||
377 | struct netaddr n1; | |||
378 | struct netaddr n2; | |||
379 | ||||
380 | if (strcasecmp(s1, s2) == 0) | |||
381 | return 1; | |||
382 | if (!text_to_netaddr(&n1, s1)) | |||
383 | return 0; | |||
384 | if (!text_to_netaddr(&n2, s2)) | |||
385 | return 0; | |||
386 | if (n1.ss.ss_family != n2.ss.ss_family) | |||
387 | return 0; | |||
388 | if (n1.ss.ss_len != n2.ss.ss_len) | |||
389 | return 0; | |||
390 | return table_match_mask(&n1.ss, &n2); | |||
391 | } | |||
392 | ||||
393 | static int | |||
394 | table_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask) | |||
395 | { | |||
396 | if (ss->ss_family == AF_INET2) | |||
397 | return table_inet4_match((struct sockaddr_in *)ss, ssmask); | |||
398 | ||||
399 | if (ss->ss_family == AF_INET624) | |||
400 | return table_inet6_match((struct sockaddr_in6 *)ss, ssmask); | |||
401 | ||||
402 | return (0); | |||
403 | } | |||
404 | ||||
405 | static int | |||
406 | table_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask) | |||
407 | { | |||
408 | in_addr_t mask; | |||
409 | int i; | |||
410 | ||||
411 | /* a.b.c.d/8 -> htonl(0xff000000) */ | |||
412 | mask = 0; | |||
413 | for (i = 0; i < ssmask->bits; ++i) | |||
414 | mask = (mask >> 1) | 0x80000000; | |||
415 | mask = htonl(mask)(__uint32_t)(__builtin_constant_p(mask) ? (__uint32_t)(((__uint32_t )(mask) & 0xff) << 24 | ((__uint32_t)(mask) & 0xff00 ) << 8 | ((__uint32_t)(mask) & 0xff0000) >> 8 | ((__uint32_t)(mask) & 0xff000000) >> 24) : __swap32md (mask)); | |||
416 | ||||
417 | /* (addr & mask) == (net & mask) */ | |||
418 | if ((ss->sin_addr.s_addr & mask) == | |||
419 | (((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask)) | |||
420 | return 1; | |||
421 | ||||
422 | return 0; | |||
423 | } | |||
424 | ||||
425 | static int | |||
426 | table_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask) | |||
427 | { | |||
428 | struct in6_addr *in; | |||
429 | struct in6_addr *inmask; | |||
430 | struct in6_addr mask; | |||
431 | int i; | |||
432 | ||||
433 | memset(&mask, 0, sizeof(mask)); | |||
434 | for (i = 0; i < ssmask->bits / 8; i++) | |||
435 | mask.s6_addr__u6_addr.__u6_addr8[i] = 0xff; | |||
436 | i = ssmask->bits % 8; | |||
437 | if (i) | |||
438 | mask.s6_addr__u6_addr.__u6_addr8[ssmask->bits / 8] = 0xff00 >> i; | |||
439 | ||||
440 | in = &ss->sin6_addr; | |||
441 | inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr; | |||
442 | ||||
443 | for (i = 0; i < 16; i++) { | |||
444 | if ((in->s6_addr__u6_addr.__u6_addr8[i] & mask.s6_addr__u6_addr.__u6_addr8[i]) != | |||
445 | (inmask->s6_addr__u6_addr.__u6_addr8[i] & mask.s6_addr__u6_addr.__u6_addr8[i])) | |||
446 | return (0); | |||
447 | } | |||
448 | ||||
449 | return (1); | |||
450 | } | |||
451 | ||||
452 | int | |||
453 | table_regex_match(const char *string, const char *pattern) | |||
454 | { | |||
455 | regex_t preg; | |||
456 | int cflags = REG_EXTENDED0001|REG_NOSUB0004; | |||
457 | int ret; | |||
458 | ||||
459 | if (strncmp(pattern, "(?i)", 4) == 0) { | |||
460 | cflags |= REG_ICASE0002; | |||
461 | pattern += 4; | |||
462 | } | |||
463 | ||||
464 | if (regcomp(&preg, pattern, cflags) != 0) | |||
465 | return (0); | |||
466 | ||||
467 | ret = regexec(&preg, string, 0, NULL((void *)0), 0); | |||
468 | ||||
469 | regfree(&preg); | |||
470 | ||||
471 | if (ret != 0) | |||
472 | return (0); | |||
473 | ||||
474 | return (1); | |||
475 | } | |||
476 | ||||
477 | void | |||
478 | table_dump_all(struct smtpd *conf) | |||
479 | { | |||
480 | struct table *t; | |||
481 | void *iter; | |||
482 | ||||
483 | iter = NULL((void *)0); | |||
484 | while (dict_iter(conf->sc_tables_dict, &iter, NULL((void *)0), (void **)&t)) | |||
485 | table_dump(t); | |||
486 | } | |||
487 | ||||
488 | void | |||
489 | table_open_all(struct smtpd *conf) | |||
490 | { | |||
491 | struct table *t; | |||
492 | void *iter; | |||
493 | ||||
494 | iter = NULL((void *)0); | |||
495 | while (dict_iter(conf->sc_tables_dict, &iter, NULL((void *)0), (void **)&t)) | |||
496 | if (!table_open(t)) | |||
497 | fatalx("failed to open table %s", t->t_name); | |||
498 | } | |||
499 | ||||
500 | void | |||
501 | table_close_all(struct smtpd *conf) | |||
502 | { | |||
503 | struct table *t; | |||
504 | void *iter; | |||
505 | ||||
506 | iter = NULL((void *)0); | |||
507 | while (dict_iter(conf->sc_tables_dict, &iter, NULL((void *)0), (void **)&t)) | |||
508 | table_close(t); | |||
509 | } | |||
510 | ||||
511 | static int | |||
512 | table_parse_lookup(enum table_service service, const char *key, | |||
513 | const char *line, union lookup *lk) | |||
514 | { | |||
515 | char buffer[LINE_MAX2048], *p; | |||
516 | size_t len; | |||
517 | ||||
518 | len = strlen(line); | |||
519 | ||||
520 | switch (service) { | |||
521 | case K_ALIAS: | |||
522 | lk->expand = calloc(1, sizeof(*lk->expand)); | |||
523 | if (lk->expand == NULL((void *)0)) | |||
524 | return (-1); | |||
525 | if (!expand_line(lk->expand, line, 1)) { | |||
526 | expand_free(lk->expand); | |||
527 | return (-1); | |||
528 | } | |||
529 | return (1); | |||
530 | ||||
531 | case K_DOMAIN: | |||
532 | if (strlcpy(lk->domain.name, line, sizeof(lk->domain.name)) | |||
533 | >= sizeof(lk->domain.name)) | |||
534 | return (-1); | |||
535 | return (1); | |||
536 | ||||
537 | case K_CREDENTIALS: | |||
538 | ||||
539 | /* credentials are stored as user:password */ | |||
540 | if (len < 3) | |||
541 | return (-1); | |||
542 | ||||
543 | /* too big to fit in a smtp session line */ | |||
544 | if (len >= LINE_MAX2048) | |||
545 | return (-1); | |||
546 | ||||
547 | p = strchr(line, ':'); | |||
548 | if (p == NULL((void *)0)) { | |||
549 | if (strlcpy(lk->creds.username, key, sizeof (lk->creds.username)) | |||
550 | >= sizeof (lk->creds.username)) | |||
551 | return (-1); | |||
552 | if (strlcpy(lk->creds.password, line, sizeof(lk->creds.password)) | |||
553 | >= sizeof(lk->creds.password)) | |||
554 | return (-1); | |||
555 | return (1); | |||
556 | } | |||
557 | ||||
558 | if (p == line || p == line + len - 1) | |||
559 | return (-1); | |||
560 | ||||
561 | memmove(lk->creds.username, line, p - line); | |||
562 | lk->creds.username[p - line] = '\0'; | |||
563 | ||||
564 | if (strlcpy(lk->creds.password, p+1, sizeof(lk->creds.password)) | |||
565 | >= sizeof(lk->creds.password)) | |||
566 | return (-1); | |||
567 | ||||
568 | return (1); | |||
569 | ||||
570 | case K_NETADDR: | |||
571 | if (!text_to_netaddr(&lk->netaddr, line)) | |||
572 | return (-1); | |||
573 | return (1); | |||
574 | ||||
575 | case K_USERINFO: | |||
576 | if (!bsnprintf(buffer, sizeof(buffer), "%s:%s", key, line)) | |||
577 | return (-1); | |||
578 | if (!text_to_userinfo(&lk->userinfo, buffer)) | |||
579 | return (-1); | |||
580 | return (1); | |||
581 | ||||
582 | case K_SOURCE: | |||
583 | if (parse_sockaddr((struct sockaddr *)&lk->source.addr, | |||
584 | PF_UNSPEC0, line) == -1) | |||
585 | return (-1); | |||
586 | return (1); | |||
587 | ||||
588 | case K_MAILADDR: | |||
589 | if (!text_to_mailaddr(&lk->mailaddr, line)) | |||
590 | return (-1); | |||
591 | return (1); | |||
592 | ||||
593 | case K_MAILADDRMAP: | |||
594 | lk->maddrmap = calloc(1, sizeof(*lk->maddrmap)); | |||
595 | if (lk->maddrmap == NULL((void *)0)) | |||
596 | return (-1); | |||
597 | maddrmap_init(lk->maddrmap); | |||
598 | if (!mailaddr_line(lk->maddrmap, line)) { | |||
599 | maddrmap_free(lk->maddrmap); | |||
600 | return (-1); | |||
601 | } | |||
602 | return (1); | |||
603 | ||||
604 | case K_ADDRNAME: | |||
605 | if (parse_sockaddr((struct sockaddr *)&lk->addrname.addr, | |||
606 | PF_UNSPEC0, key) == -1) | |||
607 | return (-1); | |||
608 | if (strlcpy(lk->addrname.name, line, sizeof(lk->addrname.name)) | |||
609 | >= sizeof(lk->addrname.name)) | |||
610 | return (-1); | |||
611 | return (1); | |||
612 | ||||
613 | case K_RELAYHOST: | |||
614 | if (strlcpy(lk->relayhost, line, sizeof(lk->relayhost)) | |||
615 | >= sizeof(lk->relayhost)) | |||
616 | return (-1); | |||
617 | return (1); | |||
618 | ||||
619 | default: | |||
620 | return (-1); | |||
621 | } | |||
622 | } | |||
623 | ||||
624 | static int | |||
625 | parse_sockaddr(struct sockaddr *sa, int family, const char *str) | |||
626 | { | |||
627 | struct in_addr ina; | |||
628 | struct in6_addr in6a; | |||
629 | struct sockaddr_in *sin; | |||
630 | struct sockaddr_in6 *sin6; | |||
631 | char *cp; | |||
632 | char addr[NI_MAXHOST256]; | |||
633 | const char *errstr; | |||
634 | ||||
635 | switch (family) { | |||
636 | case PF_UNSPEC0: | |||
637 | if (parse_sockaddr(sa, PF_INET2, str) == 0) | |||
638 | return (0); | |||
639 | return parse_sockaddr(sa, PF_INET624, str); | |||
640 | ||||
641 | case PF_INET2: | |||
642 | if (inet_pton(PF_INET2, str, &ina) != 1) | |||
643 | return (-1); | |||
644 | ||||
645 | sin = (struct sockaddr_in *)sa; | |||
646 | memset(sin, 0, sizeof *sin); | |||
647 | sin->sin_len = sizeof(struct sockaddr_in); | |||
648 | sin->sin_family = PF_INET2; | |||
649 | sin->sin_addr.s_addr = ina.s_addr; | |||
650 | return (0); | |||
651 | ||||
652 | case PF_INET624: | |||
653 | if (*str == '[') | |||
| ||||
654 | str++; | |||
655 | if (!strncasecmp("ipv6:", str, 5)) | |||
656 | str += 5; | |||
657 | ||||
658 | if (strlcpy(addr, str, sizeof(addr)) >= sizeof(addr)) | |||
659 | return (-1); | |||
660 | if ((cp = strchr(addr, ']')) != NULL((void *)0)) | |||
661 | *cp = '\0'; | |||
662 | if ((cp = strchr(addr, SCOPE_DELIMITER'%')) != NULL((void *)0)) | |||
663 | *cp++ = '\0'; | |||
664 | ||||
665 | if (inet_pton(PF_INET624, addr, &in6a) != 1) | |||
666 | return (-1); | |||
667 | ||||
668 | sin6 = (struct sockaddr_in6 *)sa; | |||
669 | memset(sin6, 0, sizeof *sin6); | |||
670 | sin6->sin6_len = sizeof(struct sockaddr_in6); | |||
671 | sin6->sin6_family = PF_INET624; | |||
672 | sin6->sin6_addr = in6a; | |||
673 | ||||
674 | if (cp == NULL((void *)0)) | |||
675 | return (0); | |||
676 | ||||
677 | if (IN6_IS_ADDR_LINKLOCAL(&in6a)(((&in6a)->__u6_addr.__u6_addr8[0] == 0xfe) && (((&in6a)->__u6_addr.__u6_addr8[1] & 0xc0) == 0x80 )) || | |||
678 | IN6_IS_ADDR_MC_LINKLOCAL(&in6a)(((&in6a)->__u6_addr.__u6_addr8[0] == 0xff) && (((&in6a)->__u6_addr.__u6_addr8[1] & 0x0f) == 0x02 )) || | |||
679 | IN6_IS_ADDR_MC_INTFACELOCAL(&in6a)(((&in6a)->__u6_addr.__u6_addr8[0] == 0xff) && (((&in6a)->__u6_addr.__u6_addr8[1] & 0x0f) == 0x01 ))) | |||
680 | if ((sin6->sin6_scope_id = if_nametoindex(cp))) | |||
681 | return (0); | |||
682 | ||||
683 | sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX0xffffffffU, &errstr); | |||
684 | if (errstr) | |||
685 | return (-1); | |||
686 | return (0); | |||
687 | ||||
688 | default: | |||
689 | break; | |||
690 | } | |||
691 | ||||
692 | return (-1); | |||
693 | } |