--- /dev/null
+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 <matthias@dragonflybsd.org>, 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 <Todd.Miller@courtesan.com>
+
+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 <imp@freebsd.org>
+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.
-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 <bsd.prog.mk>
+.include <bsd.subdir.mk>
.if !exists(${DESTDIR}/etc/dma/dma.conf)
FILES+= dma.conf
.endif
-.if !exists(${DESTDIR}/etc/dma/virtusertable)
-FILES+= virtusertable
-.endif
.include <bsd.prog.mk>
--- /dev/null
+.include "../Makefile.inc"
+++ /dev/null
-#
-# 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}
--- /dev/null
+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>
- use proper sysexit codes
- handle/use ESMTP extensions
- .forward support
+- suggest way to run a queue flush on boot
%%
[^:,#[: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;
size_t linelen;
char *p;
- p = line;
-
if ((p = strchr(line, '\n')))
*p = (char)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
*
* file format is:
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)
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 */
*/
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 -
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";
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;
-#ifdef NEED_STRLCPY
+#ifndef HAVE_STRLCPY
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* $DragonFly: src/lib/libc/string/strlcpy.c,v 1.4 2005/09/18 16:32:34 asmodai Exp $
*/
+#include "dfcompat.h"
+
#include <sys/types.h>
#include <string.h>
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 <imp@freebsd.org>
return (nptr);
}
-#endif /* NEED_REALLOCF */
+#endif /* !HAVE_REALLOCF */
-#ifdef NEED_GETPROGNAME
+#ifndef HAVE_GETPROGNAME
#ifdef __GLIBC__
-#define __USE_GNU
#include <errno.h>
const char *
#error "no getprogname implementation available"
#endif
-#endif /* NEED_GETPROGNAME */
+#endif /* !HAVE_GETPROGNAME */
#ifndef DFCOMPAT_H
#define DFCOMPAT_H
+#define _GNU_SOURCE
+
#include <sys/types.h>
#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
--- /dev/null
+.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 <bsd.prog.mk>
--- /dev/null
+/*
+ * 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 <corecode@fs.ei.tum.de>.
+ *
+ * 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 <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#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);
+}
.\"
.\" $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
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.
auth.conf
.It
dma.conf
-.It
-virtusertable
.El
.Pp
These three files are stored per default in
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
.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
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
.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
.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 ,
* SUCH DAMAGE.
*/
+#include "dfcompat.h"
+
#include <sys/param.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include <sys/wait.h>
#include <dirent.h>
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);
}
return (NULL);
}
-out:
queue->sender = sender;
return (sender);
}
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;
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 {
daemonize = 0;
bzero(&sa, sizeof(sa));
- sa.sa_flags = SA_NOCLDWAIT;
sa.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &sa, NULL);
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:
}
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:
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);
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);
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]);
}
#
# 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
# 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
# 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
#include <openssl/ssl.h>
#include <netdb.h>
-#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;
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;
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);
/* 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);
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 *);
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 *);
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
-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
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;
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)) {
}
freeaddrinfo(res0);
- return (*ps - onhosts);
+ return (0);
out:
if (res0 != NULL)
freeaddrinfo(res0);
- return (-1);
+ return (1);
}
int
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;
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),
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:
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 {
+/*
+ * Copyright (c) 2008 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>.
+ *
+ * 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 <sys/types.h>
+#include <sys/wait.h>
+
#include <err.h>
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <paths.h>
+#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <syslog.h>
#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;
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 */
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;
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)
chop:
if (ftruncate(mbox, mboxlen) != 0)
syslog(LOG_WARNING, "error recovering mbox `%s': %m", fn);
+out:
close(mbox);
return (error);
}
*/
#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
#include <syslog.h>
#include <unistd.h>
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)
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"
goto skip;
case '<':
+ /* this is the real address now */
ps->brackets = 1;
ps->pos = 0;
goto skip;
case ',':
case ';':
+ /*
+ * Next address, copy previous one.
+ * However, we might be directly after
+ * a <address>, or have two consecutive
+ * commas.
+ * Skip the comma unless there is
+ * really something to copy.
+ */
+ if (ps->pos == 0)
+ goto skip;
s++;
goto 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;
}
int had_from = 0;
int had_messagid = 0;
int had_date = 0;
+ int had_last_line = 0;
int nocopy = 0;
parse_state.state = NONE;
"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,
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) {
/*
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);
* SUCH DAMAGE.
*/
+#include "dfcompat.h"
+
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
+#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <netdb.h>
#include "dma.h"
-static jmp_buf timeout_alarm;
-char neterr[BUF_SIZE];
+char neterr[ERRMSG_SIZE];
char *
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, ...)
{
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)) {
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
* 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;
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);
}
/*
if (((config.features & SECURETRANS) != 0) &&
((config.features & NOSSL) == 0))
SSL_shutdown(config.ssl);
-
SSL_free(config.ssl);
}
}
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];
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);
}
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) { \
}
/* 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);
syslog(LOG_DEBUG, "SSL initialization successful");
else
goto out;
+
+ if ((config.features & STARTTLS) == 0)
+ READ_REMOTE_CHECK("connect", 2);
}
/* XXX allow HELO fallback */
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 */
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;
}
}
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;
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 {
}
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;
* SUCH DAMAGE.
*/
+#include "dfcompat.h"
+
+#include <sys/file.h>
#include <sys/stat.h>
#include <ctype.h>
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);
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)
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;
*/
#include <sys/param.h>
+#include <sys/file.h>
+
+#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <pwd.h>
+#include <setjmp.h>
+#include <signal.h>
#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
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
{
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 {
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 {
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
}
}
+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, ...)
{
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);
+}