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