1 |
|
|
/* $OpenBSD: table.c,v 1.24 2017/05/01 09:29:07 gilles 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/types.h> |
21 |
|
|
#include <sys/queue.h> |
22 |
|
|
#include <sys/tree.h> |
23 |
|
|
#include <sys/socket.h> |
24 |
|
|
#include <sys/stat.h> |
25 |
|
|
|
26 |
|
|
#include <netinet/in.h> |
27 |
|
|
#include <arpa/inet.h> |
28 |
|
|
#include <net/if.h> |
29 |
|
|
|
30 |
|
|
#include <ctype.h> |
31 |
|
|
#include <err.h> |
32 |
|
|
#include <errno.h> |
33 |
|
|
#include <event.h> |
34 |
|
|
#include <imsg.h> |
35 |
|
|
#include <stdio.h> |
36 |
|
|
#include <stdlib.h> |
37 |
|
|
#include <netdb.h> |
38 |
|
|
#include <limits.h> |
39 |
|
|
#include <string.h> |
40 |
|
|
#include <unistd.h> |
41 |
|
|
|
42 |
|
|
#include "smtpd.h" |
43 |
|
|
#include "log.h" |
44 |
|
|
|
45 |
|
|
struct table_backend *table_backend_lookup(const char *); |
46 |
|
|
|
47 |
|
|
extern struct table_backend table_backend_static; |
48 |
|
|
extern struct table_backend table_backend_db; |
49 |
|
|
extern struct table_backend table_backend_getpwnam; |
50 |
|
|
extern struct table_backend table_backend_proc; |
51 |
|
|
|
52 |
|
|
static const char * table_service_name(enum table_service); |
53 |
|
|
static const char * table_backend_name(struct table_backend *); |
54 |
|
|
static const char * table_dump_lookup(enum table_service, union lookup *); |
55 |
|
|
static int parse_sockaddr(struct sockaddr *, int, const char *); |
56 |
|
|
|
57 |
|
|
static unsigned int last_table_id = 0; |
58 |
|
|
|
59 |
|
|
struct table_backend * |
60 |
|
|
table_backend_lookup(const char *backend) |
61 |
|
|
{ |
62 |
|
|
if (!strcmp(backend, "static") || !strcmp(backend, "file")) |
63 |
|
|
return &table_backend_static; |
64 |
|
|
if (!strcmp(backend, "db")) |
65 |
|
|
return &table_backend_db; |
66 |
|
|
if (!strcmp(backend, "getpwnam")) |
67 |
|
|
return &table_backend_getpwnam; |
68 |
|
|
if (!strcmp(backend, "proc")) |
69 |
|
|
return &table_backend_proc; |
70 |
|
|
return NULL; |
71 |
|
|
} |
72 |
|
|
|
73 |
|
|
static const char * |
74 |
|
|
table_backend_name(struct table_backend *backend) |
75 |
|
|
{ |
76 |
|
|
if (backend == &table_backend_static) |
77 |
|
|
return "static"; |
78 |
|
|
if (backend == &table_backend_db) |
79 |
|
|
return "db"; |
80 |
|
|
if (backend == &table_backend_getpwnam) |
81 |
|
|
return "getpwnam"; |
82 |
|
|
if (backend == &table_backend_proc) |
83 |
|
|
return "proc"; |
84 |
|
|
return "???"; |
85 |
|
|
} |
86 |
|
|
|
87 |
|
|
static const char * |
88 |
|
|
table_service_name(enum table_service s) |
89 |
|
|
{ |
90 |
|
|
switch (s) { |
91 |
|
|
case K_NONE: return "NONE"; |
92 |
|
|
case K_ALIAS: return "ALIAS"; |
93 |
|
|
case K_DOMAIN: return "DOMAIN"; |
94 |
|
|
case K_CREDENTIALS: return "CREDENTIALS"; |
95 |
|
|
case K_NETADDR: return "NETADDR"; |
96 |
|
|
case K_USERINFO: return "USERINFO"; |
97 |
|
|
case K_SOURCE: return "SOURCE"; |
98 |
|
|
case K_MAILADDR: return "MAILADDR"; |
99 |
|
|
case K_ADDRNAME: return "ADDRNAME"; |
100 |
|
|
case K_MAILADDRMAP: return "MAILADDRMAP"; |
101 |
|
|
default: return "???"; |
102 |
|
|
} |
103 |
|
|
} |
104 |
|
|
|
105 |
|
|
struct table * |
106 |
|
|
table_find(const char *name, const char *tag) |
107 |
|
|
{ |
108 |
|
|
char buf[LINE_MAX]; |
109 |
|
|
|
110 |
|
|
if (tag == NULL) |
111 |
|
|
return dict_get(env->sc_tables_dict, name); |
112 |
|
|
|
113 |
|
|
if ((size_t)snprintf(buf, sizeof(buf), "%s#%s", name, tag) >= sizeof(buf)) { |
114 |
|
|
log_warnx("warn: table name too long: %s#%s", name, tag); |
115 |
|
|
return (NULL); |
116 |
|
|
} |
117 |
|
|
|
118 |
|
|
return dict_get(env->sc_tables_dict, buf); |
119 |
|
|
} |
120 |
|
|
|
121 |
|
|
int |
122 |
|
|
table_lookup(struct table *table, struct dict *params, const char *key, enum table_service kind, |
123 |
|
|
union lookup *lk) |
124 |
|
|
{ |
125 |
|
|
int r; |
126 |
|
|
char lkey[1024]; |
127 |
|
|
|
128 |
|
|
if (table->t_backend->lookup == NULL) |
129 |
|
|
return (-1); |
130 |
|
|
|
131 |
|
|
if (!lowercase(lkey, key, sizeof lkey)) { |
132 |
|
|
log_warnx("warn: lookup key too long: %s", key); |
133 |
|
|
return -1; |
134 |
|
|
} |
135 |
|
|
|
136 |
|
|
r = table->t_backend->lookup(table->t_handle, params, lkey, kind, lk); |
137 |
|
|
|
138 |
|
|
if (r == 1) |
139 |
|
|
log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s", |
140 |
|
|
lk ? "lookup" : "check", |
141 |
|
|
lkey, |
142 |
|
|
table_service_name(kind), |
143 |
|
|
table_backend_name(table->t_backend), |
144 |
|
|
table->t_name, |
145 |
|
|
lk ? "\"" : "", |
146 |
|
|
(lk) ? table_dump_lookup(kind, lk): "found", |
147 |
|
|
lk ? "\"" : ""); |
148 |
|
|
else |
149 |
|
|
log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %d", |
150 |
|
|
lk ? "lookup" : "check", |
151 |
|
|
lkey, |
152 |
|
|
table_service_name(kind), |
153 |
|
|
table_backend_name(table->t_backend), |
154 |
|
|
table->t_name, |
155 |
|
|
r); |
156 |
|
|
|
157 |
|
|
return (r); |
158 |
|
|
} |
159 |
|
|
|
160 |
|
|
int |
161 |
|
|
table_fetch(struct table *table, struct dict *params, enum table_service kind, union lookup *lk) |
162 |
|
|
{ |
163 |
|
|
int r; |
164 |
|
|
|
165 |
|
|
if (table->t_backend->fetch == NULL) |
166 |
|
|
return (-1); |
167 |
|
|
|
168 |
|
|
r = table->t_backend->fetch(table->t_handle, params, kind, lk); |
169 |
|
|
|
170 |
|
|
if (r == 1) |
171 |
|
|
log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> %s%s%s", |
172 |
|
|
table_service_name(kind), |
173 |
|
|
table_backend_name(table->t_backend), |
174 |
|
|
table->t_name, |
175 |
|
|
lk ? "\"" : "", |
176 |
|
|
(lk) ? table_dump_lookup(kind, lk): "found", |
177 |
|
|
lk ? "\"" : ""); |
178 |
|
|
else |
179 |
|
|
log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> %d", |
180 |
|
|
table_service_name(kind), |
181 |
|
|
table_backend_name(table->t_backend), |
182 |
|
|
table->t_name, |
183 |
|
|
r); |
184 |
|
|
|
185 |
|
|
return (r); |
186 |
|
|
} |
187 |
|
|
|
188 |
|
|
struct table * |
189 |
|
|
table_create(const char *backend, const char *name, const char *tag, |
190 |
|
|
const char *config) |
191 |
|
|
{ |
192 |
|
|
struct table *t; |
193 |
|
|
struct table_backend *tb; |
194 |
|
|
char buf[LINE_MAX]; |
195 |
|
|
char path[LINE_MAX]; |
196 |
|
|
size_t n; |
197 |
|
|
struct stat sb; |
198 |
|
|
|
199 |
|
|
if (name && tag) { |
200 |
|
|
if ((size_t)snprintf(buf, sizeof(buf), "%s#%s", name, tag) >= |
201 |
|
|
sizeof(buf)) |
202 |
|
|
fatalx("table_create: name too long \"%s#%s\"", |
203 |
|
|
name, tag); |
204 |
|
|
name = buf; |
205 |
|
|
} |
206 |
|
|
|
207 |
|
|
if (name && table_find(name, NULL)) |
208 |
|
|
fatalx("table_create: table \"%s\" already defined", name); |
209 |
|
|
|
210 |
|
|
if ((tb = table_backend_lookup(backend)) == NULL) { |
211 |
|
|
if ((size_t)snprintf(path, sizeof(path), PATH_LIBEXEC"/table-%s", |
212 |
|
|
backend) >= sizeof(path)) { |
213 |
|
|
fatalx("table_create: path too long \"" |
214 |
|
|
PATH_LIBEXEC"/table-%s\"", backend); |
215 |
|
|
} |
216 |
|
|
if (stat(path, &sb) == 0) { |
217 |
|
|
tb = table_backend_lookup("proc"); |
218 |
|
|
(void)strlcpy(path, backend, sizeof(path)); |
219 |
|
|
if (config) { |
220 |
|
|
(void)strlcat(path, ":", sizeof(path)); |
221 |
|
|
if (strlcat(path, config, sizeof(path)) |
222 |
|
|
>= sizeof(path)) |
223 |
|
|
fatalx("table_create: config file path too long"); |
224 |
|
|
} |
225 |
|
|
config = path; |
226 |
|
|
} |
227 |
|
|
} |
228 |
|
|
|
229 |
|
|
if (tb == NULL) |
230 |
|
|
fatalx("table_create: backend \"%s\" does not exist", backend); |
231 |
|
|
|
232 |
|
|
t = xcalloc(1, sizeof(*t), "table_create"); |
233 |
|
|
t->t_backend = tb; |
234 |
|
|
|
235 |
|
|
/* XXX */ |
236 |
|
|
/* |
237 |
|
|
* until people forget about it, "file" really means "static" |
238 |
|
|
*/ |
239 |
|
|
if (!strcmp(backend, "file")) |
240 |
|
|
backend = "static"; |
241 |
|
|
|
242 |
|
|
if (config) { |
243 |
|
|
if (strlcpy(t->t_config, config, sizeof t->t_config) |
244 |
|
|
>= sizeof t->t_config) |
245 |
|
|
fatalx("table_create: table config \"%s\" too large", |
246 |
|
|
t->t_config); |
247 |
|
|
} |
248 |
|
|
|
249 |
|
|
if (strcmp(backend, "static") != 0) |
250 |
|
|
t->t_type = T_DYNAMIC; |
251 |
|
|
|
252 |
|
|
if (name == NULL) |
253 |
|
|
(void)snprintf(t->t_name, sizeof(t->t_name), "<dynamic:%u>", |
254 |
|
|
last_table_id++); |
255 |
|
|
else { |
256 |
|
|
n = strlcpy(t->t_name, name, sizeof(t->t_name)); |
257 |
|
|
if (n >= sizeof(t->t_name)) |
258 |
|
|
fatalx("table_create: table name too long"); |
259 |
|
|
} |
260 |
|
|
|
261 |
|
|
dict_init(&t->t_dict); |
262 |
|
|
dict_set(env->sc_tables_dict, t->t_name, t); |
263 |
|
|
|
264 |
|
|
return (t); |
265 |
|
|
} |
266 |
|
|
|
267 |
|
|
void |
268 |
|
|
table_destroy(struct table *t) |
269 |
|
|
{ |
270 |
|
|
void *p = NULL; |
271 |
|
|
|
272 |
|
|
while (dict_poproot(&t->t_dict, (void **)&p)) |
273 |
|
|
free(p); |
274 |
|
|
|
275 |
|
|
dict_xpop(env->sc_tables_dict, t->t_name); |
276 |
|
|
free(t); |
277 |
|
|
} |
278 |
|
|
|
279 |
|
|
int |
280 |
|
|
table_config(struct table *t) |
281 |
|
|
{ |
282 |
|
|
if (t->t_backend->config == NULL) |
283 |
|
|
return (1); |
284 |
|
|
return (t->t_backend->config(t)); |
285 |
|
|
} |
286 |
|
|
|
287 |
|
|
void |
288 |
|
|
table_add(struct table *t, const char *key, const char *val) |
289 |
|
|
{ |
290 |
|
|
char lkey[1024], *old; |
291 |
|
|
|
292 |
|
|
if (t->t_type & T_DYNAMIC) |
293 |
|
|
fatalx("table_add: cannot add to table"); |
294 |
|
|
|
295 |
|
|
if (!lowercase(lkey, key, sizeof lkey)) { |
296 |
|
|
log_warnx("warn: lookup key too long: %s", key); |
297 |
|
|
return; |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
old = dict_set(&t->t_dict, lkey, val ? xstrdup(val, "table_add") : NULL); |
301 |
|
|
if (old) { |
302 |
|
|
log_warnx("warn: duplicate key \"%s\" in static table \"%s\"", |
303 |
|
|
lkey, t->t_name); |
304 |
|
|
free(old); |
305 |
|
|
} |
306 |
|
|
} |
307 |
|
|
|
308 |
|
|
int |
309 |
|
|
table_check_type(struct table *t, uint32_t mask) |
310 |
|
|
{ |
311 |
|
|
return t->t_type & mask; |
312 |
|
|
} |
313 |
|
|
|
314 |
|
|
int |
315 |
|
|
table_check_service(struct table *t, uint32_t mask) |
316 |
|
|
{ |
317 |
|
|
return t->t_backend->services & mask; |
318 |
|
|
} |
319 |
|
|
|
320 |
|
|
int |
321 |
|
|
table_check_use(struct table *t, uint32_t tmask, uint32_t smask) |
322 |
|
|
{ |
323 |
|
|
return table_check_type(t, tmask) && table_check_service(t, smask); |
324 |
|
|
} |
325 |
|
|
|
326 |
|
|
int |
327 |
|
|
table_open(struct table *t) |
328 |
|
|
{ |
329 |
|
|
t->t_handle = NULL; |
330 |
|
|
if (t->t_backend->open == NULL) |
331 |
|
|
return (1); |
332 |
|
|
t->t_handle = t->t_backend->open(t); |
333 |
|
|
if (t->t_handle == NULL) |
334 |
|
|
return (0); |
335 |
|
|
return (1); |
336 |
|
|
} |
337 |
|
|
|
338 |
|
|
void |
339 |
|
|
table_close(struct table *t) |
340 |
|
|
{ |
341 |
|
|
if (t->t_backend->close) |
342 |
|
|
t->t_backend->close(t->t_handle); |
343 |
|
|
} |
344 |
|
|
|
345 |
|
|
int |
346 |
|
|
table_update(struct table *t) |
347 |
|
|
{ |
348 |
|
|
if (t->t_backend->update == NULL) |
349 |
|
|
return (1); |
350 |
|
|
return (t->t_backend->update(t)); |
351 |
|
|
} |
352 |
|
|
|
353 |
|
|
|
354 |
|
|
/* |
355 |
|
|
* quick reminder: |
356 |
|
|
* in *_match() s1 comes from session, s2 comes from table |
357 |
|
|
*/ |
358 |
|
|
|
359 |
|
|
int |
360 |
|
|
table_domain_match(const char *s1, const char *s2) |
361 |
|
|
{ |
362 |
|
|
return hostname_match(s1, s2); |
363 |
|
|
} |
364 |
|
|
|
365 |
|
|
int |
366 |
|
|
table_mailaddr_match(const char *s1, const char *s2) |
367 |
|
|
{ |
368 |
|
|
struct mailaddr m1; |
369 |
|
|
struct mailaddr m2; |
370 |
|
|
|
371 |
|
|
if (!text_to_mailaddr(&m1, s1)) |
372 |
|
|
return 0; |
373 |
|
|
if (!text_to_mailaddr(&m2, s2)) |
374 |
|
|
return 0; |
375 |
|
|
return mailaddr_match(&m1, &m2); |
376 |
|
|
} |
377 |
|
|
|
378 |
|
|
static int table_match_mask(struct sockaddr_storage *, struct netaddr *); |
379 |
|
|
static int table_inet4_match(struct sockaddr_in *, struct netaddr *); |
380 |
|
|
static int table_inet6_match(struct sockaddr_in6 *, struct netaddr *); |
381 |
|
|
|
382 |
|
|
int |
383 |
|
|
table_netaddr_match(const char *s1, const char *s2) |
384 |
|
|
{ |
385 |
|
|
struct netaddr n1; |
386 |
|
|
struct netaddr n2; |
387 |
|
|
|
388 |
|
|
if (strcasecmp(s1, s2) == 0) |
389 |
|
|
return 1; |
390 |
|
|
if (!text_to_netaddr(&n1, s1)) |
391 |
|
|
return 0; |
392 |
|
|
if (!text_to_netaddr(&n2, s2)) |
393 |
|
|
return 0; |
394 |
|
|
if (n1.ss.ss_family != n2.ss.ss_family) |
395 |
|
|
return 0; |
396 |
|
|
if (n1.ss.ss_len != n2.ss.ss_len) |
397 |
|
|
return 0; |
398 |
|
|
return table_match_mask(&n1.ss, &n2); |
399 |
|
|
} |
400 |
|
|
|
401 |
|
|
static int |
402 |
|
|
table_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask) |
403 |
|
|
{ |
404 |
|
|
if (ss->ss_family == AF_INET) |
405 |
|
|
return table_inet4_match((struct sockaddr_in *)ss, ssmask); |
406 |
|
|
|
407 |
|
|
if (ss->ss_family == AF_INET6) |
408 |
|
|
return table_inet6_match((struct sockaddr_in6 *)ss, ssmask); |
409 |
|
|
|
410 |
|
|
return (0); |
411 |
|
|
} |
412 |
|
|
|
413 |
|
|
static int |
414 |
|
|
table_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask) |
415 |
|
|
{ |
416 |
|
|
in_addr_t mask; |
417 |
|
|
int i; |
418 |
|
|
|
419 |
|
|
/* a.b.c.d/8 -> htonl(0xff000000) */ |
420 |
|
|
mask = 0; |
421 |
|
|
for (i = 0; i < ssmask->bits; ++i) |
422 |
|
|
mask = (mask >> 1) | 0x80000000; |
423 |
|
|
mask = htonl(mask); |
424 |
|
|
|
425 |
|
|
/* (addr & mask) == (net & mask) */ |
426 |
|
|
if ((ss->sin_addr.s_addr & mask) == |
427 |
|
|
(((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask)) |
428 |
|
|
return 1; |
429 |
|
|
|
430 |
|
|
return 0; |
431 |
|
|
} |
432 |
|
|
|
433 |
|
|
static int |
434 |
|
|
table_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask) |
435 |
|
|
{ |
436 |
|
|
struct in6_addr *in; |
437 |
|
|
struct in6_addr *inmask; |
438 |
|
|
struct in6_addr mask; |
439 |
|
|
int i; |
440 |
|
|
|
441 |
|
|
memset(&mask, 0, sizeof(mask)); |
442 |
|
|
for (i = 0; i < ssmask->bits / 8; i++) |
443 |
|
|
mask.s6_addr[i] = 0xff; |
444 |
|
|
i = ssmask->bits % 8; |
445 |
|
|
if (i) |
446 |
|
|
mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i; |
447 |
|
|
|
448 |
|
|
in = &ss->sin6_addr; |
449 |
|
|
inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr; |
450 |
|
|
|
451 |
|
|
for (i = 0; i < 16; i++) { |
452 |
|
|
if ((in->s6_addr[i] & mask.s6_addr[i]) != |
453 |
|
|
(inmask->s6_addr[i] & mask.s6_addr[i])) |
454 |
|
|
return (0); |
455 |
|
|
} |
456 |
|
|
|
457 |
|
|
return (1); |
458 |
|
|
} |
459 |
|
|
|
460 |
|
|
void |
461 |
|
|
table_dump_all(void) |
462 |
|
|
{ |
463 |
|
|
struct table *t; |
464 |
|
|
void *iter, *i2; |
465 |
|
|
const char *key, *sep; |
466 |
|
|
char *value; |
467 |
|
|
char buf[1024]; |
468 |
|
|
|
469 |
|
|
iter = NULL; |
470 |
|
|
while (dict_iter(env->sc_tables_dict, &iter, NULL, (void **)&t)) { |
471 |
|
|
i2 = NULL; |
472 |
|
|
sep = ""; |
473 |
|
|
buf[0] = '\0'; |
474 |
|
|
if (t->t_type & T_DYNAMIC) { |
475 |
|
|
(void)strlcat(buf, "DYNAMIC", sizeof(buf)); |
476 |
|
|
sep = ","; |
477 |
|
|
} |
478 |
|
|
if (t->t_type & T_LIST) { |
479 |
|
|
(void)strlcat(buf, sep, sizeof(buf)); |
480 |
|
|
(void)strlcat(buf, "LIST", sizeof(buf)); |
481 |
|
|
sep = ","; |
482 |
|
|
} |
483 |
|
|
if (t->t_type & T_HASH) { |
484 |
|
|
(void)strlcat(buf, sep, sizeof(buf)); |
485 |
|
|
(void)strlcat(buf, "HASH", sizeof(buf)); |
486 |
|
|
sep = ","; |
487 |
|
|
} |
488 |
|
|
log_debug("TABLE \"%s\" type=%s config=\"%s\"", |
489 |
|
|
t->t_name, buf, t->t_config); |
490 |
|
|
while(dict_iter(&t->t_dict, &i2, &key, (void**)&value)) { |
491 |
|
|
if (value) |
492 |
|
|
log_debug(" \"%s\" -> \"%s\"", key, value); |
493 |
|
|
else |
494 |
|
|
log_debug(" \"%s\"", key); |
495 |
|
|
} |
496 |
|
|
} |
497 |
|
|
} |
498 |
|
|
|
499 |
|
|
void |
500 |
|
|
table_open_all(void) |
501 |
|
|
{ |
502 |
|
|
struct table *t; |
503 |
|
|
void *iter; |
504 |
|
|
|
505 |
|
|
iter = NULL; |
506 |
|
|
while (dict_iter(env->sc_tables_dict, &iter, NULL, (void **)&t)) |
507 |
|
|
if (!table_open(t)) |
508 |
|
|
fatalx("failed to open table %s", t->t_name); |
509 |
|
|
} |
510 |
|
|
|
511 |
|
|
void |
512 |
|
|
table_close_all(void) |
513 |
|
|
{ |
514 |
|
|
struct table *t; |
515 |
|
|
void *iter; |
516 |
|
|
|
517 |
|
|
iter = NULL; |
518 |
|
|
while (dict_iter(env->sc_tables_dict, &iter, NULL, (void **)&t)) |
519 |
|
|
table_close(t); |
520 |
|
|
} |
521 |
|
|
|
522 |
|
|
int |
523 |
|
|
table_parse_lookup(enum table_service service, const char *key, |
524 |
|
|
const char *line, union lookup *lk) |
525 |
|
|
{ |
526 |
|
|
char buffer[LINE_MAX], *p; |
527 |
|
|
size_t len; |
528 |
|
|
|
529 |
|
|
len = strlen(line); |
530 |
|
|
|
531 |
|
|
switch (service) { |
532 |
|
|
case K_ALIAS: |
533 |
|
|
lk->expand = calloc(1, sizeof(*lk->expand)); |
534 |
|
|
if (lk->expand == NULL) |
535 |
|
|
return (-1); |
536 |
|
|
if (!expand_line(lk->expand, line, 1)) { |
537 |
|
|
expand_free(lk->expand); |
538 |
|
|
return (-1); |
539 |
|
|
} |
540 |
|
|
return (1); |
541 |
|
|
|
542 |
|
|
case K_DOMAIN: |
543 |
|
|
if (strlcpy(lk->domain.name, line, sizeof(lk->domain.name)) |
544 |
|
|
>= sizeof(lk->domain.name)) |
545 |
|
|
return (-1); |
546 |
|
|
return (1); |
547 |
|
|
|
548 |
|
|
case K_CREDENTIALS: |
549 |
|
|
|
550 |
|
|
/* credentials are stored as user:password */ |
551 |
|
|
if (len < 3) |
552 |
|
|
return (-1); |
553 |
|
|
|
554 |
|
|
/* too big to fit in a smtp session line */ |
555 |
|
|
if (len >= LINE_MAX) |
556 |
|
|
return (-1); |
557 |
|
|
|
558 |
|
|
p = strchr(line, ':'); |
559 |
|
|
if (p == NULL) { |
560 |
|
|
if (strlcpy(lk->creds.username, key, sizeof (lk->creds.username)) |
561 |
|
|
>= sizeof (lk->creds.username)) |
562 |
|
|
return (-1); |
563 |
|
|
if (strlcpy(lk->creds.password, line, sizeof(lk->creds.password)) |
564 |
|
|
>= sizeof(lk->creds.password)) |
565 |
|
|
return (-1); |
566 |
|
|
return (1); |
567 |
|
|
} |
568 |
|
|
|
569 |
|
|
if (p == line || p == line + len - 1) |
570 |
|
|
return (-1); |
571 |
|
|
|
572 |
|
|
memmove(lk->creds.username, line, p - line); |
573 |
|
|
lk->creds.username[p - line] = '\0'; |
574 |
|
|
|
575 |
|
|
if (strlcpy(lk->creds.password, p+1, sizeof(lk->creds.password)) |
576 |
|
|
>= sizeof(lk->creds.password)) |
577 |
|
|
return (-1); |
578 |
|
|
|
579 |
|
|
return (1); |
580 |
|
|
|
581 |
|
|
case K_NETADDR: |
582 |
|
|
if (!text_to_netaddr(&lk->netaddr, line)) |
583 |
|
|
return (-1); |
584 |
|
|
return (1); |
585 |
|
|
|
586 |
|
|
case K_USERINFO: |
587 |
|
|
if (!bsnprintf(buffer, sizeof(buffer), "%s:%s", key, line)) |
588 |
|
|
return (-1); |
589 |
|
|
if (!text_to_userinfo(&lk->userinfo, buffer)) |
590 |
|
|
return (-1); |
591 |
|
|
return (1); |
592 |
|
|
|
593 |
|
|
case K_SOURCE: |
594 |
|
|
if (parse_sockaddr((struct sockaddr *)&lk->source.addr, |
595 |
|
|
PF_UNSPEC, line) == -1) |
596 |
|
|
return (-1); |
597 |
|
|
return (1); |
598 |
|
|
|
599 |
|
|
case K_MAILADDR: |
600 |
|
|
if (!text_to_mailaddr(&lk->mailaddr, line)) |
601 |
|
|
return (-1); |
602 |
|
|
return (1); |
603 |
|
|
|
604 |
|
|
case K_MAILADDRMAP: |
605 |
|
|
lk->maddrmap = calloc(1, sizeof(*lk->maddrmap)); |
606 |
|
|
if (lk->maddrmap == NULL) |
607 |
|
|
return (-1); |
608 |
|
|
maddrmap_init(lk->maddrmap); |
609 |
|
|
if (!mailaddr_line(lk->maddrmap, line)) { |
610 |
|
|
maddrmap_free(lk->maddrmap); |
611 |
|
|
return (-1); |
612 |
|
|
} |
613 |
|
|
return (1); |
614 |
|
|
|
615 |
|
|
case K_ADDRNAME: |
616 |
|
|
if (parse_sockaddr((struct sockaddr *)&lk->addrname.addr, |
617 |
|
|
PF_UNSPEC, key) == -1) |
618 |
|
|
return (-1); |
619 |
|
|
if (strlcpy(lk->addrname.name, line, sizeof(lk->addrname.name)) |
620 |
|
|
>= sizeof(lk->addrname.name)) |
621 |
|
|
return (-1); |
622 |
|
|
return (1); |
623 |
|
|
|
624 |
|
|
default: |
625 |
|
|
return (-1); |
626 |
|
|
} |
627 |
|
|
} |
628 |
|
|
|
629 |
|
|
static const char * |
630 |
|
|
table_dump_lookup(enum table_service s, union lookup *lk) |
631 |
|
|
{ |
632 |
|
|
static char buf[LINE_MAX]; |
633 |
|
|
int ret; |
634 |
|
|
|
635 |
|
|
switch (s) { |
636 |
|
|
case K_NONE: |
637 |
|
|
break; |
638 |
|
|
|
639 |
|
|
case K_ALIAS: |
640 |
|
|
expand_to_text(lk->expand, buf, sizeof(buf)); |
641 |
|
|
break; |
642 |
|
|
|
643 |
|
|
case K_DOMAIN: |
644 |
|
|
ret = snprintf(buf, sizeof(buf), "%s", lk->domain.name); |
645 |
|
|
if (ret == -1 || (size_t)ret >= sizeof (buf)) |
646 |
|
|
goto err; |
647 |
|
|
break; |
648 |
|
|
|
649 |
|
|
case K_CREDENTIALS: |
650 |
|
|
ret = snprintf(buf, sizeof(buf), "%s:%s", |
651 |
|
|
lk->creds.username, lk->creds.password); |
652 |
|
|
if (ret == -1 || (size_t)ret >= sizeof (buf)) |
653 |
|
|
goto err; |
654 |
|
|
break; |
655 |
|
|
|
656 |
|
|
case K_NETADDR: |
657 |
|
|
ret = snprintf(buf, sizeof(buf), "%s/%d", |
658 |
|
|
sockaddr_to_text((struct sockaddr *)&lk->netaddr.ss), |
659 |
|
|
lk->netaddr.bits); |
660 |
|
|
if (ret == -1 || (size_t)ret >= sizeof (buf)) |
661 |
|
|
goto err; |
662 |
|
|
break; |
663 |
|
|
|
664 |
|
|
case K_USERINFO: |
665 |
|
|
ret = snprintf(buf, sizeof(buf), "%s:%d:%d:%s", |
666 |
|
|
lk->userinfo.username, |
667 |
|
|
lk->userinfo.uid, |
668 |
|
|
lk->userinfo.gid, |
669 |
|
|
lk->userinfo.directory); |
670 |
|
|
if (ret == -1 || (size_t)ret >= sizeof (buf)) |
671 |
|
|
goto err; |
672 |
|
|
break; |
673 |
|
|
|
674 |
|
|
case K_SOURCE: |
675 |
|
|
ret = snprintf(buf, sizeof(buf), "%s", |
676 |
|
|
ss_to_text(&lk->source.addr)); |
677 |
|
|
if (ret == -1 || (size_t)ret >= sizeof (buf)) |
678 |
|
|
goto err; |
679 |
|
|
break; |
680 |
|
|
|
681 |
|
|
case K_MAILADDR: |
682 |
|
|
ret = snprintf(buf, sizeof(buf), "%s@%s", |
683 |
|
|
lk->mailaddr.user, |
684 |
|
|
lk->mailaddr.domain); |
685 |
|
|
if (ret == -1 || (size_t)ret >= sizeof (buf)) |
686 |
|
|
goto err; |
687 |
|
|
break; |
688 |
|
|
|
689 |
|
|
case K_ADDRNAME: |
690 |
|
|
ret = snprintf(buf, sizeof(buf), "%s", |
691 |
|
|
lk->addrname.name); |
692 |
|
|
if (ret == -1 || (size_t)ret >= sizeof (buf)) |
693 |
|
|
goto err; |
694 |
|
|
break; |
695 |
|
|
|
696 |
|
|
default: |
697 |
|
|
(void)strlcpy(buf, "???", sizeof(buf)); |
698 |
|
|
break; |
699 |
|
|
} |
700 |
|
|
|
701 |
|
|
return (buf); |
702 |
|
|
|
703 |
|
|
err: |
704 |
|
|
return (NULL); |
705 |
|
|
} |
706 |
|
|
|
707 |
|
|
|
708 |
|
|
static int |
709 |
|
|
parse_sockaddr(struct sockaddr *sa, int family, const char *str) |
710 |
|
|
{ |
711 |
|
|
struct in_addr ina; |
712 |
|
|
struct in6_addr in6a; |
713 |
|
|
struct sockaddr_in *sin; |
714 |
|
|
struct sockaddr_in6 *sin6; |
715 |
|
|
char *cp, *str2; |
716 |
|
|
const char *errstr; |
717 |
|
|
|
718 |
|
|
switch (family) { |
719 |
|
|
case PF_UNSPEC: |
720 |
|
|
if (parse_sockaddr(sa, PF_INET, str) == 0) |
721 |
|
|
return (0); |
722 |
|
|
return parse_sockaddr(sa, PF_INET6, str); |
723 |
|
|
|
724 |
|
|
case PF_INET: |
725 |
|
|
if (inet_pton(PF_INET, str, &ina) != 1) |
726 |
|
|
return (-1); |
727 |
|
|
|
728 |
|
|
sin = (struct sockaddr_in *)sa; |
729 |
|
|
memset(sin, 0, sizeof *sin); |
730 |
|
|
sin->sin_len = sizeof(struct sockaddr_in); |
731 |
|
|
sin->sin_family = PF_INET; |
732 |
|
|
sin->sin_addr.s_addr = ina.s_addr; |
733 |
|
|
return (0); |
734 |
|
|
|
735 |
|
|
case PF_INET6: |
736 |
|
|
if (strncasecmp("ipv6:", str, 5) == 0) |
737 |
|
|
str += 5; |
738 |
|
|
cp = strchr(str, SCOPE_DELIMITER); |
739 |
|
|
if (cp) { |
740 |
|
|
str2 = strdup(str); |
741 |
|
|
if (str2 == NULL) |
742 |
|
|
return (-1); |
743 |
|
|
str2[cp - str] = '\0'; |
744 |
|
|
if (inet_pton(PF_INET6, str2, &in6a) != 1) { |
745 |
|
|
free(str2); |
746 |
|
|
return (-1); |
747 |
|
|
} |
748 |
|
|
cp++; |
749 |
|
|
free(str2); |
750 |
|
|
} else if (inet_pton(PF_INET6, str, &in6a) != 1) |
751 |
|
|
return (-1); |
752 |
|
|
|
753 |
|
|
sin6 = (struct sockaddr_in6 *)sa; |
754 |
|
|
memset(sin6, 0, sizeof *sin6); |
755 |
|
|
sin6->sin6_len = sizeof(struct sockaddr_in6); |
756 |
|
|
sin6->sin6_family = PF_INET6; |
757 |
|
|
sin6->sin6_addr = in6a; |
758 |
|
|
|
759 |
|
|
if (cp == NULL) |
760 |
|
|
return (0); |
761 |
|
|
|
762 |
|
|
if (IN6_IS_ADDR_LINKLOCAL(&in6a) || |
763 |
|
|
IN6_IS_ADDR_MC_LINKLOCAL(&in6a) || |
764 |
|
|
IN6_IS_ADDR_MC_INTFACELOCAL(&in6a)) |
765 |
|
|
if ((sin6->sin6_scope_id = if_nametoindex(cp))) |
766 |
|
|
return (0); |
767 |
|
|
|
768 |
|
|
sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr); |
769 |
|
|
if (errstr) |
770 |
|
|
return (-1); |
771 |
|
|
return (0); |
772 |
|
|
|
773 |
|
|
default: |
774 |
|
|
break; |
775 |
|
|
} |
776 |
|
|
|
777 |
|
|
return (-1); |
778 |
|
|
} |