xref: /openbsd/usr.sbin/smtpd/table.c (revision d415bd75)
1 /*	$OpenBSD: table.c,v 1.50 2021/06/14 17:58:16 eric Exp $	*/
2 
3 /*
4  * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
5  * Copyright (c) 2008 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/stat.h>
21 
22 #include <net/if.h>
23 
24 #include <arpa/inet.h>
25 #include <errno.h>
26 #include <regex.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "smtpd.h"
31 #include "log.h"
32 
33 struct table_backend *table_backend_lookup(const char *);
34 
35 extern struct table_backend table_backend_static;
36 extern struct table_backend table_backend_db;
37 extern struct table_backend table_backend_getpwnam;
38 extern struct table_backend table_backend_proc;
39 
40 static const char * table_service_name(enum table_service);
41 static int table_parse_lookup(enum table_service, const char *, const char *,
42     union lookup *);
43 static int parse_sockaddr(struct sockaddr *, int, const char *);
44 
45 static unsigned int last_table_id = 0;
46 
47 static struct table_backend *backends[] = {
48 	&table_backend_static,
49 	&table_backend_db,
50 	&table_backend_getpwnam,
51 	&table_backend_proc,
52 	NULL
53 };
54 
55 struct table_backend *
56 table_backend_lookup(const char *backend)
57 {
58 	int i;
59 
60 	if (!strcmp(backend, "file"))
61 		backend = "static";
62 
63 	for (i = 0; backends[i]; i++)
64 		if (!strcmp(backends[i]->name, backend))
65 			return (backends[i]);
66 
67 	return NULL;
68 }
69 
70 static const char *
71 table_service_name(enum table_service s)
72 {
73 	switch (s) {
74 	case K_NONE:		return "NONE";
75 	case K_ALIAS:		return "ALIAS";
76 	case K_DOMAIN:		return "DOMAIN";
77 	case K_CREDENTIALS:	return "CREDENTIALS";
78 	case K_NETADDR:		return "NETADDR";
79 	case K_USERINFO:	return "USERINFO";
80 	case K_SOURCE:		return "SOURCE";
81 	case K_MAILADDR:	return "MAILADDR";
82 	case K_ADDRNAME:	return "ADDRNAME";
83 	case K_MAILADDRMAP:	return "MAILADDRMAP";
84 	case K_RELAYHOST:	return "RELAYHOST";
85 	case K_STRING:		return "STRING";
86 	case K_REGEX:		return "REGEX";
87 	}
88 	return "???";
89 }
90 
91 struct table *
92 table_find(struct smtpd *conf, const char *name)
93 {
94 	return dict_get(conf->sc_tables_dict, name);
95 }
96 
97 int
98 table_match(struct table *table, enum table_service kind, const char *key)
99 {
100 	return table_lookup(table, kind, key, NULL);
101 }
102 
103 int
104 table_lookup(struct table *table, enum table_service kind, const char *key,
105     union lookup *lk)
106 {
107 	char lkey[1024], *buf = NULL;
108 	int r;
109 
110 	r = -1;
111 	if (table->t_backend->lookup == NULL)
112 		errno = ENOTSUP;
113 	else if (!lowercase(lkey, key, sizeof lkey)) {
114 		log_warnx("warn: lookup key too long: %s", key);
115 		errno = EINVAL;
116 	}
117 	else
118 		r = table->t_backend->lookup(table, kind, lkey, lk ? &buf : NULL);
119 
120 	if (r == 1) {
121 		log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s",
122 		    lk ? "lookup" : "match",
123 		    key,
124 		    table_service_name(kind),
125 		    table->t_backend->name,
126 		    table->t_name,
127 		    lk ? "\"" : "",
128 		    lk ? buf : "true",
129 		    lk ? "\"" : "");
130 		if (buf)
131 			r = table_parse_lookup(kind, lkey, buf, lk);
132 	}
133 	else
134 		log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s",
135 		    lk ? "lookup" : "match",
136 		    key,
137 		    table_service_name(kind),
138 		    table->t_backend->name,
139 		    table->t_name,
140 		    (r == -1) ? "error: " : (lk ? "none" : "false"),
141 		    (r == -1) ? strerror(errno) : "");
142 
143 	free(buf);
144 
145 	return (r);
146 }
147 
148 int
149 table_fetch(struct table *table, enum table_service kind, union lookup *lk)
150 {
151 	char *buf = NULL;
152 	int r;
153 
154 	r = -1;
155 	if (table->t_backend->fetch == NULL)
156 		errno = ENOTSUP;
157 	else
158 		r = table->t_backend->fetch(table, kind, &buf);
159 
160 	if (r == 1) {
161 		log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> \"%s\"",
162 		    table_service_name(kind),
163 		    table->t_backend->name,
164 		    table->t_name,
165 		    buf);
166 		r = table_parse_lookup(kind, NULL, buf, lk);
167 	}
168 	else
169 		log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> %s%s",
170 		    table_service_name(kind),
171 		    table->t_backend->name,
172 		    table->t_name,
173 		    (r == -1) ? "error: " : "none",
174 		    (r == -1) ? strerror(errno) : "");
175 
176 	free(buf);
177 
178 	return (r);
179 }
180 
181 struct table *
182 table_create(struct smtpd *conf, const char *backend, const char *name,
183     const char *config)
184 {
185 	struct table		*t;
186 	struct table_backend	*tb;
187 	char			 path[LINE_MAX];
188 	size_t			 n;
189 	struct stat		 sb;
190 
191 	if (name && table_find(conf, name))
192 		fatalx("table_create: table \"%s\" already defined", name);
193 
194 	if ((tb = table_backend_lookup(backend)) == NULL) {
195 		if ((size_t)snprintf(path, sizeof(path), PATH_LIBEXEC"/table-%s",
196 			backend) >= sizeof(path)) {
197 			fatalx("table_create: path too long \""
198 			    PATH_LIBEXEC"/table-%s\"", backend);
199 		}
200 		if (stat(path, &sb) == 0) {
201 			tb = table_backend_lookup("proc");
202 			(void)strlcpy(path, backend, sizeof(path));
203 			if (config) {
204 				(void)strlcat(path, ":", sizeof(path));
205 				if (strlcat(path, config, sizeof(path))
206 				    >= sizeof(path))
207 					fatalx("table_create: config file path too long");
208 			}
209 			config = path;
210 		}
211 	}
212 
213 	if (tb == NULL)
214 		fatalx("table_create: backend \"%s\" does not exist", backend);
215 
216 	t = xcalloc(1, sizeof(*t));
217 	t->t_backend = tb;
218 
219 	if (config) {
220 		if (strlcpy(t->t_config, config, sizeof t->t_config)
221 		    >= sizeof t->t_config)
222 			fatalx("table_create: table config \"%s\" too large",
223 			    t->t_config);
224 	}
225 
226 	if (strcmp(tb->name, "static") != 0)
227 		t->t_type = T_DYNAMIC;
228 
229 	if (name == NULL)
230 		(void)snprintf(t->t_name, sizeof(t->t_name), "<dynamic:%u>",
231 		    last_table_id++);
232 	else {
233 		n = strlcpy(t->t_name, name, sizeof(t->t_name));
234 		if (n >= sizeof(t->t_name))
235 			fatalx("table_create: table name too long");
236 	}
237 
238 	dict_set(conf->sc_tables_dict, t->t_name, t);
239 
240 	return (t);
241 }
242 
243 void
244 table_destroy(struct smtpd *conf, struct table *t)
245 {
246 	dict_xpop(conf->sc_tables_dict, t->t_name);
247 	free(t);
248 }
249 
250 int
251 table_config(struct table *t)
252 {
253 	if (t->t_backend->config == NULL)
254 		return (1);
255 	return (t->t_backend->config(t));
256 }
257 
258 void
259 table_add(struct table *t, const char *key, const char *val)
260 {
261 	if (t->t_backend->add == NULL)
262 		fatalx("table_add: cannot add to table");
263 
264 	if (t->t_backend->add(t, key, val) == 0)
265 		log_warnx("warn: failed to add \"%s\" in table \"%s\"", key, t->t_name);
266 }
267 
268 void
269 table_dump(struct table *t)
270 {
271 	const char *type;
272 	char buf[LINE_MAX];
273 
274 	switch(t->t_type) {
275 	case T_NONE:
276 		type = "NONE";
277 		break;
278 	case T_DYNAMIC:
279 		type = "DYNAMIC";
280 		break;
281 	case T_LIST:
282 		type = "LIST";
283 		break;
284 	case T_HASH:
285 		type = "HASH";
286 		break;
287 	default:
288 		type = "???";
289 		break;
290 	}
291 
292 	if (t->t_config[0])
293 		snprintf(buf, sizeof(buf), " config=\"%s\"", t->t_config);
294 	else
295 		buf[0] = '\0';
296 
297 	log_debug("TABLE \"%s\" backend=%s type=%s%s", t->t_name,
298 	    t->t_backend->name, type, buf);
299 
300 	if (t->t_backend->dump)
301 		t->t_backend->dump(t);
302 }
303 
304 int
305 table_check_type(struct table *t, uint32_t mask)
306 {
307 	return t->t_type & mask;
308 }
309 
310 int
311 table_check_service(struct table *t, uint32_t mask)
312 {
313 	return t->t_backend->services & mask;
314 }
315 
316 int
317 table_check_use(struct table *t, uint32_t tmask, uint32_t smask)
318 {
319 	return table_check_type(t, tmask) && table_check_service(t, smask);
320 }
321 
322 int
323 table_open(struct table *t)
324 {
325 	if (t->t_backend->open == NULL)
326 		return (1);
327 	return (t->t_backend->open(t));
328 }
329 
330 void
331 table_close(struct table *t)
332 {
333 	if (t->t_backend->close)
334 		t->t_backend->close(t);
335 }
336 
337 int
338 table_update(struct table *t)
339 {
340 	if (t->t_backend->update == NULL)
341 		return (1);
342 	return (t->t_backend->update(t));
343 }
344 
345 
346 /*
347  * quick reminder:
348  * in *_match() s1 comes from session, s2 comes from table
349  */
350 
351 int
352 table_domain_match(const char *s1, const char *s2)
353 {
354 	return hostname_match(s1, s2);
355 }
356 
357 int
358 table_mailaddr_match(const char *s1, const char *s2)
359 {
360 	struct mailaddr m1;
361 	struct mailaddr m2;
362 
363 	if (!text_to_mailaddr(&m1, s1))
364 		return 0;
365 	if (!text_to_mailaddr(&m2, s2))
366 		return 0;
367 	return mailaddr_match(&m1, &m2);
368 }
369 
370 static int table_match_mask(struct sockaddr_storage *, struct netaddr *);
371 static int table_inet4_match(struct sockaddr_in *, struct netaddr *);
372 static int table_inet6_match(struct sockaddr_in6 *, struct netaddr *);
373 
374 int
375 table_netaddr_match(const char *s1, const char *s2)
376 {
377 	struct netaddr n1;
378 	struct netaddr n2;
379 
380 	if (strcasecmp(s1, s2) == 0)
381 		return 1;
382 	if (!text_to_netaddr(&n1, s1))
383 		return 0;
384 	if (!text_to_netaddr(&n2, s2))
385 		return 0;
386 	if (n1.ss.ss_family != n2.ss.ss_family)
387 		return 0;
388 	if (n1.ss.ss_len != n2.ss.ss_len)
389 		return 0;
390 	return table_match_mask(&n1.ss, &n2);
391 }
392 
393 static int
394 table_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask)
395 {
396 	if (ss->ss_family == AF_INET)
397 		return table_inet4_match((struct sockaddr_in *)ss, ssmask);
398 
399 	if (ss->ss_family == AF_INET6)
400 		return table_inet6_match((struct sockaddr_in6 *)ss, ssmask);
401 
402 	return (0);
403 }
404 
405 static int
406 table_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask)
407 {
408 	in_addr_t mask;
409 	int i;
410 
411 	/* a.b.c.d/8 -> htonl(0xff000000) */
412 	mask = 0;
413 	for (i = 0; i < ssmask->bits; ++i)
414 		mask = (mask >> 1) | 0x80000000;
415 	mask = htonl(mask);
416 
417 	/* (addr & mask) == (net & mask) */
418 	if ((ss->sin_addr.s_addr & mask) ==
419 	    (((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask))
420 		return 1;
421 
422 	return 0;
423 }
424 
425 static int
426 table_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask)
427 {
428 	struct in6_addr	*in;
429 	struct in6_addr	*inmask;
430 	struct in6_addr	 mask;
431 	int		 i;
432 
433 	memset(&mask, 0, sizeof(mask));
434 	for (i = 0; i < ssmask->bits / 8; i++)
435 		mask.s6_addr[i] = 0xff;
436 	i = ssmask->bits % 8;
437 	if (i)
438 		mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i;
439 
440 	in = &ss->sin6_addr;
441 	inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr;
442 
443 	for (i = 0; i < 16; i++) {
444 		if ((in->s6_addr[i] & mask.s6_addr[i]) !=
445 		    (inmask->s6_addr[i] & mask.s6_addr[i]))
446 			return (0);
447 	}
448 
449 	return (1);
450 }
451 
452 int
453 table_regex_match(const char *string, const char *pattern)
454 {
455 	regex_t preg;
456 	int	cflags = REG_EXTENDED|REG_NOSUB;
457 	int ret;
458 
459 	if (strncmp(pattern, "(?i)", 4) == 0) {
460 		cflags |= REG_ICASE;
461 		pattern += 4;
462 	}
463 
464 	if (regcomp(&preg, pattern, cflags) != 0)
465 		return (0);
466 
467 	ret = regexec(&preg, string, 0, NULL, 0);
468 
469 	regfree(&preg);
470 
471 	if (ret != 0)
472 		return (0);
473 
474 	return (1);
475 }
476 
477 void
478 table_dump_all(struct smtpd *conf)
479 {
480 	struct table	*t;
481 	void		*iter;
482 
483 	iter = NULL;
484 	while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t))
485 		table_dump(t);
486 }
487 
488 void
489 table_open_all(struct smtpd *conf)
490 {
491 	struct table	*t;
492 	void		*iter;
493 
494 	iter = NULL;
495 	while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t))
496 		if (!table_open(t))
497 			fatalx("failed to open table %s", t->t_name);
498 }
499 
500 void
501 table_close_all(struct smtpd *conf)
502 {
503 	struct table	*t;
504 	void		*iter;
505 
506 	iter = NULL;
507 	while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t))
508 		table_close(t);
509 }
510 
511 static int
512 table_parse_lookup(enum table_service service, const char *key,
513     const char *line, union lookup *lk)
514 {
515 	char	buffer[LINE_MAX], *p;
516 	size_t	len;
517 
518 	len = strlen(line);
519 
520 	switch (service) {
521 	case K_ALIAS:
522 		lk->expand = calloc(1, sizeof(*lk->expand));
523 		if (lk->expand == NULL)
524 			return (-1);
525 		if (!expand_line(lk->expand, line, 1)) {
526 			expand_free(lk->expand);
527 			return (-1);
528 		}
529 		return (1);
530 
531 	case K_DOMAIN:
532 		if (strlcpy(lk->domain.name, line, sizeof(lk->domain.name))
533 		    >= sizeof(lk->domain.name))
534 			return (-1);
535 		return (1);
536 
537 	case K_CREDENTIALS:
538 
539 		/* credentials are stored as user:password */
540 		if (len < 3)
541 			return (-1);
542 
543 		/* too big to fit in a smtp session line */
544 		if (len >= LINE_MAX)
545 			return (-1);
546 
547 		p = strchr(line, ':');
548 		if (p == NULL) {
549 			if (strlcpy(lk->creds.username, key, sizeof (lk->creds.username))
550 			    >= sizeof (lk->creds.username))
551 				return (-1);
552 			if (strlcpy(lk->creds.password, line, sizeof(lk->creds.password))
553 			    >= sizeof(lk->creds.password))
554 				return (-1);
555 			return (1);
556 		}
557 
558 		if (p == line || p == line + len - 1)
559 			return (-1);
560 
561 		memmove(lk->creds.username, line, p - line);
562 		lk->creds.username[p - line] = '\0';
563 
564 		if (strlcpy(lk->creds.password, p+1, sizeof(lk->creds.password))
565 		    >= sizeof(lk->creds.password))
566 			return (-1);
567 
568 		return (1);
569 
570 	case K_NETADDR:
571 		if (!text_to_netaddr(&lk->netaddr, line))
572 			return (-1);
573 		return (1);
574 
575 	case K_USERINFO:
576 		if (!bsnprintf(buffer, sizeof(buffer), "%s:%s", key, line))
577 			return (-1);
578 		if (!text_to_userinfo(&lk->userinfo, buffer))
579 			return (-1);
580  		return (1);
581 
582 	case K_SOURCE:
583 		if (parse_sockaddr((struct sockaddr *)&lk->source.addr,
584 		    PF_UNSPEC, line) == -1)
585 			return (-1);
586 		return (1);
587 
588 	case K_MAILADDR:
589 		if (!text_to_mailaddr(&lk->mailaddr, line))
590 			return (-1);
591 		return (1);
592 
593 	case K_MAILADDRMAP:
594 		lk->maddrmap = calloc(1, sizeof(*lk->maddrmap));
595 		if (lk->maddrmap == NULL)
596 			return (-1);
597 		maddrmap_init(lk->maddrmap);
598 		if (!mailaddr_line(lk->maddrmap, line)) {
599 			maddrmap_free(lk->maddrmap);
600 			return (-1);
601 		}
602 		return (1);
603 
604 	case K_ADDRNAME:
605 		if (parse_sockaddr((struct sockaddr *)&lk->addrname.addr,
606 		    PF_UNSPEC, key) == -1)
607 			return (-1);
608 		if (strlcpy(lk->addrname.name, line, sizeof(lk->addrname.name))
609 		    >= sizeof(lk->addrname.name))
610 			return (-1);
611 		return (1);
612 
613 	case K_RELAYHOST:
614 		if (strlcpy(lk->relayhost, line, sizeof(lk->relayhost))
615 		    >= sizeof(lk->relayhost))
616 			return (-1);
617 		return (1);
618 
619 	default:
620 		return (-1);
621 	}
622 }
623 
624 static int
625 parse_sockaddr(struct sockaddr *sa, int family, const char *str)
626 {
627 	struct in_addr		 ina;
628 	struct in6_addr		 in6a;
629 	struct sockaddr_in	*sin;
630 	struct sockaddr_in6	*sin6;
631 	char			*cp, *str2;
632 	const char		*errstr;
633 
634 	switch (family) {
635 	case PF_UNSPEC:
636 		if (parse_sockaddr(sa, PF_INET, str) == 0)
637 			return (0);
638 		return parse_sockaddr(sa, PF_INET6, str);
639 
640 	case PF_INET:
641 		if (inet_pton(PF_INET, str, &ina) != 1)
642 			return (-1);
643 
644 		sin = (struct sockaddr_in *)sa;
645 		memset(sin, 0, sizeof *sin);
646 		sin->sin_len = sizeof(struct sockaddr_in);
647 		sin->sin_family = PF_INET;
648 		sin->sin_addr.s_addr = ina.s_addr;
649 		return (0);
650 
651 	case PF_INET6:
652 		if (strncasecmp("ipv6:", str, 5) == 0)
653 			str += 5;
654 		cp = strchr(str, SCOPE_DELIMITER);
655 		if (cp) {
656 			str2 = strdup(str);
657 			if (str2 == NULL)
658 				return (-1);
659 			str2[cp - str] = '\0';
660 			if (inet_pton(PF_INET6, str2, &in6a) != 1) {
661 				free(str2);
662 				return (-1);
663 			}
664 			cp++;
665 			free(str2);
666 		} else if (inet_pton(PF_INET6, str, &in6a) != 1)
667 			return (-1);
668 
669 		sin6 = (struct sockaddr_in6 *)sa;
670 		memset(sin6, 0, sizeof *sin6);
671 		sin6->sin6_len = sizeof(struct sockaddr_in6);
672 		sin6->sin6_family = PF_INET6;
673 		sin6->sin6_addr = in6a;
674 
675 		if (cp == NULL)
676 			return (0);
677 
678 		if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
679 		    IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
680 		    IN6_IS_ADDR_MC_INTFACELOCAL(&in6a))
681 			if ((sin6->sin6_scope_id = if_nametoindex(cp)))
682 				return (0);
683 
684 		sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
685 		if (errstr)
686 			return (-1);
687 		return (0);
688 
689 	default:
690 		break;
691 	}
692 
693 	return (-1);
694 }
695