xref: /openbsd-src/usr.sbin/smtpd/util.c (revision d1df930ffab53da22f3324c32bed7ac5709915e6)
1 /*	$OpenBSD: util.c,v 1.137 2018/09/02 21:06:44 gilles Exp $	*/
2 
3 /*
4  * Copyright (c) 2000,2001 Markus Friedl.  All rights reserved.
5  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
6  * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
7  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 #include <sys/types.h>
23 #include <sys/queue.h>
24 #include <sys/tree.h>
25 #include <sys/socket.h>
26 #include <sys/stat.h>
27 #include <sys/resource.h>
28 
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 
32 #include <ctype.h>
33 #include <errno.h>
34 #include <event.h>
35 #include <fcntl.h>
36 #include <fts.h>
37 #include <imsg.h>
38 #include <inttypes.h>
39 #include <libgen.h>
40 #include <netdb.h>
41 #include <pwd.h>
42 #include <limits.h>
43 #include <resolv.h>
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <syslog.h>
49 #include <time.h>
50 #include <unistd.h>
51 
52 #include "smtpd.h"
53 #include "log.h"
54 
55 const char *log_in6addr(const struct in6_addr *);
56 const char *log_sockaddr(struct sockaddr *);
57 static int  parse_mailname_file(char *, size_t);
58 
59 int	tracing = 0;
60 int	foreground_log = 0;
61 
62 void *
63 xmalloc(size_t size)
64 {
65 	void	*r;
66 
67 	if ((r = malloc(size)) == NULL)
68 		fatal("malloc");
69 
70 	return (r);
71 }
72 
73 void *
74 xcalloc(size_t nmemb, size_t size)
75 {
76 	void	*r;
77 
78 	if ((r = calloc(nmemb, size)) == NULL)
79 		fatal("calloc");
80 
81 	return (r);
82 }
83 
84 char *
85 xstrdup(const char *str)
86 {
87 	char	*r;
88 
89 	if ((r = strdup(str)) == NULL)
90 		fatal("strdup");
91 
92 	return (r);
93 }
94 
95 void *
96 xmemdup(const void *ptr, size_t size)
97 {
98 	void	*r;
99 
100 	if ((r = malloc(size)) == NULL)
101 		fatal("malloc");
102 
103 	memmove(r, ptr, size);
104 
105 	return (r);
106 }
107 
108 int
109 xasprintf(char **ret, const char *format, ...)
110 {
111 	int r;
112 	va_list ap;
113 
114 	va_start(ap, format);
115 	r = vasprintf(ret, format, ap);
116 	va_end(ap);
117 	if (r == -1)
118 		fatal("vasprintf");
119 
120 	return (r);
121 }
122 
123 
124 #if !defined(NO_IO)
125 int
126 io_xprintf(struct io *io, const char *fmt, ...)
127 {
128 	va_list	ap;
129 	int len;
130 
131 	va_start(ap, fmt);
132 	len = io_vprintf(io, fmt, ap);
133 	va_end(ap);
134 	if (len == -1)
135 		fatal("io_xprintf(%p, %s, ...)", io, fmt);
136 
137 	return len;
138 }
139 
140 int
141 io_xprint(struct io *io, const char *str)
142 {
143 	int len;
144 
145 	len = io_print(io, str);
146 	if (len == -1)
147 		fatal("io_xprint(%p, %s, ...)", io, str);
148 
149 	return len;
150 }
151 #endif
152 
153 char *
154 strip(char *s)
155 {
156 	size_t	 l;
157 
158 	while (isspace((unsigned char)*s))
159 		s++;
160 
161 	for (l = strlen(s); l; l--) {
162 		if (!isspace((unsigned char)s[l-1]))
163 			break;
164 		s[l-1] = '\0';
165 	}
166 
167 	return (s);
168 }
169 
170 int
171 bsnprintf(char *str, size_t size, const char *format, ...)
172 {
173 	int ret;
174 	va_list ap;
175 
176 	va_start(ap, format);
177 	ret = vsnprintf(str, size, format, ap);
178 	va_end(ap);
179 	if (ret == -1 || ret >= (int)size)
180 		return 0;
181 
182 	return 1;
183 }
184 
185 
186 static int
187 mkdirs_component(char *path, mode_t mode)
188 {
189 	struct stat	sb;
190 
191 	if (stat(path, &sb) == -1) {
192 		if (errno != ENOENT)
193 			return 0;
194 		if (mkdir(path, mode | S_IWUSR | S_IXUSR) == -1)
195 			return 0;
196 	}
197 	else if (!S_ISDIR(sb.st_mode))
198 		return 0;
199 
200 	return 1;
201 }
202 
203 int
204 mkdirs(char *path, mode_t mode)
205 {
206 	char	 buf[PATH_MAX];
207 	int	 i = 0;
208 	int	 done = 0;
209 	char	*p;
210 
211 	/* absolute path required */
212 	if (*path != '/')
213 		return 0;
214 
215 	/* make sure we don't exceed PATH_MAX */
216 	if (strlen(path) >= sizeof buf)
217 		return 0;
218 
219 	memset(buf, 0, sizeof buf);
220 	for (p = path; *p; p++) {
221 		if (*p == '/') {
222 			if (buf[0] != '\0')
223 				if (!mkdirs_component(buf, mode))
224 					return 0;
225 			while (*p == '/')
226 				p++;
227 			buf[i++] = '/';
228 			buf[i++] = *p;
229 			if (*p == '\0' && ++done)
230 				break;
231 			continue;
232 		}
233 		buf[i++] = *p;
234 	}
235 	if (!done)
236 		if (!mkdirs_component(buf, mode))
237 			return 0;
238 
239 	if (chmod(path, mode) == -1)
240 		return 0;
241 
242 	return 1;
243 }
244 
245 int
246 ckdir(const char *path, mode_t mode, uid_t owner, gid_t group, int create)
247 {
248 	char		mode_str[12];
249 	int		ret;
250 	struct stat	sb;
251 
252 	if (stat(path, &sb) == -1) {
253 		if (errno != ENOENT || create == 0) {
254 			log_warn("stat: %s", path);
255 			return (0);
256 		}
257 
258 		/* chmod is deferred to avoid umask effect */
259 		if (mkdir(path, 0) == -1) {
260 			log_warn("mkdir: %s", path);
261 			return (0);
262 		}
263 
264 		if (chown(path, owner, group) == -1) {
265 			log_warn("chown: %s", path);
266 			return (0);
267 		}
268 
269 		if (chmod(path, mode) == -1) {
270 			log_warn("chmod: %s", path);
271 			return (0);
272 		}
273 
274 		if (stat(path, &sb) == -1) {
275 			log_warn("stat: %s", path);
276 			return (0);
277 		}
278 	}
279 
280 	ret = 1;
281 
282 	/* check if it's a directory */
283 	if (!S_ISDIR(sb.st_mode)) {
284 		ret = 0;
285 		log_warnx("%s is not a directory", path);
286 	}
287 
288 	/* check that it is owned by owner/group */
289 	if (sb.st_uid != owner) {
290 		ret = 0;
291 		log_warnx("%s is not owned by uid %d", path, owner);
292 	}
293 	if (sb.st_gid != group) {
294 		ret = 0;
295 		log_warnx("%s is not owned by gid %d", path, group);
296 	}
297 
298 	/* check permission */
299 	if ((sb.st_mode & 07777) != mode) {
300 		ret = 0;
301 		strmode(mode, mode_str);
302 		mode_str[10] = '\0';
303 		log_warnx("%s must be %s (%o)", path, mode_str + 1, mode);
304 	}
305 
306 	return ret;
307 }
308 
309 int
310 rmtree(char *path, int keepdir)
311 {
312 	char		*path_argv[2];
313 	FTS		*fts;
314 	FTSENT		*e;
315 	int		 ret, depth;
316 
317 	path_argv[0] = path;
318 	path_argv[1] = NULL;
319 	ret = 0;
320 	depth = 0;
321 
322 	fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
323 	if (fts == NULL) {
324 		log_warn("fts_open: %s", path);
325 		return (-1);
326 	}
327 
328 	while ((e = fts_read(fts)) != NULL) {
329 		switch (e->fts_info) {
330 		case FTS_D:
331 			depth++;
332 			break;
333 		case FTS_DP:
334 		case FTS_DNR:
335 			depth--;
336 			if (keepdir && depth == 0)
337 				continue;
338 			if (rmdir(e->fts_path) == -1) {
339 				log_warn("rmdir: %s", e->fts_path);
340 				ret = -1;
341 			}
342 			break;
343 
344 		case FTS_F:
345 			if (unlink(e->fts_path) == -1) {
346 				log_warn("unlink: %s", e->fts_path);
347 				ret = -1;
348 			}
349 		}
350 	}
351 
352 	fts_close(fts);
353 
354 	return (ret);
355 }
356 
357 int
358 mvpurge(char *from, char *to)
359 {
360 	size_t		 n;
361 	int		 retry;
362 	const char	*sep;
363 	char		 buf[PATH_MAX];
364 
365 	if ((n = strlen(to)) == 0)
366 		fatalx("to is empty");
367 
368 	sep = (to[n - 1] == '/') ? "" : "/";
369 	retry = 0;
370 
371 again:
372 	(void)snprintf(buf, sizeof buf, "%s%s%u", to, sep, arc4random());
373 	if (rename(from, buf) == -1) {
374 		/* ENOTDIR has actually 2 meanings, and incorrect input
375 		 * could lead to an infinite loop. Consider that after
376 		 * 20 tries something is hopelessly wrong.
377 		 */
378 		if (errno == ENOTEMPTY || errno == EISDIR || errno == ENOTDIR) {
379 			if ((retry++) >= 20)
380 				return (-1);
381 			goto again;
382 		}
383 		return -1;
384 	}
385 
386 	return 0;
387 }
388 
389 
390 int
391 mktmpfile(void)
392 {
393 	char		path[PATH_MAX];
394 	int		fd;
395 
396 	if (!bsnprintf(path, sizeof(path), "%s/smtpd.XXXXXXXXXX",
397 		PATH_TEMPORARY)) {
398 		log_warn("snprintf");
399 		fatal("exiting");
400 	}
401 
402 	if ((fd = mkstemp(path)) == -1) {
403 		log_warn("cannot create temporary file %s", path);
404 		fatal("exiting");
405 	}
406 	unlink(path);
407 	return (fd);
408 }
409 
410 
411 /* Close file, signifying temporary error condition (if any) to the caller. */
412 int
413 safe_fclose(FILE *fp)
414 {
415 	if (ferror(fp)) {
416 		fclose(fp);
417 		return 0;
418 	}
419 	if (fflush(fp)) {
420 		fclose(fp);
421 		if (errno == ENOSPC)
422 			return 0;
423 		fatal("safe_fclose: fflush");
424 	}
425 	if (fsync(fileno(fp)))
426 		fatal("safe_fclose: fsync");
427 	if (fclose(fp))
428 		fatal("safe_fclose: fclose");
429 
430 	return 1;
431 }
432 
433 int
434 hostname_match(const char *hostname, const char *pattern)
435 {
436 	while (*pattern != '\0' && *hostname != '\0') {
437 		if (*pattern == '*') {
438 			while (*pattern == '*')
439 				pattern++;
440 			while (*hostname != '\0' &&
441 			    tolower((unsigned char)*hostname) !=
442 			    tolower((unsigned char)*pattern))
443 				hostname++;
444 			continue;
445 		}
446 
447 		if (tolower((unsigned char)*pattern) !=
448 		    tolower((unsigned char)*hostname))
449 			return 0;
450 		pattern++;
451 		hostname++;
452 	}
453 
454 	return (*hostname == '\0' && *pattern == '\0');
455 }
456 
457 int
458 mailaddr_match(const struct mailaddr *maddr1, const struct mailaddr *maddr2)
459 {
460 	struct mailaddr m1 = *maddr1;
461 	struct mailaddr m2 = *maddr2;
462 	char	       *p;
463 
464 	/* catchall */
465 	if (m2.user[0] == '\0' && m2.domain[0] == '\0')
466 		return 1;
467 
468 	if (m2.domain[0] && !hostname_match(m1.domain, m2.domain))
469 		return 0;
470 
471 	if (m2.user[0]) {
472 		/* if address from table has a tag, we must respect it */
473 		if (strchr(m2.user, *env->sc_subaddressing_delim) == NULL) {
474 			/* otherwise, strip tag from session address if any */
475 			p = strchr(m1.user, *env->sc_subaddressing_delim);
476 			if (p)
477 				*p = '\0';
478 		}
479 		if (strcasecmp(m1.user, m2.user))
480 			return 0;
481 	}
482 	return 1;
483 }
484 
485 int
486 valid_localpart(const char *s)
487 {
488 #define IS_ATEXT(c) (isalnum((unsigned char)(c)) || strchr(MAILADDR_ALLOWED, (c)))
489 nextatom:
490 	if (!IS_ATEXT(*s) || *s == '\0')
491 		return 0;
492 	while (*(++s) != '\0') {
493 		if (*s == '.')
494 			break;
495 		if (IS_ATEXT(*s))
496 			continue;
497 		return 0;
498 	}
499 	if (*s == '.') {
500 		s++;
501 		goto nextatom;
502 	}
503 	return 1;
504 }
505 
506 int
507 valid_domainpart(const char *s)
508 {
509 	struct in_addr	 ina;
510 	struct in6_addr	 ina6;
511 	char		*c, domain[SMTPD_MAXDOMAINPARTSIZE];
512 	const char	*p;
513 
514 	if (*s == '[') {
515 		if (strncasecmp("[IPv6:", s, 6) == 0)
516 			p = s + 6;
517 		else
518 			p = s + 1;
519 
520 		if (strlcpy(domain, p, sizeof domain) >= sizeof domain)
521 			return 0;
522 
523 		c = strchr(domain, (int)']');
524 		if (!c || c[1] != '\0')
525 			return 0;
526 
527 		*c = '\0';
528 
529 		if (inet_pton(AF_INET6, domain, &ina6) == 1)
530 			return 1;
531 		if (inet_pton(AF_INET, domain, &ina) == 1)
532 			return 1;
533 
534 		return 0;
535 	}
536 
537 	if (*s == '\0')
538 		return 0;
539 
540 	return res_hnok(s);
541 }
542 
543 int
544 secure_file(int fd, char *path, char *userdir, uid_t uid, int mayread)
545 {
546 	char		 buf[PATH_MAX];
547 	char		 homedir[PATH_MAX];
548 	struct stat	 st;
549 	char		*cp;
550 
551 	if (realpath(path, buf) == NULL)
552 		return 0;
553 
554 	if (realpath(userdir, homedir) == NULL)
555 		homedir[0] = '\0';
556 
557 	/* Check the open file to avoid races. */
558 	if (fstat(fd, &st) < 0 ||
559 	    !S_ISREG(st.st_mode) ||
560 	    st.st_uid != uid ||
561 	    (st.st_mode & (mayread ? 022 : 066)) != 0)
562 		return 0;
563 
564 	/* For each component of the canonical path, walking upwards. */
565 	for (;;) {
566 		if ((cp = dirname(buf)) == NULL)
567 			return 0;
568 		(void)strlcpy(buf, cp, sizeof(buf));
569 
570 		if (stat(buf, &st) < 0 ||
571 		    (st.st_uid != 0 && st.st_uid != uid) ||
572 		    (st.st_mode & 022) != 0)
573 			return 0;
574 
575 		/* We can stop checking after reaching homedir level. */
576 		if (strcmp(homedir, buf) == 0)
577 			break;
578 
579 		/*
580 		 * dirname should always complete with a "/" path,
581 		 * but we can be paranoid and check for "." too
582 		 */
583 		if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
584 			break;
585 	}
586 
587 	return 1;
588 }
589 
590 void
591 addargs(arglist *args, char *fmt, ...)
592 {
593 	va_list ap;
594 	char *cp;
595 	uint nalloc;
596 	int r;
597 	char	**tmp;
598 
599 	va_start(ap, fmt);
600 	r = vasprintf(&cp, fmt, ap);
601 	va_end(ap);
602 	if (r == -1)
603 		fatal("addargs: argument too long");
604 
605 	nalloc = args->nalloc;
606 	if (args->list == NULL) {
607 		nalloc = 32;
608 		args->num = 0;
609 	} else if (args->num+2 >= nalloc)
610 		nalloc *= 2;
611 
612 	tmp = reallocarray(args->list, nalloc, sizeof(char *));
613 	if (tmp == NULL)
614 		fatal("addargs: reallocarray");
615 	args->list = tmp;
616 	args->nalloc = nalloc;
617 	args->list[args->num++] = cp;
618 	args->list[args->num] = NULL;
619 }
620 
621 int
622 lowercase(char *buf, const char *s, size_t len)
623 {
624 	if (len == 0)
625 		return 0;
626 
627 	if (strlcpy(buf, s, len) >= len)
628 		return 0;
629 
630 	while (*buf != '\0') {
631 		*buf = tolower((unsigned char)*buf);
632 		buf++;
633 	}
634 
635 	return 1;
636 }
637 
638 int
639 uppercase(char *buf, const char *s, size_t len)
640 {
641 	if (len == 0)
642 		return 0;
643 
644 	if (strlcpy(buf, s, len) >= len)
645 		return 0;
646 
647 	while (*buf != '\0') {
648 		*buf = toupper((unsigned char)*buf);
649 		buf++;
650 	}
651 
652 	return 1;
653 }
654 
655 void
656 xlowercase(char *buf, const char *s, size_t len)
657 {
658 	if (len == 0)
659 		fatalx("lowercase: len == 0");
660 
661 	if (!lowercase(buf, s, len))
662 		fatalx("lowercase: truncation");
663 }
664 
665 uint64_t
666 generate_uid(void)
667 {
668 	static uint32_t id;
669 	static uint8_t	inited;
670 	uint64_t	uid;
671 
672 	if (!inited) {
673 		id = arc4random();
674 		inited = 1;
675 	}
676 	while ((uid = ((uint64_t)(id++) << 32 | arc4random())) == 0)
677 		;
678 
679 	return (uid);
680 }
681 
682 int
683 session_socket_error(int fd)
684 {
685 	int		error;
686 	socklen_t	len;
687 
688 	len = sizeof(error);
689 	if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1)
690 		fatal("session_socket_error: getsockopt");
691 
692 	return (error);
693 }
694 
695 const char *
696 parse_smtp_response(char *line, size_t len, char **msg, int *cont)
697 {
698 	if (len >= LINE_MAX)
699 		return "line too long";
700 
701 	if (len > 3) {
702 		if (msg)
703 			*msg = line + 4;
704 		if (cont)
705 			*cont = (line[3] == '-');
706 	} else if (len == 3) {
707 		if (msg)
708 			*msg = line + 3;
709 		if (cont)
710 			*cont = 0;
711 	} else
712 		return "line too short";
713 
714 	/* validate reply code */
715 	if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) ||
716 	    !isdigit((unsigned char)line[2]))
717 		return "reply code out of range";
718 
719 	return NULL;
720 }
721 
722 static int
723 parse_mailname_file(char *hostname, size_t len)
724 {
725 	FILE	*fp;
726 	char	*buf = NULL;
727 	size_t	 bufsz = 0;
728 	ssize_t	 buflen;
729 
730 	if ((fp = fopen(MAILNAME_FILE, "r")) == NULL)
731 		return 1;
732 
733 	if ((buflen = getline(&buf, &bufsz, fp)) == -1)
734 		goto error;
735 
736 	if (buf[buflen - 1] == '\n')
737 		buf[buflen - 1] = '\0';
738 
739 	if (strlcpy(hostname, buf, len) >= len) {
740 		fprintf(stderr, MAILNAME_FILE " entry too long");
741 		goto error;
742 	}
743 
744 	return 0;
745 error:
746 	fclose(fp);
747 	free(buf);
748 	return 1;
749 }
750 
751 int
752 getmailname(char *hostname, size_t len)
753 {
754 	struct addrinfo	 hints, *res = NULL;
755 	int		 error;
756 
757 	/* Try MAILNAME_FILE first */
758 	if (parse_mailname_file(hostname, len) == 0)
759 		return 0;
760 
761 	/* Next, gethostname(3) */
762 	if (gethostname(hostname, len) == -1) {
763 		fprintf(stderr, "getmailname: gethostname() failed\n");
764 		return -1;
765 	}
766 
767 	if (strchr(hostname, '.') != NULL)
768 		return 0;
769 
770 	/* Canonicalize if domain part is missing */
771 	memset(&hints, 0, sizeof hints);
772 	hints.ai_family = PF_UNSPEC;
773 	hints.ai_flags = AI_CANONNAME;
774 	error = getaddrinfo(hostname, NULL, &hints, &res);
775 	if (error)
776 		return 0; /* Continue with non-canon hostname */
777 
778 	if (strlcpy(hostname, res->ai_canonname, len) >= len) {
779 		fprintf(stderr, "hostname too long");
780 		return -1;
781 	}
782 
783 	freeaddrinfo(res);
784 	return 0;
785 }
786 
787 int
788 base64_encode(unsigned char const *src, size_t srclen,
789 	      char *dest, size_t destsize)
790 {
791 	return __b64_ntop(src, srclen, dest, destsize);
792 }
793 
794 int
795 base64_decode(char const *src, unsigned char *dest, size_t destsize)
796 {
797 	return __b64_pton(src, dest, destsize);
798 }
799 
800 void
801 log_trace(int mask, const char *emsg, ...)
802 {
803 	va_list	 ap;
804 
805 	if (tracing & mask) {
806 		va_start(ap, emsg);
807 		vlog(LOG_DEBUG, emsg, ap);
808 		va_end(ap);
809 	}
810 }
811 
812 void
813 log_trace_verbose(int v)
814 {
815 	tracing = v;
816 
817 	/* Set debug logging in log.c */
818 	log_setverbose(v & TRACE_DEBUG ? 2 : foreground_log);
819 }
820