turn our local enqueuer setgid _smtpq and restrict access to offline queue,
authorgilles <gilles@openbsd.org>
Fri, 9 Oct 2015 14:37:38 +0000 (14:37 +0000)
committergilles <gilles@openbsd.org>
Fri, 9 Oct 2015 14:37:38 +0000 (14:37 +0000)
the enqueuer will revoke group and regain real gid right after mkstemp.

this would have prevented the symlink/hardlink attacks against offline, and
it will avoid having to deal with new ways users can mess with it.

ok eric@, ok millert@

usr.sbin/smtpd/enqueue.c
usr.sbin/smtpd/queue_backend.c
usr.sbin/smtpd/smtpctl.c
usr.sbin/smtpd/smtpctl/Makefile
usr.sbin/smtpd/smtpd-defines.h
usr.sbin/smtpd/smtpd.h

index 004a040..cd65e34 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: enqueue.c,v 1.96 2015/10/06 06:04:46 gilles Exp $     */
+/*     $OpenBSD: enqueue.c,v 1.97 2015/10/09 14:37:38 gilles Exp $     */
 
 /*
  * Copyright (c) 2005 Henning Brauer <henning@bulabula.org>
@@ -28,6 +28,7 @@
 #include <err.h>
 #include <errno.h>
 #include <event.h>
+#include <grp.h>
 #include <imsg.h>
 #include <inttypes.h>
 #include <pwd.h>
@@ -35,7 +36,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sysexits.h>
 #include <time.h>
 #include <unistd.h>
 #include <limits.h>
@@ -54,10 +54,10 @@ static void rcpt_add(char *);
 static int open_connection(void);
 static int get_responses(FILE *, int);
 static int send_line(FILE *, int, char *, ...);
-static int enqueue_offline(int, char *[], FILE *);
+static int enqueue_offline(int, char *[], FILE *, FILE *);
 static int savedeadletter(struct passwd *, FILE *);
 
-extern int srv_connect(void);
+extern int srv_connected(void);
 
 enum headerfields {
        HDR_NONE,
@@ -163,7 +163,7 @@ qp_encoded_write(FILE *fp, char *buf, size_t len)
 }
 
 int
-enqueue(int argc, char *argv[])
+enqueue(int argc, char *argv[], FILE *ofp)
 {
        int                      i, ch, tflag = 0;
        char                    *fake_from = NULL, *buf;
@@ -283,11 +283,11 @@ enqueue(int argc, char *argv[])
        /* init session */
        rewind(fp);
 
-       /* try to connect */
+       /* check if working in offline mode */
        /* If the server is not running, enqueue the message offline */
 
-       if (!srv_connect())
-               return (enqueue_offline(save_argc, save_argv, fp));
+       if (!srv_connected())
+               return (enqueue_offline(save_argc, save_argv, fp, ofp));
 
        if ((msg.fd = open_connection()) == -1)
                errx(EX_UNAVAILABLE, "server too busy");
@@ -804,59 +804,32 @@ open_connection(void)
 }
 
 static int
-enqueue_offline(int argc, char *argv[], FILE *ifile)
+enqueue_offline(int argc, char *argv[], FILE *ifile, FILE *ofile)
 {
-       char     path[PATH_MAX];
-       FILE    *fp;
-       int      i, fd, ch;
-       mode_t   omode;
-
-       if (ckdir(PATH_SPOOL PATH_OFFLINE, 01777, 0, 0, 0) == 0)
-               errx(EX_UNAVAILABLE, "error in offline directory setup");
-
-       if (! bsnprintf(path, sizeof(path), "%s%s/%lld.XXXXXXXXXX", PATH_SPOOL,
-               PATH_OFFLINE, (long long int) time(NULL)))
-               err(EX_UNAVAILABLE, "snprintf");
-
-       omode = umask(07077);
-       if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
-               warn("cannot create temporary file %s", path);
-               if (fd != -1)
-                       unlink(path);
-               exit(EX_UNAVAILABLE);
-       }
-       umask(omode);
-
-       if (fchmod(fd, 0600) == -1) {
-               unlink(path);
-               exit(EX_SOFTWARE);
-       }
+       int     i, ch;
 
        for (i = 1; i < argc; i++) {
                if (strchr(argv[i], '|') != NULL) {
                        warnx("%s contains illegal character", argv[i]);
-                       unlink(path);
                        exit(EX_SOFTWARE);
                }
-               fprintf(fp, "%s%s", i == 1 ? "" : "|", argv[i]);
+               fprintf(ofile, "%s%s", i == 1 ? "" : "|", argv[i]);
        }
 
