[bug #4302] errno is not always an extern int
[mmh] / uip / inc.c
index fd7b55e..af8180d 100644 (file)
--- a/uip/inc.c
+++ b/uip/inc.c
@@ -3,6 +3,10 @@
  * inc.c -- incorporate messages from a maildrop into a folder
  *
  * $Id$
+ *
+ * This code is Copyright (c) 2002, by the authors of nmh.  See the
+ * COPYRIGHT file in the root directory of the nmh distribution for
+ * complete copyright information.
  */
 
 #ifdef MAILGROUP
  *
  * Fri Feb  7 16:04:57 PST 1992                John Romine <bug-mh@ics.uci.edu>
  *   NB: I'm not 100% sure that this setgid stuff is secure even now.
+ *
+ * See the *GROUPPRIVS() macros later. I'm reasonably happy with the setgid
+ * attribute. Running setuid root is probably not a terribly good idea, though.
+ *       -- Peter Maydell <pmaydell@chiark.greenend.org.uk>, 04/1998
+ *
+ * Peter Maydell's patch slightly modified for nmh 0.28-pre2.
+ * Ruud de Rooij <ruud@debian.org>  Wed, 22 Jul 1998 13:24:22 +0200
  */
 #endif
 
@@ -32,8 +43,8 @@
 #include <h/fmt_scan.h>
 #include <h/scansbr.h>
 #include <h/signals.h>
-#include <zotnet/tws/tws.h>
-#include <zotnet/mts/mts.h>
+#include <h/tws.h>
+#include <h/mts.h>
 #include <errno.h>
 #include <signal.h>
 
 # define KPOPminc(a)  0
 #endif
 
+#ifndef CYRUS_SASL
+# define SASLminc(a) (a)
+#else
+# define SASLminc(a)  0
+#endif
+
 static struct swit switches[] = {
 #define        AUDSW                      0
     { "audit audit-file", 0 },
@@ -105,16 +122,18 @@ static struct swit switches[] = {
 #define VERSIONSW                 20
     { "version", 0 },
 #define        HELPSW                    21
-    { "help", 4 },
+    { "help", 0 },
 #define SNOOPSW                   22
     { "snoop", -5 },
 #define KPOPSW                    23
     { "kpop", KPOPminc (-4) },
+#define SASLSW                    24
+    { "sasl", SASLminc(-4) },
+#define SASLMECHSW                25
+    { "saslmech", SASLminc(-8) },
     { NULL, 0 }
 };
 
-extern int errno;
-
 /*
  * flags for the mail source
  */
@@ -138,13 +157,63 @@ static int pd = NOTOK;
 static FILE *pf = NULL;
 #endif /* POP */
 
+/* This is an attempt to simplify things by putting all the
+ * privilege ops into macros.
+ * *GROUPPRIVS() is related to handling the setgid MAIL property,
+ * and only applies if MAILGROUP is defined.
+ * *USERPRIVS() is related to handling the setuid root property,
+ * and only applies if POP is defined [why does POP => setuid root?]
+ * Basically, SAVEGROUPPRIVS() is called right at the top of main()
+ * to initialise things, and then DROPGROUPPRIVS() and GETGROUPPRIVS()
+ * do the obvious thing. TRYDROPGROUPPRIVS() has to be safe to call
+ * before DROPUSERPRIVS() is called [this is needed because setgid()
+ * sets both effective and real uids if euid is root.]
+ *
+ * There's probably a better implementation if we're allowed to use
+ * BSD-style setreuid() rather than using POSIX saved-ids.
+ * Anyway, if you're euid root it's a bit pointless to drop the group
+ * permissions...
+ *
+ * I'm pretty happy that the security is good provided we aren't setuid root.
+ * The only things we trust with group=mail privilege are lkfopen()
+ * and lkfclose().
+ */
 
 /*
  * For setting and returning to "mail" gid
  */
 #ifdef MAILGROUP
 static int return_gid;
+#ifndef POP
+/* easy case; we're not setuid root, so can drop group privs
+ * immediately.
+ */
+#define TRYDROPGROUPPRIVS() DROPGROUPPRIVS()
+#else /* POP ie we are setuid root */
+#define TRYDROPGROUPPRIVS() \
+if (geteuid() != 0) DROPGROUPPRIVS()
 #endif
+#define DROPGROUPPRIVS() setgid(getgid())
+#define GETGROUPPRIVS() setgid(return_gid)
+#define SAVEGROUPPRIVS() return_gid = getegid()
+#else
+/* define *GROUPPRIVS() as null; this avoids having lots of "#ifdef MAILGROUP"s */
+#define TRYDROPGROUPPRIVS()
+#define DROPGROUPPRIVS()
+#define GETGROUPPRIVS()
+#define SAVEGROUPPRIVS()
+#endif /* not MAILGROUP */
+
+#ifdef POP
+#define DROPUSERPRIVS() setuid(getuid())
+#else
+#define DROPUSERPRIVS()
+#endif
+
+/* these variables have to be globals so that done() can correctly clean up the lockfile */
+static int locked = 0;
+static char *newmail;
+static FILE *in;
 
 /*
  * prototypes
@@ -163,21 +232,23 @@ int
 main (int argc, char **argv)
 {
     int chgflag = 1, trnflag = 1;
-    int noisy = 1, width = 0, locked = 0;
+    int noisy = 1, width = 0;
     int rpop, i, hghnum, msgnum;
-    int kpop = 0;
+    int kpop = 0, sasl = 0;
     char *cp, *maildir, *folder = NULL;
     char *format = NULL, *form = NULL;
-    char *newmail, *host = NULL, *user = NULL;
-    char *audfile = NULL, *from = NULL;
+    char *host = NULL, *user = NULL;
+    char *audfile = NULL, *from = NULL, *saslmech = NULL;
     char buf[BUFSIZ], **argp, *nfs, **arguments;
     struct msgs *mp;
     struct stat st, s1;
-    FILE *in, *aud = NULL;
+    FILE *aud = NULL;
+    char       b[MAXPATHLEN + 1];
 
 #ifdef POP
     int nmsgs, nbytes, p = 0;
     char *pass = NULL;
+    char *MAILHOST_env_variable;
 #endif
 
 #ifdef MHE
@@ -186,9 +257,14 @@ main (int argc, char **argv)
 
 #ifdef HESIOD
     struct hes_postoffice *po;
-    char *tmphost;
 #endif
 
+/* absolutely the first thing we do is save our privileges,
+ * and drop them if we can.
+ */
+    SAVEGROUPPRIVS();
+    TRYDROPGROUPPRIVS();
+
 #ifdef LOCALE
     setlocale(LC_ALL, "");
 #endif
@@ -202,7 +278,6 @@ main (int argc, char **argv)
     argp = arguments;
 
 #ifdef POP
-# ifdef HESIOD
     /*
      * Scheme is:
      *        use MAILHOST environment variable if present,
@@ -210,8 +285,9 @@ main (int argc, char **argv)
      *  If that fails, use the default (if any)
      *  provided by mts.conf in mts_init()
      */
-    if ((tmphost = getenv("MAILHOST")) != NULL)
-       pophost = tmphost;
+    if ((MAILHOST_env_variable = getenv("MAILHOST")) != NULL)
+       pophost = MAILHOST_env_variable;
+# ifdef HESIOD
     else if ((po = hes_getmailhost(getusername())) != NULL &&
             strcmp(po->po_type, "POP") == 0)
        pophost = po->po_host;
@@ -358,6 +434,15 @@ main (int argc, char **argv)
            case SNOOPSW:
                snoop++;
                continue;
+       
+           case SASLSW:
+               sasl++;
+               continue;
+       
+           case SASLMECHSW:
+               if (!(saslmech = *argp++) || *saslmech == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
            }
        }
        if (*cp == '+' || *cp == '@') {
@@ -370,18 +455,19 @@ main (int argc, char **argv)
        }
     }
 
