xref: /openbsd/usr.sbin/smtpd/table_static.c (revision b613adb4)
1*b613adb4Sop /*	$OpenBSD: table_static.c,v 1.35 2024/05/14 13:28:08 op Exp $	*/
265c4fdfbSgilles 
365c4fdfbSgilles /*
4299c4efeSeric  * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
565c4fdfbSgilles  * Copyright (c) 2012 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 
2065c4fdfbSgilles #include <ctype.h>
2118236237Seric #include <errno.h>
2265c4fdfbSgilles #include <stdlib.h>
2365c4fdfbSgilles #include <string.h>
2465c4fdfbSgilles 
2565c4fdfbSgilles #include "smtpd.h"
2665c4fdfbSgilles #include "log.h"
2765c4fdfbSgilles 
2818236237Seric struct table_static_priv {
2918236237Seric 	int		 type;
3018236237Seric 	struct dict	 dict;
3118236237Seric 	void		*iter;
3218236237Seric };
3318236237Seric 
3465c4fdfbSgilles /* static backend */
35299c4efeSeric static int table_static_config(struct table *);
365f0e36ccSeric static int table_static_add(struct table *, const char *, const char *);
375f0e36ccSeric static void table_static_dump(struct table *);
3865c4fdfbSgilles static int table_static_update(struct table *);
39b700b1d8Seric static int table_static_open(struct table *);
409a2428acSeric static int table_static_lookup(struct table *, enum table_service, const char *,
41aa0d26b5Seric     char **);
429a2428acSeric static int table_static_fetch(struct table *, enum table_service, char **);
43a4c00f8aSeric static void table_static_close(struct table *);
4465c4fdfbSgilles 
4565c4fdfbSgilles struct table_backend table_backend_static = {
46*b613adb4Sop 	.name = "static",
47*b613adb4Sop 	.services = K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|
48a8e22235Sgilles 	K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST|
4981c7fb0aSgilles 	K_STRING|K_REGEX,
50*b613adb4Sop 	.config = table_static_config,
51*b613adb4Sop 	.add = table_static_add,
52*b613adb4Sop 	.dump = table_static_dump,
53*b613adb4Sop 	.open = table_static_open,
54*b613adb4Sop 	.update = table_static_update,
55*b613adb4Sop 	.close = table_static_close,
56*b613adb4Sop 	.lookup = table_static_lookup,
57*b613adb4Sop 	.fetch = table_static_fetch
5865c4fdfbSgilles };
5965c4fdfbSgilles 
6065c4fdfbSgilles static struct keycmp {
6165c4fdfbSgilles 	enum table_service	service;
6265c4fdfbSgilles 	int		       (*func)(const char *, const char *);
6365c4fdfbSgilles } keycmp[] = {
6465c4fdfbSgilles 	{ K_DOMAIN, table_domain_match },
6565c4fdfbSgilles 	{ K_NETADDR, table_netaddr_match },
6681c7fb0aSgilles 	{ K_MAILADDR, table_mailaddr_match },
6781c7fb0aSgilles 	{ K_REGEX, table_regex_match },
6865c4fdfbSgilles };
6965c4fdfbSgilles 
7065c4fdfbSgilles 
7118236237Seric static void
table_static_priv_free(struct table_static_priv * priv)7218236237Seric table_static_priv_free(struct table_static_priv *priv)
7318236237Seric {
7418236237Seric 	void *p;
7518236237Seric 
7618236237Seric 	while (dict_poproot(&priv->dict, (void **)&p))
7718236237Seric 		if (p != priv)
7818236237Seric 			free(p);
7918236237Seric 	free(priv);
8018236237Seric }
8118236237Seric 
8265c4fdfbSgilles static int
table_static_priv_add(struct table_static_priv * priv,const char * key,const char * val)8318236237Seric table_static_priv_add(struct table_static_priv *priv, const char *key, const char *val)
8418236237Seric {
8518236237Seric 	char lkey[1024];
8618236237Seric 	void *old, *new = NULL;
8718236237Seric 
8818236237Seric 	if (!lowercase(lkey, key, sizeof lkey)) {
8918236237Seric 		errno = ENAMETOOLONG;
9018236237Seric 		return (-1);
9118236237Seric 	}
9218236237Seric 
9318236237Seric 	if (val) {
9418236237Seric 		new = strdup(val);
9518236237Seric 		if (new == NULL)
9618236237Seric 			return (-1);
9718236237Seric 	}
9818236237Seric 
9918236237Seric 	/* use priv if value is null, so we can detect duplicate entries */
10018236237Seric 	old = dict_set(&priv->dict, lkey, new ? new : priv);
10118236237Seric 	if (old) {
10218236237Seric 		if (old != priv)
10318236237Seric 			free(old);
10418236237Seric 		return (1);
10518236237Seric 	}
10618236237Seric 
10718236237Seric 	return (0);
10818236237Seric }
10918236237Seric 
11018236237Seric static int
table_static_priv_load(struct table_static_priv * priv,const char * path)11118236237Seric table_static_priv_load(struct table_static_priv *priv, const char *path)
112299c4efeSeric {
113299c4efeSeric 	FILE	*fp;
114d7df8c18Sop 	char	*line = NULL;
115b7b75d53Seric 	int	 lineno = 0;
116d7df8c18Sop 	size_t	 linesize = 0;
117299c4efeSeric 	char	*keyp;
118299c4efeSeric 	char	*valp;
119d7df8c18Sop 	int	 malformed, ret = 0;
120299c4efeSeric 
12118236237Seric 	if ((fp = fopen(path, "r")) == NULL) {
12218236237Seric 		log_warn("%s: fopen", path);
123299c4efeSeric 		return 0;
12427c7fbfbSgilles 	}
125299c4efeSeric 
126d7df8c18Sop 	while (parse_table_line(fp, &line, &linesize, &priv->type,
127d7df8c18Sop 	    &keyp, &valp, &malformed) != -1) {
128b7b75d53Seric 		lineno++;
129d7df8c18Sop 		if (malformed) {
130d7df8c18Sop 			log_warnx("%s:%d invalid map entry",
13118236237Seric 			    path, lineno);
132299c4efeSeric 			goto end;
133299c4efeSeric 		}
134d7df8c18Sop 		if (keyp == NULL)
135d7df8c18Sop 			continue;
13618236237Seric 		table_static_priv_add(priv, keyp, valp);
137b7b75d53Seric 	}
138b7b75d53Seric 
139b7b75d53Seric 	if (ferror(fp)) {
14018236237Seric 		log_warn("%s: getline", path);
141b7b75d53Seric 		goto end;
142b7b75d53Seric 	}
143b7b75d53Seric 
1443f95a974Seric 	/* Accept empty alias files; treat them as hashes */
14518236237Seric 	if (priv->type == T_NONE)
14618236237Seric 		priv->type = T_HASH;
1473f95a974Seric 
148299c4efeSeric 	ret = 1;
149299c4efeSeric end:
150d7df8c18Sop 	free(line);
151299c4efeSeric 	fclose(fp);
152299c4efeSeric 	return ret;
15365c4fdfbSgilles }
15465c4fdfbSgilles 
15565c4fdfbSgilles static int
table_static_config(struct table * t)15618236237Seric table_static_config(struct table *t)
1575f0e36ccSeric {
15818236237Seric 	struct table_static_priv *priv, *old;
1595f0e36ccSeric 
16018236237Seric 	/* already up, and no config file? ok */
16118236237Seric 	if (t->t_handle && *t->t_config == '\0')
16218236237Seric 		return 1;
16318236237Seric 
16418236237Seric 	/* new config */
16518236237Seric 	priv = calloc(1, sizeof(*priv));
16618236237Seric 	if (priv == NULL)
16718236237Seric 		return 0;
16818236237Seric 	priv->type = t->t_type;
16918236237Seric 	dict_init(&priv->dict);
17018236237Seric 
17118236237Seric 	if (*t->t_config) {
17218236237Seric 		/* load the config file */
17318236237Seric 		if (table_static_priv_load(priv, t->t_config) == 0) {
17418236237Seric 			table_static_priv_free(priv);
1755f0e36ccSeric 			return 0;
1765f0e36ccSeric 		}
1775f0e36ccSeric 	}
1785f0e36ccSeric 
17918236237Seric 	if ((old = t->t_handle))
18018236237Seric 		table_static_priv_free(old);
18118236237Seric 	t->t_handle = priv;
18218236237Seric 	t->t_type = priv->type;
18318236237Seric 
18418236237Seric 	return 1;
18518236237Seric }
18618236237Seric 
18718236237Seric static int
table_static_add(struct table * table,const char * key,const char * val)18818236237Seric table_static_add(struct table *table, const char *key, const char *val)
18918236237Seric {
19018236237Seric 	struct table_static_priv *priv = table->t_handle;
19118236237Seric 	int r;
19218236237Seric 
19318236237Seric 	/* cannot add to a table read from a file */
19418236237Seric 	if (*table->t_config)
19518236237Seric 		return 0;
19618236237Seric 
197f27a1daaSeric 	if (table->t_type == T_NONE)
198f27a1daaSeric 		table->t_type = val ? T_HASH : T_LIST;
199f27a1daaSeric 	else if (table->t_type == T_LIST && val)
200f27a1daaSeric 		return 0;
201f27a1daaSeric 	else if (table->t_type == T_HASH && val == NULL)
202f27a1daaSeric 		return 0;
203f27a1daaSeric 
20418236237Seric 	if (priv == NULL) {
20518236237Seric 		if (table_static_config(table) == 0)
20618236237Seric 			return 0;
20718236237Seric 		priv = table->t_handle;
20818236237Seric 	}
20918236237Seric 
21018236237Seric 	r = table_static_priv_add(priv, key, val);
21118236237Seric 	if (r == -1)
21218236237Seric 		return 0;
2135f0e36ccSeric 	return 1;
2145f0e36ccSeric }
2155f0e36ccSeric 
2165f0e36ccSeric static void
table_static_dump(struct table * table)2175f0e36ccSeric table_static_dump(struct table *table)
2185f0e36ccSeric {
21918236237Seric 	struct table_static_priv *priv = table->t_handle;
2205f0e36ccSeric 	const char *key;
2215f0e36ccSeric 	char *value;
2225f0e36ccSeric 	void *iter;
2235f0e36ccSeric 
2245f0e36ccSeric 	iter = NULL;
22518236237Seric 	while (dict_iter(&priv->dict, &iter, &key, (void**)&value)) {
226d968baabSeric 		if (value && (void*)value != (void*)priv)
2275f0e36ccSeric 			log_debug("	\"%s\" -> \"%s\"", key, value);
2285f0e36ccSeric 		else
2295f0e36ccSeric 			log_debug("	\"%s\"", key);
2305f0e36ccSeric 	}
2315f0e36ccSeric }
2325f0e36ccSeric 
2335f0e36ccSeric static int
table_static_update(struct table * table)23465c4fdfbSgilles table_static_update(struct table *table)
23565c4fdfbSgilles {
23618236237Seric 	if (table_static_config(table) == 1) {
237dd047c3bSgilles 		log_info("info: Table \"%s\" successfully updated", table->t_name);
23865c4fdfbSgilles 		return 1;
23918236237Seric 	}
24065c4fdfbSgilles 
241dd047c3bSgilles 	log_info("info: Failed to update table \"%s\"", table->t_name);
24265c4fdfbSgilles 	return 0;
24365c4fdfbSgilles }
24465c4fdfbSgilles 
245b700b1d8Seric static int
table_static_open(struct table * table)24665c4fdfbSgilles table_static_open(struct table *table)
24765c4fdfbSgilles {
24818236237Seric 	if (table->t_handle == NULL)
24918236237Seric 		return table_static_config(table);
250b700b1d8Seric 	return 1;
25165c4fdfbSgilles }
25265c4fdfbSgilles 
25365c4fdfbSgilles static void
table_static_close(struct table * table)254a4c00f8aSeric table_static_close(struct table *table)
25565c4fdfbSgilles {
25618236237Seric 	struct table_static_priv *priv = table->t_handle;
25718236237Seric 
25818236237Seric 	if (priv)
25918236237Seric 		table_static_priv_free(priv);
260a4c00f8aSeric 	table->t_handle = NULL;
26165c4fdfbSgilles }
26265c4fdfbSgilles 
26365c4fdfbSgilles static int
table_static_lookup(struct table * table,enum table_service service,const char * key,char ** dst)26418236237Seric table_static_lookup(struct table *table, enum table_service service, const char *key,
26593cc0b04Seric     char **dst)
26665c4fdfbSgilles {
26718236237Seric 	struct table_static_priv *priv = table->t_handle;
26865c4fdfbSgilles 	char	       *line;
26965c4fdfbSgilles 	int		ret;
27065c4fdfbSgilles 	int	       (*match)(const char *, const char *) = NULL;
27165c4fdfbSgilles 	size_t		i;
27265c4fdfbSgilles 	void	       *iter;
27365c4fdfbSgilles 	const char     *k;
27465c4fdfbSgilles 	char	       *v;
27565c4fdfbSgilles 
27665c4fdfbSgilles 	for (i = 0; i < nitems(keycmp); ++i)
27765c4fdfbSgilles 		if (keycmp[i].service == service)
27865c4fdfbSgilles 			match = keycmp[i].func;
27965c4fdfbSgilles 
28065c4fdfbSgilles 	line = NULL;
28165c4fdfbSgilles 	iter = NULL;
28265c4fdfbSgilles 	ret = 0;
28318236237Seric 	while (dict_iter(&priv->dict, &iter, &k, (void **)&v)) {
28465c4fdfbSgilles 		if (match) {
28565c4fdfbSgilles 			if (match(key, k)) {
28665c4fdfbSgilles 				line = v;
28765c4fdfbSgilles 				ret = 1;
28865c4fdfbSgilles 			}
28965c4fdfbSgilles 		}
29065c4fdfbSgilles 		else {
29165c4fdfbSgilles 			if (strcmp(key, k) == 0) {
29265c4fdfbSgilles 				line = v;
29365c4fdfbSgilles 				ret = 1;
29465c4fdfbSgilles 			}
29565c4fdfbSgilles 		}
29665c4fdfbSgilles 		if (ret)
29765c4fdfbSgilles 			break;
29865c4fdfbSgilles 	}
29965c4fdfbSgilles 
300aa0d26b5Seric 	if (dst == NULL)
30165c4fdfbSgilles 		return ret ? 1 : 0;
30265c4fdfbSgilles 
303299c4efeSeric 	if (ret == 0)
30465c4fdfbSgilles 		return 0;
30565c4fdfbSgilles 
306aa0d26b5Seric 	*dst = strdup(line);
307aa0d26b5Seric 	if (*dst == NULL)
308aa0d26b5Seric 		return -1;
309aa0d26b5Seric 
310aa0d26b5Seric 	return 1;
31165c4fdfbSgilles }
31265c4fdfbSgilles 
31365c4fdfbSgilles static int
table_static_fetch(struct table * t,enum table_service service,char ** dst)3149a2428acSeric table_static_fetch(struct table *t, enum table_service service, char **dst)
31565c4fdfbSgilles {
31618236237Seric 	struct table_static_priv *priv = t->t_handle;
31765c4fdfbSgilles 	const char *k;
31865c4fdfbSgilles 
31918236237Seric 	if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) {
32018236237Seric 		priv->iter = NULL;
32118236237Seric 		if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL))
32265c4fdfbSgilles 			return 0;
32365c4fdfbSgilles 	}
32465c4fdfbSgilles 
325aa0d26b5Seric 	*dst = strdup(k);
326aa0d26b5Seric 	if (*dst == NULL)
327aa0d26b5Seric 		return -1;
328aa0d26b5Seric 
329aa0d26b5Seric 	return 1;
33065c4fdfbSgilles }
331