From c8b07ee5563f43ea65ee46cec4d8215ba54e6b3a Mon Sep 17 00:00:00 2001 From: Sascha Wildner Date: Tue, 3 Jan 2012 15:31:49 +0100 Subject: [PATCH] dma(8): Upgrade to v0.7. For the change log, see: https://github.com/corecode/dma/blob/master/debian/changelog Tested-by: luxh Submitted-by: corecode --- libexec/dma/LICENSE | 109 +++++++++ libexec/dma/Makefile | 16 +- libexec/dma/Makefile.etc | 3 - libexec/dma/Makefile.inc | 1 + libexec/dma/Makefile.plain | 48 ---- libexec/dma/README.markdown | 30 +++ libexec/dma/TODO | 1 + libexec/dma/VERSION | 1 + libexec/dma/aliases_scan.l | 2 +- libexec/dma/conf.c | 81 ++----- libexec/dma/crypto.c | 19 +- libexec/dma/dfcompat.c | 15 +- libexec/dma/dfcompat.h | 8 +- libexec/dma/dma-mbox-create/Makefile | 15 ++ libexec/dma/dma-mbox-create/dma-mbox-create.c | 160 +++++++++++++ libexec/dma/dma.8 | 125 ++++++++--- libexec/dma/dma.c | 149 ++++++++----- libexec/dma/dma.conf | 33 ++- libexec/dma/dma.h | 53 +++-- libexec/dma/{ => dma}/Makefile | 7 +- libexec/dma/dns.c | 43 ++-- libexec/dma/local.c | 171 +++++++++++++- libexec/dma/mail.c | 49 +++- libexec/dma/net.c | 150 ++++++++----- libexec/dma/spool.c | 23 +- libexec/dma/util.c | 211 +++++++++++++----- 26 files changed, 1092 insertions(+), 431 deletions(-) create mode 100644 libexec/dma/LICENSE create mode 100644 libexec/dma/Makefile.inc delete mode 100644 libexec/dma/Makefile.plain create mode 100644 libexec/dma/README.markdown create mode 100644 libexec/dma/VERSION create mode 100644 libexec/dma/dma-mbox-create/Makefile create mode 100644 libexec/dma/dma-mbox-create/dma-mbox-create.c copy libexec/dma/{ => dma}/Makefile (56%) diff --git a/libexec/dma/LICENSE b/libexec/dma/LICENSE new file mode 100644 index 0000000000..b89e5bcd16 --- /dev/null +++ b/libexec/dma/LICENSE @@ -0,0 +1,109 @@ +Copyright (c) 2008 The DragonFly Project. +Copyright (c) 2008-2011, Simon Schubert <2@0x2c.org>. +All rights reserved. + +This code is derived from software contributed to The DragonFly Project +by Simon Schubert <2@0x2c.org>. + +This code is derived from software contributed to The DragonFly Project +by Matthias Schmidt , University of Marburg, +Germany. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. +3. Neither the name of The DragonFly Project nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific, prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + +Copyright (c) 1995-2001 Kungliga Tekniska Högskolan +(Royal Institute of Technology, Stockholm, Sweden). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the Institute nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + +Copyright (c) 1998 Todd C. Miller + +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. + + +Copyright (c) 1998, M. Warner Losh +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/libexec/dma/Makefile b/libexec/dma/Makefile index 42f40d07aa..0bf83b6a37 100644 --- a/libexec/dma/Makefile +++ b/libexec/dma/Makefile @@ -1,15 +1,3 @@ -CFLAGS+= -I${.CURDIR} +SUBDIR= dma dma-mbox-create -DPADD= ${LIBSSL} ${LIBCRYPTO} -LDADD= -lssl -lcrypto - -PROG= dma -SRCS= aliases_parse.y aliases_scan.l base64.c conf.c crypto.c -SRCS+= dma.c dns.c local.c mail.c net.c spool.c util.c -MAN= dma.8 - -BINOWN= root -BINGRP= mail -BINMODE=2555 - -.include +.include diff --git a/libexec/dma/Makefile.etc b/libexec/dma/Makefile.etc index 7965caaf18..fca23b94a6 100644 --- a/libexec/dma/Makefile.etc +++ b/libexec/dma/Makefile.etc @@ -11,8 +11,5 @@ FILES+= auth.conf .if !exists(${DESTDIR}/etc/dma/dma.conf) FILES+= dma.conf .endif -.if !exists(${DESTDIR}/etc/dma/virtusertable) -FILES+= virtusertable -.endif .include diff --git a/libexec/dma/Makefile.inc b/libexec/dma/Makefile.inc new file mode 100644 index 0000000000..01b5f23410 --- /dev/null +++ b/libexec/dma/Makefile.inc @@ -0,0 +1 @@ +.include "../Makefile.inc" diff --git a/libexec/dma/Makefile.plain b/libexec/dma/Makefile.plain deleted file mode 100644 index a24dba9132..0000000000 --- a/libexec/dma/Makefile.plain +++ /dev/null @@ -1,48 +0,0 @@ -# -# If you want to compile dma for Linux, you probably have to do: -# make -f Makefile.plain CPPFLAGS="-DNEED_REALLOCF -DNEED_STRLCPY \ -# -DNEED_GETPROGNAME" -# - -CC?= gcc -CFLAGS?= -O -pipe -LDADD?= -lssl -lcrypto -lresolv - -INSTALL?= install -p -PREFIX?= /usr/local -SBIN?= ${PREFIX}/sbin -CONFDIR?= ${PREFIX}/etc -MAN?= ${PREFIX}/share/man - -YACC?= yacc -LEX?= lex - -OBJS= aliases_parse.o aliases_scan.o base64.o conf.o crypto.o -OBJS+= dma.o dns.o local.o mail.o net.o spool.o util.o -OBJS+= dfcompat.o - -all: dma - -clean: - -rm -f .depend dma *.[do] - -rm -f aliases_parse.[ch] aliases_scan.c - -install: all - ${INSTALL} -d ${DESTDIR}${SBIN} ${DESTDIR}${CONFDIR} - ${INSTALL} -d ${DESTDIR}${MAN}/man8 - ${INSTALL} -g mail -m 2755 dma ${DESTDIR}${SBIN} - ${INSTALL} -m 0644 dma.8 ${DESTDIR}${MAN}/man8/ - -aliases_parse.c: aliases_parse.y - ${YACC} -d -o aliases_parse.c aliases_parse.y - -aliases_scan.c: aliases_scan.l - ${LEX} -t aliases_scan.l > aliases_scan.c - -.SUFFIXES: .c .o - -.c.o: - ${CC} ${CFLAGS} ${CPPFLAGS} -include dfcompat.h -o $@ -c $< - -dma: ${OBJS} - ${CC} ${LDFLAGS} ${LDADD} -o $@ ${OBJS} diff --git a/libexec/dma/README.markdown b/libexec/dma/README.markdown new file mode 100644 index 0000000000..528a4d65c8 --- /dev/null +++ b/libexec/dma/README.markdown @@ -0,0 +1,30 @@ +dma -- DragonFly Mail Agent +=========================== + +dma is a small Mail Transport Agent (MTA), designed for home and +office use. It accepts mails from locally installed Mail User Agents (MUA) +and delivers the mails either locally or to a remote destination. +Remote delivery includes several features like TLS/SSL support and +SMTP authentication. + +dma is not intended as a replacement for real, big MTAs like sendmail(8) +or postfix(1). Consequently, dma does not listen on port 25 for +incoming connections. + + +Building +-------- + + make + + +Installation +------------ + + make install sendmail-link mailq-link install-spool-dirs install-etc + + +Contact +------- + +Simon Schubert <2@0x2c.org> diff --git a/libexec/dma/TODO b/libexec/dma/TODO index 77b497ffb5..01de465d66 100644 --- a/libexec/dma/TODO +++ b/libexec/dma/TODO @@ -2,3 +2,4 @@ - use proper sysexit codes - handle/use ESMTP extensions - .forward support +- suggest way to run a queue flush on boot diff --git a/libexec/dma/VERSION b/libexec/dma/VERSION new file mode 100644 index 0000000000..03776fb05e --- /dev/null +++ b/libexec/dma/VERSION @@ -0,0 +1 @@ +v0.7 diff --git a/libexec/dma/aliases_scan.l b/libexec/dma/aliases_scan.l index 5e6bdd5d9e..809d1e1687 100644 --- a/libexec/dma/aliases_scan.l +++ b/libexec/dma/aliases_scan.l @@ -14,8 +14,8 @@ int yylex(void); %% [^:,#[:space:][:cntrl:]]+ {yylval.ident = strdup(yytext); return T_IDENT;} -[:,\n] return yytext[0]; ^([[:blank:]]*(#.*)?\n)+ ;/* ignore empty lines */ +[:,\n] return yytext[0]; (\n?[[:blank:]]+|#.*)+ ;/* ignore whitespace and continuation */ \\\n ;/* ignore continuation. not allowed in comments */ . return T_ERROR; diff --git a/libexec/dma/conf.c b/libexec/dma/conf.c index b676f9ce92..e8cb463383 100644 --- a/libexec/dma/conf.c +++ b/libexec/dma/conf.c @@ -56,8 +56,6 @@ trim_line(char *line) size_t linelen; char *p; - p = line; - if ((p = strchr(line, '\n'))) *p = (char)0; @@ -84,59 +82,6 @@ chomp(char *str) str[len - 1] = 0; } -/* - * Read the virtual user table - */ -void -parse_virtuser(const char *path) -{ - char line[2048]; - FILE *v; - char *data; - struct virtuser *vu; - int lineno = 0; - - v = fopen(path, "r"); - if (v == NULL) { - errlog(1, "can not open virtuser file `%s'", path); - /* NOTREACHED */ - } - - while (!feof(v)) { - if (fgets(line, sizeof(line), v) == NULL) - break; - lineno++; - - chomp(line); - - /* We hit a comment */ - if (*line == '#') - continue; - /* Ignore empty lines */ - if (*line == 0) - continue; - - vu = calloc(1, sizeof(*vu)); - if (vu == NULL) - errlog(1, NULL); - - data = strdup(line); - vu->login = strsep(&data, DP); - vu->address = data; - - if (vu->login == NULL || - vu->address == NULL) { - errlogx(1, "syntax error in virtuser file %s:%d", - path, lineno); - /* NOTREACHED */ - } - - SLIST_INSERT_HEAD(&virtusers, vu, next); - } - - fclose(v); -} - /* * Read the SMTP authentication config file * @@ -250,20 +195,32 @@ parse_conf(const char *config_path) config.aliases = data; else if (strcmp(word, "SPOOLDIR") == 0 && data != NULL) config.spooldir = data; - else if (strcmp(word, "VIRTPATH") == 0 && data != NULL) - config.virtualpath = data; else if (strcmp(word, "AUTHPATH") == 0 && data != NULL) config.authpath= data; else if (strcmp(word, "CERTFILE") == 0 && data != NULL) config.certfile = data; else if (strcmp(word, "MAILNAME") == 0 && data != NULL) config.mailname = data; - else if (strcmp(word, "MAILNAMEFILE") == 0 && data != NULL) - config.mailnamefile = data; - else if (strcmp(word, "VIRTUAL") == 0 && data == NULL) - config.features |= VIRTUAL; - else if (strcmp(word, "STARTTLS") == 0 && data == NULL) + else if (strcmp(word, "MASQUERADE") == 0 && data != NULL) { + char *user = NULL, *host = NULL; + if (strrchr(data, '@')) { + host = strrchr(data, '@'); + *host = 0; + host++; + user = data; + } else { + host = data; + } + if (host && *host == 0) + host = NULL; + if (user && *user == 0) + user = NULL; + config.masquerade_host = host; + config.masquerade_user = user; + } else if (strcmp(word, "STARTTLS") == 0 && data == NULL) config.features |= STARTTLS; + else if (strcmp(word, "OPPORTUNISTIC_TLS") == 0 && data == NULL) + config.features |= TLS_OPP; else if (strcmp(word, "SECURETRANSFER") == 0 && data == NULL) config.features |= SECURETRANS; else if (strcmp(word, "DEFER") == 0 && data == NULL) diff --git a/libexec/dma/crypto.c b/libexec/dma/crypto.c index 13ab18d110..7e8865c602 100644 --- a/libexec/dma/crypto.c +++ b/libexec/dma/crypto.c @@ -118,9 +118,13 @@ smtp_init_crypto(int fd, int feature) if (read_remote(fd, 0, NULL) == 2) { send_remote_command(fd, "STARTTLS"); if (read_remote(fd, 0, NULL) != 2) { - syslog(LOG_ERR, "remote delivery deferred:" - " STARTTLS not available: %s", neterr); - return (1); + if ((feature & TLS_OPP) == 0) { + syslog(LOG_ERR, "remote delivery deferred: STARTTLS not available: %s", neterr); + return (1); + } else { + syslog(LOG_INFO, "in opportunistic TLS mode, STARTTLS not available: %s", neterr); + return (0); + } } } /* End of TLS init phase, enable SSL_write/read */ @@ -176,7 +180,7 @@ smtp_init_crypto(int fd, int feature) */ void hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len, - caddr_t digest) + unsigned char* digest) { MD5_CTX context; unsigned char k_ipad[65]; /* inner padding - @@ -248,7 +252,8 @@ hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len, int smtp_auth_md5(int fd, char *login, char *password) { - unsigned char buffer[BUF_SIZE], digest[BUF_SIZE], ascii_digest[33]; + unsigned char digest[BUF_SIZE]; + char buffer[BUF_SIZE], ascii_digest[33]; char *temp; int len, i; static char hextab[] = "0123456789abcdef"; @@ -264,12 +269,14 @@ smtp_auth_md5(int fd, char *login, char *password) syslog(LOG_DEBUG, "smarthost authentication:" " AUTH cram-md5 not available: %s", neterr); /* if cram-md5 is not available */ + free(temp); return (-1); } /* skip 3 char status + 1 char space */ base64_decode(buffer + 4, temp); - hmac_md5(temp, strlen(temp), password, strlen(password), digest); + hmac_md5((unsigned char *)temp, strlen(temp), + (unsigned char *)password, strlen(password), digest); free(temp); ascii_digest[32] = 0; diff --git a/libexec/dma/dfcompat.c b/libexec/dma/dfcompat.c index b5c9010002..8df906e96e 100644 --- a/libexec/dma/dfcompat.c +++ b/libexec/dma/dfcompat.c @@ -1,4 +1,4 @@ -#ifdef NEED_STRLCPY +#ifndef HAVE_STRLCPY /* * Copyright (c) 1998 Todd C. Miller @@ -20,6 +20,8 @@ * $DragonFly: src/lib/libc/string/strlcpy.c,v 1.4 2005/09/18 16:32:34 asmodai Exp $ */ +#include "dfcompat.h" + #include #include @@ -54,9 +56,9 @@ strlcpy(char *dst, const char *src, size_t siz) return(s - src - 1); /* count does not include NUL */ } -#endif /* NEED_STRLCPY */ +#endif /* !HAVE_STRLCPY */ -#ifdef NEED_REALLOCF +#ifndef HAVE_REALLOCF /*- * Copyright (c) 1998, M. Warner Losh @@ -99,13 +101,12 @@ reallocf(void *ptr, size_t size) return (nptr); } -#endif /* NEED_REALLOCF */ +#endif /* !HAVE_REALLOCF */ -#ifdef NEED_GETPROGNAME +#ifndef HAVE_GETPROGNAME #ifdef __GLIBC__ -#define __USE_GNU #include const char * @@ -118,4 +119,4 @@ getprogname(void) #error "no getprogname implementation available" #endif -#endif /* NEED_GETPROGNAME */ +#endif /* !HAVE_GETPROGNAME */ diff --git a/libexec/dma/dfcompat.h b/libexec/dma/dfcompat.h index fc962d15e6..bb0a0bbd0d 100644 --- a/libexec/dma/dfcompat.h +++ b/libexec/dma/dfcompat.h @@ -1,21 +1,23 @@ #ifndef DFCOMPAT_H #define DFCOMPAT_H +#define _GNU_SOURCE + #include #ifndef __DECONST #define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) #endif -#ifdef NEED_STRLCPY +#ifndef HAVE_STRLCPY size_t strlcpy(char *, const char *, size_t); #endif -#ifdef NEED_REALLOCF +#ifndef HAVE_REALLOCF void *reallocf(void *, size_t); #endif -#ifdef NEED_GETPROGNAME +#ifndef HAVE_GETPROGNAME const char *getprogname(void); #endif diff --git a/libexec/dma/dma-mbox-create/Makefile b/libexec/dma/dma-mbox-create/Makefile new file mode 100644 index 0000000000..0b8a14eee3 --- /dev/null +++ b/libexec/dma/dma-mbox-create/Makefile @@ -0,0 +1,15 @@ +.PATH: ${.CURDIR}/.. + +CFLAGS+=-I${.CURDIR}/.. +CFLAGS+= -DHAVE_REALLOCF -DHAVE_STRLCPY -DHAVE_GETPROGNAME +CFLAGS+=-DCONF_PATH='"/etc/dma"' +CFLAGS+=-DLIBEXEC_PATH='"/usr/libexec"' -DDMA_VERSION='"v0.7"' + +PROG= dma-mbox-create +NOMAN= + +BINOWN= root +BINGRP= mail +BINMODE=4554 + +.include diff --git a/libexec/dma/dma-mbox-create/dma-mbox-create.c b/libexec/dma/dma-mbox-create/dma-mbox-create.c new file mode 100644 index 0000000000..c84652b74d --- /dev/null +++ b/libexec/dma/dma-mbox-create/dma-mbox-create.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2010 Simon Schubert <2@0x2c.org> + * Copyright (c) 2008 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Simon 'corecode' Schubert . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This binary is setuid root. Use extreme caution when touching + * user-supplied information. Keep the root window as small as possible. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dma.h" + + +static void +logfail(const char *fmt, ...) +{ + int oerrno = errno; + va_list ap; + char outs[1024]; + + outs[0] = 0; + if (fmt != NULL) { + va_start(ap, fmt); + vsnprintf(outs, sizeof(outs), fmt, ap); + va_end(ap); + } + + errno = oerrno; + if (*outs != 0) + syslog(LOG_ERR, errno ? "%s: %m" : "%s", outs); + else + syslog(LOG_ERR, errno ? "%m" : "unknown error"); + + exit(1); +} + +/* + * Create a mbox in /var/mail for a given user, or make sure + * the permissions are correct for dma. + */ + +int +main(int argc, char **argv) +{ + const char *user; + struct passwd *pw; + struct group *gr; + uid_t user_uid; + gid_t mail_gid; + int error; + char fn[PATH_MAX+1]; + int f; + + openlog("dma-mbox-create", 0, LOG_MAIL); + + errno = 0; + gr = getgrnam(DMA_GROUP); + if (!gr) + logfail("cannot find dma group `%s'", DMA_GROUP); + + mail_gid = gr->gr_gid; + + if (setgid(mail_gid) != 0) + logfail("cannot set gid to %d (%s)", mail_gid, DMA_GROUP); + if (getegid() != mail_gid) + logfail("cannot set gid to %d (%s), still at %d", mail_gid, DMA_GROUP, getegid()); + + /* + * We take exactly one argument: the username. + */ + if (argc != 2) { + errno = 0; + logfail("no arguments"); + } + user = argv[1]; + + syslog(LOG_NOTICE, "creating mbox for `%s'", user); + + /* the username may not contain a pathname separator */ + if (strchr(user, '/')) { + errno = 0; + logfail("path separator in username `%s'", user); + exit(1); + } + + /* verify the user exists */ + errno = 0; + pw = getpwnam(user); + if (!pw) + logfail("cannot find user `%s'", user); + + user_uid = pw->pw_uid; + + error = snprintf(fn, sizeof(fn), "%s/%s", _PATH_MAILDIR, user); + if (error < 0 || (size_t)error >= sizeof(fn)) { + if (error >= 0) { + errno = 0; + logfail("mbox path too long"); + } + logfail("cannot build mbox path for `%s/%s'", _PATH_MAILDIR, user); + } + + f = open(fn, O_RDONLY|O_CREAT, 0600); + if (f < 0) + logfail("cannot open mbox `%s'", fn); + + if (fchown(f, user_uid, mail_gid)) + logfail("cannot change owner of mbox `%s'", fn); + + if (fchmod(f, 0620)) + logfail("cannot change permissions of mbox `%s'", fn); + + /* file should be present with the right owner and permissions */ + + syslog(LOG_NOTICE, "successfully created mbox for `%s'", user); + + return (0); +} diff --git a/libexec/dma/dma.8 b/libexec/dma/dma.8 index 1fd37241ee..e5c7182334 100644 --- a/libexec/dma/dma.8 +++ b/libexec/dma/dma.8 @@ -31,7 +31,7 @@ .\" .\" $DragonFly: src/libexec/dma/dma.8,v 1.10 2008/09/06 14:17:56 swildner Exp $ .\" -.Dd September 17, 2010 +.Dd April 22, 2010 .Dt DMA 8 .Os .Sh NAME @@ -88,8 +88,10 @@ s are are ignored. Don't run in the background. Useful for debugging. .It Fl f Ar sender -Set sender address to +Set sender address (envelope-from) to .Ar sender . +This overrides the value of the environment variable +.Ev EMAIL . .It Fl i Ignore dots alone on lines by themselves in incoming messages. This should be set if you are reading data from a file. @@ -135,8 +137,6 @@ can be configured with three config files: auth.conf .It dma.conf -.It -virtusertable .El .Pp These three files are stored per default in @@ -181,10 +181,26 @@ option, because will deliver all mails to this port, regardless of whether a smarthost is set or not. .It Ic ALIASES Xo -(string, default=/etc/mail/aliases) +(string, default=/etc/aliases) .Xc Path to the local aliases file. Just stick with the default. +The aliases file is of the format +.Dl nam: dest1 dest2 ... +In this case, mails to +.Li nam +will instead be delivered to +.Li dest1 +and +.Li dest2 , +which in turn could be entries in +.Pa /etc/aliases . +The special name +.Ql * +can be used to create a catch-all alias, which gets used if no other +matching alias is found. +Use the catch-all alias only if you don't want any local mail to be +delivered. .It Ic SPOOLDIR Xo (string, default=/var/spool/dma) .Xc @@ -192,22 +208,12 @@ Path to .Nm Ap s spool directory. Just stick with the default. -.It Ic VIRTPATH Xo -(string, default=/etc/dma/virtusertable) -.Xc -Path to the -.Sq virtusertable -file. .It Ic AUTHPATH Xo -(string, default=/etc/dma/auth.conf) +(string, default=not set) .Xc Path to the .Sq auth.conf file. -.It Ic VIRTUAL Xo -(boolean, default=commented) -.Xc -Uncomment if you want virtual user support. .It Ic SECURETRANS Xo (boolean, default=commented) .Xc @@ -218,6 +224,20 @@ Uncomment if you want TLS/SSL secured transfer. Uncomment if you want to use STARTTLS. Only useful together with .Sq SECURETRANS . +.It Ic OPPORTUNISTIC_TLS Xo +(boolean, default=commented) +.Xc +Uncomment if you want to allow the STARTTLS negotiation to fail. +Most useful when +.Nm +is used without a smarthost, delivering remote messages directly to +the outside mail exchangers; in opportunistic TLS mode, the connection will +be encrypted if the remote server supports STARTTLS, but an unencrypted +delivery will still be made if the negotiation fails. +Only useful together with +.Sq SECURETRANS +and +.Sq STARTTLS . .It Ic CERTFILE Xo (string, default=empty) .Xc @@ -225,7 +245,7 @@ Path to your SSL certificate file. .It Ic SECURE Xo (boolean, default=commented) .Xc -Change this entry to +Uncomment this entry and change it to .Sq INSECURE to use plain text SMTP login over an insecure connection. You have to rename this variable manually to prevent that you send your @@ -248,27 +268,62 @@ message, not just the headers. .It Ic MAILNAME Xo (string, default=empty) .Xc -The name to be used when introducing this host, if different from -the result of -.Xr hostname 1 . -If specified, this option overrides -.Sq MAILNAMEFILE . -.It Ic MAILNAMEFILE Xo +The internet hostname +.Nm +uses to identify the host. +If not set or empty, the result of +.Xr gethostname 2 +is used. +If +.Sq MAILNAME +is an absolute path to a file, the first line of this file will be used +as the hostname. +.It Ic MASQUERADE Xo (string, default=empty) .Xc -The name of the file to read the -.Sq MAILNAME -from. +Masquerade the envelope from addresses with this address/hostname. +Use this setting if mails are not accepted by destination mail servers +because your sender domain is invalid. +This setting is overridden by the +.Fl f +flag and the +.Ev EMAIL +environment variable. +.Pp +If +.Sq MASQUERADE +does not contain a +.Li @ +sign, the string is interpreted as a host name. +For example, setting +.Sq MASQUERADE +to +.Ql john@ +on host +.Ql hamlet +will send all mails as +.Ql john@hamlet ; +setting it to +.Ql percolator +will send all mails as +.Ql Sm off Va username @percolator . +.Sm on +.El +.Ss Environment variables +The behavior of +.Nm +can be influenced by some environment variables. +.Pp +.Bl -tag -width 4n +.It Ev EMAIL Xo +.Xc +Used to set the sender address (envelope-from). +Use a plain address, in the form of +.Li user@example.com . +This value will be overridden when the +.Fl f +flag is used. .El -.Ss virtusertable -The -.Pa virtusertable -file specifies a virtual user table. -Each line has the format -.Dq Li localuser:mail-address . -Some smarthosts do not accept mails from unresolvable email address -(e.g.\& user@localhost) so you have to rewrite your outgoing email -address to a valid address. .Sh SEE ALSO .Xr mailaddr 7 , .Xr mailwrapper 8 , diff --git a/libexec/dma/dma.c b/libexec/dma/dma.c index 83f2bbb036..48c429c443 100644 --- a/libexec/dma/dma.c +++ b/libexec/dma/dma.c @@ -32,10 +32,13 @@ * SUCH DAMAGE. */ +#include "dfcompat.h" + #include #include #include #include +#include #include #include @@ -60,50 +63,56 @@ static void deliver(struct qitem *); struct aliases aliases = LIST_HEAD_INITIALIZER(aliases); struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs); -struct virtusers virtusers = LIST_HEAD_INITIALIZER(virtusers); struct authusers authusers = LIST_HEAD_INITIALIZER(authusers); -const char *username; +char username[USERNAME_SIZE]; +uid_t useruid; const char *logident_base; +char errmsg[ERRMSG_SIZE]; static int daemonize = 1; struct config config = { .smarthost = NULL, .port = 25, - .aliases = "/var/mail/aliases", + .aliases = "/etc/aliases", .spooldir = "/var/spool/dma", - .virtualpath = NULL, .authpath = NULL, .certfile = NULL, .features = 0, .mailname = NULL, - .mailnamefile = NULL, + .masquerade_host = NULL, + .masquerade_user = NULL, }; +static void +sighup_handler(int signo) +{ + (void)signo; /* so that gcc doesn't complain */ +} + static char * set_from(struct queue *queue, const char *osender) { - struct virtuser *v; char *sender; - if ((config.features & VIRTUAL) != 0) { - SLIST_FOREACH(v, &virtusers, next) { - if (strcmp(v->login, username) == 0) { - sender = strdup(v->address); - if (sender == NULL) - return(NULL); - goto out; - } - } - } - if (osender) { sender = strdup(osender); if (sender == NULL) return (NULL); + } else if (getenv("EMAIL") != NULL) { + sender = strdup(getenv("EMAIL")); + if (sender == NULL) + return (NULL); } else { - if (asprintf(&sender, "%s@%s", username, hostname()) <= 0) + const char *from_user = username; + const char *from_host = hostname(); + + if (config.masquerade_user) + from_user = config.masquerade_user; + if (config.masquerade_host) + from_host = config.masquerade_host; + if (asprintf(&sender, "%s@%s", from_user, from_host) <= 0) return (NULL); } @@ -112,7 +121,6 @@ set_from(struct queue *queue, const char *osender) return (NULL); } -out: queue->sender = sender; return (sender); } @@ -136,12 +144,30 @@ read_aliases(void) return (0); } +static int +do_alias(struct queue *queue, const char *addr) +{ + struct alias *al; + struct stritem *sit; + int aliased = 0; + + LIST_FOREACH(al, &aliases, next) { + if (strcmp(al->alias, addr) != 0) + continue; + SLIST_FOREACH(sit, &al->dests, next) { + if (add_recp(queue, sit->str, EXPAND_ADDR) != 0) + return (-1); + } + aliased = 1; + } + + return (aliased); +} + int add_recp(struct queue *queue, const char *str, int expand) { struct qitem *it, *tit; - struct stritem *sit; - struct alias *al; struct passwd *pw; char *host; int aliased = 0; @@ -172,15 +198,11 @@ add_recp(struct queue *queue, const char *str, int expand) if (strrchr(it->addr, '@') == NULL) { it->remote = 0; if (expand) { - LIST_FOREACH(al, &aliases, next) { - if (strcmp(al->alias, it->addr) != 0) - continue; - SLIST_FOREACH(sit, &al->dests, next) { - if (add_recp(queue, sit->str, 1) != 0) - return (-1); - } - aliased = 1; - } + aliased = do_alias(queue, it->addr); + if (!aliased && expand == EXPAND_WILDCARD) + aliased = do_alias(queue, "*"); + if (aliased < 0) + return (-1); if (aliased) { LIST_REMOVE(it, next); } else { @@ -218,7 +240,6 @@ go_background(struct queue *queue) daemonize = 0; bzero(&sa, sizeof(sa)); - sa.sa_flags = SA_NOCLDWAIT; sa.sa_handler = SIG_IGN; sigaction(SIGCHLD, &sa, NULL); @@ -270,18 +291,19 @@ static void deliver(struct qitem *it) { int error; - unsigned int backoff = it->remote? MIN_RETRY: MIN_RETRY_LOCAL; - const char *errmsg = "unknown bounce reason"; + unsigned int backoff = MIN_RETRY; struct timeval now; struct stat st; + snprintf(errmsg, sizeof(errmsg), "unknown bounce reason"); + retry: syslog(LOG_INFO, "trying delivery"); if (it->remote) - error = deliver_remote(it, &errmsg); + error = deliver_remote(it); else - error = deliver_local(it, &errmsg); + error = deliver_local(it); switch (error) { case 0: @@ -296,21 +318,17 @@ retry: } if (gettimeofday(&now, NULL) == 0 && (now.tv_sec - st.st_mtim.tv_sec > MAX_TIMEOUT)) { - asprintf(__DECONST(void *, &errmsg), + snprintf(errmsg, sizeof(errmsg), "Could not deliver for the last %d seconds. Giving up.", MAX_TIMEOUT); goto bounce; } - sleep(backoff); - backoff = backoff * 2 + ( -#ifdef HAVE_RANDOM - random() -#else - rand() -#endif - % RETRY_JITTER); - if (backoff > MAX_RETRY) - backoff = MAX_RETRY; + if (sleep(backoff) == 0) { + /* pick the next backoff between [1.5, 2.5) times backoff */ + backoff = backoff + backoff / 2 + random() % backoff; + if (backoff > MAX_RETRY) + backoff = MAX_RETRY; + } goto retry; case -1: @@ -371,13 +389,35 @@ show_queue(struct queue *queue) int main(int argc, char **argv) { + struct sigaction act; char *sender = NULL; struct queue queue; int i, ch; int nodot = 0, doqueue = 0, showq = 0, queue_only = 0; int recp_from_header = 0; + set_username(); + + /* + * We never run as root. If called by root, drop permissions + * to the mail user. + */ + if (geteuid() == 0 || getuid() == 0) { + struct passwd *pw; + + pw = getpwnam(DMA_ROOT_USER); + if (pw == NULL) + err(1, "cannot drop root privileges"); + + if (setuid(pw->pw_uid) != 0) + err(1, "cannot drop root privileges"); + + if (geteuid() == 0 || getuid() == 0) + errx(1, "cannot drop root privileges"); + } + atexit(deltmp); + init_random(); bzero(&queue, sizeof(queue)); LIST_INIT(&queue.queue); @@ -480,17 +520,14 @@ skipopts: if (logident_base == NULL) logident_base = "dma"; setlogident(NULL); - set_username(); - /* XXX fork root here */ + act.sa_handler = sighup_handler; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + if (sigaction(SIGHUP, &act, NULL) != 0) + syslog(LOG_WARNING, "can not set signal handler: %m"); - parse_conf(CONF_PATH); - - if (config.features & VIRTUAL) { - if (config.virtualpath == NULL) - errlogx(1, "no virtuser file specified, but VIRTUAL configured"); - parse_virtuser(config.virtualpath); - } + parse_conf(CONF_PATH "/dma.conf"); if (config.authpath != NULL) parse_authfile(config.authpath); @@ -521,7 +558,7 @@ skipopts: setlogident("%s", queue.id); for (i = 0; i < argc; i++) { - if (add_recp(&queue, argv[i], 1) != 0) + if (add_recp(&queue, argv[i], EXPAND_WILDCARD) != 0) errlogx(1, "invalid recipient `%s'", argv[i]); } diff --git a/libexec/dma/dma.conf b/libexec/dma/dma.conf index 8fdb9a1759..56fa1045e0 100644 --- a/libexec/dma/dma.conf +++ b/libexec/dma/dma.conf @@ -2,26 +2,22 @@ # # Your smarthost (also called relayhost). Leave blank if you don't want # smarthost support. +# NOTE: on Debian systems this is handled via debconf! +# Please use dpkg-reconfigure dma to change this value. #SMARTHOST # Use this SMTP port. Most users will be fine with the default (25) #PORT 25 # Path to your alias file. Just stay with the default. -#ALIASES /etc/mail/aliases +#ALIASES /etc/aliases # Path to your spooldir. Just stay with the default. #SPOOLDIR /var/spool/dma -# Path to your virtual user file. Just stay with the default. -#VIRTPATH /etc/dma/virtusertable - # SMTP authentication #AUTHPATH /etc/dma/auth.conf -# Uncomment if you want address rewriting -#VIRTUAL - # Uncomment if yout want TLS/SSL support #SECURETRANSFER @@ -29,6 +25,11 @@ # SECURETRANSFER) #STARTTLS +# Uncomment if you have specified STARTTLS above and it should be allowed +# to fail ("opportunistic TLS", use an encrypted connection when available +# but allow an unencrypted one to servers that do not support it) +#OPPORTUNISTIC_TLS + # Path to your local SSL certificate #CERTFILE @@ -45,10 +46,18 @@ # message, not just the headers. #FULLBOUNCE -# The name to be used when introducing this host, if different from -# the result of "hostname". If specified, this overrides MAILNAMEFILE. +# The internet hostname dma uses to identify the host. +# If not set or empty, the result of gethostname(2) is used. +# If MAILNAME is an absolute path to a file, the first line of this file +# will be used as the hostname. #MAILNAME mail.example.net -# The name of the file to read the MAILNAME from; if this file is not -# present, the result of "hostname" will be used. -#MAILNAMEFILE /etc/mailname +# Masquerade envelope from addresses with this address/hostname. +# Use this if mails are not accepted by destination mail servers because +# your sender domain is invalid. +# By default, MASQUERADE is not set. +# Format: MASQUERADE [user@][host] +# Examples: +# MASQUERADE john@ on host "hamlet" will send all mails as john@hamlet +# MASQUERADE percolator will send mails as $username@percolator, e.g. fish@percolator +# MASQUERADE herb@ert will send all mails as herb@ert diff --git a/libexec/dma/dma.h b/libexec/dma/dma.h index 97aab7e528..44ba84b9bd 100644 --- a/libexec/dma/dma.h +++ b/libexec/dma/dma.h @@ -44,32 +44,44 @@ #include #include -#define VERSION "DragonFly Mail Agent" +#define VERSION "DragonFly Mail Agent " DMA_VERSION #define BUF_SIZE 2048 +#define ERRMSG_SIZE 200 +#define USERNAME_SIZE 50 #define MIN_RETRY 300 /* 5 minutes */ -#define MIN_RETRY_LOCAL 30 /* 30 seconds */ #define MAX_RETRY (3*60*60) /* retry at least every 3 hours */ -#define RETRY_JITTER 10 #define MAX_TIMEOUT (5*24*60*60) /* give up after 5 days */ #ifndef PATH_MAX #define PATH_MAX 1024 /* Max path len */ #endif #define SMTP_PORT 25 /* Default SMTP port */ -#define CON_TIMEOUT 120 /* Connection timeout */ +#define CON_TIMEOUT (5*60) /* Connection timeout per RFC5321 */ -#define VIRTUAL 0x001 /* Support for address rewrites */ #define STARTTLS 0x002 /* StartTLS support */ #define SECURETRANS 0x004 /* SSL/TLS in general */ #define NOSSL 0x008 /* Do not use SSL */ #define DEFER 0x010 /* Defer mails */ #define INSECURE 0x020 /* Allow plain login w/o encryption */ #define FULLBOUNCE 0x040 /* Bounce the full message */ +#define TLS_OPP 0x080 /* Opportunistic STARTTLS */ #ifndef CONF_PATH -#define CONF_PATH "/etc/dma/dma.conf" /* Default path to dma.conf */ +#error Please define CONF_PATH #endif +#ifndef LIBEXEC_PATH +#error Please define LIBEXEC_PATH +#endif + +#define DMA_ROOT_USER "mail" +#define DMA_GROUP "mail" + +#ifndef MBOX_STRICT +#define MBOX_STRICT 0 +#endif + + struct stritem { SLIST_ENTRY(stritem) next; char *str; @@ -109,25 +121,18 @@ struct config { int port; const char *aliases; const char *spooldir; - const char *virtualpath; const char *authpath; const char *certfile; int features; const char *mailname; - const char *mailnamefile; + const char *masquerade_host; + const char *masquerade_user; /* XXX does not belong into config */ SSL *ssl; }; -struct virtuser { - SLIST_ENTRY(virtuser) next; - char *login; - char *address; -}; -SLIST_HEAD(virtusers, virtuser); - struct authuser { SLIST_ENTRY(authuser) next; char *login; @@ -150,12 +155,13 @@ struct mx_hostentry { extern struct aliases aliases; extern struct config config; extern struct strlist tmpfs; -extern struct virtusers virtusers; extern struct authusers authusers; -extern const char *username; +extern char username[USERNAME_SIZE]; +extern uid_t useruid; extern const char *logident_base; -extern char neterr[BUF_SIZE]; +extern char neterr[ERRMSG_SIZE]; +extern char errmsg[ERRMSG_SIZE]; /* aliases_parse.y */ int yyparse(void); @@ -164,11 +170,10 @@ extern FILE *yyin; /* conf.c */ void trim_line(char *); void parse_conf(const char *); -void parse_virtuser(const char *); void parse_authfile(const char *); /* crypto.c */ -void hmac_md5(unsigned char *, int, unsigned char *, int, caddr_t); +void hmac_md5(unsigned char *, int, unsigned char *, int, unsigned char *); int smtp_auth_md5(int, char *, char *); int smtp_init_crypto(int, int); @@ -179,13 +184,15 @@ int dns_get_mx_list(const char *, int, struct mx_hostentry **, int); char *ssl_errstr(void); int read_remote(int, int, char *); ssize_t send_remote_command(int, const char*, ...) __printflike(2, 3); -int deliver_remote(struct qitem *, const char **); +int deliver_remote(struct qitem *); /* base64.c */ int base64_encode(const void *, int, char **); int base64_decode(const char *, void *); /* dma.c */ +#define EXPAND_ADDR 1 +#define EXPAND_WILDCARD 2 int add_recp(struct queue *, const char *, int); void run_queue(struct queue *); @@ -198,7 +205,7 @@ int acquirespool(struct qitem *); void dropspool(struct queue *, struct qitem *); /* local.c */ -int deliver_local(struct qitem *, const char **errmsg); +int deliver_local(struct qitem *); /* mail.c */ void bounce(struct qitem *, const char *); @@ -211,8 +218,10 @@ void errlog(int, const char *, ...) __printf0like(2, 3); void errlogx(int, const char *, ...) __printf0like(2, 3); void set_username(void); void deltmp(void); +int do_timeout(int, int); int open_locked(const char *, int, ...); char *rfc822date(void); int strprefixcmp(const char *, const char *); +void init_random(void); #endif diff --git a/libexec/dma/Makefile b/libexec/dma/dma/Makefile similarity index 56% copy from libexec/dma/Makefile copy to libexec/dma/dma/Makefile index 42f40d07aa..2d89c536c6 100644 --- a/libexec/dma/Makefile +++ b/libexec/dma/dma/Makefile @@ -1,4 +1,9 @@ -CFLAGS+= -I${.CURDIR} +.PATH: ${.CURDIR}/.. + +CFLAGS+=-I${.CURDIR}/.. +CFLAGS+= -DHAVE_REALLOCF -DHAVE_STRLCPY -DHAVE_GETPROGNAME +CFLAGS+=-DCONF_PATH='"/etc/dma"' +CFLAGS+=-DLIBEXEC_PATH='"/usr/libexec"' -DDMA_VERSION='"v0.7"' DPADD= ${LIBSSL} ${LIBCRYPTO} LDADD= -lssl -lcrypto diff --git a/libexec/dma/dns.c b/libexec/dma/dns.c index 5a72dfef12..fc5213f1ca 100644 --- a/libexec/dma/dns.c +++ b/libexec/dma/dns.c @@ -66,12 +66,9 @@ add_host(int pref, const char *host, int port, struct mx_hostentry **he, size_t struct addrinfo hints, *res, *res0 = NULL; char servname[10]; struct mx_hostentry *p; - size_t onhosts; const int count_inc = 10; int err; - onhosts = *ps; - memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; @@ -80,7 +77,7 @@ add_host(int pref, const char *host, int port, struct mx_hostentry **he, size_t snprintf(servname, sizeof(servname), "%d", port); err = getaddrinfo(host, servname, &hints, &res0); if (err) - return (-1); + return (err == EAI_AGAIN ? 1 : -1); for (res = res0; res != NULL; res = res->ai_next) { if (*ps + 1 >= roundup(*ps, count_inc)) { @@ -105,12 +102,12 @@ add_host(int pref, const char *host, int port, struct mx_hostentry **he, size_t } freeaddrinfo(res0); - return (*ps - onhosts); + return (0); out: if (res0 != NULL) freeaddrinfo(res0); - return (-1); + return (1); } int @@ -120,13 +117,14 @@ dns_get_mx_list(const char *host, int port, struct mx_hostentry **he, int no_mx) ns_msg msg; ns_rr rr; const char *searchhost; - const char *cp; - char *ans; + const unsigned char *cp; + unsigned char *ans; struct mx_hostentry *hosts = NULL; size_t nhosts = 0; size_t anssz; int pref; int cname_recurse; + int have_mx = 0; int err; int i; @@ -179,10 +177,11 @@ repeat: if (ns_parserr(&msg, ns_s_an, i, &rr)) goto transerr; - cp = (const char *)ns_rr_rdata(rr); + cp = ns_rr_rdata(rr); switch (ns_rr_type(rr)) { case ns_t_mx: + have_mx = 1; pref = ns_get16(cp); cp += 2; err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg), @@ -190,7 +189,9 @@ repeat: if (err < 0) goto transerr; - add_host(pref, outname, port, &hosts, &nhosts); + err = add_host(pref, outname, port, &hosts, &nhosts); + if (err == -1) + goto err; break; case ns_t_cname: @@ -225,17 +226,23 @@ err: free(ans); - if (!err) { - /* - * If we didn't find any MX, use the hostname instead. - */ - if (nhosts == 0) - add_host(0, searchhost, port, &hosts, &nhosts); - - qsort(hosts, nhosts, sizeof(*hosts), sort_pref); + if (err == 0) { + if (!have_mx) { + /* + * If we didn't find any MX, use the hostname instead. + */ + err = add_host(0, host, port, &hosts, &nhosts); + } else if (nhosts == 0) { + /* + * We did get MX, but couldn't resolve any of them + * due to transient errors. + */ + err = 1; + } } if (nhosts > 0) { + qsort(hosts, nhosts, sizeof(*hosts), sort_pref); /* terminate list */ *hosts[nhosts].host = 0; } else { diff --git a/libexec/dma/local.c b/libexec/dma/local.c index 9ce49284f4..6a6407ef86 100644 --- a/libexec/dma/local.c +++ b/libexec/dma/local.c @@ -1,7 +1,46 @@ +/* + * Copyright (c) 2008 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Simon 'corecode' Schubert . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + #include #include #include +#include #include +#include #include #include #include @@ -9,14 +48,88 @@ #include "dma.h" +static int +create_mbox(const char *name) +{ + struct sigaction sa, osa; + pid_t child, waitchild; + int status; + int i; + long maxfd; + int e; + int r = -1; + + /* + * We need to enable SIGCHLD temporarily so that waitpid works. + */ + bzero(&sa, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &sa, &osa); + + do_timeout(100, 0); + + child = fork(); + switch (child) { + case 0: + /* child */ + maxfd = sysconf(_SC_OPEN_MAX); + if (maxfd == -1) + maxfd = 1024; /* what can we do... */ + + for (i = 3; i <= maxfd; ++i) + close(i); + + execl(LIBEXEC_PATH "/dma-mbox-create", "dma-mbox-create", name, NULL); + syslog(LOG_ERR, "cannot execute "LIBEXEC_PATH"/dma-mbox-create: %m"); + exit(1); + + default: + /* parent */ + waitchild = waitpid(child, &status, 0); + + e = errno; + + do_timeout(0, 0); + + if (waitchild == -1 && e == EINTR) { + syslog(LOG_ERR, "hung child while creating mbox `%s': %m", name); + break; + } + + if (waitchild == -1) { + syslog(LOG_ERR, "child disappeared while creating mbox `%s': %m", name); + break; + } + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + syslog(LOG_ERR, "error creating mbox `%s'", name); + break; + } + + /* success */ + r = 0; + break; + + case -1: + /* error */ + syslog(LOG_ERR, "error creating mbox"); + break; + } + + sigaction(SIGCHLD, &osa, NULL); + + return (r); +} + int -deliver_local(struct qitem *it, const char **errmsg) +deliver_local(struct qitem *it) { char fn[PATH_MAX+1]; char line[1000]; const char *sender; const char *newline = "\n"; size_t linelen; + int tries = 0; int mbox; int error; int hadnl = 0; @@ -29,12 +142,43 @@ deliver_local(struct qitem *it, const char **errmsg) return (1); } - /* mailx removes users mailspool file if empty, so open with O_CREAT */ - mbox = open_locked(fn, O_WRONLY|O_APPEND|O_NONBLOCK|O_CREAT, 0660); +retry: + /* wait for a maximum of 100s to get the lock to the file */ + do_timeout(100, 0); + + /* don't use O_CREAT here, because we might be running as the wrong user. */ + mbox = open_locked(fn, O_WRONLY|O_APPEND); if (mbox < 0) { - syslog(LOG_NOTICE, "local delivery deferred: can not open `%s': %m", fn); + int e = errno; + + do_timeout(0, 0); + + switch (e) { + case EACCES: + case ENOENT: + /* + * The file does not exist or we can't access it. + * Call dma-mbox-create to create it and fix permissions. + */ + if (tries > 0 || create_mbox(it->addr) != 0) { + syslog(LOG_ERR, "local delivery deferred: can not create `%s'", fn); + return (1); + } + ++tries; + goto retry; + + case EINTR: + syslog(LOG_NOTICE, "local delivery deferred: can not lock `%s'", fn); + break; + + default: + syslog(LOG_NOTICE, "local delivery deferred: can not open `%s': %m", fn); + break; + } return (1); } + do_timeout(0, 0); + mboxlen = lseek(mbox, 0, SEEK_END); /* New mails start with \nFrom ...., unless we're at the beginning of the mbox */ @@ -48,13 +192,13 @@ deliver_local(struct qitem *it, const char **errmsg) if (fseek(it->mailf, 0, SEEK_SET) != 0) { syslog(LOG_NOTICE, "local delivery deferred: can not seek: %m"); - return (1); + goto out; } error = snprintf(line, sizeof(line), "%sFrom %s\t%s", newline, sender, ctime(&now)); if (error < 0 || (size_t)error >= sizeof(line)) { syslog(LOG_NOTICE, "local delivery deferred: can not write header: %m"); - return (1); + goto out; } if (write(mbox, line, error) != error) goto wrerror; @@ -65,12 +209,22 @@ deliver_local(struct qitem *it, const char **errmsg) linelen = strlen(line); if (linelen == 0 || line[linelen - 1] != '\n') { syslog(LOG_CRIT, "local delivery failed: corrupted queue file"); - *errmsg = "corrupted queue file"; + snprintf(errmsg, sizeof(errmsg), "corrupted queue file"); error = -1; goto chop; } - if (hadnl && strncmp(line, "From ", 5) == 0) { + /* + * mboxro processing: + * - escape lines that start with "From " with a > sign. + * - be reversable by escaping lines that contain an arbitrary + * number of > signs, followed by "From ", i.e. />*From / in regexp. + * - strict mbox processing only requires escaping after empty lines, + * yet most MUAs seem to relax this requirement and will treat any + * line starting with "From " as the beginning of a new mail. + */ + if ((!MBOX_STRICT || hadnl) && + strncmp(&line[strspn(line, ">")], "From ", 5) == 0) { const char *gt = ">"; if (write(mbox, gt, 1) != 1) @@ -93,6 +247,7 @@ wrerror: chop: if (ftruncate(mbox, mboxlen) != 0) syslog(LOG_WARNING, "error recovering mbox `%s': %m", fn); +out: close(mbox); return (error); } diff --git a/libexec/dma/mail.c b/libexec/dma/mail.c index 16efef1ca2..9cbde41d98 100644 --- a/libexec/dma/mail.c +++ b/libexec/dma/mail.c @@ -33,6 +33,8 @@ */ #include +#include +#include #include #include @@ -55,7 +57,7 @@ bounce(struct qitem *it, const char *reason) bzero(&bounceq, sizeof(bounceq)); LIST_INIT(&bounceq.queue); bounceq.sender = ""; - if (add_recp(&bounceq, it->sender, 1) != 0) + if (add_recp(&bounceq, it->sender, EXPAND_WILDCARD) != 0) goto fail; if (newspoolf(&bounceq) != 0) @@ -67,7 +69,7 @@ bounce(struct qitem *it, const char *reason) error = fprintf(bounceq.mailf, "Received: from MAILER-DAEMON\n" "\tid %s\n" - "\tby %s (%s)\n" + "\tby %s (%s);\n" "\t%s\n" "X-Original-To: <%s>\n" "From: MAILER-DAEMON <>\n" @@ -268,6 +270,7 @@ again: goto skip; case '<': + /* this is the real address now */ ps->brackets = 1; ps->pos = 0; goto skip; @@ -287,6 +290,16 @@ again: case ',': case ';': + /* + * Next address, copy previous one. + * However, we might be directly after + * a
, or have two consecutive + * commas. + * Skip the comma unless there is + * really something to copy. + */ + if (ps->pos == 0) + goto skip; s++; goto newaddr; @@ -321,9 +334,9 @@ newaddr: if (addr == NULL) errlog(1, NULL); - if (add_recp(queue, addr, 1) != 0) + if (add_recp(queue, addr, EXPAND_WILDCARD) != 0) errlogx(1, "invalid recipient `%s'", addr); - fprintf(stderr, "parsed `%s'\n", addr); + goto again; } @@ -338,6 +351,7 @@ readmail(struct queue *queue, int nodot, int recp_from_header) int had_from = 0; int had_messagid = 0; int had_date = 0; + int had_last_line = 0; int nocopy = 0; parse_state.state = NONE; @@ -346,9 +360,9 @@ readmail(struct queue *queue, int nodot, int recp_from_header) "Received: from %s (uid %d)\n" "\t(envelope-from %s)\n" "\tid %s\n" - "\tby %s (%s)\n" + "\tby %s (%s);\n" "\t%s\n", - username, getuid(), + username, useruid, queue->sender, queue->id, hostname(), VERSION, @@ -357,12 +371,20 @@ readmail(struct queue *queue, int nodot, int recp_from_header) return (-1); while (!feof(stdin)) { - if (fgets(line, sizeof(line), stdin) == NULL) + if (fgets(line, sizeof(line) - 1, stdin) == NULL) break; + if (had_last_line) + errlogx(1, "bad mail input format"); linelen = strlen(line); if (linelen == 0 || line[linelen - 1] != '\n') { - errno = EINVAL; /* XXX mark permanent errors */ - return (-1); + /* + * This line did not end with a newline character. + * If we fix it, it better be the last line of + * the file. + */ + line[linelen] = '\n'; + line[linelen + 1] = 0; + had_last_line = 1; } if (!had_headers) { /* @@ -407,10 +429,13 @@ readmail(struct queue *queue, int nodot, int recp_from_header) had_date = 1; snprintf(line, sizeof(line), "Date: %s\n", rfc822date()); } else if (!had_messagid) { - /* XXX better msgid, assign earlier and log? */ + /* XXX msgid, assign earlier and log? */ had_messagid = 1; - snprintf(line, sizeof(line), "Message-Id: <%s@%s>\n", - queue->id, hostname()); + snprintf(line, sizeof(line), "Message-Id: <%"PRIxMAX".%s.%"PRIxMAX"@%s>\n", + (uintmax_t)time(NULL), + queue->id, + (uintmax_t)random(), + hostname()); } else if (!had_from) { had_from = 1; snprintf(line, sizeof(line), "From: <%s>\n", queue->sender); diff --git a/libexec/dma/net.c b/libexec/dma/net.c index 3dc9520f08..985c5393ac 100644 --- a/libexec/dma/net.c +++ b/libexec/dma/net.c @@ -33,6 +33,8 @@ * SUCH DAMAGE. */ +#include "dfcompat.h" + #include #include #include @@ -44,6 +46,7 @@ #include #include +#include #include #include #include @@ -54,8 +57,7 @@ #include "dma.h" -static jmp_buf timeout_alarm; -char neterr[BUF_SIZE]; +char neterr[ERRMSG_SIZE]; char * ssl_errstr(void) @@ -69,13 +71,6 @@ ssl_errstr(void) return (ERR_error_string(oerr, NULL)); } -static void -sig_alarm(int signo) -{ - (void)signo; /* so that gcc doesn't complain */ - longjmp(timeout_alarm, 1); -} - ssize_t send_remote_command(int fd, const char* fmt, ...) { @@ -125,26 +120,25 @@ int read_remote(int fd, int extbufsize, char *extbuf) { ssize_t rlen = 0; - size_t pos, len; + size_t pos, len, copysize; char buff[BUF_SIZE]; - int done = 0, status = 0, extbufpos = 0; + int done = 0, status = 0, status_running = 0, extbufpos = 0; + enum { parse_status, parse_spacedash, parse_rest } parsestate; - if (signal(SIGALRM, sig_alarm) == SIG_ERR) { - snprintf(neterr, sizeof(neterr), "SIGALRM error: %s", - strerror(errno)); - return (-1); - } - if (setjmp(timeout_alarm) != 0) { + if (do_timeout(CON_TIMEOUT, 1) != 0) { snprintf(neterr, sizeof(neterr), "Timeout reached"); return (-1); } - alarm(CON_TIMEOUT); /* * Remote reading code from femail.c written by Henning Brauer of * OpenBSD and released under a BSD style license. */ - for (len = pos = 0; !done; ) { + len = 0; + pos = 0; + parsestate = parse_status; + neterr[0] = 0; + while (!(done && parsestate == parse_status)) { rlen = 0; if (pos == 0 || (pos > 0 && memchr(buff + pos, '\n', len - pos) == NULL)) { @@ -153,18 +147,22 @@ read_remote(int fd, int extbufsize, char *extbuf) pos = 0; if (((config.features & SECURETRANS) != 0) && (config.features & NOSSL) == 0) { - if ((rlen = SSL_read(config.ssl, buff + len, - sizeof(buff) - len)) == -1) { + if ((rlen = SSL_read(config.ssl, buff + len, sizeof(buff) - len)) == -1) { strncpy(neterr, ssl_errstr(), sizeof(neterr)); - return (-1); + goto error; } } else { if ((rlen = read(fd, buff + len, sizeof(buff) - len)) == -1) { strncpy(neterr, strerror(errno), sizeof(neterr)); - return (-1); + goto error; } } len += rlen; + + copysize = sizeof(neterr) - strlen(neterr) - 1; + if (copysize > len) + copysize = len; + strncat(neterr, buff, copysize); } /* * If there is an external buffer with a size bigger than zero @@ -172,8 +170,7 @@ read_remote(int fd, int extbufsize, char *extbuf) * there are new characters read from the mailserver * copy them to the external buffer */ - if (extbufpos <= (extbufsize - 1) && rlen && extbufsize > 0 - && extbuf != NULL) { + if (extbufpos <= (extbufsize - 1) && rlen > 0 && extbufsize > 0 && extbuf != NULL) { /* do not write over the bounds of the buffer */ if(extbufpos + rlen > (extbufsize - 1)) { rlen = extbufsize - extbufpos; @@ -181,32 +178,68 @@ read_remote(int fd, int extbufsize, char *extbuf) memcpy(extbuf + extbufpos, buff + len - rlen, rlen); extbufpos += rlen; } - for (; pos < len && buff[pos] >= '0' && buff[pos] <= '9'; pos++) - ; /* Do nothing */ if (pos == len) - return (0); + continue; + + switch (parsestate) { + case parse_status: + for (; pos < len; pos++) { + if (isdigit(buff[pos])) { + status_running = status_running * 10 + (buff[pos] - '0'); + } else { + status = status_running; + status_running = 0; + parsestate = parse_spacedash; + break; + } + } + continue; + + case parse_spacedash: + switch (buff[pos]) { + case ' ': + done = 1; + break; + + case '-': + /* ignore */ + /* XXX read capabilities */ + break; + + default: + strcpy(neterr, "invalid syntax in reply from server"); + goto error; + } - if (buff[pos] == ' ') { - done = 1; - } else if (buff[pos] != '-') { - strcpy(neterr, "invalid syntax in reply from server"); - return (-1); + pos++; + parsestate = parse_rest; + continue; + + case parse_rest: + /* skip up to \n */ + for (; pos < len; pos++) { + if (buff[pos] == '\n') { + pos++; + parsestate = parse_status; + break; + } + } } - /* skip up to \n */ - for (; pos < len && buff[pos - 1] != '\n'; pos++) - ; /* Do nothing */ - } - alarm(0); - buff[len] = '\0'; - while (len > 0 && (buff[len - 1] == '\r' || buff[len - 1] == '\n')) - buff[--len] = '\0'; - snprintf(neterr, sizeof(neterr), "%s", buff); - status = atoi(buff); + do_timeout(0, 0); + + /* chop off trailing newlines */ + while (neterr[0] != 0 && strchr("\r\n", neterr[strlen(neterr) - 1]) != 0) + neterr[strlen(neterr) - 1] = 0; + return (status/100); + +error: + do_timeout(0, 0); + return (-1); } /* @@ -308,7 +341,6 @@ close_connection(int fd) if (((config.features & SECURETRANS) != 0) && ((config.features & NOSSL) == 0)) SSL_shutdown(config.ssl); - SSL_free(config.ssl); } @@ -316,7 +348,7 @@ close_connection(int fd) } static int -deliver_to_host(struct qitem *it, struct mx_hostentry *host, void *errmsgc) +deliver_to_host(struct qitem *it, struct mx_hostentry *host) { struct authuser *a; char line[1000]; @@ -324,7 +356,7 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host, void *errmsgc) int fd, error = 0, do_auth = 0, res = 0; if (fseek(it->mailf, 0, SEEK_SET) != 0) { - asprintf(errmsgc, "can not seek: %s", strerror(errno)); + snprintf(errmsg, sizeof(errmsg), "can not seek: %s", strerror(errno)); return (-1); } @@ -337,7 +369,7 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host, void *errmsgc) if (res == 5) { \ syslog(LOG_ERR, "remote delivery to %s [%s] failed after %s: %s", \ host->host, host->addr, c, neterr); \ - asprintf(errmsgc, "%s [%s] did not like our %s:\n%s", \ + snprintf(errmsg, sizeof(errmsg), "%s [%s] did not like our %s:\n%s", \ host->host, host->addr, c, neterr); \ return (-1); \ } else if (res != exp) { \ @@ -347,10 +379,13 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host, void *errmsgc) } /* Check first reply from remote host */ - config.features |= NOSSL; - READ_REMOTE_CHECK("connect", 2); + if ((config.features & SECURETRANS) == 0 || + (config.features & STARTTLS) != 0) { + config.features |= NOSSL; + READ_REMOTE_CHECK("connect", 2); - config.features &= ~NOSSL; + config.features &= ~NOSSL; + } if ((config.features & SECURETRANS) != 0) { error = smtp_init_crypto(fd, config.features); @@ -358,6 +393,9 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host, void *errmsgc) syslog(LOG_DEBUG, "SSL initialization successful"); else goto out; + + if ((config.features & STARTTLS) == 0) + READ_REMOTE_CHECK("connect", 2); } /* XXX allow HELO fallback */ @@ -386,7 +424,7 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host, void *errmsgc) if (error < 0) { syslog(LOG_ERR, "remote delivery failed:" " SMTP login failed: %m"); - asprintf(errmsgc, "SMTP login to %s failed", host->host); + snprintf(errmsg, sizeof(errmsg), "SMTP login to %s failed", host->host); return (-1); } /* SMTP login is not available, so try without */ @@ -413,7 +451,7 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host, void *errmsgc) linelen = strlen(line); if (linelen == 0 || line[linelen - 1] != '\n') { syslog(LOG_CRIT, "remote delivery failed: corrupted queue file"); - *(const char **)errmsgc = "corrupted queue file"; + snprintf(errmsg, sizeof(errmsg), "corrupted queue file"); error = -1; goto out; } @@ -448,10 +486,8 @@ out: } int -deliver_remote(struct qitem *it, const char **errmsg) +deliver_remote(struct qitem *it) { - /* asprintf can't take const */ - void *errmsgc = __DECONST(char **, errmsg); struct mx_hostentry *hosts, *h; const char *host; int port; @@ -460,7 +496,7 @@ deliver_remote(struct qitem *it, const char **errmsg) host = strrchr(it->addr, '@'); /* Should not happen */ if (host == NULL) { - asprintf(errmsgc, "Internal error: badly formed address %s", + snprintf(errmsg, sizeof(errmsg), "Internal error: badly formed address %s", it->addr); return(-1); } else { @@ -487,7 +523,7 @@ deliver_remote(struct qitem *it, const char **errmsg) } for (h = hosts; *h->host != 0; h++) { - switch (deliver_to_host(it, h, errmsgc)) { + switch (deliver_to_host(it, h)) { case 0: /* success */ error = 0; diff --git a/libexec/dma/spool.c b/libexec/dma/spool.c index f5a9d790ee..25c6a99d2c 100644 --- a/libexec/dma/spool.c +++ b/libexec/dma/spool.c @@ -32,6 +32,9 @@ * SUCH DAMAGE. */ +#include "dfcompat.h" + +#include #include #include @@ -120,9 +123,11 @@ writequeuef(struct qitem *it) int error; int queuefd; - queuefd = open_locked(it->queuefn, O_CREAT|O_EXCL|O_RDWR, 0600); + queuefd = open_locked(it->queuefn, O_CREAT|O_EXCL|O_RDWR, 0660); if (queuefd == -1) return (-1); + if (fchmod(queuefd, 0660) < 0) + return (-1); it->queuef = fdopen(queuefd, "w+"); if (it->queuef == NULL) return (-1); @@ -290,9 +295,7 @@ load_queue(struct queue *queue) queuefn = NULL; mailfn = NULL; - /* ignore temp files */ - if (strncmp(de->d_name, "tmp_", 4) == 0 || de->d_type != DT_REG) - continue; + /* ignore non-queue files */ if (de->d_name[0] != 'Q') continue; if (asprintf(&queuefn, "%s/Q%s", config.spooldir, de->d_name + 1) < 0) @@ -300,6 +303,18 @@ load_queue(struct queue *queue) if (asprintf(&mailfn, "%s/M%s", config.spooldir, de->d_name + 1) < 0) goto fail; + /* + * Some file systems don't provide a de->d_type, so we have to + * do an explicit stat on the queue file. + * Move on if it turns out to be something else than a file. + */ + if (stat(queuefn, &sb) != 0) + goto skip_item; + if (!S_ISREG(sb.st_mode)) { + errno = EINVAL; + goto skip_item; + } + if (stat(mailfn, &sb) != 0) goto skip_item; diff --git a/libexec/dma/util.c b/libexec/dma/util.c index cf26acc60b..a139b20d38 100644 --- a/libexec/dma/util.c +++ b/libexec/dma/util.c @@ -33,10 +33,15 @@ */ #include +#include + +#include #include #include #include #include +#include +#include #include #include #include @@ -46,61 +51,87 @@ const char * hostname(void) { - static char name[MAXHOSTNAMELEN+1]; - int initialized = 0; - FILE *fp; - size_t len; +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 255 +#endif + static char name[HOST_NAME_MAX+1]; + static int initialized = 0; + char *s; if (initialized) return (name); - if (config.mailname != NULL && config.mailname[0] != '\0') { + if (config.mailname == NULL || !*config.mailname) + goto local; + + if (config.mailname[0] == '/') { + /* + * If the mailname looks like an absolute path, + * treat it as a file. + */ + FILE *fp; + + fp = fopen(config.mailname, "r"); + if (fp == NULL) + goto local; + + s = fgets(name, sizeof(name), fp); + fclose(fp); + if (s == NULL) + goto local; + + for (s = name; *s != 0 && (isalnum(*s) || strchr("_.-", *s)); ++s) + /* NOTHING */; + *s = 0; + + if (!*name) + goto local; + + initialized = 1; + return (name); + } else { snprintf(name, sizeof(name), "%s", config.mailname); initialized = 1; return (name); } - if (config.mailnamefile != NULL && config.mailnamefile[0] != '\0') { - fp = fopen(config.mailnamefile, "r"); - if (fp != NULL) { - if (fgets(name, sizeof(name), fp) != NULL) { - len = strlen(name); - while (len > 0 && - (name[len - 1] == '\r' || - name[len - 1] == '\n')) - name[--len] = '\0'; - if (name[0] != '\0') { - initialized = 1; - return (name); - } - } - fclose(fp); - } - } + +local: if (gethostname(name, sizeof(name)) != 0) - strcpy(name, "(unknown hostname)"); + *name = 0; + /* + * gethostname() is allowed to truncate name without NUL-termination + * and at the same time not return an error. + */ + name[sizeof(name) - 1] = 0; + + for (s = name; *s != 0 && (isalnum(*s) || strchr("_.-", *s)); ++s) + /* NOTHING */; + *s = 0; + + if (!*name) + snprintf(name, sizeof(name), "unknown-hostname"); + initialized = 1; - return name; + return (name); } void setlogident(const char *fmt, ...) { - char *tag = NULL; + static char tag[50]; + snprintf(tag, sizeof(tag), "%s", logident_base); if (fmt != NULL) { va_list ap; - char *sufx; + char sufx[50]; va_start(ap, fmt); - vasprintf(&sufx, fmt, ap); - if (sufx != NULL) { - asprintf(&tag, "%s[%s]", logident_base, sufx); - free(sufx); - } + vsnprintf(sufx, sizeof(sufx), fmt, ap); va_end(ap); + snprintf(tag, sizeof(tag), "%s[%s]", logident_base, sufx); } closelog(); - openlog(tag != NULL ? tag : logident_base, 0, LOG_MAIL); + openlog(tag, 0, LOG_MAIL); } void @@ -108,15 +139,17 @@ errlog(int exitcode, const char *fmt, ...) { int oerrno = errno; va_list ap; - char *outs = NULL; + char outs[ERRMSG_SIZE]; + outs[0] = 0; if (fmt != NULL) { va_start(ap, fmt); - vasprintf(&outs, fmt, ap); + vsnprintf(outs, sizeof(outs), fmt, ap); va_end(ap); } - if (outs != NULL) { + errno = oerrno; + if (*outs != 0) { syslog(LOG_ERR, "%s: %m", outs); fprintf(stderr, "%s: %s: %s\n", getprogname(), outs, strerror(oerrno)); } else { @@ -131,15 +164,16 @@ void errlogx(int exitcode, const char *fmt, ...) { va_list ap; - char *outs = NULL; + char outs[ERRMSG_SIZE]; + outs[0] = 0; if (fmt != NULL) { va_start(ap, fmt); - vasprintf(&outs, fmt, ap); + vsnprintf(outs, sizeof(outs), fmt, ap); va_end(ap); } - if (outs != NULL) { + if (*outs != 0) { syslog(LOG_ERR, "%s", outs); fprintf(stderr, "%s: %s\n", getprogname(), outs); } else { @@ -150,49 +184,38 @@ errlogx(int exitcode, const char *fmt, ...) exit(exitcode); } -static const char * +static int check_username(const char *name, uid_t ckuid) { struct passwd *pwd; if (name == NULL) - return (NULL); + return (0); pwd = getpwnam(name); if (pwd == NULL || pwd->pw_uid != ckuid) - return (NULL); - return (name); + return (0); + snprintf(username, sizeof(username), "%s", name); + return (1); } void set_username(void) { struct passwd *pwd; - char *u = NULL; - uid_t uid; - uid = getuid(); - username = check_username(getlogin(), uid); - if (username != NULL) + useruid = getuid(); + if (check_username(getlogin(), useruid)) return; - username = check_username(getenv("LOGNAME"), uid); - if (username != NULL) + if (check_username(getenv("LOGNAME"), useruid)) return; - username = check_username(getenv("USER"), uid); - if (username != NULL) + if (check_username(getenv("USER"), useruid)) return; - pwd = getpwuid(uid); - if (pwd != NULL && pwd->pw_name != NULL && pwd->pw_name[0] != '\0' && - (u = strdup(pwd->pw_name)) != NULL) { - username = check_username(u, uid); - if (username != NULL) + pwd = getpwuid(useruid); + if (pwd != NULL && pwd->pw_name != NULL && pwd->pw_name[0] != '\0') { + if (check_username(pwd->pw_name, useruid)) return; - else - free(u); } - asprintf(__DECONST(void *, &username), "%ld", (long)uid); - if (username != NULL) - return; - username = "unknown-or-invalid-username"; + snprintf(username, sizeof(username), "uid=%ld", (long)useruid); } void @@ -205,6 +228,52 @@ deltmp(void) } } +static sigjmp_buf sigbuf; +static int sigbuf_valid; + +static void +sigalrm_handler(int signo) +{ + (void)signo; /* so that gcc doesn't complain */ + if (sigbuf_valid) + siglongjmp(sigbuf, 1); +} + +int +do_timeout(int timeout, int dojmp) +{ + struct sigaction act; + int ret = 0; + + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + + if (timeout) { + act.sa_handler = sigalrm_handler; + if (sigaction(SIGALRM, &act, NULL) != 0) + syslog(LOG_WARNING, "can not set signal handler: %m"); + if (dojmp) { + ret = sigsetjmp(sigbuf, 1); + if (ret) + goto disable; + /* else just programmed */ + sigbuf_valid = 1; + } + + alarm(timeout); + } else { +disable: + alarm(0); + + act.sa_handler = SIG_IGN; + if (sigaction(SIGALRM, &act, NULL) != 0) + syslog(LOG_WARNING, "can not remove signal handler: %m"); + sigbuf_valid = 0; + } + + return (ret); +} + int open_locked(const char *fname, int flags, ...) { @@ -256,3 +325,21 @@ strprefixcmp(const char *str, const char *prefix) return (strncasecmp(str, prefix, strlen(prefix))); } +void +init_random(void) +{ + unsigned int seed; + int rf; + + rf = open("/dev/urandom", O_RDONLY); + if (rf == -1) + rf = open("/dev/random", O_RDONLY); + + if (!(rf != -1 && read(rf, &seed, sizeof(seed)) == sizeof(seed))) + seed = (time(NULL) ^ getpid()) + (uintptr_t)&seed; + + srandom(seed); + + if (rf != -1) + close(rf); +} -- 2.41.0