-#ifdef MAILGROUP
-    return_gid = getegid();  /* Save effective gid, assuming we'll use it */
-    setgid(getgid());        /* Turn off extraordinary privileges         */
-#endif /* MAILGROUP */
-
+    /* NOTE: above this point you should use TRYDROPGROUPPRIVS(),
+     * not DROPGROUPPRIVS().
+     */
 #ifdef POP
     if (host && !*host)
        host = NULL;
     if (from || !host || rpop <= 0)
-       setuid (getuid ());
+       DROPUSERPRIVS();
 #endif /* POP */
 
+    /* guarantee dropping group priveleges; we might not have done so earlier */
+    DROPGROUPPRIVS();
+
     /*
      * Where are we getting the new mail?
      */
@@ -402,7 +488,10 @@ main (int argc, char **argv)
     if (inc_type == INC_POP) {
        if (user == NULL)
            user = getusername ();
-       if (kpop || ( rpop > 0))
+       if ( strcmp( POPSERVICE, "kpop" ) == 0 ) {
+           kpop = 1;
+       }
+       if (kpop || sasl || ( rpop > 0))
            pass = getusername ();
        else
            ruserpass (host, &user, &pass);
@@ -410,7 +499,8 @@ main (int argc, char **argv)
        /*
         * initialize POP connection
         */
-       if (pop_init (host, user, pass, snoop, kpop ? 1 : rpop, kpop) == NOTOK)
+       if (pop_init (host, user, pass, snoop, kpop ? 1 : rpop, kpop,
+                     sasl, saslmech) == NOTOK)
            adios (NULL, "%s", response);
 
        /* Check if there are any messages */
@@ -418,7 +508,7 @@ main (int argc, char **argv)
            adios (NULL, "%s", response);
 
        if (rpop > 0)
-           setuid (getuid ());
+           DROPUSERPRIVS();
        if (nmsgs == 0) {
            pop_quit();
            adios (NULL, "no mail to incorporate");
@@ -489,17 +579,11 @@ go_to_it:
                SIGNAL (SIGTERM, SIG_IGN);
            }
 
-#ifdef MAILGROUP
-           setgid(return_gid); /* Reset gid to lock mail file */
-#endif /* MAILGROUP */
-
-           /* lock and fopen the mail spool */
-           if ((in = lkfopen (newmail, "r")) == NULL)
+            GETGROUPPRIVS();       /* Reset gid to lock mail file */
+            in = lkfopen (newmail, "r");
+            DROPGROUPPRIVS();
+            if (in == NULL)
                adios (NULL, "unable to lock and fopen %s", newmail);
-
-#ifdef MAILGROUP
-           setgid(getgid());   /* Return us to normal privileges */
-#endif /* MAILGROUP */
            fstat (fileno(in), &s1);
        } else {
            trnflag = 0;
@@ -508,9 +592,8 @@ go_to_it:
        }
     }
 
