diff options
author | Eric Faurot <eric@cvs.openbsd.org> | 2013-07-20 09:06:47 +0000 |
---|---|---|
committer | Eric Faurot <eric@cvs.openbsd.org> | 2013-07-20 09:06:47 +0000 |
commit | 423fb084212ae7bd9fb0bf673eaa82d7259e724f (patch) | |
tree | eb70b490f97fcad2ce2d58da5fc61100c5094d98 | |
parent | d7a4fe68373b71c4e4bec9c27af2d346d28920df (diff) |
Update ldap and sqlite table backends and provide them as external backends.
-rw-r--r-- | usr.sbin/smtpd/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/smtpd/table-ldap/Makefile | 28 | ||||
-rw-r--r-- | usr.sbin/smtpd/table-sqlite/Makefile | 27 | ||||
-rw-r--r-- | usr.sbin/smtpd/table.c | 6 | ||||
-rw-r--r-- | usr.sbin/smtpd/table_api.c | 6 | ||||
-rw-r--r-- | usr.sbin/smtpd/table_ldap.c | 912 | ||||
-rw-r--r-- | usr.sbin/smtpd/table_sqlite.c | 709 |
7 files changed, 888 insertions, 804 deletions
diff --git a/usr.sbin/smtpd/Makefile b/usr.sbin/smtpd/Makefile index 06920b9248e..d26558ad77d 100644 --- a/usr.sbin/smtpd/Makefile +++ b/usr.sbin/smtpd/Makefile @@ -1,7 +1,9 @@ -# $OpenBSD: Makefile,v 1.10 2013/05/24 17:03:14 eric Exp $ +# $OpenBSD: Makefile,v 1.11 2013/07/20 09:06:46 eric Exp $ .include <bsd.own.mk> SUBDIR = makemap smtpd smtpctl +SUBDIR+= table-ldap +SUBDIR+= table-sqlite .include <bsd.subdir.mk> diff --git a/usr.sbin/smtpd/table-ldap/Makefile b/usr.sbin/smtpd/table-ldap/Makefile new file mode 100644 index 00000000000..1a766b71877 --- /dev/null +++ b/usr.sbin/smtpd/table-ldap/Makefile @@ -0,0 +1,28 @@ +# $OpenBSD: Makefile,v 1.1 2013/07/20 09:06:46 eric Exp $ + +.PATH: ${.CURDIR}/.. + +PROG= table-ldap + +SRCS= table_ldap.c +SRCS+= table_api.c +SRCS+= aldap.c +SRCS+= ber.c +SRCS+= log.c + +NOMAN= noman + +BINDIR= /usr/libexec/smtpd + +DPADD= ${LIBUTIL} +LDADD= -lutil + +CFLAGS+= -g3 -ggdb -I${.CURDIR}/.. +CFLAGS+= -Wall -Wstrict-prototypes -Wmissing-prototypes +CFLAGS+= -Wmissing-declarations +CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS+= -Wsign-compare -Wbounded +CFLAGS+= -DNO_IO +#CFLAGS+= -Werror # during development phase (breaks some archs) + +.include <bsd.prog.mk> diff --git a/usr.sbin/smtpd/table-sqlite/Makefile b/usr.sbin/smtpd/table-sqlite/Makefile new file mode 100644 index 00000000000..b69b120a0a0 --- /dev/null +++ b/usr.sbin/smtpd/table-sqlite/Makefile @@ -0,0 +1,27 @@ +# $OpenBSD: Makefile,v 1.1 2013/07/20 09:06:46 eric Exp $ + +.PATH: ${.CURDIR}/.. + +PROG= table-sqlite + +SRCS= table_sqlite.c +SRCS+= table_api.c +SRCS+= dict.c +SRCS+= log.c + +NOMAN= noman + +BINDIR= /usr/libexec/smtpd + +DPADD= ${LIBUTIL} ${LIBSQLITE3} +LDADD= -lutil -lsqlite3 + +CFLAGS+= -g3 -ggdb -I${.CURDIR}/.. +CFLAGS+= -Wall -Wstrict-prototypes -Wmissing-prototypes +CFLAGS+= -Wmissing-declarations +CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS+= -Wsign-compare -Wbounded +CFLAGS+= -DNO_IO +#CFLAGS+= -Werror # during development phase (breaks some archs) + +.include <bsd.prog.mk> diff --git a/usr.sbin/smtpd/table.c b/usr.sbin/smtpd/table.c index 8cc30983256..45e4210edb0 100644 --- a/usr.sbin/smtpd/table.c +++ b/usr.sbin/smtpd/table.c @@ -1,4 +1,4 @@ -/* $OpenBSD: table.c,v 1.6 2013/07/19 19:53:33 eric Exp $ */ +/* $OpenBSD: table.c,v 1.7 2013/07/20 09:06:46 eric Exp $ */ /* * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> @@ -205,10 +205,10 @@ table_create(const char *backend, const char *name, const char *tag, errx(1, "table_create: table \"%s\" already defined", name); if ((tb = table_backend_lookup(backend)) == NULL) { - if (snprintf(path, sizeof(path), PATH_TABLES "/backend-table-%s", + if (snprintf(path, sizeof(path), PATH_TABLES "/table-%s", backend) >= (int)sizeof(path)) { errx(1, "table_create: path too long \"" - PATH_TABLES "/backend-table-%s\"", backend); + PATH_TABLES "/table-%s\"", backend); } if (stat(path, &sb) == 0) { tb = table_backend_lookup("proc"); diff --git a/usr.sbin/smtpd/table_api.c b/usr.sbin/smtpd/table_api.c index 4008d30a884..93ba00ecc41 100644 --- a/usr.sbin/smtpd/table_api.c +++ b/usr.sbin/smtpd/table_api.c @@ -1,4 +1,4 @@ -/* $OpenBSD: table_api.c,v 1.1 2013/07/19 19:53:33 eric Exp $ */ +/* $OpenBSD: table_api.c,v 1.2 2013/07/20 09:06:46 eric Exp $ */ /* * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> @@ -44,8 +44,10 @@ static struct imsg imsg; static size_t rlen; static char *rdata; static struct ibuf *buf; +#if 0 static char *rootpath; static char *user = SMTPD_USER; +#endif static void table_msg_get(void *dst, size_t len) @@ -229,7 +231,9 @@ table_api_on_fetch(int(*cb)(int, char *, size_t)) int table_api_dispatch(void) { +#if 0 struct passwd *pw; +#endif ssize_t n; #if 0 diff --git a/usr.sbin/smtpd/table_ldap.c b/usr.sbin/smtpd/table_ldap.c index f3aff8b43a7..51d170144cb 100644 --- a/usr.sbin/smtpd/table_ldap.c +++ b/usr.sbin/smtpd/table_ldap.c @@ -1,7 +1,7 @@ -/* $OpenBSD: table_ldap.c,v 1.4 2013/05/24 17:03:14 eric Exp $ */ +/* $OpenBSD: table_ldap.c,v 1.5 2013/07/20 09:06:46 eric Exp $ */ /* - * Copyright (c) 2010-2012 Gilles Chehade <gilles@poolp.org> + * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,237 +17,422 @@ */ #include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <netinet/in.h> -#include <arpa/inet.h> #include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <stdio.h> +#include <getopt.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include "smtpd.h" -#include "aldap.h" +#include "smtpd-defines.h" +#include "smtpd-api.h" #include "log.h" +#include "aldap.h" -#define MAX_LDAP_IDENTIFIER 32 -#define MAX_LDAP_URL 256 -#define MAX_LDAP_USERNAME 256 -#define MAX_LDAP_PASSWORD 256 -#define MAX_LDAP_BASELEN 128 -#define MAX_LDAP_FILTERLEN 1024 -#define MAX_LDAP_FIELDLEN 128 - -static void *table_ldap_open(struct table *); -static int table_ldap_update(struct table *); -static int table_ldap_config(struct table *); -static int table_ldap_lookup(void *, const char *, enum table_service, union lookup *); -static int table_ldap_fetch(void *, enum table_service, union lookup *); -static void table_ldap_close(void *); -static struct aldap *ldap_client_connect(const char *); - -struct table_backend table_backend_ldap = { - K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_USERINFO, /* K_NETADDR|K_SOURCE,*/ - table_ldap_config, - table_ldap_open, - table_ldap_update, - table_ldap_close, - table_ldap_lookup, - table_ldap_fetch +#define MAX_LDAP_IDENTIFIER 32 +#define MAX_LDAP_URL 256 +#define MAX_LDAP_USERNAME 256 +#define MAX_LDAP_PASSWORD 256 +#define MAX_LDAP_BASELEN 128 +#define MAX_LDAP_FILTERLEN 1024 +#define MAX_LDAP_FIELDLEN 128 + + +enum { + LDAP_ALIAS = 0, + LDAP_DOMAIN, + LDAP_CREDENTIALS, + LDAP_NETADDR, + LDAP_USERINFO, + LDAP_SOURCE, + LDAP_MAILADDR, + LDAP_ADDRNAME, + + LDAP_MAX }; -struct table_ldap_handle { - struct aldap *aldap; - struct table *table; +#define MAX_ATTRS 6 + +struct query { + char *filter; + char *attrs[MAX_ATTRS]; + int attrn; }; -static int parse_attributes(char **, const char *, size_t); -static int table_ldap_internal_query(struct aldap *, const char *, - const char *, char **, char ***, size_t); +static int table_ldap_update(void); +static int table_ldap_check(int, const char *); +static int table_ldap_lookup(int, const char *, char *, size_t); +static int table_ldap_fetch(int, char *, size_t); -static int table_ldap_alias(struct table_ldap_handle *, const char *, union lookup *); -static int table_ldap_credentials(struct table_ldap_handle *, const char *, union lookup *); -static int table_ldap_domain(struct table_ldap_handle *, const char *, union lookup *); -static int table_ldap_userinfo(struct table_ldap_handle *, const char *, union lookup *); +static int ldap_config(void); +static int ldap_open(void); +static int ldap_query(const char *, char **, char ***, size_t); +static int ldap_parse_attributes(char **, const char *, const char *, size_t); +static int ldap_run_query(int type, const char *, char *, size_t); +static char *config; -static int -table_ldap_config(struct table *table) +static char *url; +static char *username; +static char *password; +static char *basedn; + +static struct aldap *aldap; +static struct query queries[LDAP_MAX]; + +int +main(int argc, char **argv) { - struct table *cfg = NULL; + int ch; - /* no config ? broken */ - if (table->t_config[0] == '\0') - return 0; + log_init(1); + log_verbose(~0); - cfg = table_create("static", table->t_name, "conf", table->t_config); - if (!table_config(cfg)) - goto err; + while ((ch = getopt(argc, argv, "")) != -1) { + switch (ch) { + default: + log_warnx("warn: table-ldap: bad option"); + return (1); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; - /* sanity checks */ - if (table_get(cfg, "url") == NULL) { - log_warnx("table_ldap: missing 'url' configuration"); - goto err; + if (argc != 1) { + log_warnx("warn: table-ldap: bogus argument(s)"); + return (1); } - if (table_get(cfg, "basedn") == NULL) { - log_warnx("table_ldap: missing 'basedn' configuration"); - goto err; + config = argv[0]; + + if (!ldap_config()) { + log_warnx("warn: table-ldap: could not parse config"); + return (1); } - return 1; + log_debug("debug: table-ldap: done reading config"); -err: - table_destroy(cfg); - return 0; + if (!ldap_open()) { + log_warnx("warn: table-ldap: failed to connect"); + return (1); + } + + log_debug("debug: table-ldap: connected"); + table_api_on_update(table_ldap_update); + table_api_on_check(table_ldap_check); + table_api_on_lookup(table_ldap_lookup); + table_api_on_fetch(table_ldap_fetch); + table_api_dispatch(); + + return (0); } static int -table_ldap_update(struct table *table) +table_ldap_update(void) { - return 1; + return (1); } -static void * -table_ldap_open(struct table *table) +static int +table_ldap_check(int service, const char *key) { - struct table *cfg = NULL; - struct table_ldap_handle *tlh = NULL; - struct aldap_message *message = NULL; - char *url = NULL; - char *username = NULL; - char *password = NULL; - - cfg = table_find(table->t_name, "conf"); - if (table_get(cfg, "url") == NULL || - table_get(cfg, "username") == NULL || - table_get(cfg, "password") == NULL) - goto err; - - url = xstrdup(table_get(cfg, "url"), "table_ldap_open"); - username = xstrdup(table_get(cfg, "username"), "table_ldap_open"); - password = xstrdup(table_get(cfg, "password"), "table_ldap_open"); - - tlh = xcalloc(1, sizeof(*tlh), "table_ldap_open"); - tlh->table = table; - tlh->aldap = ldap_client_connect(url); - if (tlh->aldap == NULL) { - log_warnx("table_ldap_open: ldap_client_connect error"); - goto err; + switch(service) { + case K_ALIAS: + case K_DOMAIN: + case K_CREDENTIALS: + case K_USERINFO: + return ldap_run_query(service, key, NULL, 0); + default: + return (-1); } +} - if (aldap_bind(tlh->aldap, username, password) == -1) { - log_warnx("table_ldap_open: aldap_bind error"); - goto err; +static int +table_ldap_lookup(int service, const char *key, char *dst, size_t sz) +{ + switch(service) { + case K_ALIAS: + case K_DOMAIN: + case K_CREDENTIALS: + case K_USERINFO: + return ldap_run_query(service, key, dst, sz); + default: + return (-1); } +} - if ((message = aldap_parse(tlh->aldap)) == NULL) { - log_warnx("table_ldap_open: aldap_parse"); - goto err; +static int +table_ldap_fetch(int service, char *dst, size_t sz) +{ + return (-1); +} + +static struct aldap * +ldap_connect(const char *addr) +{ + struct aldap_url lu; + struct addrinfo hints, *res0, *res; + char *buf; + int error, fd = -1; + + if ((buf = strdup(addr)) == NULL) + return (NULL); + + /* XXX buf leak */ + + if (aldap_parse_url(buf, &lu) != 1) { + log_warnx("warn: table-ldap: ldap_parse_url fail"); + return (NULL); } - switch (aldap_get_resultcode(message)) { - case LDAP_SUCCESS: - log_warnx("table_ldap_open: ldap server accepted credentials"); - break; - case LDAP_INVALID_CREDENTIALS: - log_warnx("table_ldap_open: ldap server refused credentials"); - goto err; - default: - log_warnx("table_ldap_open: failed to bind, result #%d", aldap_get_resultcode(message)); - goto err; + bzero(&hints, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; /* DUMMY */ + error = getaddrinfo(lu.host, NULL, &hints, &res0); + if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME) + return (NULL); + if (error) { + log_warnx("warn: table-ldap: could not parse \"%s\": %s", + lu.host, gai_strerror(error)); + return (NULL); } - return tlh; + for (res = res0; res; res = res->ai_next) { + if (res->ai_family != AF_INET && res->ai_family != AF_INET6) + continue; + + fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (fd == -1) + continue; -err: - if (tlh) { - if (tlh->aldap != NULL) - aldap_close(tlh->aldap); - free(tlh); + if (res->ai_family == AF_INET) { + struct sockaddr_in sin4 = *(struct sockaddr_in *)res->ai_addr; + sin4.sin_port = htons(lu.port); + if (connect(fd, (struct sockaddr *)&sin4, res->ai_addrlen) == 0) + return aldap_init(fd); + } + else if (res->ai_family == AF_INET6) { + struct sockaddr_in6 sin6 = *(struct sockaddr_in6 *)res->ai_addr; + sin6.sin6_port = htons(lu.port); + if (connect(fd, (struct sockaddr *)&sin6, res->ai_addrlen) == 0) + return aldap_init(fd); + } + + close(fd); + fd = -1; } - if (message != NULL) - aldap_freemsg(message); - return NULL; + + return (NULL); } -static void -table_ldap_close(void *hdl) +static int +read_value(char **store, const char *key, const char *value) { - struct table_ldap_handle *tlh = hdl; + log_debug("debug: table-ldap: reading key \"%s\" -> \"%s\"", + key, value); + + if (*store) { + log_warnx("warn: table-ldap: duplicate key %s", key); + return (0); + } + + if ((*store = strdup(value)) == NULL) { + log_warn("warn: table-ldap: strdup"); + return (0); + } - aldap_close(tlh->aldap); - free(tlh); + return (1); } static int -table_ldap_lookup(void *hdl, const char *key, enum table_service service, - union lookup *lk) +ldap_parse_attributes(char **attributes, const char *key, const char *line, + size_t expect) { - struct table_ldap_handle *tlh = hdl; - - switch (service) { - case K_ALIAS: - return table_ldap_alias(tlh, key, lk); - - case K_CREDENTIALS: - return table_ldap_credentials(tlh, key, lk); + char buffer[1024]; + char *p; + size_t m, n; - case K_DOMAIN: - return table_ldap_domain(tlh, key, lk); + log_debug("debug: table-ldap: parsing attribute \"%s\" (%zu) -> \"%s\"", + key, expect, line); - case K_USERINFO: - return table_ldap_userinfo(tlh, key, lk); + if (strlcpy(buffer, line, sizeof buffer) >= sizeof buffer) + return (0); - default: - break; + m = 1; + for (p = buffer; *p; ++p) { + if (*p == ',') { + *p = 0; + m++; + } } + if (expect != m) + return (0); - return 0; + p = buffer; + for (n = 0; n < expect; ++n) + attributes[n] = NULL; + for (n = 0; n < m; ++n) { + attributes[n] = strdup(p); + if (attributes[n] == NULL) { + log_warnx("warn: table-ldap: strdup"); + return (0); /* XXX cleanup */ + } + p += strlen(p) + 1; + } + return (1); } static int -table_ldap_fetch(void *hdl, enum table_service service, union lookup *lk) +ldap_config(void) { - /* fetch not support for LDAP at this point */ - return -1; + size_t flen; + FILE *fp; + char *key, *value, *buf, *lbuf; + + fp = fopen(config, "r"); + if (fp == NULL) + return (0); + + lbuf = NULL; + while ((buf = fgetln(fp, &flen))) { + if (buf[flen - 1] == '\n') + buf[flen - 1] = '\0'; + else { + lbuf = malloc(flen + 1); + if (lbuf == NULL) { + log_warn("warn: table-ldap: malloc"); + return (0); + } + memcpy(lbuf, buf, flen); + lbuf[flen] = '\0'; + buf = lbuf; + } + + key = buf; + while (isspace((int)*key)) + ++key; + if (*key == '\0' || *key == '#') + continue; + value = key; + strsep(&value, " \t:"); + if (value) { + while (*value) { + if (!isspace(*value) && + !(*value == ':' && isspace(*(value + 1)))) + break; + ++value; + } + if (*value == '\0') + value = NULL; + } + + if (value == NULL) { + log_warnx("warn: table-ldap: missing value for key %s", key); + continue; + } + + if (!strcmp(key, "url")) + read_value(&url, key, value); + else if (!strcmp(key, "username")) + read_value(&username, key, value); + else if (!strcmp(key, "password")) + read_value(&password, key, value); + else if (!strcmp(key, "basedn")) + read_value(&basedn, key, value); + + else if (!strcmp(key, "alias_filter")) + read_value(&queries[LDAP_ALIAS].filter, key, value); + else if (!strcmp(key, "alias_attributes")) + ldap_parse_attributes(queries[LDAP_ALIAS].attrs, + key, value, 1); + + else if (!strcmp(key, "credentials_filter")) + read_value(&queries[LDAP_CREDENTIALS].filter, key, value); + else if (!strcmp(key, "credentials_attributes")) + ldap_parse_attributes(queries[LDAP_CREDENTIALS].attrs, + key, value, 2); + + else if (!strcmp(key, "domain_filter")) + read_value(&queries[LDAP_DOMAIN].filter, key, value); + else if (!strcmp(key, "domain_attributes")) + ldap_parse_attributes(queries[LDAP_DOMAIN].attrs, + key, value, 1); + + else if (!strcmp(key, "userinfo_filter")) + read_value(&queries[LDAP_USERINFO].filter, key, value); + else if (!strcmp(key, "userinfo_attributes")) + ldap_parse_attributes(queries[LDAP_USERINFO].attrs, + key, value, 4); + else + log_warnx("warn: table-ldap: bogus entry \"%s\"", key); + } + + free(lbuf); + fclose(fp); + return (1); } static int -filter_expand(char **expfilter, const char *filter, const char *key) +ldap_open(void) { - if (asprintf(expfilter, filter, key) < 0) - return 0; - return 1; + struct aldap_message *amsg = NULL; + + aldap = ldap_connect(url); + if (aldap == NULL) { + log_warnx("warn: table-ldap: ldap_connect error"); + goto err; + } + + if (aldap_bind(aldap, username, password) == -1) { + log_warnx("warn: table-ldap: aldap_bind error"); + goto err; + } + + if ((amsg = aldap_parse(aldap)) == NULL) { + log_warnx("warn: table-ldap: aldap_parse"); + goto err; + } + + switch (aldap_get_resultcode(amsg)) { + case LDAP_SUCCESS: + log_debug("debug: table-ldap: ldap server accepted credentials"); + break; + case LDAP_INVALID_CREDENTIALS: + log_warnx("warn: table-ldap: ldap server refused credentials"); + goto err; + default: + log_warnx("warn: table-ldap: failed to bind, result #%d", + aldap_get_resultcode(amsg)); + goto err; + } + + if (amsg) + aldap_freemsg(amsg); + return (1); + +err: + if (aldap) + aldap_close(aldap); + if (amsg) + aldap_freemsg(amsg); + return (0); } static int -table_ldap_internal_query(struct aldap *aldap, const char *basedn, - const char *filter, char **attributes, char ***outp, size_t n) +ldap_query(const char *filter, char **attributes, char ***outp, size_t n) { - struct aldap_message *m = NULL; - struct aldap_page_control *pg = NULL; - int ret; - int found; - size_t i; - char basedn__[MAX_LDAP_BASELEN]; - char filter__[MAX_LDAP_FILTERLEN]; - - if (strlcpy(basedn__, basedn, sizeof basedn__) - >= sizeof basedn__) + struct aldap_message *m = NULL; + struct aldap_page_control *pg = NULL; + int ret, found; + size_t i; + char basedn__[MAX_LDAP_BASELEN]; + char filter__[MAX_LDAP_FILTERLEN]; + + if (strlcpy(basedn__, basedn, sizeof basedn__) >= sizeof basedn__) return -1; - if (strlcpy(filter__, filter, sizeof filter__) - >= sizeof filter__) + if (strlcpy(filter__, filter, sizeof filter__) >= sizeof filter__) return -1; found = 0; do { @@ -296,360 +481,73 @@ end: return ret; } - -static int -table_ldap_credentials(struct table_ldap_handle *tlh, const char *key, union lookup *lk) -{ - struct aldap *aldap = tlh->aldap; - struct table *cfg = table_find(tlh->table->t_name, "conf"); - const char *filter = NULL; - const char *basedn = NULL; - char *expfilter = NULL; - char *attributes[4]; - char **ret_attr[4]; - const char *attr; - char line[1024]; - int ret = -1; - size_t i; - - bzero(&attributes, sizeof attributes); - bzero(&ret_attr, sizeof ret_attr); - - basedn = table_get(cfg, "basedn"); - if ((filter = table_get(cfg, "credentials_filter")) == NULL) { - log_warnx("table_ldap: lookup: no filter configured for credentials"); - goto end; - } - - if ((attr = table_get(cfg, "credentials_attributes")) == NULL) { - log_warnx("table_ldap: lookup: no attributes configured for credentials"); - goto end; - } - - if (! filter_expand(&expfilter, filter, key)) { - log_warnx("table_ldap: lookup: couldn't expand filter"); - goto end; - } - - if (! parse_attributes(attributes, attr, 2)) { - log_warnx("table_ldap: lookup: failed to parse attributes"); - goto end; - } - - if ((ret = table_ldap_internal_query(aldap, basedn, expfilter, attributes, - ret_attr, nitems(attributes))) <= 0) - goto end; - - if (lk == NULL) - goto end; - - if (! bsnprintf(line, sizeof line, "%s:%s", ret_attr[0][0], ret_attr[0][1])) { - ret = -1; - goto end; - } - - bzero(&lk->creds, sizeof(lk->creds)); - if (! text_to_credentials(&lk->creds, line)) - ret = -1; - -end: - for (i = 0; i < nitems(attributes); ++i) { - free(attributes[i]); - if (ret_attr[i]) - aldap_free_attr(ret_attr[i]); - } - - free(expfilter); - log_debug("debug: table_ldap_credentials: ret=%d", ret); - return ret; -} - static int -table_ldap_domain(struct table_ldap_handle *tlh, const char *key, union lookup *lk) +ldap_run_query(int type, const char *key, char *dst, size_t sz) { - struct aldap *aldap = tlh->aldap; - struct table *cfg = table_find(tlh->table->t_name, "conf"); - const char *filter = NULL; - const char *basedn = NULL; - char *expfilter = NULL; - char *attributes[1]; - char **ret_attr[1]; - const char *attr; - int ret = -1; - size_t i; - - bzero(&attributes, sizeof attributes); - bzero(&ret_attr, sizeof ret_attr); - - log_debug("domain: %s", key); - basedn = table_get(cfg, "basedn"); - if ((filter = table_get(cfg, "domain_filter")) == NULL) { - log_warnx("table_ldap: lookup: no filter configured for domain"); - goto end; - } - - if ((attr = table_get(cfg, "domain_attributes")) == NULL) { - log_warnx("table_ldap: lookup: no attributes configured for domain"); - goto end; - } - - if (! filter_expand(&expfilter, filter, key)) { - log_warnx("table_ldap: lookup: couldn't expand filter"); - goto end; - } - - if (! parse_attributes(attributes, attr, 1)) { - log_warnx("table_ldap: lookup: failed to parse attributes"); - goto end; - } - - if ((ret = table_ldap_internal_query(aldap, basedn, expfilter, attributes, - ret_attr, nitems(attributes))) <= 0) - goto end; - - if (lk == NULL) - goto end; - - bzero(&lk->domain, sizeof(lk->domain)); - if (strlcpy(lk->domain.name, ret_attr[0][0], sizeof(lk->domain.name)) - >= sizeof(lk->domain.name)) - ret = -1; - -end: - for (i = 0; i < nitems(attributes); ++i) { - free(attributes[i]); - if (ret_attr[i]) - aldap_free_attr(ret_attr[i]); - } - free(expfilter); - log_debug("debug: table_ldap_domain: ret=%d", ret); - return ret; -} - -static int -table_ldap_userinfo(struct table_ldap_handle *tlh, const char *key, union lookup *lk) -{ - struct aldap *aldap = tlh->aldap; - struct table *cfg = table_find(tlh->table->t_name, "conf"); - const char *filter = NULL; - const char *basedn = NULL; - char *expfilter = NULL; - char *attributes[4]; - char **ret_attr[4]; - const char *attr; - char line[1024]; - int ret = -1; - size_t i; - - bzero(&attributes, sizeof attributes); - bzero(&ret_attr, sizeof ret_attr); - - basedn = table_get(cfg, "basedn"); - if ((filter = table_get(cfg, "userinfo_filter")) == NULL) { - log_warnx("table_ldap: lookup: no filter configured for userinfo"); - goto end; - } - - if ((attr = table_get(cfg, "userinfo_attributes")) == NULL) { - log_warnx("table_ldap: lookup: no attributes configured for userinfo"); - goto end; - } - - if (! filter_expand(&expfilter, filter, key)) { - log_warnx("table_ldap: lookup: couldn't expand filter"); - goto end; - } - - if (! parse_attributes(attributes, attr, 4)) { - log_warnx("table_ldap: lookup: failed to parse attributes"); - goto end; - } - - if ((ret = table_ldap_internal_query(aldap, basedn, expfilter, attributes, - ret_attr, nitems(attributes))) <= 0) - goto end; - - if (lk == NULL) - goto end; - - if (! bsnprintf(line, sizeof line, "%s:%s:%s:%s", - ret_attr[0][0], ret_attr[1][0], ret_attr[2][0], ret_attr[3][0])) { - ret = -1; - goto end; - } - - bzero(&lk->userinfo, sizeof(lk->userinfo)); - if (!text_to_userinfo(&(lk->userinfo), line)) - ret = -1; - -end: - for (i = 0; i < nitems(attributes); ++i) { - free(attributes[i]); - if (ret_attr[i]) - aldap_free_attr(ret_attr[i]); - } - free(expfilter); - log_debug("debug: table_ldap_userinfo: ret=%d", ret); - return ret; -} - -static int -table_ldap_alias(struct table_ldap_handle *tlh, const char *key, union lookup *lk) -{ - struct aldap *aldap = tlh->aldap; - struct table *cfg = table_find(tlh->table->t_name, "conf"); - const char *filter = NULL; - const char *basedn = NULL; - char *expfilter = NULL; - char *attributes[1]; - char **ret_attr[1]; - const char *attr; - int ret = -1; - size_t i; - - bzero(&attributes, sizeof attributes); - bzero(&ret_attr, sizeof ret_attr); - if (lk) - lk->expand = NULL; - - basedn = table_get(cfg, "basedn"); - if ((filter = table_get(cfg, "alias_filter")) == NULL) { - log_warnx("table_ldap: lookup: no filter configured for alias"); - goto end; - } - - if ((attr = table_get(cfg, "alias_attributes")) == NULL) { - log_warnx("table_ldap: lookup: no attributes configured for alias"); - goto end; - } - - if (! filter_expand(&expfilter, filter, key)) { - log_warnx("table_ldap: lookup: couldn't expand filter"); - goto end; + struct query *q; + char **res[4], filter[MAX_LDAP_FILTERLEN]; + int ret, i; + + switch (type) { + case K_ALIAS: q = &queries[LDAP_ALIAS]; break; + case K_DOMAIN: q = &queries[LDAP_DOMAIN]; break; + case K_CREDENTIALS: q = &queries[LDAP_CREDENTIALS]; break; + case K_NETADDR: q = &queries[LDAP_NETADDR]; break; + case K_USERINFO: q = &queries[LDAP_USERINFO]; break; + case K_SOURCE: q = &queries[LDAP_SOURCE]; break; + case K_MAILADDR: q = &queries[LDAP_MAILADDR]; break; + case K_ADDRNAME: q = &queries[LDAP_ADDRNAME]; break; + default: + return (-1); } - if (! parse_attributes(attributes, attr, 1)) { - log_warnx("table_ldap: lookup: failed to parse attributes"); - goto end; + if (snprintf(filter, sizeof(filter), q->filter, key) + >= (int)sizeof(filter)) { + log_warnx("warn: table-ldap: filter too large"); + return (-1); } - if ((ret = table_ldap_internal_query(aldap, basedn, expfilter, attributes, - ret_attr, nitems(attributes))) <= 0) + bzero(res, sizeof(res)); + ret = ldap_query(filter, q->attrs, res, q->attrn); + if (ret <= 0 || dst == NULL) goto end; - if (lk == NULL) - goto end; - - lk->expand = xcalloc(1, sizeof(*lk->expand), "table_ldap_alias"); - for (i = 0; ret_attr[0][i]; ++i) { - if (! expand_line(lk->expand, ret_attr[0][i], 1)) { - ret = -1; - goto end; - } - } + switch (type) { -end: - for (i = 0; i < nitems(attributes); ++i) { - free(attributes[i]); - if (ret_attr[i]) - aldap_free_attr(ret_attr[i]); - } - if (ret != 1 && lk && lk->expand) - expand_free(lk->expand); - - free(expfilter); - log_debug("debug: table_ldap_alias: ret=%d", ret); - return ret; -} - -static struct aldap * -ldap_client_connect(const char *addr) -{ - struct aldap_url lu; - struct addrinfo hints, *res0, *res; - int error; - - char *url; - int fd = -1; - - if ((url = strdup(addr)) == NULL) - err(1, NULL); - - if (aldap_parse_url(url, &lu) != 1) { - warnx("aldap_parse_url fail"); - goto err; - } - url = NULL; - - bzero(&hints, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; /* DUMMY */ - error = getaddrinfo(lu.host, NULL, &hints, &res0); - if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME) - goto err; - if (error) { - log_warnx("ldap_client_connect: could not parse \"%s\": %s", lu.host, - gai_strerror(error)); - goto err; - } - - for (res = res0; res; res = res->ai_next) { - if (res->ai_family != AF_INET && res->ai_family != AF_INET6) - continue; - - fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (fd == -1) - continue; - - if (res->ai_family == AF_INET) { - struct sockaddr_in sin4 = *(struct sockaddr_in *)res->ai_addr; - sin4.sin_port = htons(lu.port); - if (connect(fd, (struct sockaddr *)&sin4, res->ai_addrlen) == 0) - return aldap_init(fd); - } - else if (res->ai_family == AF_INET6) { - struct sockaddr_in6 sin6 = *(struct sockaddr_in6 *)res->ai_addr; - sin6.sin6_port = htons(lu.port); - if (connect(fd, (struct sockaddr *)&sin6, res->ai_addrlen) == 0) - return aldap_init(fd); + case K_ALIAS: + for (i = 0; res[0][i]; i++) { + if (i && strlcat(dst, ", ", sz) >= sz) { + ret = -1; + break; + } + if (strlcat(dst, res[0][i], sz) >= sz) { + ret = -1; + break; + } } - - close(fd); - fd = -1; + break; + case K_DOMAIN: + if (strlcpy(dst, res[0][0], sz) >= sz) + ret = -1; + break; + case K_CREDENTIALS: + if (snprintf(dst, sz, "%s:%s", res[0][0], res[0][1]) >= (int)sz) + ret = -1; + break; + case K_USERINFO: + if (snprintf(dst, sz, "%s:%s:%s:%s", res[0][0], res[1][0], + res[2][0], res[3][0]) >= (int)sz) + ret = -1; + break; } -err: - free(url); - return NULL; -} - -static int -parse_attributes(char **attributes, const char *line, size_t expect) -{ - char buffer[1024]; - char *p; - size_t m, n; - - if (strlcpy(buffer, line, sizeof buffer) - >= sizeof buffer) - return 0; + if (ret == -1) + log_warnx("warn: table-ldap: could not format result"); - m = 1; - for (p = buffer; *p; ++p) { - if (*p == ',') { - *p = 0; - m++; - } - } - if (expect != m) - return 0; +end: + for (i = 0; i < q->attrn; ++i) + if (res[i]) + aldap_free_attr(res[i]); - p = buffer; - for (n = 0; n < expect; ++n) - attributes[n] = NULL; - for (n = 0; n < m; ++n) { - attributes[n] = xstrdup(p, "parse_attributes"); - p += strlen(p) + 1; - } - return 1; + return (ret); } diff --git a/usr.sbin/smtpd/table_sqlite.c b/usr.sbin/smtpd/table_sqlite.c index bfcf11fa4d4..9b9dff84240 100644 --- a/usr.sbin/smtpd/table_sqlite.c +++ b/usr.sbin/smtpd/table_sqlite.c @@ -1,7 +1,7 @@ -/* $OpenBSD: table_sqlite.c,v 1.3 2013/05/24 17:03:14 eric Exp $ */ +/* $OpenBSD: table_sqlite.c,v 1.4 2013/07/20 09:06:46 eric Exp $ */ /* - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> + * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,384 +17,509 @@ */ #include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> #include <ctype.h> -#include <err.h> -#include <event.h> #include <fcntl.h> -#include <imsg.h> #include <sqlite3.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <time.h> -#include "smtpd.h" +#include "smtpd-defines.h" +#include "smtpd-api.h" #include "log.h" -/* sqlite(3) backend */ -static int table_sqlite_config(struct table *); -static int table_sqlite_update(struct table *); -static void *table_sqlite_open(struct table *); -static int table_sqlite_lookup(void *, const char *, enum table_service, - union lookup *); -static void table_sqlite_close(void *); - -struct table_backend table_backend_sqlite = { - K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO, - table_sqlite_config, - table_sqlite_open, - table_sqlite_update, - table_sqlite_close, - table_sqlite_lookup, +enum { + SQL_ALIAS = 0, + SQL_DOMAIN, + SQL_CREDENTIALS, + SQL_NETADDR, + SQL_USERINFO, + SQL_SOURCE, + SQL_MAILADDR, + SQL_ADDRNAME, + + SQL_MAX }; -struct table_sqlite_handle { - sqlite3 *ppDb; - struct table *table; -}; +static int table_sqlite_update(void); +static int table_sqlite_lookup(int, const char *, char *, size_t); +static int table_sqlite_check(int, const char *); +static int table_sqlite_fetch(int, char *, size_t); + +static sqlite3_stmt *table_sqlite_query(const char *, int); + +#define DEFAULT_EXPIRE 60 +#define DEFAULT_REFRESH 1000 + +static char *config; +static sqlite3 *db; +static sqlite3_stmt *statements[SQL_MAX]; +static sqlite3_stmt *stmt_fetch_source; +static struct dict sources; +static void *source_iter; +static size_t source_refresh = 1000; +static size_t source_ncall; +static int source_expire = 60; +static time_t source_update; + +int +main(int argc, char **argv) +{ + int ch; -static int table_sqlite_alias(struct table_sqlite_handle *, const char *, union lookup *); -static int table_sqlite_domain(struct table_sqlite_handle *, const char *, union lookup *); -static int table_sqlite_userinfo(struct table_sqlite_handle *, const char *, union lookup *); -static int table_sqlite_credentials(struct table_sqlite_handle *, const char *, union lookup *); -static int table_sqlite_netaddr(struct table_sqlite_handle *, const char *, union lookup *); + log_init(1); + log_verbose(~0); -static int -table_sqlite_config(struct table *table) -{ - struct table *cfg; + while ((ch = getopt(argc, argv, "")) != -1) { + switch (ch) { + default: + log_warnx("warn: table-sqlite: bad option"); + return (1); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; - /* no config ? broken */ - if (table->t_config[0] == '\0') - return 0; + if (argc != 1) { + log_warnx("warn: table-sqlite: bogus argument(s)"); + return (1); + } + + config = argv[0]; - cfg = table_create("static", table->t_name, "conf", table->t_config); - if (!table_config(cfg)) - goto err; + dict_init(&sources); - /* sanity checks */ - if (table_get(cfg, "dbpath") == NULL) { - log_warnx("table_sqlite: missing 'dbpath' configuration"); - return 0; + if (table_sqlite_update() == 0) { + log_warnx("warn: table-sqlite: error parsing config file"); + return (1); } - return 1; + table_api_on_update(table_sqlite_update); + table_api_on_check(table_sqlite_check); + table_api_on_lookup(table_sqlite_lookup); + table_api_on_fetch(table_sqlite_fetch); + table_api_dispatch(); -err: - table_destroy(cfg); - return 0; + return (0); } static int -table_sqlite_update(struct table *table) +table_sqlite_getconfstr(const char *key, const char *value, char **var) { - log_info("info: Table \"%s\" successfully updated", table->t_name); - return 1; + if (*var) { + log_warnx("warn: table-sqlite: duplicate %s %s", key, value); + free(*var); + } + *var = strdup(value); + if (*var == NULL) { + log_warn("warn: table-sqlite: strdup"); + return (-1); + } + return (0); } -static void * -table_sqlite_open(struct table *table) +static sqlite3_stmt * +table_sqlite_prepare_stmt(sqlite3 *_db, const char *query, int ncols) { - struct table_sqlite_handle *tsh; - struct table *cfg; - const char *dbpath; - - tsh = xcalloc(1, sizeof *tsh, "table_sqlite_open"); - tsh->table = table; + sqlite3_stmt *stmt; - cfg = table_find(table->t_name, "conf"); - dbpath = table_get(cfg, "dbpath"); - - if (sqlite3_open(dbpath, &tsh->ppDb) != SQLITE_OK) { - log_warnx("table_sqlite: open: %s", sqlite3_errmsg(tsh->ppDb)); - free(tsh); - return NULL; + if (sqlite3_prepare_v2(_db, query, -1, &stmt, 0) != SQLITE_OK) { + log_warnx("warn: table-sqlite: sqlite3_prepare_v2: %s", + sqlite3_errmsg(_db)); + goto end; + } + if (sqlite3_column_count(stmt) != ncols) { + log_warnx("warn: table-sqlite: columns: invalid resultset"); + goto end; } - return tsh; -} - -static void -table_sqlite_close(void *hdl) -{ - return; + return (stmt); + end: + sqlite3_finalize(stmt); + return (NULL); } static int -table_sqlite_lookup(void *hdl, const char *key, enum table_service service, - union lookup *lk) +table_sqlite_update(void) { - struct table_sqlite_handle *tsh = hdl; + static const struct { + const char *name; + int cols; + } qspec[SQL_MAX] = { + { "query_alias", 1 }, + { "query_domain", 1 }, + { "query_credentials", 2 }, + { "query_netaddr", 1 }, + { "query_userinfo", 4 }, + { "query_source", 1 }, + { "query_mailaddr", 1 }, + { "query_addrname", 1 }, + }; + sqlite3 *_db; + sqlite3_stmt *_statements[SQL_MAX]; + sqlite3_stmt *_stmt_fetch_source; + char *_query_fetch_source; + char *queries[SQL_MAX]; + size_t flen; + size_t _source_refresh; + int _source_expire; + FILE *fp; + char *key, *value, *buf, *lbuf, *dbpath; + const char *e; + int i, ret; + long long ll; + + dbpath = NULL; + _db = NULL; + bzero(queries, sizeof(queries)); + bzero(_statements, sizeof(_statements)); + _query_fetch_source = NULL; + _stmt_fetch_source = NULL; + + _source_refresh = DEFAULT_REFRESH; + _source_expire = DEFAULT_EXPIRE; + + ret = 0; + + /* Parse configuration */ + + fp = fopen(config, "r"); + if (fp == NULL) + return (0); + + lbuf = NULL; + while ((buf = fgetln(fp, &flen))) { + if (buf[flen - 1] == '\n') + buf[flen - 1] = '\0'; + else { + lbuf = malloc(flen + 1); + if (lbuf == NULL) { + log_warn("warn: table-sqlite: malloc"); + return (0); + } + memcpy(lbuf, buf, flen); + lbuf[flen] = '\0'; + buf = lbuf; + } - switch (service) { - case K_ALIAS: - return table_sqlite_alias(tsh, key, lk); - case K_DOMAIN: - return table_sqlite_domain(tsh, key, lk); - case K_USERINFO: - return table_sqlite_userinfo(tsh, key, lk); - case K_CREDENTIALS: - return table_sqlite_credentials(tsh, key, lk); - case K_NETADDR: - return table_sqlite_netaddr(tsh, key, lk); - default: - log_warnx("table_sqlite: lookup: unsupported lookup service"); - return -1; - } + key = buf; + while (isspace((int)*key)) + ++key; + if (*key == '\0' || *key == '#') + continue; + value = key; + strsep(&value, " \t:"); + if (value) { + while (*value) { + if (!isspace(*value) && + !(*value == ':' && isspace(*(value + 1)))) + break; + ++value; + } + if (*value == '\0') + value = NULL; + } - return 0; -} + if (value == NULL) { + log_warnx("warn: table-sqlite: missing value for key %s", key); + continue; + } -static int -table_sqlite_alias(struct table_sqlite_handle *tsh, const char *key, union lookup *lk) + if (!strcmp("dbpath", key)) { + if (table_sqlite_getconfstr(key, value, &dbpath) == -1) + goto end; + continue; + } + if (!strcmp("fetch_source", key)) { + if (table_sqlite_getconfstr(key, value, &_query_fetch_source) == -1) + goto end; + continue; + } + if (!strcmp("fetch_source_expire", key)) { + e = NULL; + ll = strtonum(value, 0, INT_MAX, &e); + if (e) { + log_warnx("warn: table-sqlite: bad value for %s: %s", key, e); + goto end; + } + _source_expire = ll; + continue; + } + if (!strcmp("fetch_source_refresh", key)) { + e = NULL; + ll = strtonum(value, 0, INT_MAX, &e); + if (e) { + log_warnx("warn: table-sqlite: bad value for %s: %s", key, e); + goto end; + } + _source_refresh = ll; + continue; + } -{ - struct table *cfg = table_find(tsh->table->t_name, "conf"); - const char *query = table_get(cfg, "query_alias"); - sqlite3_stmt *stmt; - struct expandnode xn; - int nrows; - - if (query == NULL) { - log_warnx("table_sqlite: lookup: no query configured for aliases"); - return -1; - } + for(i = 0; i < SQL_MAX; i++) + if (!strcmp(qspec[i].name, key)) + break; + if (i == SQL_MAX) { + log_warnx("warn: table-sqlite: bogus key %s", key); + continue; + } - if (sqlite3_prepare_v2(tsh->ppDb, query, -1, &stmt, 0) != SQLITE_OK) { - log_warnx("table_sqlite: prepare: %s", sqlite3_errmsg(tsh->ppDb)); - return -1; - } + if (queries[i]) { + log_warnx("warn: table-sqlite: duplicate key %s", key); + continue; + } - if (sqlite3_column_count(stmt) != 1) { - log_warnx("table_sqlite: columns: invalid resultset"); - sqlite3_finalize(stmt); - return -1; + queries[i] = strdup(value); + if (queries[i] == NULL) { + log_warnx("warn: table-sqlite: strdup"); + goto end; + } } - if (lk) - lk->expand = xcalloc(1, sizeof(*lk->expand), "table_sqlite_alias"); + /* Setup db */ - nrows = 0; + log_debug("debug: table-sqlite: opening %s", dbpath); - sqlite3_bind_text(stmt, 1, key, strlen(key), NULL); - while (sqlite3_step(stmt) == SQLITE_ROW) { - if (lk == NULL) { - sqlite3_finalize(stmt); - return 1; - } - if (! text_to_expandnode(&xn, sqlite3_column_text(stmt, 0))) - goto error; - expand_insert(lk->expand, &xn); - nrows++; + if (sqlite3_open(dbpath, &_db) != SQLITE_OK) { + log_warnx("warn: table-sqlite: open: %s", + sqlite3_errmsg(_db)); + goto end; } - sqlite3_finalize(stmt); - return nrows ? 1 : 0; + for (i = 0; i < SQL_MAX; i++) { + if (queries[i] == NULL) + continue; + if ((_statements[i] = table_sqlite_prepare_stmt(_db, queries[i], qspec[i].cols)) == NULL) + goto end; + } -error: - if (lk && lk->expand) - expand_free(lk->expand); - return -1; -} + if (_query_fetch_source && + (_stmt_fetch_source = table_sqlite_prepare_stmt(_db, _query_fetch_source, 1)) == NULL) + goto end; -static int -table_sqlite_domain(struct table_sqlite_handle *tsh, const char *key, union lookup *lk) -{ - struct table *cfg = table_find(tsh->table->t_name, "conf"); - const char *query = table_get(cfg, "query_domain"); - sqlite3_stmt *stmt; - - if (query == NULL) { - log_warnx("table_sqlite: lookup: no query configured for domain"); - return -1; - } + /* Replace previous setup */ - if (sqlite3_prepare_v2(tsh->ppDb, query, -1, &stmt, 0) != SQLITE_OK) { - log_warnx("table_sqlite: prepare: %s", sqlite3_errmsg(tsh->ppDb)); - return -1; + for (i = 0; i < SQL_MAX; i++) { + if (statements[i]) + sqlite3_finalize(statements[i]); + statements[i] = _statements[i]; + _statements[i] = NULL; } - - if (sqlite3_column_count(stmt) != 1) { - log_warnx("table_sqlite: columns: invalid resultset"); - sqlite3_finalize(stmt); - return -1; + if (stmt_fetch_source) + sqlite3_finalize(stmt_fetch_source); + stmt_fetch_source = _stmt_fetch_source; + _stmt_fetch_source = NULL; + + if (db) + sqlite3_close(_db); + db = _db; + _db = NULL; + + source_update = 0; /* force update */ + source_expire = _source_expire; + source_refresh = _source_refresh; + + log_debug("debug: table-sqlite: config successfully updated"); + ret = 1; + + end: + + /* Cleanup */ + for (i = 0; i < SQL_MAX; i++) { + if (_statements[i]) + sqlite3_finalize(_statements[i]); + free(queries[i]); } + if (_db) + sqlite3_close(_db); - sqlite3_bind_text(stmt, 1, key, strlen(key), NULL); + free(dbpath); + free(_query_fetch_source); - switch (sqlite3_step(stmt)) { - case SQLITE_ROW: - if (lk) - strlcpy(lk->domain.name, sqlite3_column_text(stmt, 0), sizeof(lk->domain.name)); - sqlite3_finalize(stmt); - return 1; + free(lbuf); + fclose(fp); + return (ret); +} - case SQLITE_DONE: - sqlite3_finalize(stmt); - return 0; +static sqlite3_stmt * +table_sqlite_query(const char *key, int service) +{ + int i; + sqlite3_stmt *stmt; + + stmt = NULL; + for(i = 0; i < SQL_MAX; i++) + if (service == 1 << i) { + stmt = statements[i]; + break; + } - default: - sqlite3_finalize(stmt); + if (stmt == NULL) + return (NULL); + + if (sqlite3_bind_text(stmt, 1, key, strlen(key), NULL) != SQLITE_OK) { + log_warnx("table-sqlite: sqlite3_bind_text: %s", + sqlite3_errmsg(db)); + return (NULL); } - return -1; + return (stmt); } static int -table_sqlite_userinfo(struct table_sqlite_handle *tsh, const char *key, union lookup *lk) +table_sqlite_check(int service, const char *key) { - struct table *cfg = table_find(tsh->table->t_name, "conf"); - const char *query = table_get(cfg, "query_userinfo"); - sqlite3_stmt *stmt; - size_t s; - - if (query == NULL) { - log_warnx("table_sqlite: lookup: no query configured for user"); - return -1; - } - - if (sqlite3_prepare_v2(tsh->ppDb, query, -1, &stmt, 0) != SQLITE_OK) { - log_warnx("table_sqlite: prepare: %s", sqlite3_errmsg(tsh->ppDb)); - return -1; - } + sqlite3_stmt *stmt; + int r; - if (sqlite3_column_count(stmt) != 4) { - log_warnx("table_sqlite: columns: invalid resultset"); - sqlite3_finalize(stmt); - return -1; - } + stmt = table_sqlite_query(key, service); + if (stmt == NULL) + return (-1); - sqlite3_bind_text(stmt, 1, key, strlen(key), NULL); - - switch (sqlite3_step(stmt)) { - case SQLITE_ROW: - if (lk) { - s = strlcpy(lk->userinfo.username, sqlite3_column_text(stmt, 0), - sizeof(lk->userinfo.username)); - if (s >= sizeof(lk->userinfo.username)) - goto error; - lk->userinfo.uid = sqlite3_column_int(stmt, 1); - lk->userinfo.gid = sqlite3_column_int(stmt, 2); - s = strlcpy(lk->userinfo.directory, sqlite3_column_text(stmt, 3), - sizeof(lk->userinfo.directory)); - if (s >= sizeof(lk->userinfo.directory)) - goto error; - } - sqlite3_finalize(stmt); - return 1; + r = sqlite3_step(stmt); + sqlite3_reset(stmt); - case SQLITE_DONE: - sqlite3_finalize(stmt); - return 0; + if (r == SQLITE_ROW) + return (1); - default: - goto error; - } + if (r == SQLITE_DONE) + return (0); -error: - sqlite3_finalize(stmt); - return -1; + return (-1); } static int -table_sqlite_credentials(struct table_sqlite_handle *tsh, const char *key, union lookup *lk) +table_sqlite_lookup(int service, const char *key, char *dst, size_t sz) { - struct table *cfg = table_find(tsh->table->t_name, "conf"); - const char *query = table_get(cfg, "query_credentials"); - sqlite3_stmt *stmt; - size_t s; - - if (query == NULL) { - log_warnx("table_sqlite: lookup: no query configured for credentials"); - return -1; + sqlite3_stmt *stmt; + const char *value; + int r, s; + + stmt = table_sqlite_query(key, service); + if (stmt == NULL) + return (-1); + + s = sqlite3_step(stmt); + if (s == SQLITE_DONE) { + sqlite3_reset(stmt); + return (0); } - if (sqlite3_prepare_v2(tsh->ppDb, query, -1, &stmt, 0) != SQLITE_OK) { - log_warnx("table_sqlite: prepare: %s", sqlite3_errmsg(tsh->ppDb)); - return -1; + if (s != SQLITE_ROW) { + log_warnx("table-sqlite: sqlite3_step: %s", + sqlite3_errmsg(db)); + sqlite3_reset(stmt); + return (-1); } - if (sqlite3_column_count(stmt) != 2) { - log_warnx("table_sqlite: columns: invalid resultset"); - sqlite3_finalize(stmt); - return -1; - } + r = 1; - sqlite3_bind_text(stmt, 1, key, strlen(key), NULL); - switch (sqlite3_step(stmt)) { - case SQLITE_ROW: - if (lk) { - s = strlcpy(lk->creds.username, sqlite3_column_text(stmt, 0), - sizeof(lk->creds.username)); - if (s >= sizeof(lk->creds.username)) - goto error; - s = strlcpy(lk->creds.password, sqlite3_column_text(stmt, 1), - sizeof(lk->creds.password)); - if (s >= sizeof(lk->creds.password)) - goto error; + switch(service) { + case K_ALIAS: + do { + value = sqlite3_column_text(stmt, 0); + if (dst[0] && strlcat(dst, ", ", sz) >= sz) { + log_warnx("warn: table-sqlite: result too large"); + r = -1; + break; + } + if (strlcat(dst, value, sz) >= sz) { + log_warnx("warn: table-sqlite: result too large"); + r = -1; + break; + } + s = sqlite3_step(stmt); + } while (s == SQLITE_ROW); + + if (s != SQLITE_ROW && s != SQLITE_DONE) { + log_warnx("table-sqlite: sqlite3_step: %s", + sqlite3_errmsg(db)); + r = -1; } - sqlite3_finalize(stmt); - return 1; - - case SQLITE_DONE: - sqlite3_finalize(stmt); - return 0; - + break; + case K_CREDENTIALS: + if (snprintf(dst, sz, "%s:%s", + sqlite3_column_text(stmt, 0), + sqlite3_column_text(stmt, 1)) > (ssize_t)sz) { + log_warnx("warn: table-sqlite: result too large"); + r = -1; + } + break; + case K_USERINFO: + if (snprintf(dst, sz, "%s:%i:%i:%s", + sqlite3_column_text(stmt, 0), + sqlite3_column_int(stmt, 1), + sqlite3_column_int(stmt, 2), + sqlite3_column_text(stmt, 3)) > (ssize_t)sz) { + log_warnx("warn: table-sqlite: result too large"); + r = -1; + } + break; + case K_DOMAIN: + case K_NETADDR: + case K_SOURCE: + case K_MAILADDR: + case K_ADDRNAME: + if (strlcpy(dst, sqlite3_column_text(stmt, 0), sz) >= sz) { + log_warnx("warn: table-sqlite: result too large"); + r = -1; + } + break; default: - goto error; + log_warnx("warn: table-sqlite: unknown service %i", service); + r = -1; } -error: - sqlite3_finalize(stmt); - return -1; + return (r); } - static int -table_sqlite_netaddr(struct table_sqlite_handle *tsh, const char *key, union lookup *lk) +table_sqlite_fetch(int service, char *dst, size_t sz) { - struct table *cfg = table_find(tsh->table->t_name, "conf"); - const char *query = table_get(cfg, "query_netaddr"); - sqlite3_stmt *stmt; - - if (query == NULL) { - log_warnx("table_sqlite: lookup: no query configured for netaddr"); - return -1; - } + const char *k; + int s; - if (sqlite3_prepare_v2(tsh->ppDb, query, -1, &stmt, 0) != SQLITE_OK) { - log_warnx("table_sqlite: prepare: %s", sqlite3_errmsg(tsh->ppDb)); - return -1; - } + if (service != K_SOURCE) + return (-1); - if (sqlite3_column_count(stmt) != 1) { - log_warnx("table_sqlite: columns: invalid resultset"); - sqlite3_finalize(stmt); - return -1; - } + if (stmt_fetch_source == NULL) + return (-1); - sqlite3_bind_text(stmt, 1, key, strlen(key), NULL); - switch (sqlite3_step(stmt)) { - case SQLITE_ROW: - if (lk) { - if (! text_to_netaddr(&lk->netaddr, sqlite3_column_text(stmt, 0))) - goto error; - } - sqlite3_finalize(stmt); - return 1; + if (source_ncall < source_refresh && + time(NULL) - source_update < source_expire) + goto fetch; - case SQLITE_DONE: - sqlite3_finalize(stmt); - return 0; + source_iter = NULL; + while(dict_poproot(&sources, NULL, NULL)) + ; - default: - goto error; + while ((s = sqlite3_step(stmt_fetch_source)) == SQLITE_ROW) + dict_set(&sources, sqlite3_column_text(stmt_fetch_source, 0), NULL); + + if (s != SQLITE_DONE) + log_warnx("warn: table-sqlite: sqlite3_step: %s", + sqlite3_errmsg(db)); + + sqlite3_reset(stmt_fetch_source); + + source_update = time(NULL); + source_ncall = 0; + + fetch: + + source_ncall += 1; + + if (! dict_iter(&sources, &source_iter, &k, (void **)NULL)) { + source_iter = NULL; + if (! dict_iter(&sources, &source_iter, &k, (void **)NULL)) + return (0); } -error: - sqlite3_finalize(stmt); - return -1; + if (strlcpy(dst, k, sz) >= sz) + return (-1); + + return (1); } |