-       fprintf(fp, "\n");
+       fprintf(ofile, "\n");
 
        while ((ch = fgetc(ifile)) != EOF)
-               if (fputc(ch, fp) == EOF) {
+               if (fputc(ch, ofile) == EOF) {
                        warn("write error");
-                       unlink(path);
                        exit(EX_UNAVAILABLE);
                }
 
        if (ferror(ifile)) {
                warn("read error");
-               unlink(path);
                exit(EX_UNAVAILABLE);
        }
 
-       fclose(fp);
+       fclose(ofile);
 
        return (EX_TEMPFAIL);
 }
index 1acb938..ced1c81 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: queue_backend.c,v 1.55 2015/01/20 17:37:54 deraadt Exp $      */
+/*     $OpenBSD: queue_backend.c,v 1.56 2015/10/09 14:37:38 gilles Exp $       */
 
 /*
  * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
@@ -27,6 +27,7 @@
 #include <errno.h>
 #include <event.h>
 #include <fcntl.h>
+#include <grp.h>
 #include <imsg.h>
 #include <limits.h>
 #include <inttypes.h>
@@ -113,12 +114,17 @@ int
 queue_init(const char *name, int server)
 {
        struct passwd   *pwq;
+       struct group    *gr;
        int              r;
 
        pwq = getpwnam(SMTPD_QUEUE_USER);
        if (pwq == NULL)
                errx(1, "unknown user %s", SMTPD_QUEUE_USER);
 
+       gr = getgrnam(SMTPD_QUEUE_GROUP);
+       if (gr == NULL)
+               errx(1, "unknown group %s", SMTPD_QUEUE_GROUP);
+       
        tree_init(&evpcache_tree);
        TAILQ_INIT(&evpcache_list);
 
@@ -134,7 +140,7 @@ queue_init(const char *name, int server)
        if (server) {
                if (ckdir(PATH_SPOOL, 0711, 0, 0, 1) == 0)
                        errx(1, "error in spool directory setup");
-               if (ckdir(PATH_SPOOL PATH_OFFLINE, 01777, 0, 0, 1) == 0)
+               if (ckdir(PATH_SPOOL PATH_OFFLINE, 0770, 0, gr->gr_gid, 1) == 0)
                        errx(1, "error in offline directory setup");
                if (ckdir(PATH_SPOOL PATH_PURGE, 0700, pwq->pw_uid, 0, 1) == 0)
                        errx(1, "error in purge directory setup");
index eeb9e3d..b53d2c2 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: smtpctl.c,v 1.127 2015/10/06 06:07:28 gilles Exp $    */
+/*     $OpenBSD: smtpctl.c,v 1.128 2015/10/09 14:37:38 gilles Exp $    */
 
 /*
  * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
@@ -27,6 +27,7 @@
 #include <sys/tree.h>
 #include <sys/un.h>
 #include <sys/wait.h>
+#include <sys/stat.h>
 
 #include <err.h>
 #include <errno.h>
@@ -52,6 +53,7 @@
 #define PATH_ENCRYPT   "/usr/bin/encrypt"
 
 int srv_connect(void);
+int srv_connected(void);
 
 void usage(void);
 static void show_queue_envelope(struct envelope *, int);
@@ -64,6 +66,7 @@ static int is_gzip_fp(FILE *);
 static int is_encrypted_fp(FILE *);
 static int is_encrypted_buffer(const char *);
 static int is_gzip_buffer(const char *);
+static FILE *offline_file(void);
 
 extern char    *__progname;
 int             sendmail;
@@ -126,6 +129,41 @@ srv_connect(void)
        return (1);
 }
 
+int
+srv_connected(void)
+{
+       return ibuf != NULL ? 1 : 0;
+}
+
+FILE *
+offline_file(void)
+{
+       char    path[PATH_MAX];
+       mode_t  omode;
+       int     fd;
+       FILE   *fp;
+
+       if (! bsnprintf(path, sizeof(path), "%s%s/%lld.XXXXXXXXXX", PATH_SPOOL,
+               PATH_OFFLINE, (long long int) time(NULL)))
+               err(EX_UNAVAILABLE, "snprintf");
+
+       omode = umask(07077);
+       if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
+               if (fd != -1)
+                       unlink(path);
+               err(EX_UNAVAILABLE, "cannot create temporary file %s", path);
+       }
+       umask(omode);
+
+       if (fchmod(fd, 0600) == -1) {
+               unlink(path);
+               err(EX_SOFTWARE, "fchmod");
+       }
+
+       return fp;
+}
+
+
 static void
 srv_flush(void)
 {
@@ -883,17 +921,29 @@ do_show_mta_block(int argc, struct parameter *argv)
 int
 main(int argc, char **argv)
 {
-       char    *argv_mailq[] = { "show", "queue", NULL };
+       gid_t            gid;
+       char            *argv_mailq[] = { "show", "queue", NULL };
+       FILE            *offlinefp = NULL;
 
+       gid = getgid();
        if (strcmp(__progname, "sendmail") == 0 ||
            strcmp(__progname, "send-mail") == 0) {
+               if (!srv_connect())
+                       offlinefp = offline_file();
+
+               if (setresgid(gid, gid, gid) == -1)
+                       err(1, "setresgid");
+
                sendmail = 1;
-               return (enqueue(argc, argv));
+               return (enqueue(argc, argv, offlinefp));
        }
 
        if (geteuid())
                errx(1, "need root privileges");
 
+       if (setresgid(gid, gid, gid) == -1)
+               err(1, "setresgid");
+
        cmd_install("encrypt",                  do_encrypt);
        cmd_install("encrypt <str>",            do_encrypt);
        cmd_install("pause mta from <addr> for <str>", do_block_mta);
index d83abc1..08950cd 100644 (file)
@@ -1,11 +1,12 @@
-#      $OpenBSD: Makefile,v 1.38 2015/10/06 00:30:32 deraadt Exp $
+#      $OpenBSD: Makefile,v 1.39 2015/10/09 14:37:38 gilles Exp $
 
 .PATH:         ${.CURDIR}/..
 
 PROG=  smtpctl
 BINOWN=        root
+BINGRP=        _smtpq
 
-BINMODE?=555
+BINMODE?=2555
 
 BINDIR=        /usr/sbin
 MAN=   smtpctl.8
index 8704056..4c3be1f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: smtpd-defines.h,v 1.4 2015/01/20 17:37:54 deraadt Exp $       */
+/*     $OpenBSD: smtpd-defines.h,v 1.5 2015/10/09 14:37:38 gilles Exp $        */
 
 /*
  * Copyright (c) 2013 Gilles Chehade <gilles@poolp.org>
 
 #define SMTPD_USER             "_smtpd"
 #define PATH_CHROOT            "/var/empty"
-#define SMTPD_QUEUE_USER        "_smtpq"
+#define SMTPD_QUEUE_USER       "_smtpq"
+#define SMTPD_QUEUE_GROUP      "_smtpq"
 #define PATH_SPOOL             "/var/spool/smtpd"
 
 #define TAG_CHAR               '+'
+
+
+/* sendmail compat */
+
+#define        EX_OK                   0
+#define        EX_NOHOST               68
+#define        EX_UNAVAILABLE          69
+#define        EX_SOFTWARE             70
+#define        EX_TEMPFAIL             75
index 30c192c..5441f12 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: smtpd.h,v 1.475 2015/09/07 15:36:53 gilles Exp $      */
+/*     $OpenBSD: smtpd.h,v 1.476 2015/10/09 14:37:38 gilles Exp $      */
 
 /*
  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -1125,7 +1125,7 @@ void dns_imsg(struct mproc *, struct imsg *);
 
 
 /* enqueue.c */
-int             enqueue(int, char **);
+int             enqueue(int, char **, FILE *);
 
 
 /* envelope.c */