xref: /openbsd-src/usr.sbin/smtpd/table_static.c (revision de8cc8edbc71bd3e3bc7fbffa27ba0e564c37d8b)
1 /*	$OpenBSD: table_static.c,v 1.32 2018/12/28 14:21:02 eric Exp $	*/
2 
3 /*
4  * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
5  * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/queue.h>
22 #include <sys/tree.h>
23 #include <sys/socket.h>
24 
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 
28 #include <ctype.h>
29 #include <errno.h>
30 
31 #include <event.h>
32 #include <fcntl.h>
33 #include <imsg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <limits.h>
37 #include <string.h>
38 
39 #include "smtpd.h"
40 #include "log.h"
41 
42 struct table_static_priv {
43 	int		 type;
44 	struct dict	 dict;
45 	void		*iter;
46 };
47 
48 /* static backend */
49 static int table_static_config(struct table *);
50 static int table_static_add(struct table *, const char *, const char *);
51 static void table_static_dump(struct table *);
52 static int table_static_update(struct table *);
53 static int table_static_open(struct table *);
54 static int table_static_lookup(struct table *, enum table_service, const char *,
55     char **);
56 static int table_static_fetch(struct table *, enum table_service, char **);
57 static void table_static_close(struct table *);
58 
59 struct table_backend table_backend_static = {
60 	"static",
61 	K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|
62 	K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST|
63 	K_STRING|K_REGEX,
64 	table_static_config,
65 	table_static_add,
66 	table_static_dump,
67 	table_static_open,
68 	table_static_update,
69 	table_static_close,
70 	table_static_lookup,
71 	table_static_fetch
72 };
73 
74 static struct keycmp {
75 	enum table_service	service;
76 	int		       (*func)(const char *, const char *);
77 } keycmp[] = {
78 	{ K_DOMAIN, table_domain_match },
79 	{ K_NETADDR, table_netaddr_match },
80 	{ K_MAILADDR, table_mailaddr_match },
81 	{ K_REGEX, table_regex_match },
82 };
83 
84 
85 static void
86 table_static_priv_free(struct table_static_priv *priv)
87 {
88 	void *p;
89 
90 	while (dict_poproot(&priv->dict, (void **)&p))
91 		if (p != priv)
92 			free(p);
93 	free(priv);
94 }
95 
96 static int
97 table_static_priv_add(struct table_static_priv *priv, const char *key, const char *val)
98 {
99 	char lkey[1024];
100 	void *old, *new = NULL;
101 
102 	if (!lowercase(lkey, key, sizeof lkey)) {
103 		errno = ENAMETOOLONG;
104 		return (-1);
105 	}
106 
107 	if (val) {
108 		new = strdup(val);
109 		if (new == NULL)
110 			return (-1);
111 	}
112 
113 	/* use priv if value is null, so we can detect duplicate entries */
114 	old = dict_set(&priv->dict, lkey, new ? new : priv);
115 	if (old) {
116 		if (old != priv)
117 			free(old);
118 		return (1);
119 	}
120 
121 	return (0);
122 }
123 
124 static int
125 table_static_priv_load(struct table_static_priv *priv, const char *path)
126 {
127 	FILE	*fp;
128 	char	*buf = NULL, *p;
129 	int	 lineno = 0;
130 	size_t	 sz = 0;
131 	ssize_t	 flen;
132 	char	*keyp;
133 	char	*valp;
134 	int	 ret = 0;
135 
136 	if ((fp = fopen(path, "r")) == NULL) {
137 		log_warn("%s: fopen", path);
138 		return 0;
139 	}
140 
141 	while ((flen = getline(&buf, &sz, fp)) != -1) {
142 		lineno++;
143 		if (buf[flen - 1] == '\n')
144 			buf[--flen] = '\0';
145 
146 		keyp = buf;
147 		while (isspace((unsigned char)*keyp)) {
148 			++keyp;
149 			--flen;
150 		}
151 		if (*keyp == '\0')
152 			continue;
153 		while (isspace((unsigned char)keyp[flen - 1]))
154 			keyp[--flen] = '\0';
155 		if (*keyp == '#') {
156 			if (priv->type == T_NONE) {
157 				keyp++;
158 				while (isspace((unsigned char)*keyp))
159 					++keyp;
160 				if (!strcmp(keyp, "@list"))
161 					priv->type = T_LIST;
162 			}
163 			continue;
164 		}
165 
166 		if (priv->type == T_NONE) {
167 			for (p = keyp; *p; p++) {
168 				if (*p == ' ' || *p == '\t' || *p == ':') {
169 					priv->type = T_HASH;
170 					break;
171 				}
172 			}
173 			if (priv->type == T_NONE)
174 				priv->type = T_LIST;
175 		}
176 
177 		if (priv->type == T_LIST) {
178 			table_static_priv_add(priv, keyp, NULL);
179 			continue;
180 		}
181 
182 		/* T_HASH */
183 		valp = keyp;
184 		strsep(&valp, " \t:");
185 		if (valp) {
186 			while (*valp) {
187 				if (!isspace((unsigned char)*valp) &&
188 				    !(*valp == ':' &&
189 				    isspace((unsigned char)*(valp + 1))))
190 					break;
191 				++valp;
192 			}
193 			if (*valp == '\0')
194 				valp = NULL;
195 		}
196 		if (valp == NULL) {
197 			log_warnx("%s: invalid map entry line %d",
198 			    path, lineno);
199 			goto end;
200 		}
201 
202 		table_static_priv_add(priv, keyp, valp);
203 	}
204 
205 	if (ferror(fp)) {
206 		log_warn("%s: getline", path);
207 		goto end;
208 	}
209 
210 	/* Accept empty alias files; treat them as hashes */
211 	if (priv->type == T_NONE)
212 		priv->type = T_HASH;
213 
214 	ret = 1;
215 end:
216 	free(buf);
217 	fclose(fp);
218 	return ret;
219 }
220 
221 static int
222 table_static_config(struct table *t)
223 {
224 	struct table_static_priv *priv, *old;
225 
226 	/* already up, and no config file? ok */
227 	if (t->t_handle && *t->t_config == '\0')
228 		return 1;
229 
230 	/* new config */
231 	priv = calloc(1, sizeof(*priv));
232 	if (priv == NULL)
233 		return 0;
234 	priv->type = t->t_type;
235 	dict_init(&priv->dict);
236 
237 	if (*t->t_config) {
238 		/* load the config file */
239 		if (table_static_priv_load(priv, t->t_config) == 0) {
240 			table_static_priv_free(priv);
241 			return 0;
242 		}
243 	}
244 
245 	if ((old = t->t_handle))
246 		table_static_priv_free(old);
247 	t->t_handle = priv;
248 	t->t_type = priv->type;
249 
250 	return 1;
251 }
252 
253 static int
254 table_static_add(struct table *table, const char *key, const char *val)
255 {
256 	struct table_static_priv *priv = table->t_handle;
257 	int r;
258 
259 	/* cannot add to a table read from a file */
260 	if (*table->t_config)
261 		return 0;
262 
263 	if (table->t_type == T_NONE)
264 		table->t_type = val ? T_HASH : T_LIST;
265 	else if (table->t_type == T_LIST && val)
266 		return 0;
267 	else if (table->t_type == T_HASH && val == NULL)
268 		return 0;
269 
270 	if (priv == NULL) {
271 		if (table_static_config(table) == 0)
272 			return 0;
273 		priv = table->t_handle;
274 	}
275 
276 	r = table_static_priv_add(priv, key, val);
277 	if (r == -1)
278 		return 0;
279 	return 1;
280 }
281 
282 static void
283 table_static_dump(struct table *table)
284 {
285 	struct table_static_priv *priv = table->t_handle;
286 	const char *key;
287 	char *value;
288 	void *iter;
289 
290 	iter = NULL;
291 	while (dict_iter(&priv->dict, &iter, &key, (void**)&value)) {
292 		if (value && (void*)value != (void*)priv)
293 			log_debug("	\"%s\" -> \"%s\"", key, value);
294 		else
295 			log_debug("	\"%s\"", key);
296 	}
297 }
298 
299 static int
300 table_static_update(struct table *table)
301 {
302 	if (table_static_config(table) == 1) {
303 		log_info("info: Table \"%s\" successfully updated", table->t_name);
304 		return 1;
305 	}
306 
307 	log_info("info: Failed to update table \"%s\"", table->t_name);
308 	return 0;
309 }
310 
311 static int
312 table_static_open(struct table *table)
313 {
314 	if (table->t_handle == NULL)
315 		return table_static_config(table);
316 	return 1;
317 }
318 
319 static void
320 table_static_close(struct table *table)
321 {
322 	struct table_static_priv *priv = table->t_handle;
323 
324 	if (priv)
325 		table_static_priv_free(priv);
326 	table->t_handle = NULL;
327 }
328 
329 static int
330 table_static_lookup(struct table *table, enum table_service service, const char *key,
331     char **dst)
332 {
333 	struct table_static_priv *priv = table->t_handle;
334 	char	       *line;
335 	int		ret;
336 	int	       (*match)(const char *, const char *) = NULL;
337 	size_t		i;
338 	void	       *iter;
339 	const char     *k;
340 	char	       *v;
341 
342 	for (i = 0; i < nitems(keycmp); ++i)
343 		if (keycmp[i].service == service)
344 			match = keycmp[i].func;
345 
346 	line = NULL;
347 	iter = NULL;
348 	ret = 0;
349 	while (dict_iter(&priv->dict, &iter, &k, (void **)&v)) {
350 		if (match) {
351 			if (match(key, k)) {
352 				line = v;
353 				ret = 1;
354 			}
355 		}
356 		else {
357 			if (strcmp(key, k) == 0) {
358 				line = v;
359 				ret = 1;
360 			}
361 		}
362 		if (ret)
363 			break;
364 	}
365 
366 	if (dst == NULL)
367 		return ret ? 1 : 0;
368 
369 	if (ret == 0)
370 		return 0;
371 
372 	*dst = strdup(line);
373 	if (*dst == NULL)
374 		return -1;
375 
376 	return 1;
377 }
378 
379 static int
380 table_static_fetch(struct table *t, enum table_service service, char **dst)
381 {
382 	struct table_static_priv *priv = t->t_handle;
383 	const char *k;
384 
385 	if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) {
386 		priv->iter = NULL;
387 		if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL))
388 			return 0;
389 	}
390 
391 	*dst = strdup(k);
392 	if (*dst == NULL)
393 		return -1;
394 
395 	return 1;
396 }
397