1*47388f99Sgilles /* $OpenBSD: table.c,v 1.54 2024/06/09 10:13:05 gilles Exp $ */
265c4fdfbSgilles
365c4fdfbSgilles /*
4299c4efeSeric * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
565c4fdfbSgilles * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
665c4fdfbSgilles *
765c4fdfbSgilles * Permission to use, copy, modify, and distribute this software for any
865c4fdfbSgilles * purpose with or without fee is hereby granted, provided that the above
965c4fdfbSgilles * copyright notice and this permission notice appear in all copies.
1065c4fdfbSgilles *
1165c4fdfbSgilles * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1265c4fdfbSgilles * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1365c4fdfbSgilles * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1465c4fdfbSgilles * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1565c4fdfbSgilles * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1665c4fdfbSgilles * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1765c4fdfbSgilles * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1865c4fdfbSgilles */
1965c4fdfbSgilles
208a1302cdSeric #include <sys/stat.h>
2165c4fdfbSgilles
22299c4efeSeric #include <net/if.h>
2365c4fdfbSgilles
24d3140113Seric #include <arpa/inet.h>
2565c4fdfbSgilles #include <errno.h>
2681c7fb0aSgilles #include <regex.h>
27d3140113Seric #include <stdlib.h>
2865c4fdfbSgilles #include <string.h>
2965c4fdfbSgilles
3065c4fdfbSgilles #include "smtpd.h"
3165c4fdfbSgilles #include "log.h"
3265c4fdfbSgilles
3365c4fdfbSgilles struct table_backend *table_backend_lookup(const char *);
3465c4fdfbSgilles
3565c4fdfbSgilles extern struct table_backend table_backend_static;
3665c4fdfbSgilles extern struct table_backend table_backend_db;
3765c4fdfbSgilles extern struct table_backend table_backend_getpwnam;
388a1302cdSeric extern struct table_backend table_backend_proc;
3965c4fdfbSgilles
40aa0d26b5Seric static int table_parse_lookup(enum table_service, const char *, const char *,
41aa0d26b5Seric union lookup *);
42299c4efeSeric static int parse_sockaddr(struct sockaddr *, int, const char *);
43299c4efeSeric
44299c4efeSeric static unsigned int last_table_id = 0;
4565c4fdfbSgilles
464f15e081Seric static struct table_backend *backends[] = {
474f15e081Seric &table_backend_static,
484f15e081Seric &table_backend_db,
494f15e081Seric &table_backend_getpwnam,
504f15e081Seric &table_backend_proc,
514f15e081Seric NULL
524f15e081Seric };
534f15e081Seric
5465c4fdfbSgilles struct table_backend *
table_backend_lookup(const char * backend)5565c4fdfbSgilles table_backend_lookup(const char *backend)
5665c4fdfbSgilles {
574f15e081Seric int i;
5865c4fdfbSgilles
594f15e081Seric if (!strcmp(backend, "file"))
604f15e081Seric backend = "static";
614f15e081Seric
624f15e081Seric for (i = 0; backends[i]; i++)
634f15e081Seric if (!strcmp(backends[i]->name, backend))
644f15e081Seric return (backends[i]);
654f15e081Seric
664f15e081Seric return NULL;
67299c4efeSeric }
68299c4efeSeric
698380d000Sop const char *
table_service_name(enum table_service s)70299c4efeSeric table_service_name(enum table_service s)
71299c4efeSeric {
72299c4efeSeric switch (s) {
738380d000Sop case K_NONE: return "none";
748380d000Sop case K_ALIAS: return "alias";
758380d000Sop case K_DOMAIN: return "domain";
768380d000Sop case K_CREDENTIALS: return "credentials";
778380d000Sop case K_NETADDR: return "netaddr";
788380d000Sop case K_USERINFO: return "userinfo";
798380d000Sop case K_SOURCE: return "source";
808380d000Sop case K_MAILADDR: return "mailaddr";
818380d000Sop case K_ADDRNAME: return "addrname";
828380d000Sop case K_MAILADDRMAP: return "mailaddrmap";
838380d000Sop case K_RELAYHOST: return "relayhost";
848380d000Sop case K_STRING: return "string";
858380d000Sop case K_REGEX: return "regex";
86*47388f99Sgilles case K_AUTH: return "auth";
87299c4efeSeric }
8824b79e37Seric return "???";
8965c4fdfbSgilles }
9065c4fdfbSgilles
918380d000Sop int
table_service_from_name(const char * service)928380d000Sop table_service_from_name(const char *service)
938380d000Sop {
948380d000Sop if (!strcmp(service, "none"))
958380d000Sop return K_NONE;
968380d000Sop if (!strcmp(service, "alias"))
978380d000Sop return K_ALIAS;
988380d000Sop if (!strcmp(service, "domain"))
998380d000Sop return K_DOMAIN;
1008380d000Sop if (!strcmp(service, "credentials"))
1018380d000Sop return K_CREDENTIALS;
1028380d000Sop if (!strcmp(service, "netaddr"))
1038380d000Sop return K_NETADDR;
1048380d000Sop if (!strcmp(service, "userinfo"))
1058380d000Sop return K_USERINFO;
1068380d000Sop if (!strcmp(service, "source"))
1078380d000Sop return K_SOURCE;
1088380d000Sop if (!strcmp(service, "mailaddr"))
1098380d000Sop return K_MAILADDR;
1108380d000Sop if (!strcmp(service, "addrname"))
1118380d000Sop return K_ADDRNAME;
1128380d000Sop if (!strcmp(service, "mailaddrmap"))
1138380d000Sop return K_MAILADDRMAP;
1148380d000Sop if (!strcmp(service, "relayhost"))
1158380d000Sop return K_RELAYHOST;
1168380d000Sop if (!strcmp(service, "string"))
1178380d000Sop return K_STRING;
1188380d000Sop if (!strcmp(service, "regex"))
1198380d000Sop return K_REGEX;
120*47388f99Sgilles if (!strcmp(service, "auth"))
121*47388f99Sgilles return K_AUTH;
1228380d000Sop return (-1);
1238380d000Sop }
1248380d000Sop
12565c4fdfbSgilles struct table *
table_find(struct smtpd * conf,const char * name)126ff18143eSeric table_find(struct smtpd *conf, const char *name)
12765c4fdfbSgilles {
128b80b41afSgilles return dict_get(conf->sc_tables_dict, name);
12965c4fdfbSgilles }
13065c4fdfbSgilles
13165c4fdfbSgilles int
table_match(struct table * table,enum table_service kind,const char * key)132ef7a27e8Seric table_match(struct table *table, enum table_service kind, const char *key)
133ef7a27e8Seric {
13493cc0b04Seric return table_lookup(table, kind, key, NULL);
135ef7a27e8Seric }
136ef7a27e8Seric
137ef7a27e8Seric int
table_lookup(struct table * table,enum table_service kind,const char * key,union lookup * lk)13893cc0b04Seric table_lookup(struct table *table, enum table_service kind, const char *key,
139299c4efeSeric union lookup *lk)
14065c4fdfbSgilles {
141aa0d26b5Seric char lkey[1024], *buf = NULL;
14224b79e37Seric int r;
143299c4efeSeric
14424b79e37Seric r = -1;
145299c4efeSeric if (table->t_backend->lookup == NULL)
14624b79e37Seric errno = ENOTSUP;
14724b79e37Seric else if (!lowercase(lkey, key, sizeof lkey)) {
148299c4efeSeric log_warnx("warn: lookup key too long: %s", key);
14924b79e37Seric errno = EINVAL;
150299c4efeSeric }
15124b79e37Seric else
1529a2428acSeric r = table->t_backend->lookup(table, kind, lkey, lk ? &buf : NULL);
153299c4efeSeric
154aa0d26b5Seric if (r == 1) {
155299c4efeSeric log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s",
15624b79e37Seric lk ? "lookup" : "match",
15724b79e37Seric key,
158299c4efeSeric table_service_name(kind),
1594f15e081Seric table->t_backend->name,
160299c4efeSeric table->t_name,
161299c4efeSeric lk ? "\"" : "",
16224b79e37Seric lk ? buf : "true",
163299c4efeSeric lk ? "\"" : "");
164aa0d26b5Seric if (buf)
165aa0d26b5Seric r = table_parse_lookup(kind, lkey, buf, lk);
166aa0d26b5Seric }
167299c4efeSeric else
16824b79e37Seric log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s",
16924b79e37Seric lk ? "lookup" : "match",
17024b79e37Seric key,
171299c4efeSeric table_service_name(kind),
1724f15e081Seric table->t_backend->name,
173299c4efeSeric table->t_name,
17424b79e37Seric (r == -1) ? "error: " : (lk ? "none" : "false"),
17524b79e37Seric (r == -1) ? strerror(errno) : "");
176299c4efeSeric
177aa0d26b5Seric free(buf);
178aa0d26b5Seric
179299c4efeSeric return (r);
18065c4fdfbSgilles }
18165c4fdfbSgilles
18265c4fdfbSgilles int
table_fetch(struct table * table,enum table_service kind,union lookup * lk)183699c3f98Seric table_fetch(struct table *table, enum table_service kind, union lookup *lk)
18465c4fdfbSgilles {
185aa0d26b5Seric char *buf = NULL;
18624b79e37Seric int r;
187299c4efeSeric
18824b79e37Seric r = -1;
189299c4efeSeric if (table->t_backend->fetch == NULL)
19024b79e37Seric errno = ENOTSUP;
19124b79e37Seric else
1921cd87e61Seric r = table->t_backend->fetch(table, kind, &buf);
193299c4efeSeric
194aa0d26b5Seric if (r == 1) {
1951cd87e61Seric log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> \"%s\"",
196299c4efeSeric table_service_name(kind),
1974f15e081Seric table->t_backend->name,
198299c4efeSeric table->t_name,
1991cd87e61Seric buf);
200aa0d26b5Seric r = table_parse_lookup(kind, NULL, buf, lk);
201aa0d26b5Seric }
202299c4efeSeric else
20324b79e37Seric log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> %s%s",
204299c4efeSeric table_service_name(kind),
2054f15e081Seric table->t_backend->name,
206299c4efeSeric table->t_name,
20724b79e37Seric (r == -1) ? "error: " : "none",
20824b79e37Seric (r == -1) ? strerror(errno) : "");
209299c4efeSeric
210aa0d26b5Seric free(buf);
211aa0d26b5Seric
212299c4efeSeric return (r);
21365c4fdfbSgilles }
21465c4fdfbSgilles
21565c4fdfbSgilles struct table *
table_create(struct smtpd * conf,const char * backend,const char * name,const char * config)216ff18143eSeric table_create(struct smtpd *conf, const char *backend, const char *name,
217299c4efeSeric const char *config)
21865c4fdfbSgilles {
21965c4fdfbSgilles struct table *t;
22065c4fdfbSgilles struct table_backend *tb;
221953aae25Sderaadt char path[LINE_MAX];
22265c4fdfbSgilles size_t n;
2238a1302cdSeric struct stat sb;
22465c4fdfbSgilles
225ff18143eSeric if (name && table_find(conf, name))
226d6f2ac01Seric fatalx("table_create: table \"%s\" already defined", name);
22765c4fdfbSgilles
2288a1302cdSeric if ((tb = table_backend_lookup(backend)) == NULL) {
229ad8f062fSjung if ((size_t)snprintf(path, sizeof(path), PATH_LIBEXEC"/table-%s",
230ad8f062fSjung backend) >= sizeof(path)) {
231d6f2ac01Seric fatalx("table_create: path too long \""
232ad8f062fSjung PATH_LIBEXEC"/table-%s\"", backend);
2338a1302cdSeric }
2348a1302cdSeric if (stat(path, &sb) == 0) {
2358a1302cdSeric tb = table_backend_lookup("proc");
236d6f2ac01Seric (void)strlcpy(path, backend, sizeof(path));
2378a1302cdSeric if (config) {
238d6f2ac01Seric (void)strlcat(path, ":", sizeof(path));
23903e748b3Sgilles if (strlcat(path, config, sizeof(path))
24003e748b3Sgilles >= sizeof(path))
241d6f2ac01Seric fatalx("table_create: config file path too long");
2428a1302cdSeric }
2438a1302cdSeric config = path;
2448a1302cdSeric }
2458a1302cdSeric }
2468a1302cdSeric
2478a1302cdSeric if (tb == NULL)
248d6f2ac01Seric fatalx("table_create: backend \"%s\" does not exist", backend);
24965c4fdfbSgilles
250118c16f3Sgilles t = xcalloc(1, sizeof(*t));
2512ee83f25Sop t->t_services = tb->services;
25265c4fdfbSgilles t->t_backend = tb;
25365c4fdfbSgilles
254299c4efeSeric if (config) {
25565c4fdfbSgilles if (strlcpy(t->t_config, config, sizeof t->t_config)
25665c4fdfbSgilles >= sizeof t->t_config)
257d6f2ac01Seric fatalx("table_create: table config \"%s\" too large",
25865c4fdfbSgilles t->t_config);
25965c4fdfbSgilles }
26065c4fdfbSgilles
26184450e5eSeric if (strcmp(tb->name, "static") != 0)
26265c4fdfbSgilles t->t_type = T_DYNAMIC;
26365c4fdfbSgilles
26465c4fdfbSgilles if (name == NULL)
26503e748b3Sgilles (void)snprintf(t->t_name, sizeof(t->t_name), "<dynamic:%u>",
266299c4efeSeric last_table_id++);
26765c4fdfbSgilles else {
26865c4fdfbSgilles n = strlcpy(t->t_name, name, sizeof(t->t_name));
26965c4fdfbSgilles if (n >= sizeof(t->t_name))
270d6f2ac01Seric fatalx("table_create: table name too long");
27165c4fdfbSgilles }
27265c4fdfbSgilles
273b80b41afSgilles dict_set(conf->sc_tables_dict, t->t_name, t);
27465c4fdfbSgilles
27565c4fdfbSgilles return (t);
27665c4fdfbSgilles }
27765c4fdfbSgilles
27865c4fdfbSgilles void
table_destroy(struct smtpd * conf,struct table * t)279b80b41afSgilles table_destroy(struct smtpd *conf, struct table *t)
28065c4fdfbSgilles {
281b80b41afSgilles dict_xpop(conf->sc_tables_dict, t->t_name);
28265c4fdfbSgilles free(t);
28365c4fdfbSgilles }
28465c4fdfbSgilles
285299c4efeSeric int
table_config(struct table * t)286299c4efeSeric table_config(struct table *t)
287dd047c3bSgilles {
288299c4efeSeric if (t->t_backend->config == NULL)
289299c4efeSeric return (1);
290299c4efeSeric return (t->t_backend->config(t));
29165c4fdfbSgilles }
29265c4fdfbSgilles
29365c4fdfbSgilles void
table_add(struct table * t,const char * key,const char * val)29465c4fdfbSgilles table_add(struct table *t, const char *key, const char *val)
29565c4fdfbSgilles {
2965f0e36ccSeric if (t->t_backend->add == NULL)
297d6f2ac01Seric fatalx("table_add: cannot add to table");
298c89b4b77Seric
2995f0e36ccSeric if (t->t_backend->add(t, key, val) == 0)
3005f0e36ccSeric log_warnx("warn: failed to add \"%s\" in table \"%s\"", key, t->t_name);
30165c4fdfbSgilles }
30265c4fdfbSgilles
3039c07e903Seric void
table_dump(struct table * t)3049c07e903Seric table_dump(struct table *t)
3059c07e903Seric {
3069c07e903Seric const char *type;
3079c07e903Seric char buf[LINE_MAX];
3089c07e903Seric
3099c07e903Seric switch(t->t_type) {
3109c07e903Seric case T_NONE:
3119c07e903Seric type = "NONE";
3129c07e903Seric break;
3139c07e903Seric case T_DYNAMIC:
3149c07e903Seric type = "DYNAMIC";
3159c07e903Seric break;
3169c07e903Seric case T_LIST:
3179c07e903Seric type = "LIST";
3189c07e903Seric break;
3199c07e903Seric case T_HASH:
3209c07e903Seric type = "HASH";
3219c07e903Seric break;
3229c07e903Seric default:
3239c07e903Seric type = "???";
3249c07e903Seric break;
3259c07e903Seric }
3269c07e903Seric
3279c07e903Seric if (t->t_config[0])
3289c07e903Seric snprintf(buf, sizeof(buf), " config=\"%s\"", t->t_config);
3299c07e903Seric else
3309c07e903Seric buf[0] = '\0';
3319c07e903Seric
3329c07e903Seric log_debug("TABLE \"%s\" backend=%s type=%s%s", t->t_name,
3339c07e903Seric t->t_backend->name, type, buf);
3349c07e903Seric
3359c07e903Seric if (t->t_backend->dump)
3369c07e903Seric t->t_backend->dump(t);
3379c07e903Seric }
3389c07e903Seric
33965c4fdfbSgilles int
table_check_type(struct table * t,uint32_t mask)34065c4fdfbSgilles table_check_type(struct table *t, uint32_t mask)
34165c4fdfbSgilles {
34265c4fdfbSgilles return t->t_type & mask;
34365c4fdfbSgilles }
34465c4fdfbSgilles
34565c4fdfbSgilles int
table_check_service(struct table * t,uint32_t mask)34665c4fdfbSgilles table_check_service(struct table *t, uint32_t mask)
34765c4fdfbSgilles {
3482ee83f25Sop return t->t_services & mask;
34965c4fdfbSgilles }
35065c4fdfbSgilles
35165c4fdfbSgilles int
table_check_use(struct table * t,uint32_t tmask,uint32_t smask)35265c4fdfbSgilles table_check_use(struct table *t, uint32_t tmask, uint32_t smask)
35365c4fdfbSgilles {
35465c4fdfbSgilles return table_check_type(t, tmask) && table_check_service(t, smask);
35565c4fdfbSgilles }
35665c4fdfbSgilles
35765c4fdfbSgilles int
table_open(struct table * t)35865c4fdfbSgilles table_open(struct table *t)
35965c4fdfbSgilles {
360299c4efeSeric if (t->t_backend->open == NULL)
361299c4efeSeric return (1);
362b700b1d8Seric return (t->t_backend->open(t));
36365c4fdfbSgilles }
36465c4fdfbSgilles
36565c4fdfbSgilles void
table_close(struct table * t)36665c4fdfbSgilles table_close(struct table *t)
36765c4fdfbSgilles {
368299c4efeSeric if (t->t_backend->close)
369a4c00f8aSeric t->t_backend->close(t);
37065c4fdfbSgilles }
37165c4fdfbSgilles
372299c4efeSeric int
table_update(struct table * t)37365c4fdfbSgilles table_update(struct table *t)
37465c4fdfbSgilles {
375299c4efeSeric if (t->t_backend->update == NULL)
376299c4efeSeric return (1);
377299c4efeSeric return (t->t_backend->update(t));
37865c4fdfbSgilles }
37965c4fdfbSgilles
380710050efSgilles
381710050efSgilles /*
382710050efSgilles * quick reminder:
383710050efSgilles * in *_match() s1 comes from session, s2 comes from table
384710050efSgilles */
385710050efSgilles
38665c4fdfbSgilles int
table_domain_match(const char * s1,const char * s2)38765c4fdfbSgilles table_domain_match(const char *s1, const char *s2)
38865c4fdfbSgilles {
38965c4fdfbSgilles return hostname_match(s1, s2);
39065c4fdfbSgilles }
39165c4fdfbSgilles
39265c4fdfbSgilles int
table_mailaddr_match(const char * s1,const char * s2)39365c4fdfbSgilles table_mailaddr_match(const char *s1, const char *s2)
39465c4fdfbSgilles {
39565c4fdfbSgilles struct mailaddr m1;
39665c4fdfbSgilles struct mailaddr m2;
39765c4fdfbSgilles
39865c4fdfbSgilles if (!text_to_mailaddr(&m1, s1))
39965c4fdfbSgilles return 0;
40065c4fdfbSgilles if (!text_to_mailaddr(&m2, s2))
40165c4fdfbSgilles return 0;
402787a07c5Sgilles return mailaddr_match(&m1, &m2);
40365c4fdfbSgilles }
40465c4fdfbSgilles
40565c4fdfbSgilles static int table_match_mask(struct sockaddr_storage *, struct netaddr *);
40665c4fdfbSgilles static int table_inet4_match(struct sockaddr_in *, struct netaddr *);
40765c4fdfbSgilles static int table_inet6_match(struct sockaddr_in6 *, struct netaddr *);
40865c4fdfbSgilles
40965c4fdfbSgilles int
table_netaddr_match(const char * s1,const char * s2)41065c4fdfbSgilles table_netaddr_match(const char *s1, const char *s2)
41165c4fdfbSgilles {
41265c4fdfbSgilles struct netaddr n1;
41365c4fdfbSgilles struct netaddr n2;
41465c4fdfbSgilles
415299c4efeSeric if (strcasecmp(s1, s2) == 0)
41665c4fdfbSgilles return 1;
41765c4fdfbSgilles if (!text_to_netaddr(&n1, s1))
41865c4fdfbSgilles return 0;
41965c4fdfbSgilles if (!text_to_netaddr(&n2, s2))
42065c4fdfbSgilles return 0;
42165c4fdfbSgilles if (n1.ss.ss_family != n2.ss.ss_family)
42265c4fdfbSgilles return 0;
42365c4fdfbSgilles if (n1.ss.ss_len != n2.ss.ss_len)
42465c4fdfbSgilles return 0;
42565c4fdfbSgilles return table_match_mask(&n1.ss, &n2);
42665c4fdfbSgilles }
42765c4fdfbSgilles
42865c4fdfbSgilles static int
table_match_mask(struct sockaddr_storage * ss,struct netaddr * ssmask)42965c4fdfbSgilles table_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask)
43065c4fdfbSgilles {
43165c4fdfbSgilles if (ss->ss_family == AF_INET)
43265c4fdfbSgilles return table_inet4_match((struct sockaddr_in *)ss, ssmask);
43365c4fdfbSgilles
43465c4fdfbSgilles if (ss->ss_family == AF_INET6)
43565c4fdfbSgilles return table_inet6_match((struct sockaddr_in6 *)ss, ssmask);
43665c4fdfbSgilles
43765c4fdfbSgilles return (0);
43865c4fdfbSgilles }
43965c4fdfbSgilles
44065c4fdfbSgilles static int
table_inet4_match(struct sockaddr_in * ss,struct netaddr * ssmask)44165c4fdfbSgilles table_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask)
44265c4fdfbSgilles {
44365c4fdfbSgilles in_addr_t mask;
44465c4fdfbSgilles int i;
44565c4fdfbSgilles
44665c4fdfbSgilles /* a.b.c.d/8 -> htonl(0xff000000) */
44765c4fdfbSgilles mask = 0;
44865c4fdfbSgilles for (i = 0; i < ssmask->bits; ++i)
44965c4fdfbSgilles mask = (mask >> 1) | 0x80000000;
45065c4fdfbSgilles mask = htonl(mask);
45165c4fdfbSgilles
45265c4fdfbSgilles /* (addr & mask) == (net & mask) */
45365c4fdfbSgilles if ((ss->sin_addr.s_addr & mask) ==
45465c4fdfbSgilles (((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask))
45565c4fdfbSgilles return 1;
45665c4fdfbSgilles
45765c4fdfbSgilles return 0;
45865c4fdfbSgilles }
45965c4fdfbSgilles
46065c4fdfbSgilles static int
table_inet6_match(struct sockaddr_in6 * ss,struct netaddr * ssmask)46165c4fdfbSgilles table_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask)
46265c4fdfbSgilles {
46365c4fdfbSgilles struct in6_addr *in;
46465c4fdfbSgilles struct in6_addr *inmask;
46565c4fdfbSgilles struct in6_addr mask;
46665c4fdfbSgilles int i;
46765c4fdfbSgilles
468c1392a69Seric memset(&mask, 0, sizeof(mask));
46965c4fdfbSgilles for (i = 0; i < ssmask->bits / 8; i++)
47065c4fdfbSgilles mask.s6_addr[i] = 0xff;
47165c4fdfbSgilles i = ssmask->bits % 8;
47265c4fdfbSgilles if (i)
47365c4fdfbSgilles mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i;
47465c4fdfbSgilles
47565c4fdfbSgilles in = &ss->sin6_addr;
47665c4fdfbSgilles inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr;
47765c4fdfbSgilles
47865c4fdfbSgilles for (i = 0; i < 16; i++) {
47965c4fdfbSgilles if ((in->s6_addr[i] & mask.s6_addr[i]) !=
48065c4fdfbSgilles (inmask->s6_addr[i] & mask.s6_addr[i]))
48165c4fdfbSgilles return (0);
48265c4fdfbSgilles }
48365c4fdfbSgilles
48465c4fdfbSgilles return (1);
48565c4fdfbSgilles }
48665c4fdfbSgilles
48781c7fb0aSgilles int
table_regex_match(const char * string,const char * pattern)48881c7fb0aSgilles table_regex_match(const char *string, const char *pattern)
48981c7fb0aSgilles {
49081c7fb0aSgilles regex_t preg;
4914614b657Sgilles int cflags = REG_EXTENDED|REG_NOSUB;
49279a034b4Smartijn int ret;
49381c7fb0aSgilles
4944614b657Sgilles if (strncmp(pattern, "(?i)", 4) == 0) {
4954614b657Sgilles cflags |= REG_ICASE;
4964614b657Sgilles pattern += 4;
4974614b657Sgilles }
4984614b657Sgilles
4994614b657Sgilles if (regcomp(&preg, pattern, cflags) != 0)
50081c7fb0aSgilles return (0);
50181c7fb0aSgilles
50279a034b4Smartijn ret = regexec(&preg, string, 0, NULL, 0);
50379a034b4Smartijn
50479a034b4Smartijn regfree(&preg);
50579a034b4Smartijn
50679a034b4Smartijn if (ret != 0)
50781c7fb0aSgilles return (0);
50881c7fb0aSgilles
50981c7fb0aSgilles return (1);
51081c7fb0aSgilles }
51181c7fb0aSgilles
51265c4fdfbSgilles void
table_dump_all(struct smtpd * conf)513b80b41afSgilles table_dump_all(struct smtpd *conf)
514299c4efeSeric {
515299c4efeSeric struct table *t;
5165f0e36ccSeric void *iter;
517299c4efeSeric
518299c4efeSeric iter = NULL;
5199c07e903Seric while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t))
5209c07e903Seric table_dump(t);
521299c4efeSeric }
522299c4efeSeric
523299c4efeSeric void
table_open_all(struct smtpd * conf)524b80b41afSgilles table_open_all(struct smtpd *conf)
52565c4fdfbSgilles {
52665c4fdfbSgilles struct table *t;
52765c4fdfbSgilles void *iter;
52865c4fdfbSgilles
52965c4fdfbSgilles iter = NULL;
530b80b41afSgilles while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t))
53165c4fdfbSgilles if (!table_open(t))
532d6f2ac01Seric fatalx("failed to open table %s", t->t_name);
53365c4fdfbSgilles }
53465c4fdfbSgilles
53565c4fdfbSgilles void
table_close_all(struct smtpd * conf)536b80b41afSgilles table_close_all(struct smtpd *conf)
53765c4fdfbSgilles {
53865c4fdfbSgilles struct table *t;
53965c4fdfbSgilles void *iter;
54065c4fdfbSgilles
54165c4fdfbSgilles iter = NULL;
542b80b41afSgilles while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t))
54365c4fdfbSgilles table_close(t);
54465c4fdfbSgilles }
545299c4efeSeric
546aa0d26b5Seric static int
table_parse_lookup(enum table_service service,const char * key,const char * line,union lookup * lk)547299c4efeSeric table_parse_lookup(enum table_service service, const char *key,
548299c4efeSeric const char *line, union lookup *lk)
549299c4efeSeric {
550953aae25Sderaadt char buffer[LINE_MAX], *p;
551299c4efeSeric size_t len;
552299c4efeSeric
553299c4efeSeric len = strlen(line);
554299c4efeSeric
555299c4efeSeric switch (service) {
556299c4efeSeric case K_ALIAS:
557299c4efeSeric lk->expand = calloc(1, sizeof(*lk->expand));
558299c4efeSeric if (lk->expand == NULL)
559299c4efeSeric return (-1);
560299c4efeSeric if (!expand_line(lk->expand, line, 1)) {
561299c4efeSeric expand_free(lk->expand);
562299c4efeSeric return (-1);
563299c4efeSeric }
564299c4efeSeric return (1);
565299c4efeSeric
566299c4efeSeric case K_DOMAIN:
567299c4efeSeric if (strlcpy(lk->domain.name, line, sizeof(lk->domain.name))
568299c4efeSeric >= sizeof(lk->domain.name))
569299c4efeSeric return (-1);
570299c4efeSeric return (1);
571299c4efeSeric
572299c4efeSeric case K_CREDENTIALS:
573299c4efeSeric
574299c4efeSeric /* credentials are stored as user:password */
575299c4efeSeric if (len < 3)
576299c4efeSeric return (-1);
577299c4efeSeric
578299c4efeSeric /* too big to fit in a smtp session line */
579953aae25Sderaadt if (len >= LINE_MAX)
580299c4efeSeric return (-1);
581299c4efeSeric
582299c4efeSeric p = strchr(line, ':');
583b705b0d2Seric if (p == NULL) {
584b705b0d2Seric if (strlcpy(lk->creds.username, key, sizeof (lk->creds.username))
585b705b0d2Seric >= sizeof (lk->creds.username))
586b705b0d2Seric return (-1);
587b705b0d2Seric if (strlcpy(lk->creds.password, line, sizeof(lk->creds.password))
588b705b0d2Seric >= sizeof(lk->creds.password))
589b705b0d2Seric return (-1);
590b705b0d2Seric return (1);
591b705b0d2Seric }
592b705b0d2Seric
593b705b0d2Seric if (p == line || p == line + len - 1)
594299c4efeSeric return (-1);
595299c4efeSeric
596299c4efeSeric memmove(lk->creds.username, line, p - line);
597299c4efeSeric lk->creds.username[p - line] = '\0';
598299c4efeSeric
599299c4efeSeric if (strlcpy(lk->creds.password, p+1, sizeof(lk->creds.password))
600299c4efeSeric >= sizeof(lk->creds.password))
601299c4efeSeric return (-1);
602299c4efeSeric
603299c4efeSeric return (1);
604299c4efeSeric
605299c4efeSeric case K_NETADDR:
606299c4efeSeric if (!text_to_netaddr(&lk->netaddr, line))
607299c4efeSeric return (-1);
608299c4efeSeric return (1);
609299c4efeSeric
610299c4efeSeric case K_USERINFO:
611299c4efeSeric if (!bsnprintf(buffer, sizeof(buffer), "%s:%s", key, line))
612299c4efeSeric return (-1);
613299c4efeSeric if (!text_to_userinfo(&lk->userinfo, buffer))
614299c4efeSeric return (-1);
615299c4efeSeric return (1);
616299c4efeSeric
617299c4efeSeric case K_SOURCE:
618299c4efeSeric if (parse_sockaddr((struct sockaddr *)&lk->source.addr,
619299c4efeSeric PF_UNSPEC, line) == -1)
620299c4efeSeric return (-1);
621299c4efeSeric return (1);
622299c4efeSeric
623299c4efeSeric case K_MAILADDR:
624299c4efeSeric if (!text_to_mailaddr(&lk->mailaddr, line))
625299c4efeSeric return (-1);
626299c4efeSeric return (1);
627299c4efeSeric
628787a07c5Sgilles case K_MAILADDRMAP:
629787a07c5Sgilles lk->maddrmap = calloc(1, sizeof(*lk->maddrmap));
630787a07c5Sgilles if (lk->maddrmap == NULL)
631787a07c5Sgilles return (-1);
632787a07c5Sgilles maddrmap_init(lk->maddrmap);
633787a07c5Sgilles if (!mailaddr_line(lk->maddrmap, line)) {
634787a07c5Sgilles maddrmap_free(lk->maddrmap);
635787a07c5Sgilles return (-1);
636787a07c5Sgilles }
637787a07c5Sgilles return (1);
638787a07c5Sgilles
639299c4efeSeric case K_ADDRNAME:
640299c4efeSeric if (parse_sockaddr((struct sockaddr *)&lk->addrname.addr,
641299c4efeSeric PF_UNSPEC, key) == -1)
642299c4efeSeric return (-1);
643299c4efeSeric if (strlcpy(lk->addrname.name, line, sizeof(lk->addrname.name))
644299c4efeSeric >= sizeof(lk->addrname.name))
645299c4efeSeric return (-1);
646299c4efeSeric return (1);
647299c4efeSeric
648a8e22235Sgilles case K_RELAYHOST:
6498f342d13Seric if (strlcpy(lk->relayhost, line, sizeof(lk->relayhost))
6508f342d13Seric >= sizeof(lk->relayhost))
651a8e22235Sgilles return (-1);
652a8e22235Sgilles return (1);
653a8e22235Sgilles
654299c4efeSeric default:
655299c4efeSeric return (-1);
656299c4efeSeric }
657299c4efeSeric }
658299c4efeSeric
659299c4efeSeric static int
parse_sockaddr(struct sockaddr * sa,int family,const char * str)660299c4efeSeric parse_sockaddr(struct sockaddr *sa, int family, const char *str)
661299c4efeSeric {
662299c4efeSeric struct in_addr ina;
663299c4efeSeric struct in6_addr in6a;
664299c4efeSeric struct sockaddr_in *sin;
665299c4efeSeric struct sockaddr_in6 *sin6;
666d1ee423bSop char *cp;
667d1ee423bSop char addr[NI_MAXHOST];
668299c4efeSeric const char *errstr;
669299c4efeSeric
670299c4efeSeric switch (family) {
671299c4efeSeric case PF_UNSPEC:
672299c4efeSeric if (parse_sockaddr(sa, PF_INET, str) == 0)
673299c4efeSeric return (0);
674299c4efeSeric return parse_sockaddr(sa, PF_INET6, str);
675299c4efeSeric
676299c4efeSeric case PF_INET:
677299c4efeSeric if (inet_pton(PF_INET, str, &ina) != 1)
678299c4efeSeric return (-1);
679299c4efeSeric
680299c4efeSeric sin = (struct sockaddr_in *)sa;
681299c4efeSeric memset(sin, 0, sizeof *sin);
682299c4efeSeric sin->sin_len = sizeof(struct sockaddr_in);
683299c4efeSeric sin->sin_family = PF_INET;
684299c4efeSeric sin->sin_addr.s_addr = ina.s_addr;
685299c4efeSeric return (0);
686299c4efeSeric
687299c4efeSeric case PF_INET6:
688d1ee423bSop if (*str == '[')
689d1ee423bSop str++;
690d1ee423bSop if (!strncasecmp("ipv6:", str, 5))
6919b25d60eSeric str += 5;
692d1ee423bSop
693d1ee423bSop if (strlcpy(addr, str, sizeof(addr)) >= sizeof(addr))
694299c4efeSeric return (-1);
695d1ee423bSop if ((cp = strchr(addr, ']')) != NULL)
696d1ee423bSop *cp = '\0';
697d1ee423bSop if ((cp = strchr(addr, SCOPE_DELIMITER)) != NULL)
698d1ee423bSop *cp++ = '\0';
699d1ee423bSop
700d1ee423bSop if (inet_pton(PF_INET6, addr, &in6a) != 1)
701299c4efeSeric return (-1);
702299c4efeSeric
703299c4efeSeric sin6 = (struct sockaddr_in6 *)sa;
704299c4efeSeric memset(sin6, 0, sizeof *sin6);
705299c4efeSeric sin6->sin6_len = sizeof(struct sockaddr_in6);
706299c4efeSeric sin6->sin6_family = PF_INET6;
707299c4efeSeric sin6->sin6_addr = in6a;
708299c4efeSeric
709299c4efeSeric if (cp == NULL)
710299c4efeSeric return (0);
711299c4efeSeric
712299c4efeSeric if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
713299c4efeSeric IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
714299c4efeSeric IN6_IS_ADDR_MC_INTFACELOCAL(&in6a))
715299c4efeSeric if ((sin6->sin6_scope_id = if_nametoindex(cp)))
716299c4efeSeric return (0);
717299c4efeSeric
718299c4efeSeric sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
719299c4efeSeric if (errstr)
720299c4efeSeric return (-1);
721299c4efeSeric return (0);
722299c4efeSeric
723299c4efeSeric default:
724299c4efeSeric break;
725299c4efeSeric }
726299c4efeSeric
727299c4efeSeric return (-1);
728299c4efeSeric }
729