xref: /openbsd/usr.sbin/smtpd/table_static.c (revision 18236237)
1*18236237Seric /*	$OpenBSD: table_static.c,v 1.30 2018/12/28 10:42:18 eric 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 <sys/types.h>
2165c4fdfbSgilles #include <sys/queue.h>
2265c4fdfbSgilles #include <sys/tree.h>
2365c4fdfbSgilles #include <sys/socket.h>
2465c4fdfbSgilles 
2565c4fdfbSgilles #include <netinet/in.h>
2665c4fdfbSgilles #include <arpa/inet.h>
2765c4fdfbSgilles 
2865c4fdfbSgilles #include <ctype.h>
29*18236237Seric #include <errno.h>
30*18236237Seric 
3165c4fdfbSgilles #include <event.h>
3265c4fdfbSgilles #include <fcntl.h>
3365c4fdfbSgilles #include <imsg.h>
3465c4fdfbSgilles #include <stdio.h>
3565c4fdfbSgilles #include <stdlib.h>
36953aae25Sderaadt #include <limits.h>
3765c4fdfbSgilles #include <string.h>
3865c4fdfbSgilles 
3965c4fdfbSgilles #include "smtpd.h"
4065c4fdfbSgilles #include "log.h"
4165c4fdfbSgilles 
42*18236237Seric struct table_static_priv {
43*18236237Seric 	int		 type;
44*18236237Seric 	struct dict	 dict;
45*18236237Seric 	void		*iter;
46*18236237Seric };
47*18236237Seric 
4865c4fdfbSgilles /* static backend */
49299c4efeSeric static int table_static_config(struct table *);
505f0e36ccSeric static int table_static_add(struct table *, const char *, const char *);
515f0e36ccSeric static void table_static_dump(struct table *);
5265c4fdfbSgilles static int table_static_update(struct table *);
53b700b1d8Seric static int table_static_open(struct table *);
549a2428acSeric static int table_static_lookup(struct table *, enum table_service, const char *,
55aa0d26b5Seric     char **);
569a2428acSeric static int table_static_fetch(struct table *, enum table_service, char **);
57a4c00f8aSeric static void table_static_close(struct table *);
5865c4fdfbSgilles 
5965c4fdfbSgilles struct table_backend table_backend_static = {
604f15e081Seric 	"static",
6193262a01Ssunil 	K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|
62a8e22235Sgilles 	K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST|
6381c7fb0aSgilles 	K_STRING|K_REGEX,
6465c4fdfbSgilles 	table_static_config,
655f0e36ccSeric 	table_static_add,
665f0e36ccSeric 	table_static_dump,
6765c4fdfbSgilles 	table_static_open,
6865c4fdfbSgilles 	table_static_update,
6965c4fdfbSgilles 	table_static_close,
7065c4fdfbSgilles 	table_static_lookup,
7165c4fdfbSgilles 	table_static_fetch
7265c4fdfbSgilles };
7365c4fdfbSgilles 
7465c4fdfbSgilles static struct keycmp {
7565c4fdfbSgilles 	enum table_service	service;
7665c4fdfbSgilles 	int		       (*func)(const char *, const char *);
7765c4fdfbSgilles } keycmp[] = {
7865c4fdfbSgilles 	{ K_DOMAIN, table_domain_match },
7965c4fdfbSgilles 	{ K_NETADDR, table_netaddr_match },
8081c7fb0aSgilles 	{ K_MAILADDR, table_mailaddr_match },
8181c7fb0aSgilles 	{ K_REGEX, table_regex_match },
8265c4fdfbSgilles };
8365c4fdfbSgilles 
8465c4fdfbSgilles 
85*18236237Seric static void
86*18236237Seric table_static_priv_free(struct table_static_priv *priv)
87*18236237Seric {
88*18236237Seric 	void *p;
89*18236237Seric 
90*18236237Seric 	while (dict_poproot(&priv->dict, (void **)&p))
91*18236237Seric 		if (p != priv)
92*18236237Seric 			free(p);
93*18236237Seric 	free(priv);
94*18236237Seric }
95*18236237Seric 
9665c4fdfbSgilles static int
97*18236237Seric table_static_priv_add(struct table_static_priv *priv, const char *key, const char *val)
98*18236237Seric {
99*18236237Seric 	char lkey[1024];
100*18236237Seric 	void *old, *new = NULL;
101*18236237Seric 
102*18236237Seric 	if (!lowercase(lkey, key, sizeof lkey)) {
103*18236237Seric 		errno = ENAMETOOLONG;
104*18236237Seric 		return (-1);
105*18236237Seric 	}
106*18236237Seric 
107*18236237Seric 	if (val) {
108*18236237Seric 		new = strdup(val);
109*18236237Seric 		if (new == NULL)
110*18236237Seric 			return (-1);
111*18236237Seric 	}
112*18236237Seric 
113*18236237Seric 	/* use priv if value is null, so we can detect duplicate entries */
114*18236237Seric 	old = dict_set(&priv->dict, lkey, new ? new : priv);
115*18236237Seric 	if (old) {
116*18236237Seric 		if (old != priv)
117*18236237Seric 			free(old);
118*18236237Seric 		return (1);
119*18236237Seric 	}
120*18236237Seric 
121*18236237Seric 	return (0);
122*18236237Seric }
123*18236237Seric 
124*18236237Seric static int
125*18236237Seric table_static_priv_load(struct table_static_priv *priv, const char *path)
126299c4efeSeric {
127299c4efeSeric 	FILE	*fp;
128b7b75d53Seric 	char	*buf = NULL, *p;
129b7b75d53Seric 	int	 lineno = 0;
130c17c2b51Ssunil 	size_t	 sz = 0;
131c17c2b51Ssunil 	ssize_t	 flen;
132299c4efeSeric 	char	*keyp;
133299c4efeSeric 	char	*valp;
134*18236237Seric 	int	 ret = 0;
135299c4efeSeric 
136*18236237Seric 	if ((fp = fopen(path, "r")) == NULL) {
137*18236237Seric 		log_warn("%s: fopen", path);
138299c4efeSeric 		return 0;
13927c7fbfbSgilles 	}
140299c4efeSeric 
141c17c2b51Ssunil 	while ((flen = getline(&buf, &sz, fp)) != -1) {
142b7b75d53Seric 		lineno++;
143299c4efeSeric 		if (buf[flen - 1] == '\n')
144b7b75d53Seric 			buf[--flen] = '\0';
145299c4efeSeric 
146299c4efeSeric 		keyp = buf;
147b7b75d53Seric 		while (isspace((unsigned char)*keyp)) {
148b7b75d53Seric 			++keyp;
149b7b75d53Seric 			--flen;
150b7b75d53Seric 		}
151b7b75d53Seric 		if (*keyp == '\0')
152b7b75d53Seric 			continue;
153b7b75d53Seric 		while (isspace((unsigned char)keyp[flen - 1]))
154b7b75d53Seric 			keyp[--flen] = '\0';
155b7b75d53Seric 		if (*keyp == '#') {
156*18236237Seric 			if (priv->type == T_NONE) {
157b7b75d53Seric 				keyp++;
158fc3a8311Seric 				while (isspace((unsigned char)*keyp))
159299c4efeSeric 					++keyp;
160b7b75d53Seric 				if (!strcmp(keyp, "@list"))
161*18236237Seric 					priv->type = T_LIST;
162b7b75d53Seric 			}
163299c4efeSeric 			continue;
164b7b75d53Seric 		}
165b7b75d53Seric 
166*18236237Seric 		if (priv->type == T_NONE) {
167b7b75d53Seric 			for (p = keyp; *p; p++) {
168b7b75d53Seric 				if (*p == ' ' || *p == '\t' || *p == ':') {
169*18236237Seric 					priv->type = T_HASH;
170b7b75d53Seric 					break;
171b7b75d53Seric 				}
172b7b75d53Seric 			}
173*18236237Seric 			if (priv->type == T_NONE)
174*18236237Seric 				priv->type = T_LIST;
175b7b75d53Seric 		}
176b7b75d53Seric 
177*18236237Seric 		if (priv->type == T_LIST) {
178*18236237Seric 			table_static_priv_add(priv, keyp, NULL);
179b7b75d53Seric 			continue;
180b7b75d53Seric 		}
181b7b75d53Seric 
182b7b75d53Seric 		/* T_HASH */
183299c4efeSeric 		valp = keyp;
184299c4efeSeric 		strsep(&valp, " \t:");
185299c4efeSeric 		if (valp) {
186299c4efeSeric 			while (*valp) {
187fc3a8311Seric 				if (!isspace((unsigned char)*valp) &&
18893262a01Ssunil 				    !(*valp == ':' &&
18993262a01Ssunil 				    isspace((unsigned char)*(valp + 1))))
190299c4efeSeric 					break;
191299c4efeSeric 				++valp;
192299c4efeSeric 			}
193299c4efeSeric 			if (*valp == '\0')
194299c4efeSeric 				valp = NULL;
195299c4efeSeric 		}
196b7b75d53Seric 		if (valp == NULL) {
197*18236237Seric 			log_warnx("%s: invalid map entry line %d",
198*18236237Seric 			    path, lineno);
199299c4efeSeric 			goto end;
200299c4efeSeric 		}
201b7b75d53Seric 
202*18236237Seric 		table_static_priv_add(priv, keyp, valp);
203b7b75d53Seric 	}
204b7b75d53Seric 
205b7b75d53Seric 	if (ferror(fp)) {
206*18236237Seric 		log_warn("%s: getline", path);
207b7b75d53Seric 		goto end;
208b7b75d53Seric 	}
209b7b75d53Seric 
2103f95a974Seric 	/* Accept empty alias files; treat them as hashes */
211*18236237Seric 	if (priv->type == T_NONE)
212*18236237Seric 		priv->type = T_HASH;
2133f95a974Seric 
214299c4efeSeric 	ret = 1;
215299c4efeSeric end:
216c17c2b51Ssunil 	free(buf);
217299c4efeSeric 	fclose(fp);
218299c4efeSeric 	return ret;
21965c4fdfbSgilles }
22065c4fdfbSgilles 
22165c4fdfbSgilles static int
222*18236237Seric table_static_config(struct table *t)
2235f0e36ccSeric {
224*18236237Seric 	struct table_static_priv *priv, *old;
2255f0e36ccSeric 
226*18236237Seric 	/* already up, and no config file? ok */
227*18236237Seric 	if (t->t_handle && *t->t_config == '\0')
228*18236237Seric 		return 1;
229*18236237Seric 
230*18236237Seric 	/* new config */
231*18236237Seric 	priv = calloc(1, sizeof(*priv));
232*18236237Seric 	if (priv == NULL)
233*18236237Seric 		return 0;
234*18236237Seric 	priv->type = t->t_type;
235*18236237Seric 	dict_init(&priv->dict);
236*18236237Seric 
237*18236237Seric 	if (*t->t_config) {
238*18236237Seric 		/* load the config file */
239*18236237Seric 		if (table_static_priv_load(priv, t->t_config) == 0) {
240*18236237Seric 			table_static_priv_free(priv);
2415f0e36ccSeric 			return 0;
2425f0e36ccSeric 		}
2435f0e36ccSeric 	}
2445f0e36ccSeric 
245*18236237Seric 	if ((old = t->t_handle))
246*18236237Seric 		table_static_priv_free(old);
247*18236237Seric 	t->t_handle = priv;
248*18236237Seric 	t->t_type = priv->type;
249*18236237Seric 
250*18236237Seric 	return 1;
251*18236237Seric }
252*18236237Seric 
253*18236237Seric static int
254*18236237Seric table_static_add(struct table *table, const char *key, const char *val)
255*18236237Seric {
256*18236237Seric 	struct table_static_priv *priv = table->t_handle;
257*18236237Seric 	int r;
258*18236237Seric 
259*18236237Seric 	/* cannot add to a table read from a file */
260*18236237Seric 	if (*table->t_config)
261*18236237Seric 		return 0;
262*18236237Seric 
263*18236237Seric 	if (priv == NULL) {
264*18236237Seric 		if (table_static_config(table) == 0)
265*18236237Seric 			return 0;
266*18236237Seric 		priv = table->t_handle;
267*18236237Seric 	}
268*18236237Seric 
269*18236237Seric 	r = table_static_priv_add(priv, key, val);
270*18236237Seric 	if (r == -1)
271*18236237Seric 		return 0;
2725f0e36ccSeric 	return 1;
2735f0e36ccSeric }
2745f0e36ccSeric 
2755f0e36ccSeric static void
2765f0e36ccSeric table_static_dump(struct table *table)
2775f0e36ccSeric {
278*18236237Seric 	struct table_static_priv *priv = table->t_handle;
2795f0e36ccSeric 	const char *key;
2805f0e36ccSeric 	char *value;
2815f0e36ccSeric 	void *iter;
2825f0e36ccSeric 
2835f0e36ccSeric 	iter = NULL;
284*18236237Seric 	while (dict_iter(&priv->dict, &iter, &key, (void**)&value)) {
2855f0e36ccSeric 		if (value)
2865f0e36ccSeric 			log_debug("	\"%s\" -> \"%s\"", key, value);
2875f0e36ccSeric 		else
2885f0e36ccSeric 			log_debug("	\"%s\"", key);
2895f0e36ccSeric 	}
2905f0e36ccSeric }
2915f0e36ccSeric 
2925f0e36ccSeric static int
29365c4fdfbSgilles table_static_update(struct table *table)
29465c4fdfbSgilles {
295*18236237Seric 	if (table_static_config(table) == 1) {
296dd047c3bSgilles 		log_info("info: Table \"%s\" successfully updated", table->t_name);
29765c4fdfbSgilles 		return 1;
298*18236237Seric 	}
29965c4fdfbSgilles 
300dd047c3bSgilles 	log_info("info: Failed to update table \"%s\"", table->t_name);
30165c4fdfbSgilles 	return 0;
30265c4fdfbSgilles }
30365c4fdfbSgilles 
304b700b1d8Seric static int
30565c4fdfbSgilles table_static_open(struct table *table)
30665c4fdfbSgilles {
307*18236237Seric 	if (table->t_handle == NULL)
308*18236237Seric 		return table_static_config(table);
309b700b1d8Seric 	return 1;
31065c4fdfbSgilles }
31165c4fdfbSgilles 
31265c4fdfbSgilles static void
313a4c00f8aSeric table_static_close(struct table *table)
31465c4fdfbSgilles {
315*18236237Seric 	struct table_static_priv *priv = table->t_handle;
316*18236237Seric 
317*18236237Seric 	if (priv)
318*18236237Seric 		table_static_priv_free(priv);
319a4c00f8aSeric 	table->t_handle = NULL;
32065c4fdfbSgilles }
32165c4fdfbSgilles 
32265c4fdfbSgilles static int
323*18236237Seric table_static_lookup(struct table *table, enum table_service service, const char *key,
32493cc0b04Seric     char **dst)
32565c4fdfbSgilles {
326*18236237Seric 	struct table_static_priv *priv = table->t_handle;
32765c4fdfbSgilles 	char	       *line;
32865c4fdfbSgilles 	int		ret;
32965c4fdfbSgilles 	int	       (*match)(const char *, const char *) = NULL;
33065c4fdfbSgilles 	size_t		i;
33165c4fdfbSgilles 	void	       *iter;
33265c4fdfbSgilles 	const char     *k;
33365c4fdfbSgilles 	char	       *v;
33465c4fdfbSgilles 
33565c4fdfbSgilles 	for (i = 0; i < nitems(keycmp); ++i)
33665c4fdfbSgilles 		if (keycmp[i].service == service)
33765c4fdfbSgilles 			match = keycmp[i].func;
33865c4fdfbSgilles 
33965c4fdfbSgilles 	line = NULL;
34065c4fdfbSgilles 	iter = NULL;
34165c4fdfbSgilles 	ret = 0;
342*18236237Seric 	while (dict_iter(&priv->dict, &iter, &k, (void **)&v)) {
34365c4fdfbSgilles 		if (match) {
34465c4fdfbSgilles 			if (match(key, k)) {
34565c4fdfbSgilles 				line = v;
34665c4fdfbSgilles 				ret = 1;
34765c4fdfbSgilles 			}
34865c4fdfbSgilles 		}
34965c4fdfbSgilles 		else {
35065c4fdfbSgilles 			if (strcmp(key, k) == 0) {
35165c4fdfbSgilles 				line = v;
35265c4fdfbSgilles 				ret = 1;
35365c4fdfbSgilles 			}
35465c4fdfbSgilles 		}
35565c4fdfbSgilles 		if (ret)
35665c4fdfbSgilles 			break;
35765c4fdfbSgilles 	}
35865c4fdfbSgilles 
359aa0d26b5Seric 	if (dst == NULL)
36065c4fdfbSgilles 		return ret ? 1 : 0;
36165c4fdfbSgilles 
362299c4efeSeric 	if (ret == 0)
36365c4fdfbSgilles 		return 0;
36465c4fdfbSgilles 
365aa0d26b5Seric 	*dst = strdup(line);
366aa0d26b5Seric 	if (*dst == NULL)
367aa0d26b5Seric 		return -1;
368aa0d26b5Seric 
369aa0d26b5Seric 	return 1;
37065c4fdfbSgilles }
37165c4fdfbSgilles 
37265c4fdfbSgilles static int
3739a2428acSeric table_static_fetch(struct table *t, enum table_service service, char **dst)
37465c4fdfbSgilles {
375*18236237Seric 	struct table_static_priv *priv = t->t_handle;
37665c4fdfbSgilles 	const char *k;
37765c4fdfbSgilles 
378*18236237Seric 	if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) {
379*18236237Seric 		priv->iter = NULL;
380*18236237Seric 		if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL))
38165c4fdfbSgilles 			return 0;
38265c4fdfbSgilles 	}
38365c4fdfbSgilles 
384aa0d26b5Seric 	*dst = strdup(k);
385aa0d26b5Seric 	if (*dst == NULL)
386aa0d26b5Seric 		return -1;
387aa0d26b5Seric 
388aa0d26b5Seric 	return 1;
38965c4fdfbSgilles }
390