-#ifdef MAILGROUP
-    setgid(getgid());  /* Return us to normal privileges */
-#endif /* MAILGROUP */
+    /* This shouldn't be necessary but it can't hurt. */
+    DROPGROUPPRIVS();
 
     if (audfile) {
        if ((i = stat (audfile, &st)) == NOTOK)
@@ -718,7 +801,6 @@ go_to_it:
            /* link message into folder */
            newmsg = folder_addmsg(mp, tmpfilenam);
 #endif
-
            /* create scanline for new message */
            switch (i = scan (in, msgnum + 1, msgnum + 1, nfs, width,
                              msgnum == hghnum && chgflag, 1, NULL, 0L, noisy)) {
@@ -742,6 +824,13 @@ go_to_it:
 
            case SCNMSG:
            case SCNENC:
+               /*
+                *  Run the external program hook on the message.
+                */
+
+               (void)snprintf(b, sizeof (b), "%s/%d", maildir, msgnum + 1);
+               (void)ext_hook("add-hook", b, (char *)0);
+
                if (aud)
                    fputs (scanl, aud);
 #ifdef MHE
@@ -753,6 +842,9 @@ go_to_it:
 
                msgnum++;
                mp->hghmsg++;
+               mp->nummsg++;
+               if (mp->lowmsg == 0) mp->lowmsg = 1;
+
                clear_msg_flags (mp, msgnum);
                set_exists (mp, msgnum);
                set_unseen (mp, msgnum);
@@ -769,19 +861,11 @@ go_to_it:
     if (i < 0) {               /* error */
 #endif
        if (locked) {
-#ifdef MAILGROUP
-           /* Be sure we can unlock mail file */
-           setgid(return_gid);
-#endif /* MAILGROUP */
-
-           lkfclose (in, newmail);
-
-#ifdef MAILGROUP
-           /* And then return us to normal privileges */
-           setgid(getgid());
-#endif /* MAILGROUP */
+            GETGROUPPRIVS();      /* Be sure we can unlock mail file */
+            (void) lkfclose (in, newmail); in = NULL;
+            DROPGROUPPRIVS();    /* And then return us to normal privileges */
        } else {
-           fclose (in);
+           fclose (in); in = NULL;
        }
        adios (NULL, "failed");
     }
@@ -840,17 +924,11 @@ go_to_it:
      */
     if (inc_type == INC_FILE) {
        if (locked) {
-#ifdef MAILGROUP
-           setgid(return_gid); /* Be sure we can unlock mail file */
-#endif /* MAILGROUP */
-
-           lkfclose (in, newmail);
-
-#ifdef MAILGROUP
-           setgid(getgid());   /* And then return us to normal privileges */
-#endif /* MAILGROUP */
+           GETGROUPPRIVS();        /* Be sure we can unlock mail file */
+           (void) lkfclose (in, newmail); in = NULL;
+           DROPGROUPPRIVS();       /* And then return us to normal privileges */
        } else {
-           fclose (in);
+           fclose (in); in = NULL;
        }
     }
 
@@ -894,17 +972,24 @@ cpymsg (FILE *in, FILE *out)
 #endif /* if 0 */
 
 
-#ifdef POP
 int
 done (int status)
 {
+#ifdef POP
     if (packfile && pd != NOTOK)
        mbx_close (packfile, pd);
-
+#endif /* POP */
+    if (locked)
+    {
+        GETGROUPPRIVS();
+        lkfclose(in, newmail);
+        DROPGROUPPRIVS();
+    }
     exit (status);
     return 1;  /* dead code to satisfy the compiler */
 }
 
+#ifdef POP
 static int
 pop_action (char *s)
 {