diff options
Diffstat (limited to 'smtpd/table.c')
-rw-r--r-- | smtpd/table.c | 709 |
1 files changed, 0 insertions, 709 deletions
diff --git a/smtpd/table.c b/smtpd/table.c deleted file mode 100644 index 469eeee1..00000000 --- a/smtpd/table.c +++ /dev/null @@ -1,709 +0,0 @@ -/* $OpenBSD: table.c,v 1.48 2019/01/10 07:40:52 eric Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <netinet/in.h> -#include <arpa/inet.h> -#include <net/if.h> - -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <regex.h> -#include <limits.h> -#include <string.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -struct table_backend *table_backend_lookup(const char *); - -extern struct table_backend table_backend_static; -#ifdef HAVE_DB_API -extern struct table_backend table_backend_db; -#endif -extern struct table_backend table_backend_getpwnam; -extern struct table_backend table_backend_proc; - -static const char * table_service_name(enum table_service); -static int table_parse_lookup(enum table_service, const char *, const char *, - union lookup *); -static int parse_sockaddr(struct sockaddr *, int, const char *); - -static unsigned int last_table_id = 0; - -static struct table_backend *backends[] = { - &table_backend_static, -#ifdef HAVE_DB_API - &table_backend_db, -#endif - &table_backend_getpwnam, - &table_backend_proc, - NULL -}; - -struct table_backend * -table_backend_lookup(const char *backend) -{ - int i; - - if (!strcmp(backend, "file")) - backend = "static"; - - for (i = 0; backends[i]; i++) - if (!strcmp(backends[i]->name, backend)) - return (backends[i]); - - return NULL; -} - -static const char * -table_service_name(enum table_service s) -{ - switch (s) { - case K_NONE: return "NONE"; - case K_ALIAS: return "ALIAS"; - case K_DOMAIN: return "DOMAIN"; - case K_CREDENTIALS: return "CREDENTIALS"; - case K_NETADDR: return "NETADDR"; - case K_USERINFO: return "USERINFO"; - case K_SOURCE: return "SOURCE"; - case K_MAILADDR: return "MAILADDR"; - case K_ADDRNAME: return "ADDRNAME"; - case K_MAILADDRMAP: return "MAILADDRMAP"; - case K_RELAYHOST: return "RELAYHOST"; - case K_STRING: return "STRING"; - case K_REGEX: return "REGEX"; - } - return "???"; -} - -struct table * -table_find(struct smtpd *conf, const char *name) -{ - return dict_get(conf->sc_tables_dict, name); -} - -int -table_match(struct table *table, enum table_service kind, const char *key) -{ - return table_lookup(table, kind, key, NULL); -} - -int -table_lookup(struct table *table, enum table_service kind, const char *key, - union lookup *lk) -{ - char lkey[1024], *buf = NULL; - int r; - - r = -1; - if (table->t_backend->lookup == NULL) - errno = ENOTSUP; - else if (!lowercase(lkey, key, sizeof lkey)) { - log_warnx("warn: lookup key too long: %s", key); - errno = EINVAL; - } - else - r = table->t_backend->lookup(table, kind, lkey, lk ? &buf : NULL); - - if (r == 1) { - log_trace(TRACE_LOOKUP, "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 ? "\"" : ""); - if (buf) - r = table_parse_lookup(kind, lkey, buf, lk); - } - else - log_trace(TRACE_LOOKUP, "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) : ""); - - free(buf); - - return (r); -} - -int -table_fetch(struct table *table, enum table_service kind, union lookup *lk) -{ - char *buf = NULL; - int r; - - r = -1; - if (table->t_backend->fetch == NULL) - errno = ENOTSUP; - else - r = table->t_backend->fetch(table, kind, &buf); - - if (r == 1) { - log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> \"%s\"", - table_service_name(kind), - table->t_backend->name, - table->t_name, - buf); - r = table_parse_lookup(kind, NULL, buf, lk); - } - else - log_trace(TRACE_LOOKUP, "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) : ""); - - free(buf); - - return (r); -} - -struct table * -table_create(struct smtpd *conf, const char *backend, const char *name, - const char *config) -{ - struct table *t; - struct table_backend *tb; - char path[LINE_MAX]; - size_t n; - struct stat sb; - - if (name && table_find(conf, name)) - fatalx("table_create: table \"%s\" already defined", name); - - if ((tb = table_backend_lookup(backend)) == NULL) { - if ((size_t)snprintf(path, sizeof(path), PATH_LIBEXEC"/table-%s", - backend) >= sizeof(path)) { - fatalx("table_create: path too long \"" - PATH_LIBEXEC"/table-%s\"", backend); - } - if (stat(path, &sb) == 0) { - tb = table_backend_lookup("proc"); - (void)strlcpy(path, backend, sizeof(path)); - if (config) { - (void)strlcat(path, ":", sizeof(path)); - if (strlcat(path, config, sizeof(path)) - >= sizeof(path)) - fatalx("table_create: config file path too long"); - } - config = path; - } - } - - if (tb == NULL) - fatalx("table_create: backend \"%s\" does not exist", backend); - - t = xcalloc(1, sizeof(*t)); - t->t_backend = tb; - - if (config) { - if (strlcpy(t->t_config, config, sizeof t->t_config) - >= sizeof t->t_config) - fatalx("table_create: table config \"%s\" too large", - t->t_config); - } - - if (strcmp(tb->name, "static") != 0) - t->t_type = T_DYNAMIC; - - if (name == NULL) - (void)snprintf(t->t_name, sizeof(t->t_name), "<dynamic:%u>", - last_table_id++); - else { - n = strlcpy(t->t_name, name, sizeof(t->t_name)); - if (n >= sizeof(t->t_name)) - fatalx("table_create: table name too long"); - } - - dict_set(conf->sc_tables_dict, t->t_name, t); - - return (t); -} - -void -table_destroy(struct smtpd *conf, struct table *t) -{ - dict_xpop(conf->sc_tables_dict, t->t_name); - free(t); -} - -int -table_config(struct table *t) -{ - if (t->t_backend->config == NULL) - return (1); - return (t->t_backend->config(t)); -} - -void -table_add(struct table *t, const char *key, const char *val) -{ - if (t->t_backend->add == NULL) - fatalx("table_add: cannot add to table"); - - if (t->t_backend->add(t, key, val) == 0) - log_warnx("warn: failed to add \"%s\" in table \"%s\"", key, t->t_name); -} - -void -table_dump(struct table *t) -{ - const char *type; - char buf[LINE_MAX]; - - switch(t->t_type) { - case T_NONE: - type = "NONE"; - break; - case T_DYNAMIC: - type = "DYNAMIC"; - break; - case T_LIST: - type = "LIST"; - break; - case T_HASH: - type = "HASH"; - break; - default: - type = "???"; - break; - } - - if (t->t_config[0]) - snprintf(buf, sizeof(buf), " config=\"%s\"", t->t_config); - else - buf[0] = '\0'; - - log_debug("TABLE \"%s\" backend=%s type=%s%s", t->t_name, - t->t_backend->name, type, buf); - - if (t->t_backend->dump) - t->t_backend->dump(t); -} - -int -table_check_type(struct table *t, uint32_t mask) -{ - return t->t_type & mask; -} - -int -table_check_service(struct table *t, uint32_t mask) -{ - return t->t_backend->services & mask; -} - -int -table_check_use(struct table *t, uint32_t tmask, uint32_t smask) -{ - return table_check_type(t, tmask) && table_check_service(t, smask); -} - -int -table_open(struct table *t) -{ - if (t->t_backend->open == NULL) - return (1); - return (t->t_backend->open(t)); -} - -void -table_close(struct table *t) -{ - if (t->t_backend->close) - t->t_backend->close(t); -} - -int -table_update(struct table *t) -{ - if (t->t_backend->update == NULL) - return (1); - return (t->t_backend->update(t)); -} - - -/* - * quick reminder: - * in *_match() s1 comes from session, s2 comes from table - */ - -int -table_domain_match(const char *s1, const char *s2) -{ - return hostname_match(s1, s2); -} - -int -table_mailaddr_match(const char *s1, const char *s2) -{ - struct mailaddr m1; - struct mailaddr m2; - - if (!text_to_mailaddr(&m1, s1)) - return 0; - if (!text_to_mailaddr(&m2, s2)) - return 0; - return mailaddr_match(&m1, &m2); -} - -static int table_match_mask(struct sockaddr_storage *, struct netaddr *); -static int table_inet4_match(struct sockaddr_in *, struct netaddr *); -static int table_inet6_match(struct sockaddr_in6 *, struct netaddr *); - -int -table_netaddr_match(const char *s1, const char *s2) -{ - struct netaddr n1; - struct netaddr n2; - - if (strcasecmp(s1, s2) == 0) - return 1; - if (!text_to_netaddr(&n1, s1)) - return 0; - if (!text_to_netaddr(&n2, s2)) - return 0; - if (n1.ss.ss_family != n2.ss.ss_family) - return 0; - if (SS_LEN(&n1.ss) != SS_LEN(&n2.ss)) - return 0; - return table_match_mask(&n1.ss, &n2); -} - -static int -table_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask) -{ - if (ss->ss_family == AF_INET) - return table_inet4_match((struct sockaddr_in *)ss, ssmask); - - if (ss->ss_family == AF_INET6) - return table_inet6_match((struct sockaddr_in6 *)ss, ssmask); - - return (0); -} - -static int -table_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask) -{ - in_addr_t mask; - int i; - - /* a.b.c.d/8 -> htonl(0xff000000) */ - mask = 0; - for (i = 0; i < ssmask->bits; ++i) - mask = (mask >> 1) | 0x80000000; - mask = htonl(mask); - - /* (addr & mask) == (net & mask) */ - if ((ss->sin_addr.s_addr & mask) == - (((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask)) - return 1; - - return 0; -} - -static int -table_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask) -{ - struct in6_addr *in; - struct in6_addr *inmask; - struct in6_addr mask; - int i; - - memset(&mask, 0, sizeof(mask)); - for (i = 0; i < ssmask->bits / 8; i++) - mask.s6_addr[i] = 0xff; - i = ssmask->bits % 8; - if (i) - mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i; - - in = &ss->sin6_addr; - inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr; - - for (i = 0; i < 16; i++) { - if ((in->s6_addr[i] & mask.s6_addr[i]) != - (inmask->s6_addr[i] & mask.s6_addr[i])) - return (0); - } - - return (1); -} - -int -table_regex_match(const char *string, const char *pattern) -{ - regex_t preg; - int cflags = REG_EXTENDED|REG_NOSUB; - - if (strncmp(pattern, "(?i)", 4) == 0) { - cflags |= REG_ICASE; - pattern += 4; - } - - if (regcomp(&preg, pattern, cflags) != 0) - return (0); - - if (regexec(&preg, string, 0, NULL, 0) != 0) - return (0); - - return (1); -} - -void -table_dump_all(struct smtpd *conf) -{ - struct table *t; - void *iter; - - iter = NULL; - while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t)) - table_dump(t); -} - -void -table_open_all(struct smtpd *conf) -{ - struct table *t; - void *iter; - - iter = NULL; - while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t)) - if (!table_open(t)) - fatalx("failed to open table %s", t->t_name); -} - -void -table_close_all(struct smtpd *conf) -{ - struct table *t; - void *iter; - - iter = NULL; - while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t)) - table_close(t); -} - -static int -table_parse_lookup(enum table_service service, const char *key, - const char *line, union lookup *lk) -{ - char buffer[LINE_MAX], *p; - size_t len; - - len = strlen(line); - - switch (service) { - case K_ALIAS: - lk->expand = calloc(1, sizeof(*lk->expand)); - if (lk->expand == NULL) - return (-1); - if (!expand_line(lk->expand, line, 1)) { - expand_free(lk->expand); - return (-1); - } - return (1); - - case K_DOMAIN: - if (strlcpy(lk->domain.name, line, sizeof(lk->domain.name)) - >= sizeof(lk->domain.name)) - return (-1); - return (1); - - case K_CREDENTIALS: - - /* credentials are stored as user:password */ - if (len < 3) - return (-1); - - /* too big to fit in a smtp session line */ - if (len >= LINE_MAX) - return (-1); - - p = strchr(line, ':'); - if (p == NULL) { - if (strlcpy(lk->creds.username, key, sizeof (lk->creds.username)) - >= sizeof (lk->creds.username)) - return (-1); - if (strlcpy(lk->creds.password, line, sizeof(lk->creds.password)) - >= sizeof(lk->creds.password)) - return (-1); - return (1); - } - - if (p == line || p == line + len - 1) - return (-1); - - memmove(lk->creds.username, line, p - line); - lk->creds.username[p - line] = '\0'; - - if (strlcpy(lk->creds.password, p+1, sizeof(lk->creds.password)) - >= sizeof(lk->creds.password)) - return (-1); - - return (1); - - case K_NETADDR: - if (!text_to_netaddr(&lk->netaddr, line)) - return (-1); - return (1); - - case K_USERINFO: - if (!bsnprintf(buffer, sizeof(buffer), "%s:%s", key, line)) - return (-1); - if (!text_to_userinfo(&lk->userinfo, buffer)) - return (-1); - return (1); - - case K_SOURCE: - if (parse_sockaddr((struct sockaddr *)&lk->source.addr, - PF_UNSPEC, line) == -1) - return (-1); - return (1); - - case K_MAILADDR: - if (!text_to_mailaddr(&lk->mailaddr, line)) - return (-1); - return (1); - - case K_MAILADDRMAP: - lk->maddrmap = calloc(1, sizeof(*lk->maddrmap)); - if (lk->maddrmap == NULL) - return (-1); - maddrmap_init(lk->maddrmap); - if (!mailaddr_line(lk->maddrmap, line)) { - maddrmap_free(lk->maddrmap); - return (-1); - } - return (1); - - case K_ADDRNAME: - if (parse_sockaddr((struct sockaddr *)&lk->addrname.addr, - PF_UNSPEC, key) == -1) - return (-1); - if (strlcpy(lk->addrname.name, line, sizeof(lk->addrname.name)) - >= sizeof(lk->addrname.name)) - return (-1); - return (1); - - case K_RELAYHOST: - if (strlcpy(lk->relayhost, line, sizeof(lk->relayhost)) - >= sizeof(lk->relayhost)) - return (-1); - return (1); - - default: - return (-1); - } -} - -static int -parse_sockaddr(struct sockaddr *sa, int family, const char *str) -{ - struct in_addr ina; - struct in6_addr in6a; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - char *cp, *str2; - const char *errstr; - - switch (family) { - case PF_UNSPEC: - if (parse_sockaddr(sa, PF_INET, str) == 0) - return (0); - return parse_sockaddr(sa, PF_INET6, str); - - case PF_INET: - if (inet_pton(PF_INET, str, &ina) != 1) - return (-1); - - sin = (struct sockaddr_in *)sa; - memset(sin, 0, sizeof *sin); -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin->sin_len = sizeof(struct sockaddr_in); -#endif - sin->sin_family = PF_INET; - sin->sin_addr.s_addr = ina.s_addr; - return (0); - - case PF_INET6: - if (strncasecmp("ipv6:", str, 5) == 0) - str += 5; - cp = strchr(str, SCOPE_DELIMITER); - if (cp) { - str2 = strdup(str); - if (str2 == NULL) - return (-1); - str2[cp - str] = '\0'; - if (inet_pton(PF_INET6, str2, &in6a) != 1) { - free(str2); - return (-1); - } - cp++; - free(str2); - } else if (inet_pton(PF_INET6, str, &in6a) != 1) - return (-1); - - sin6 = (struct sockaddr_in6 *)sa; - memset(sin6, 0, sizeof *sin6); -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - sin6->sin6_len = sizeof(struct sockaddr_in6); -#endif - sin6->sin6_family = PF_INET6; - sin6->sin6_addr = in6a; - - if (cp == NULL) - return (0); - - if (IN6_IS_ADDR_LINKLOCAL(&in6a) || - IN6_IS_ADDR_MC_LINKLOCAL(&in6a) || - IN6_IS_ADDR_MC_NODELOCAL(&in6a)) - if ((sin6->sin6_scope_id = if_nametoindex(cp))) - return (0); - - sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr); - if (errstr) - return (-1); - return (0); - - default: - break; - } - - return (-1); -} |