aboutsummaryrefslogtreecommitdiffstats
path: root/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'util.c')
-rw-r--r--util.c844
1 files changed, 0 insertions, 844 deletions
diff --git a/util.c b/util.c
deleted file mode 100644
index fce97ee7..00000000
--- a/util.c
+++ /dev/null
@@ -1,844 +0,0 @@
-/* $OpenBSD: util.c,v 1.151 2020/02/24 23:54:28 millert Exp $ */
-
-/*
- * Copyright (c) 2000,2001 Markus Friedl. All rights reserved.
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <fts.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <libgen.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <limits.h>
-#include <resolv.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-const char *log_in6addr(const struct in6_addr *);
-const char *log_sockaddr(struct sockaddr *);
-static int parse_mailname_file(char *, size_t);
-
-int tracing = 0;
-int foreground_log = 0;
-
-void *
-xmalloc(size_t size)
-{
- void *r;
-
- if ((r = malloc(size)) == NULL)
- fatal("malloc");
-
- return (r);
-}
-
-void *
-xcalloc(size_t nmemb, size_t size)
-{
- void *r;
-
- if ((r = calloc(nmemb, size)) == NULL)
- fatal("calloc");
-
- return (r);
-}
-
-char *
-xstrdup(const char *str)
-{
- char *r;
-
- if ((r = strdup(str)) == NULL)
- fatal("strdup");
-
- return (r);
-}
-
-void *
-xmemdup(const void *ptr, size_t size)
-{
- void *r;
-
- if ((r = malloc(size)) == NULL)
- fatal("malloc");
-
- memmove(r, ptr, size);
-
- return (r);
-}
-
-int
-xasprintf(char **ret, const char *format, ...)
-{
- int r;
- va_list ap;
-
- va_start(ap, format);
- r = vasprintf(ret, format, ap);
- va_end(ap);
- if (r == -1)
- fatal("vasprintf");
-
- return (r);
-}
-
-
-#if !defined(NO_IO)
-int
-io_xprintf(struct io *io, const char *fmt, ...)
-{
- va_list ap;
- int len;
-
- va_start(ap, fmt);
- len = io_vprintf(io, fmt, ap);
- va_end(ap);
- if (len == -1)
- fatal("io_xprintf(%p, %s, ...)", io, fmt);
-
- return len;
-}
-
-int
-io_xprint(struct io *io, const char *str)
-{
- int len;
-
- len = io_print(io, str);
- if (len == -1)
- fatal("io_xprint(%p, %s, ...)", io, str);
-
- return len;
-}
-#endif
-
-char *
-strip(char *s)
-{
- size_t l;
-
- while (isspace((unsigned char)*s))
- s++;
-
- for (l = strlen(s); l; l--) {
- if (!isspace((unsigned char)s[l-1]))
- break;
- s[l-1] = '\0';
- }
-
- return (s);
-}
-
-int
-bsnprintf(char *str, size_t size, const char *format, ...)
-{
- int ret;
- va_list ap;
-
- va_start(ap, format);
- ret = vsnprintf(str, size, format, ap);
- va_end(ap);
- if (ret < 0 || ret >= (int)size)
- return 0;
-
- return 1;
-}
-
-
-int
-ckdir(const char *path, mode_t mode, uid_t owner, gid_t group, int create)
-{
- char mode_str[12];
- int ret;
- struct stat sb;
-
- if (stat(path, &sb) == -1) {
- if (errno != ENOENT || create == 0) {
- log_warn("stat: %s", path);
- return (0);
- }
-
- /* chmod is deferred to avoid umask effect */
- if (mkdir(path, 0) == -1) {
- log_warn("mkdir: %s", path);
- return (0);
- }
-
- if (chown(path, owner, group) == -1) {
- log_warn("chown: %s", path);
- return (0);
- }
-
- if (chmod(path, mode) == -1) {
- log_warn("chmod: %s", path);
- return (0);
- }
-
- if (stat(path, &sb) == -1) {
- log_warn("stat: %s", path);
- return (0);
- }
- }
-
- ret = 1;
-
- /* check if it's a directory */
- if (!S_ISDIR(sb.st_mode)) {
- ret = 0;
- log_warnx("%s is not a directory", path);
- }
-
- /* check that it is owned by owner/group */
- if (sb.st_uid != owner) {
- ret = 0;
- log_warnx("%s is not owned by uid %d", path, owner);
- }
- if (sb.st_gid != group) {
- ret = 0;
- log_warnx("%s is not owned by gid %d", path, group);
- }
-
- /* check permission */
- if ((sb.st_mode & 07777) != mode) {
- ret = 0;
- strmode(mode, mode_str);
- mode_str[10] = '\0';
- log_warnx("%s must be %s (%o)", path, mode_str + 1, mode);
- }
-
- return ret;
-}
-
-int
-rmtree(char *path, int keepdir)
-{
- char *path_argv[2];
- FTS *fts;
- FTSENT *e;
- int ret, depth;
-
- path_argv[0] = path;
- path_argv[1] = NULL;
- ret = 0;
- depth = 0;
-
- fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
- if (fts == NULL) {
- log_warn("fts_open: %s", path);
- return (-1);
- }
-
- while ((e = fts_read(fts)) != NULL) {
- switch (e->fts_info) {
- case FTS_D:
- depth++;
- break;
- case FTS_DP:
- case FTS_DNR:
- depth--;
- if (keepdir && depth == 0)
- continue;
- if (rmdir(e->fts_path) == -1) {
- log_warn("rmdir: %s", e->fts_path);
- ret = -1;
- }
- break;
-
- case FTS_F:
- if (unlink(e->fts_path) == -1) {
- log_warn("unlink: %s", e->fts_path);
- ret = -1;
- }
- }
- }
-
- fts_close(fts);
-
- return (ret);
-}
-
-int
-mvpurge(char *from, char *to)
-{
- size_t n;
- int retry;
- const char *sep;
- char buf[PATH_MAX];
-
- if ((n = strlen(to)) == 0)
- fatalx("to is empty");
-
- sep = (to[n - 1] == '/') ? "" : "/";
- retry = 0;
-
-again:
- (void)snprintf(buf, sizeof buf, "%s%s%u", to, sep, arc4random());
- if (rename(from, buf) == -1) {
- /* ENOTDIR has actually 2 meanings, and incorrect input
- * could lead to an infinite loop. Consider that after
- * 20 tries something is hopelessly wrong.
- */
- if (errno == ENOTEMPTY || errno == EISDIR || errno == ENOTDIR) {
- if ((retry++) >= 20)
- return (-1);
- goto again;
- }
- return -1;
- }
-
- return 0;
-}
-
-
-int
-mktmpfile(void)
-{
- char path[PATH_MAX];
- int fd;
-
- if (!bsnprintf(path, sizeof(path), "%s/smtpd.XXXXXXXXXX",
- PATH_TEMPORARY)) {
- log_warn("snprintf");
- fatal("exiting");
- }
-
- if ((fd = mkstemp(path)) == -1) {
- log_warn("cannot create temporary file %s", path);
- fatal("exiting");
- }
- unlink(path);
- return (fd);
-}
-
-
-/* Close file, signifying temporary error condition (if any) to the caller. */
-int
-safe_fclose(FILE *fp)
-{
- if (ferror(fp)) {
- fclose(fp);
- return 0;
- }
- if (fflush(fp)) {
- fclose(fp);
- if (errno == ENOSPC)
- return 0;
- fatal("safe_fclose: fflush");
- }
- if (fsync(fileno(fp)))
- fatal("safe_fclose: fsync");
- if (fclose(fp))
- fatal("safe_fclose: fclose");
-
- return 1;
-}
-
-int
-hostname_match(const char *hostname, const char *pattern)
-{
- while (*pattern != '\0' && *hostname != '\0') {
- if (*pattern == '*') {
- while (*pattern == '*')
- pattern++;
- while (*hostname != '\0' &&
- tolower((unsigned char)*hostname) !=
- tolower((unsigned char)*pattern))
- hostname++;
- continue;
- }
-
- if (tolower((unsigned char)*pattern) !=
- tolower((unsigned char)*hostname))
- return 0;
- pattern++;
- hostname++;
- }
-
- return (*hostname == '\0' && *pattern == '\0');
-}
-
-int
-mailaddr_match(const struct mailaddr *maddr1, const struct mailaddr *maddr2)
-{
- struct mailaddr m1 = *maddr1;
- struct mailaddr m2 = *maddr2;
- char *p;
-
- /* catchall */
- if (m2.user[0] == '\0' && m2.domain[0] == '\0')
- return 1;
-
- if (m2.domain[0] && !hostname_match(m1.domain, m2.domain))
- return 0;
-
- if (m2.user[0]) {
- /* if address from table has a tag, we must respect it */
- if (strchr(m2.user, *env->sc_subaddressing_delim) == NULL) {
- /* otherwise, strip tag from session address if any */
- p = strchr(m1.user, *env->sc_subaddressing_delim);
- if (p)
- *p = '\0';
- }
- if (strcasecmp(m1.user, m2.user))
- return 0;
- }
- return 1;
-}
-
-int
-valid_localpart(const char *s)
-{
-#define IS_ATEXT(c) (isalnum((unsigned char)(c)) || strchr(MAILADDR_ALLOWED, (c)))
-nextatom:
- if (!IS_ATEXT(*s) || *s == '\0')
- return 0;
- while (*(++s) != '\0') {
- if (*s == '.')
- break;
- if (IS_ATEXT(*s))
- continue;
- return 0;
- }
- if (*s == '.') {
- s++;
- goto nextatom;
- }
- return 1;
-}
-
-int
-valid_domainpart(const char *s)
-{
- struct in_addr ina;
- struct in6_addr ina6;
- char *c, domain[SMTPD_MAXDOMAINPARTSIZE];
- const char *p;
- size_t dlen;
-
- if (*s == '[') {
- if (strncasecmp("[IPv6:", s, 6) == 0)
- p = s + 6;
- else
- p = s + 1;
-
- if (strlcpy(domain, p, sizeof domain) >= sizeof domain)
- return 0;
-
- c = strchr(domain, ']');
- if (!c || c[1] != '\0')
- return 0;
-
- *c = '\0';
-
- if (inet_pton(AF_INET6, domain, &ina6) == 1)
- return 1;
- if (inet_pton(AF_INET, domain, &ina) == 1)
- return 1;
-
- return 0;
- }
-
- if (*s == '\0')
- return 0;
-
- dlen = strlen(s);
- if (dlen >= sizeof domain)
- return 0;
-
- if (s[dlen - 1] == '.')
- return 0;
-
- return res_hnok(s);
-}
-
-#define LABELCHR(c) ((c) == '-' || (c) == '_' || isalpha((unsigned char)(c)) || isdigit((unsigned char)(c)))
-#define LABELMAX 63
-#define DNAMEMAX 253
-
-int
-valid_domainname(const char *str)
-{
- const char *label, *s;
-
- /*
- * Expect a sequence of dot-separated labels, possibly with a trailing
- * dot. The empty string is rejected, as well a single dot.
- */
- for (s = str; *s; s++) {
-
- /* Start of a new label. */
- label = s;
- while (LABELCHR(*s))
- s++;
-
- /* Must have at least one char and at most LABELMAX. */
- if (s == label || s - label > LABELMAX)
- return 0;
-
- /* If last label, stop here. */
- if (*s == '\0')
- break;
-
- /* Expect a dot as label separator or last char. */
- if (*s != '.')
- return 0;
- }
-
- /* Must have at leat one label and no more than DNAMEMAX chars. */
- if (s == str || s - str > DNAMEMAX)
- return 0;
-
- return 1;
-}
-
-int
-valid_smtp_response(const char *s)
-{
- if (strlen(s) < 5)
- return 0;
-
- if ((s[0] < '2' || s[0] > '5') ||
- (s[1] < '0' || s[1] > '9') ||
- (s[2] < '0' || s[2] > '9') ||
- (s[3] != ' '))
- return 0;
-
- return 1;
-}
-
-int
-secure_file(int fd, char *path, char *userdir, uid_t uid, int mayread)
-{
- char buf[PATH_MAX];
- char homedir[PATH_MAX];
- struct stat st;
- char *cp;
-
- if (realpath(path, buf) == NULL)
- return 0;
-
- if (realpath(userdir, homedir) == NULL)
- homedir[0] = '\0';
-
- /* Check the open file to avoid races. */
- if (fstat(fd, &st) == -1 ||
- !S_ISREG(st.st_mode) ||
- st.st_uid != uid ||
- (st.st_mode & (mayread ? 022 : 066)) != 0)
- return 0;
-
- /* For each component of the canonical path, walking upwards. */
- for (;;) {
- if ((cp = dirname(buf)) == NULL)
- return 0;
- (void)strlcpy(buf, cp, sizeof(buf));
-
- if (stat(buf, &st) == -1 ||
- (st.st_uid != 0 && st.st_uid != uid) ||
- (st.st_mode & 022) != 0)
- return 0;
-
- /* We can stop checking after reaching homedir level. */
- if (strcmp(homedir, buf) == 0)
- break;
-
- /*
- * dirname should always complete with a "/" path,
- * but we can be paranoid and check for "." too
- */
- if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
- break;
- }
-
- return 1;
-}
-
-void
-addargs(arglist *args, char *fmt, ...)
-{
- va_list ap;
- char *cp;
- uint nalloc;
- int r;
- char **tmp;
-
- va_start(ap, fmt);
- r = vasprintf(&cp, fmt, ap);
- va_end(ap);
- if (r == -1)
- fatal("addargs: argument too long");
-
- nalloc = args->nalloc;
- if (args->list == NULL) {
- nalloc = 32;
- args->num = 0;
- } else if (args->num+2 >= nalloc)
- nalloc *= 2;
-
- tmp = reallocarray(args->list, nalloc, sizeof(char *));
- if (tmp == NULL)
- fatal("addargs: reallocarray");
- args->list = tmp;
- args->nalloc = nalloc;
- args->list[args->num++] = cp;
- args->list[args->num] = NULL;
-}
-
-int
-lowercase(char *buf, const char *s, size_t len)
-{
- if (len == 0)
- return 0;
-
- if (strlcpy(buf, s, len) >= len)
- return 0;
-
- while (*buf != '\0') {
- *buf = tolower((unsigned char)*buf);
- buf++;
- }
-
- return 1;
-}
-
-int
-uppercase(char *buf, const char *s, size_t len)
-{
- if (len == 0)
- return 0;
-
- if (strlcpy(buf, s, len) >= len)
- return 0;
-
- while (*buf != '\0') {
- *buf = toupper((unsigned char)*buf);
- buf++;
- }
-
- return 1;
-}
-
-void
-xlowercase(char *buf, const char *s, size_t len)
-{
- if (len == 0)
- fatalx("lowercase: len == 0");
-
- if (!lowercase(buf, s, len))
- fatalx("lowercase: truncation");
-}
-
-uint64_t
-generate_uid(void)
-{
- static uint32_t id;
- static uint8_t inited;
- uint64_t uid;
-
- if (!inited) {
- id = arc4random();
- inited = 1;
- }
- while ((uid = ((uint64_t)(id++) << 32 | arc4random())) == 0)
- ;
-
- return (uid);
-}
-
-int
-session_socket_error(int fd)
-{
- int error;
- socklen_t len;
-
- len = sizeof(error);
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1)
- fatal("session_socket_error: getsockopt");
-
- return (error);
-}
-
-const char *
-parse_smtp_response(char *line, size_t len, char **msg, int *cont)
-{
- if (len >= LINE_MAX)
- return "line too long";
-
- if (len > 3) {
- if (msg)
- *msg = line + 4;
- if (cont)
- *cont = (line[3] == '-');
- } else if (len == 3) {
- if (msg)
- *msg = line + 3;
- if (cont)
- *cont = 0;
- } else
- return "line too short";
-
- /* validate reply code */
- if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) ||
- !isdigit((unsigned char)line[2]))
- return "reply code out of range";
-
- return NULL;
-}
-
-static int
-parse_mailname_file(char *hostname, size_t len)
-{
- FILE *fp;
- char *buf = NULL;
- size_t bufsz = 0;
- ssize_t buflen;
-
- if ((fp = fopen(MAILNAME_FILE, "r")) == NULL)
- return 1;
-
- if ((buflen = getline(&buf, &bufsz, fp)) == -1)
- goto error;
-
- if (buf[buflen - 1] == '\n')
- buf[buflen - 1] = '\0';
-
- if (strlcpy(hostname, buf, len) >= len) {
- fprintf(stderr, MAILNAME_FILE " entry too long");
- goto error;
- }
-
- return 0;
-error:
- fclose(fp);
- free(buf);
- return 1;
-}
-
-int
-getmailname(char *hostname, size_t len)
-{
- struct addrinfo hints, *res = NULL;
- int error;
-
- /* Try MAILNAME_FILE first */
- if (parse_mailname_file(hostname, len) == 0)
- return 0;
-
- /* Next, gethostname(3) */
- if (gethostname(hostname, len) == -1) {
- fprintf(stderr, "getmailname: gethostname() failed\n");
- return -1;
- }
-
- if (strchr(hostname, '.') != NULL)
- return 0;
-
- /* Canonicalize if domain part is missing */
- memset(&hints, 0, sizeof hints);
- hints.ai_family = PF_UNSPEC;
- hints.ai_flags = AI_CANONNAME;
- error = getaddrinfo(hostname, NULL, &hints, &res);
- if (error)
- return 0; /* Continue with non-canon hostname */
-
- if (strlcpy(hostname, res->ai_canonname, len) >= len) {
- fprintf(stderr, "hostname too long");
- freeaddrinfo(res);
- return -1;
- }
-
- freeaddrinfo(res);
- return 0;
-}
-
-int
-base64_encode(unsigned char const *src, size_t srclen,
- char *dest, size_t destsize)
-{
- return __b64_ntop(src, srclen, dest, destsize);
-}
-
-int
-base64_decode(char const *src, unsigned char *dest, size_t destsize)
-{
- return __b64_pton(src, dest, destsize);
-}
-
-int
-base64_encode_rfc3548(unsigned char const *src, size_t srclen,
- char *dest, size_t destsize)
-{
- size_t i;
- int ret;
-
- if ((ret = base64_encode(src, srclen, dest, destsize)) == -1)
- return -1;
-
- for (i = 0; i < destsize; ++i) {
- if (dest[i] == '/')
- dest[i] = '_';
- else if (dest[i] == '+')
- dest[i] = '-';
- }
-
- return ret;
-}
-
-void
-log_trace(int mask, const char *emsg, ...)
-{
- va_list ap;
-
- if (tracing & mask) {
- va_start(ap, emsg);
- vlog(LOG_DEBUG, emsg, ap);
- va_end(ap);
- }
-}
-
-void
-log_trace_verbose(int v)
-{
- tracing = v;
-
- /* Set debug logging in log.c */
- log_setverbose(v & TRACE_DEBUG ? 2 : foreground_log);
-}