Module Name: src
Committed By: christos
Date: Tue Dec 16 19:30:24 UTC 2014
Modified Files:
src/usr.bin/mail: cmd3.c extern.h fio.c mail.1 names.c send.c
Log Message:
Fix various security related issues:
0001. Do not recognize paths, mail folders, and pipes in mail addresses
by default. That avoids a direct command injection with syntactically
valid email addresses starting with |.
Such addresses can be specified both on the command line, the mail
headers (with -t) or in address lines copied over from previous
while replying.
This was assigned CVE-2014-7844 for some versions of BSD mailx. It is
documented behavior for Heirloom mailx, and was mentioned in an old
technical report about BSD mailx (which does not usually make its way
into operating system installations). The patch switches off this
processing and updates the documentation.
Added expandaddr option to explicitly enable this behavior.
0002. When invoking sendmail, prevent option processing for email
address arguments. This prevents changing e.g. the Postfix
configuration file in unexpected ways. This behavior was documented for
BSD mailx (sort of), but not for Heirloom mailx. We did not assign a
CVE to this because it is more of a missing feature, and code invoking
mailx needs adjustment in the caller as well.
Fixed.
0003. Make wordexp support mandatory. (No functional change.)
Fixed (replaced explicit shell pipe implementation).
0004. Prevent command execution in the expand function, which is IMHO
unexpected. (Not really required with patch 1, and there is still
information disclosure/DoS potential if this expansion occurs.) This is
a historic vulnerability already fixed in the Debian package,
retroactively assigned CVE-2004-2771:
Fixed (as part of the pipe replacement with wordexp).
To generate a diff of this commit:
cvs rdiff -u -r1.42 -r1.43 src/usr.bin/mail/cmd3.c
cvs rdiff -u -r1.32 -r1.33 src/usr.bin/mail/extern.h
cvs rdiff -u -r1.40 -r1.41 src/usr.bin/mail/fio.c
cvs rdiff -u -r1.60 -r1.61 src/usr.bin/mail/mail.1
cvs rdiff -u -r1.30 -r1.31 src/usr.bin/mail/names.c
cvs rdiff -u -r1.37 -r1.38 src/usr.bin/mail/send.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/usr.bin/mail/cmd3.c
diff -u src/usr.bin/mail/cmd3.c:1.42 src/usr.bin/mail/cmd3.c:1.43
--- src/usr.bin/mail/cmd3.c:1.42 Sun Apr 29 19:50:22 2012
+++ src/usr.bin/mail/cmd3.c Tue Dec 16 14:30:24 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: cmd3.c,v 1.42 2012/04/29 23:50:22 christos Exp $ */
+/* $NetBSD: cmd3.c,v 1.43 2014/12/16 19:30:24 christos Exp $ */
/*
* Copyright (c) 1980, 1993
@@ -34,7 +34,7 @@
#if 0
static char sccsid[] = "@(#)cmd3.c 8.2 (Berkeley) 4/20/95";
#else
-__RCSID("$NetBSD: cmd3.c,v 1.42 2012/04/29 23:50:22 christos Exp $");
+__RCSID("$NetBSD: cmd3.c,v 1.43 2014/12/16 19:30:24 christos Exp $");
#endif
#endif /* not lint */
@@ -577,7 +577,7 @@ bounce(void *v)
if (hdr.h_to == NULL)
return 1;
- smargs = unpack(hdr.h_to);
+ smargs = unpack(NULL, hdr.h_to);
for (ip = msgvec; *ip; ip++) {
int e;
if ((e = bounce_one(*ip, smargs, hdr.h_to)) != 0)
Index: src/usr.bin/mail/extern.h
diff -u src/usr.bin/mail/extern.h:1.32 src/usr.bin/mail/extern.h:1.33
--- src/usr.bin/mail/extern.h:1.32 Tue Feb 28 17:30:44 2012
+++ src/usr.bin/mail/extern.h Tue Dec 16 14:30:24 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: extern.h,v 1.32 2012/02/28 22:30:44 joerg Exp $ */
+/* $NetBSD: extern.h,v 1.33 2014/12/16 19:30:24 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -29,7 +29,7 @@
* SUCH DAMAGE.
*
* @(#)extern.h 8.2 (Berkeley) 4/20/95
- * $NetBSD: extern.h,v 1.32 2012/02/28 22:30:44 joerg Exp $
+ * $NetBSD: extern.h,v 1.33 2014/12/16 19:30:24 christos Exp $
*/
#ifndef __EXTERN_H__
@@ -224,7 +224,7 @@ struct name * extract(char [], int);
struct name * gexpand(struct name *, struct grouphead *, int, int);
struct name * nalloc(char [], int);
struct name * outof(struct name *, FILE *, struct header *);
-const char ** unpack(struct name *);
+const char ** unpack(struct name *, struct name *);
struct name * usermap(struct name *);
#if 0
void prettyprint(struct name *); /* commented out? */
Index: src/usr.bin/mail/fio.c
diff -u src/usr.bin/mail/fio.c:1.40 src/usr.bin/mail/fio.c:1.41
--- src/usr.bin/mail/fio.c:1.40 Sat Mar 9 14:43:07 2013
+++ src/usr.bin/mail/fio.c Tue Dec 16 14:30:24 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: fio.c,v 1.40 2013/03/09 19:43:07 christos Exp $ */
+/* $NetBSD: fio.c,v 1.41 2014/12/16 19:30:24 christos Exp $ */
/*
* Copyright (c) 1980, 1993
@@ -34,7 +34,7 @@
#if 0
static char sccsid[] = "@(#)fio.c 8.2 (Berkeley) 4/20/95";
#else
-__RCSID("$NetBSD: fio.c,v 1.40 2013/03/09 19:43:07 christos Exp $");
+__RCSID("$NetBSD: fio.c,v 1.41 2014/12/16 19:30:24 christos Exp $");
#endif
#endif /* not lint */
@@ -42,6 +42,7 @@ __RCSID("$NetBSD: fio.c,v 1.40 2013/03/0
#include "extern.h"
#include "thread.h"
#include "sig.h"
+#include <wordexp.h>
/*
* Mail -- a mail program
@@ -424,13 +425,10 @@ PUBLIC const char *
expand(const char *name)
{
char xname[PATHSIZE];
- char cmdbuf[PATHSIZE]; /* also used for file names */
- pid_t pid;
- ssize_t l;
- char *cp;
- const char *shellcmd;
- int pivec[2];
- struct stat sbuf;
+ char cmdbuf[PATHSIZE];
+ int e;
+ wordexp_t we;
+ sigset_t nset, oset;
/*
* The order of evaluation is "%" and "#" expand into constants.
@@ -466,47 +464,53 @@ expand(const char *name)
}
if (strpbrk(name, "~{[*?$`'\"\\") == NULL)
return name;
- if (pipe(pivec) < 0) {
- warn("pipe");
- return name;
- }
- (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name);
- if ((shellcmd = value(ENAME_SHELL)) == NULL)
- shellcmd = _PATH_CSHELL;
- pid = start_command(shellcmd, NULL, -1, pivec[1], "-c", cmdbuf, NULL);
- if (pid < 0) {
- (void)close(pivec[0]);
- (void)close(pivec[1]);
+
+ *xname = '\0';
+
+ sigemptyset(&nset);
+ sigaddset(&nset, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &nset, &oset);
+ e = wordexp(name, &we, WRDE_NOCMD);
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+
+ switch (e) {
+ case 0: /* OK */
+ case WRDE_NOSPACE:
+ warnx("Out of memory expanding `%s'", name);
return NULL;
- }
- (void)close(pivec[1]);
- l = read(pivec[0], xname, sizeof(xname));
- (void)close(pivec[0]);
- if (wait_child(pid) < 0 && WTERMSIG(wait_status) != SIGPIPE) {
- warnx("Expansion `%s' failed [%x]", cmdbuf, wait_status);
+ case WRDE_BADVAL:
+ case WRDE_BADCHAR:
+ case WRDE_SYNTAX:
+ warnx("Syntax error expanding `%s'", name);
return NULL;
- }
- if (l < 0) {
- warn("read");
+ case WRDE_CMDSUB:
+ warnx("Command substitution not allowed expanding `%s'",
+ name);
return NULL;
- }
- if (l == 0) {
- warnx("No match for `%s'", name);
+ default:
+ warnx("Unknown expansion error %d expanding `%s'", e, name);
return NULL;
}
- if (l == sizeof(xname)) {
- warnx("Expansion buffer overflow for `%s'", name);
- return NULL;
- }
- xname[l] = '\0';
- for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
- continue;
- cp[1] = '\0';
- if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) {
+
+ switch (we.we_wordc) {
+ case 0:
+ warnx("No match for `%s'", name);
+ break;
+ case 1:
+ if (strlen(we.we_wordv[1]) >= PATHSIZE)
+ warnx("Expansion too long for `%s'", name);
+ strlcpy(xname, we.we_wordv[1], PATHSIZE);
+ break;
+ default:
warnx("Ambiguous expansion for `%s'", name);
- return NULL;
+ break;
}
- return savestr(xname);
+
+ wordfree(&we);
+ if (!*xname)
+ return NULL;
+ else
+ return savestr(xname);
}
/*
Index: src/usr.bin/mail/mail.1
diff -u src/usr.bin/mail/mail.1:1.60 src/usr.bin/mail/mail.1:1.61
--- src/usr.bin/mail/mail.1:1.60 Sat Mar 9 14:43:20 2013
+++ src/usr.bin/mail/mail.1 Tue Dec 16 14:30:24 2014
@@ -1,4 +1,4 @@
-.\" $NetBSD: mail.1,v 1.60 2013/03/09 19:43:20 christos Exp $
+.\" $NetBSD: mail.1,v 1.61 2014/12/16 19:30:24 christos Exp $
.\"
.\" Copyright (c) 1980, 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -29,7 +29,7 @@
.\"
.\" @(#)mail.1 8.8 (Berkeley) 4/28/95
.\"
-.Dd March 9, 2013
+.Dd December 15, 2014
.Dt MAIL 1
.Os
.Sh NAME
@@ -725,6 +725,10 @@ modifying the user's system mailbox, his
.Ar mbox
file, or his edit file in
.Fl f .
+.It Ic expandaddr
+If unset (the default), recipient addresses must be names of local mailboxes
+or Internet mail addresses.
+If set, shell expansion of existing mailbox names will be performed.
.It Ic expose
Expose the thread structure so all messages appear in header listings.
(See
Index: src/usr.bin/mail/names.c
diff -u src/usr.bin/mail/names.c:1.30 src/usr.bin/mail/names.c:1.31
--- src/usr.bin/mail/names.c:1.30 Sat Oct 20 21:11:23 2012
+++ src/usr.bin/mail/names.c Tue Dec 16 14:30:24 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: names.c,v 1.30 2012/10/21 01:11:23 christos Exp $ */
+/* $NetBSD: names.c,v 1.31 2014/12/16 19:30:24 christos Exp $ */
/*
* Copyright (c) 1980, 1993
@@ -34,7 +34,7 @@
#if 0
static char sccsid[] = "@(#)names.c 8.1 (Berkeley) 6/6/93";
#else
-__RCSID("$NetBSD: names.c,v 1.30 2012/10/21 01:11:23 christos Exp $");
+__RCSID("$NetBSD: names.c,v 1.31 2014/12/16 19:30:24 christos Exp $");
#endif
#endif /* not lint */
@@ -254,6 +254,9 @@ outof(struct name *names, FILE *fo, stru
int ispipe;
char tempname[PATHSIZE];
+ if (value("expandaddr") == NULL)
+ return names;
+
begin = names;
np = names;
(void)time(&now);
@@ -532,7 +535,7 @@ count(struct name *np)
* Return an error if the name list won't fit.
*/
PUBLIC const char **
-unpack(struct name *np)
+unpack(struct name *smopts, struct name *np)
{
const char **ap, **begin;
struct name *n;
@@ -547,7 +550,7 @@ unpack(struct name *np)
* the terminating 0 pointer. Additional spots may be needed
* to pass along -f to the host mailer.
*/
- extra = 2;
+ extra = 3 * count(smopts);
extra++;
metoo = value(ENAME_METOO) != NULL;
if (metoo)
@@ -563,6 +566,10 @@ unpack(struct name *np)
*ap++ = "-m";
if (verbose)
*ap++ = "-v";
+ for (/*EMPTY*/; smopts != NULL; smopts = smopts->n_flink)
+ if ((smopts->n_type & GDEL) == 0)
+ *ap++ = smopts->n_name;
+ *ap++ = "--";
for (/*EMPTY*/; n != NULL; n = n->n_flink)
if ((n->n_type & GDEL) == 0)
*ap++ = n->n_name;
Index: src/usr.bin/mail/send.c
diff -u src/usr.bin/mail/send.c:1.37 src/usr.bin/mail/send.c:1.38
--- src/usr.bin/mail/send.c:1.37 Sun Apr 29 19:50:22 2012
+++ src/usr.bin/mail/send.c Tue Dec 16 14:30:24 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: send.c,v 1.37 2012/04/29 23:50:22 christos Exp $ */
+/* $NetBSD: send.c,v 1.38 2014/12/16 19:30:24 christos Exp $ */
/*
* Copyright (c) 1980, 1993
@@ -34,7 +34,7 @@
#if 0
static char sccsid[] = "@(#)send.c 8.1 (Berkeley) 6/6/93";
#else
-__RCSID("$NetBSD: send.c,v 1.37 2012/04/29 23:50:22 christos Exp $");
+__RCSID("$NetBSD: send.c,v 1.38 2014/12/16 19:30:24 christos Exp $");
#endif
#endif /* not lint */
@@ -761,7 +761,7 @@ mail1(struct header *hp, int printheader
goto out;
}
}
- namelist = unpack(cat(hp->h_smopts, to));
+ namelist = unpack(hp->h_smopts, to);
mail2(mtf, namelist);
out:
(void)Fclose(mtf);