xref: /openbsd/usr.sbin/smtpd/table_static.c (revision a4c00f8a)
1*a4c00f8aSeric /*	$OpenBSD: table_static.c,v 1.26 2018/12/27 08:57:03 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>
2965c4fdfbSgilles #include <err.h>
3065c4fdfbSgilles #include <event.h>
3165c4fdfbSgilles #include <fcntl.h>
3265c4fdfbSgilles #include <imsg.h>
3365c4fdfbSgilles #include <stdio.h>
3465c4fdfbSgilles #include <stdlib.h>
35953aae25Sderaadt #include <limits.h>
3665c4fdfbSgilles #include <string.h>
3765c4fdfbSgilles 
3865c4fdfbSgilles #include "smtpd.h"
3965c4fdfbSgilles #include "log.h"
4065c4fdfbSgilles 
4165c4fdfbSgilles /* static backend */
42299c4efeSeric static int table_static_config(struct table *);
4365c4fdfbSgilles static int table_static_update(struct table *);
44b700b1d8Seric static int table_static_open(struct table *);
4593cc0b04Seric static int table_static_lookup(void *, enum table_service, const char *,
46aa0d26b5Seric     char **);
47699c3f98Seric static int table_static_fetch(void *, enum table_service, char **);
48*a4c00f8aSeric static void table_static_close(struct table *);
4965c4fdfbSgilles 
5065c4fdfbSgilles struct table_backend table_backend_static = {
514f15e081Seric 	"static",
5293262a01Ssunil 	K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|
53a8e22235Sgilles 	K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST|
5481c7fb0aSgilles 	K_STRING|K_REGEX,
5565c4fdfbSgilles 	table_static_config,
5665c4fdfbSgilles 	table_static_open,
5765c4fdfbSgilles 	table_static_update,
5865c4fdfbSgilles 	table_static_close,
5965c4fdfbSgilles 	table_static_lookup,
6065c4fdfbSgilles 	table_static_fetch
6165c4fdfbSgilles };
6265c4fdfbSgilles 
6365c4fdfbSgilles static struct keycmp {
6465c4fdfbSgilles 	enum table_service	service;
6565c4fdfbSgilles 	int		       (*func)(const char *, const char *);
6665c4fdfbSgilles } keycmp[] = {
6765c4fdfbSgilles 	{ K_DOMAIN, table_domain_match },
6865c4fdfbSgilles 	{ K_NETADDR, table_netaddr_match },
6981c7fb0aSgilles 	{ K_MAILADDR, table_mailaddr_match },
7081c7fb0aSgilles 	{ K_REGEX, table_regex_match },
7165c4fdfbSgilles };
7265c4fdfbSgilles 
7365c4fdfbSgilles 
7465c4fdfbSgilles static int
753cedc4a5Seric table_static_config(struct table *t)
76299c4efeSeric {
77299c4efeSeric 	FILE	*fp;
78b7b75d53Seric 	char	*buf = NULL, *p;
79b7b75d53Seric 	int	 lineno = 0;
80c17c2b51Ssunil 	size_t	 sz = 0;
81c17c2b51Ssunil 	ssize_t	 flen;
82299c4efeSeric 	char	*keyp;
83299c4efeSeric 	char	*valp;
84299c4efeSeric 	size_t	 ret = 0;
85299c4efeSeric 
863cedc4a5Seric 	/* no config ? ok */
873cedc4a5Seric 	if (*t->t_config == '\0')
883cedc4a5Seric 		return 1;
893cedc4a5Seric 
903cedc4a5Seric 	if ((fp = fopen(t->t_config, "r")) == NULL) {
913cedc4a5Seric 		log_warn("warn: Table \"%s\"", t->t_config);
92299c4efeSeric 		return 0;
9327c7fbfbSgilles 	}
94299c4efeSeric 
95c17c2b51Ssunil 	while ((flen = getline(&buf, &sz, fp)) != -1) {
96b7b75d53Seric 		lineno++;
97299c4efeSeric 		if (buf[flen - 1] == '\n')
98b7b75d53Seric 			buf[--flen] = '\0';
99299c4efeSeric 
100299c4efeSeric 		keyp = buf;
101b7b75d53Seric 		while (isspace((unsigned char)*keyp)) {
102b7b75d53Seric 			++keyp;
103b7b75d53Seric 			--flen;
104b7b75d53Seric 		}
105b7b75d53Seric 		if (*keyp == '\0')
106b7b75d53Seric 			continue;
107b7b75d53Seric 		while (isspace((unsigned char)keyp[flen - 1]))
108b7b75d53Seric 			keyp[--flen] = '\0';
109b7b75d53Seric 		if (*keyp == '#') {
110b7b75d53Seric 			if (t->t_type == T_NONE) {
111b7b75d53Seric 				keyp++;
112fc3a8311Seric 				while (isspace((unsigned char)*keyp))
113299c4efeSeric 					++keyp;
114b7b75d53Seric 				if (!strcmp(keyp, "@list"))
115b7b75d53Seric 					t->t_type = T_LIST;
116b7b75d53Seric 			}
117299c4efeSeric 			continue;
118b7b75d53Seric 		}
119b7b75d53Seric 
120b7b75d53Seric 		if (t->t_type == T_NONE) {
121b7b75d53Seric 			for (p = keyp; *p; p++) {
122b7b75d53Seric 				if (*p == ' ' || *p == '\t' || *p == ':') {
123b7b75d53Seric 					t->t_type = T_HASH;
124b7b75d53Seric 					break;
125b7b75d53Seric 				}
126b7b75d53Seric 			}
127b7b75d53Seric 			if (t->t_type == T_NONE)
128b7b75d53Seric 				t->t_type = T_LIST;
129b7b75d53Seric 		}
130b7b75d53Seric 
131b7b75d53Seric 		if (t->t_type == T_LIST) {
132b7b75d53Seric 			table_add(t, keyp, NULL);
133b7b75d53Seric 			continue;
134b7b75d53Seric 		}
135b7b75d53Seric 
136b7b75d53Seric 		/* T_HASH */
137299c4efeSeric 		valp = keyp;
138299c4efeSeric 		strsep(&valp, " \t:");
139299c4efeSeric 		if (valp) {
140299c4efeSeric 			while (*valp) {
141fc3a8311Seric 				if (!isspace((unsigned char)*valp) &&
14293262a01Ssunil 				    !(*valp == ':' &&
14393262a01Ssunil 				    isspace((unsigned char)*(valp + 1))))
144299c4efeSeric 					break;
145299c4efeSeric 				++valp;
146299c4efeSeric 			}
147299c4efeSeric 			if (*valp == '\0')
148299c4efeSeric 				valp = NULL;
149299c4efeSeric 		}
150b7b75d53Seric 		if (valp == NULL) {
151b7b75d53Seric 			log_warnx("%s: invalid map entry line %d", t->t_config,
152b7b75d53Seric 			    lineno);
153299c4efeSeric 			goto end;
154299c4efeSeric 		}
155b7b75d53Seric 
156b7b75d53Seric 		table_add(t, keyp, valp);
157b7b75d53Seric 	}
158b7b75d53Seric 
159b7b75d53Seric 	if (ferror(fp)) {
160b7b75d53Seric 		log_warn("%s: getline", t->t_config);
161b7b75d53Seric 		goto end;
162b7b75d53Seric 	}
163b7b75d53Seric 
1643f95a974Seric 	/* Accept empty alias files; treat them as hashes */
1653f95a974Seric 	if (t->t_type == T_NONE && t->t_backend->services & K_ALIAS)
1663f95a974Seric 	    t->t_type = T_HASH;
1673f95a974Seric 
168299c4efeSeric 	ret = 1;
169299c4efeSeric end:
170c17c2b51Ssunil 	free(buf);
171299c4efeSeric 	fclose(fp);
172299c4efeSeric 	return ret;
17365c4fdfbSgilles }
17465c4fdfbSgilles 
17565c4fdfbSgilles static int
17665c4fdfbSgilles table_static_update(struct table *table)
17765c4fdfbSgilles {
17865c4fdfbSgilles 	struct table	*t;
179299c4efeSeric 	void		*p = NULL;
18065c4fdfbSgilles 
18165c4fdfbSgilles 	/* no config ? ok */
18265c4fdfbSgilles 	if (table->t_config[0] == '\0')
18365c4fdfbSgilles 		goto ok;
18465c4fdfbSgilles 
185b80b41afSgilles 	t = table_create(env, "static", table->t_name, "update", table->t_config);
186299c4efeSeric 	if (!table_config(t))
18765c4fdfbSgilles 		goto err;
18865c4fdfbSgilles 
189dd047c3bSgilles 	/* replace former table, frees t */
19019503c5aSeric 	while (dict_poproot(&table->t_dict, (void **)&p))
191299c4efeSeric 		free(p);
192299c4efeSeric 	dict_merge(&table->t_dict, &t->t_dict);
193b80b41afSgilles 	table_destroy(env, t);
19465c4fdfbSgilles 
19565c4fdfbSgilles ok:
196dd047c3bSgilles 	log_info("info: Table \"%s\" successfully updated", table->t_name);
19765c4fdfbSgilles 	return 1;
19865c4fdfbSgilles 
19965c4fdfbSgilles err:
200b80b41afSgilles 	table_destroy(env, t);
201dd047c3bSgilles 	log_info("info: Failed to update table \"%s\"", table->t_name);
20265c4fdfbSgilles 	return 0;
20365c4fdfbSgilles }
20465c4fdfbSgilles 
205b700b1d8Seric static int
20665c4fdfbSgilles table_static_open(struct table *table)
20765c4fdfbSgilles {
208b700b1d8Seric 	table->t_handle = table;
209b700b1d8Seric 	return 1;
21065c4fdfbSgilles }
21165c4fdfbSgilles 
21265c4fdfbSgilles static void
213*a4c00f8aSeric table_static_close(struct table *table)
21465c4fdfbSgilles {
215*a4c00f8aSeric 	table->t_handle = NULL;
21665c4fdfbSgilles }
21765c4fdfbSgilles 
21865c4fdfbSgilles static int
21993cc0b04Seric table_static_lookup(void *hdl, enum table_service service, const char *key,
22093cc0b04Seric     char **dst)
22165c4fdfbSgilles {
22265c4fdfbSgilles 	struct table   *m  = hdl;
22365c4fdfbSgilles 	char	       *line;
22465c4fdfbSgilles 	int		ret;
22565c4fdfbSgilles 	int	       (*match)(const char *, const char *) = NULL;
22665c4fdfbSgilles 	size_t		i;
22765c4fdfbSgilles 	void	       *iter;
22865c4fdfbSgilles 	const char     *k;
22965c4fdfbSgilles 	char	       *v;
23065c4fdfbSgilles 
23165c4fdfbSgilles 	for (i = 0; i < nitems(keycmp); ++i)
23265c4fdfbSgilles 		if (keycmp[i].service == service)
23365c4fdfbSgilles 			match = keycmp[i].func;
23465c4fdfbSgilles 
23565c4fdfbSgilles 	line = NULL;
23665c4fdfbSgilles 	iter = NULL;
23765c4fdfbSgilles 	ret = 0;
23865c4fdfbSgilles 	while (dict_iter(&m->t_dict, &iter, &k, (void **)&v)) {
23965c4fdfbSgilles 		if (match) {
24065c4fdfbSgilles 			if (match(key, k)) {
24165c4fdfbSgilles 				line = v;
24265c4fdfbSgilles 				ret = 1;
24365c4fdfbSgilles 			}
24465c4fdfbSgilles 		}
24565c4fdfbSgilles 		else {
24665c4fdfbSgilles 			if (strcmp(key, k) == 0) {
24765c4fdfbSgilles 				line = v;
24865c4fdfbSgilles 				ret = 1;
24965c4fdfbSgilles 			}
25065c4fdfbSgilles 		}
25165c4fdfbSgilles 		if (ret)
25265c4fdfbSgilles 			break;
25365c4fdfbSgilles 	}
25465c4fdfbSgilles 
255aa0d26b5Seric 	if (dst == NULL)
25665c4fdfbSgilles 		return ret ? 1 : 0;
25765c4fdfbSgilles 
258299c4efeSeric 	if (ret == 0)
25965c4fdfbSgilles 		return 0;
26065c4fdfbSgilles 
261aa0d26b5Seric 	*dst = strdup(line);
262aa0d26b5Seric 	if (*dst == NULL)
263aa0d26b5Seric 		return -1;
264aa0d26b5Seric 
265aa0d26b5Seric 	return 1;
26665c4fdfbSgilles }
26765c4fdfbSgilles 
26865c4fdfbSgilles static int
269699c3f98Seric table_static_fetch(void *hdl, enum table_service service, char **dst)
27065c4fdfbSgilles {
27165c4fdfbSgilles 	struct table   *t = hdl;
27265c4fdfbSgilles 	const char     *k;
27365c4fdfbSgilles 
27465c4fdfbSgilles 	if (!dict_iter(&t->t_dict, &t->t_iter, &k, (void **)NULL)) {
27565c4fdfbSgilles 		t->t_iter = NULL;
27665c4fdfbSgilles 		if (!dict_iter(&t->t_dict, &t->t_iter, &k, (void **)NULL))
27765c4fdfbSgilles 			return 0;
27865c4fdfbSgilles 	}
27965c4fdfbSgilles 
280aa0d26b5Seric 	if (dst == NULL)
28165c4fdfbSgilles 		return 1;
28265c4fdfbSgilles 
283aa0d26b5Seric 	*dst = strdup(k);
284aa0d26b5Seric 	if (*dst == NULL)
285aa0d26b5Seric 		return -1;
286aa0d26b5Seric 
287aa0d26b5Seric 	return 1;
28865c4fdfbSgilles }
289