<<< Date Index >>>     <<< Thread Index >>>

[PATCH] IMAP authuser support



This patch is not judged to be ready for inclusion, but I'd like it
to be on track for inclusion if it meets standards.

We used to use UW IMAP on our site.  UW has an "authuser" mode, where an
account designated as a mail adminstrator has authority to log in as any
other user on the system.  It achieved this by replacing the username
with "realuser*authuser" in an IMAP LOGIN command, and requiring the
password of the administrative user.  This allowed an administrator to
act on a user's behalf via IMAP.  It's also convenient that you can plug
"realuser*authuser" into just about any mail client and make it work.

We never really used this, because we had filesystem access on the
server and could manipulate users' folders any way we needed to.

Recently we moved our IMAP service to Mirapoint mail appliances.  The
Mirapoint boxes, like many other commercial IMAP devices, also have
an authuser kind of access.  This becomes much more important with a
closed appliance, since you can't reach the filestore directly.

Mirapoint have chosen a different method of doing superuser
authentication, though -- they compile a string of the real user name,
the authuser name, and the [authuser] password, separated by null bytes,
and submit that base64-encoded in an AUTHENTICATE PLAIN negotiation.
This unfortunately doesn't work with any IMAP client without explicit
authuser support in the client.

(I don't know whether this is Mirapoint's own invention, or whether it's
commonly used, but for the present I've called it "Mirapoint style".)

We need this support desperately in some kind of mail tool that lets us
upload folders.  It made sense to patch it into mutt.

This patch implements several things:
    - AUTHENTICATE PLAIN support
    - an $imap_authuser variable, which contains the name of your
      server administrative user account.  If unset, no authuser
      mode is attempted.
    - an $imap_authuser_style variable, which switches between UW
      and Mirapoint authuser methods, so that the same interface can
      be used with either server type.  This should be set to
      "none", "uw", or "mirapoint".

The patch does not autodetect which method the server uses.  I'm not
confident that reliable tests for this can be devised, really.

The patch provides extensibility, if you know of another style of
authuser implementation.


I haven't tested this very much, and I wouldn't say it's ready for
inclusion in anything that's about to be a 1.6 release.  However I would
like to put it out quickly for comments (especially if Brendan has any)
and for expansion, if anyone has anything to add.

Patch is against current cvs.

-- 
 -D.    dgc@xxxxxxxxxxxx        NSIT    University of Chicago
diff -Pur mutt.orig/PATCHES mutt/PATCHES
--- mutt.orig/PATCHES   Tue May 10 21:34:14 2005
+++ mutt/PATCHES        Tue May 10 21:36:32 2005
@@ -0,0 +1 @@
+patch-1.5.9.dgc.authuser.1
diff -Pur mutt.orig/globals.h mutt/globals.h
--- mutt.orig/globals.h Tue May 10 21:34:14 2005
+++ mutt/globals.h      Tue May 10 21:36:32 2005
@@ -54,6 +54,7 @@
 WHERE char *Hostname;
 #ifdef USE_IMAP
 WHERE char *ImapAuthenticators INITVAL (NULL);
+WHERE char *ImapAuthUser INITVAL (NULL);
 WHERE char *ImapDelimChars INITVAL (NULL);
 WHERE char *ImapHeaders;
 WHERE char *ImapHomeNamespace INITVAL (NULL);
@@ -165,6 +166,7 @@
 
 WHERE short ConnectTimeout;
 WHERE short HistSize;
+WHERE short ImapAuthUserStyle;
 WHERE short MenuContext;
 WHERE short PagerContext;
 WHERE short PagerIndexLines;
diff -Pur mutt.orig/imap/Makefile.am mutt/imap/Makefile.am
--- mutt.orig/imap/Makefile.am  Tue May 10 21:34:15 2005
+++ mutt/imap/Makefile.am       Tue May 10 21:36:33 2005
@@ -14,12 +14,13 @@
 AUTHENTICATORS = auth_anon.c auth_cram.c
 endif
 
-EXTRA_DIST = BUGS README TODO auth_anon.c auth_cram.c auth_gss.c auth_sasl.c
+EXTRA_DIST = BUGS README TODO auth_anon.c auth_cram.c auth_gss.c auth_sasl.c 
auth_login.c
 
 INCLUDES = -I$(top_srcdir) -I../intl
 
 noinst_LIBRARIES = libimap.a
 noinst_HEADERS = auth.h imap_private.h message.h
 
-libimap_a_SOURCES = auth.c auth_login.c browse.c command.c imap.c imap.h \
+libimap_a_SOURCES = auth.c auth_login.c auth_plain.c  browse.c command.c \
+       imap.c imap.h \
        message.c utf7.c util.c $(AUTHENTICATORS) $(GSSSOURCES)
diff -Pur mutt.orig/imap/auth.c mutt/imap/auth.c
--- mutt.orig/imap/auth.c       Tue May 10 21:34:15 2005
+++ mutt/imap/auth.c    Tue May 10 21:36:33 2005
@@ -41,9 +41,17 @@
 #ifndef USE_SASL
   { imap_auth_cram_md5, "cram-md5" },
 #endif
+  { imap_auth_plain, "plain" },
   { imap_auth_login, "login" },
 
   { NULL }
+};
+
+const char *AuthUserStyles[DT_AUTHUSER_END+1] = {
+  "none",
+  "uw",
+  "mirapoint",
+  NULL
 };
 
 /* imap_authenticate: Attempt to authenticate using either user-specified
diff -Pur mutt.orig/imap/auth.h mutt/imap/auth.h
--- mutt.orig/imap/auth.h       Tue May 10 21:34:15 2005
+++ mutt/imap/auth.h    Tue May 10 21:36:33 2005
@@ -45,6 +45,7 @@
 imap_auth_res_t imap_auth_cram_md5 (IMAP_DATA* idata, const char* method);
 #endif
 imap_auth_res_t imap_auth_login (IMAP_DATA* idata, const char* method);
+imap_auth_res_t imap_auth_plain (IMAP_DATA* idata, const char* method);
 #ifdef USE_GSS
 imap_auth_res_t imap_auth_gss (IMAP_DATA* idata, const char* method);
 #endif
diff -Pur mutt.orig/imap/auth_login.c mutt/imap/auth_login.c
--- mutt.orig/imap/auth_login.c Tue May 10 21:34:15 2005
+++ mutt/imap/auth_login.c      Tue May 10 21:36:33 2005
@@ -46,7 +46,16 @@
 
   mutt_message _("Logging in...");
 
-  imap_quote_string (q_user, sizeof (q_user), idata->conn->account.user);
+  if (ImapAuthUser && ImapAuthUserStyle == DT_AUTHUSER_UW)
+  {
+    snprintf(buf, sizeof(buf), "%s*%s",
+             idata->conn->account.user, NONULL(ImapAuthUser));
+    imap_quote_string (q_user, sizeof (q_user), buf);
+  }
+  else
+  {
+    imap_quote_string (q_user, sizeof (q_user), idata->conn->account.user);
+  }
   imap_quote_string (q_pass, sizeof (q_pass), idata->conn->account.pass);
 
 #ifdef DEBUG
diff -Pur mutt.orig/imap/auth_plain.c mutt/imap/auth_plain.c
--- mutt.orig/imap/auth_plain.c Wed Dec 31 18:00:00 1969
+++ mutt/imap/auth_plain.c      Tue May 10 21:37:06 2005
@@ -0,0 +1,124 @@
+/*
+ * Portions copyright (C) 1999-2000 Brendan Cully <brendan@xxxxxxxxxx>
+ * Portions copyright (C) 2005      David Champion <dgc@xxxxxxxxxxxx>
+ * 
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */ 
+
+/* AUTHENTICATE PLAIN code */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "mutt.h"
+#include "imap_private.h"
+#include "auth.h"
+
+
+/* imap_auth_plain: AUTHENTICATE PLAIN support */
+imap_auth_res_t imap_auth_plain (IMAP_DATA* idata, const char* method)
+{
+  char buf[STRING], b64[STRING];
+  int buflen, max;
+  int rc;
+  char *authuser = NULL;
+  char user[SHORT_STRING];
+
+  if (!mutt_bit_isset (idata->capabilities, AUTH_PLAIN))
+    return IMAP_AUTH_UNAVAIL;
+
+  if (mutt_account_getuser (&idata->conn->account))
+    return IMAP_AUTH_FAILURE;
+  if (mutt_account_getpass (&idata->conn->account))
+    return IMAP_AUTH_FAILURE;
+  
+
+  /* now begin login */
+  mutt_message _("Authenticating (PLAIN)...");
+
+  imap_cmd_start (idata, "AUTHENTICATE PLAIN");
+
+  /* expect a null continuation response ("+") */
+  do
+    rc = imap_cmd_step (idata);
+  while (rc == IMAP_CMD_CONTINUE);
+
+  if (rc != IMAP_CMD_RESPOND)
+  {
+    dprint (2, (debugfile, "Invalid response from server: %s\n", 
idata->cmd.buf));
+    goto bail;
+  }
+
+  /* now start the security context initialisation loop... */
+  dprint (2, (debugfile, "Creating/encoding PLAIN credentials\n"));
+
+  strncpy(user, idata->conn->account.user, sizeof(user)-1);
+
+  if (ImapAuthUserStyle == DT_AUTHUSER_MIRA)
+  {
+    authuser = ImapAuthUser;
+    if (!authuser)
+    {
+      /* UW style is "realuser*authuser".
+       * See if we can extract an AuthUser from the current username.
+       */
+      if ((authuser = strchr(user, '*')))
+       *authuser++ = '\0';
+    }
+  }
+
+  max = sizeof(buf) - 2 - 1;
+  buflen = 0;
+  strncpy(&buf[buflen], user, max-buflen);
+  buflen += strlen(&buf[buflen]);
+  buf[buflen++] = '\0';
+
+  if (ImapAuthUserStyle == DT_AUTHUSER_MIRA && authuser)
+  {
+    strncpy(&buf[buflen], authuser, max - buflen);
+    buflen += strlen(&buf[buflen]);
+    buf[buflen++] = '\0';
+  }
+
+  strncpy(&buf[buflen], idata->conn->account.pass, max - buflen);
+  buflen += strlen(&buf[buflen]);
+
+  mutt_to_base64 ((unsigned char *)b64, (unsigned char *)buf,
+                 buflen, sizeof(b64) - 2);
+  safe_strcat (b64, sizeof (b64), "\r\n");
+  mutt_socket_write (idata->conn, b64);
+
+  do
+    rc = imap_cmd_step (idata);
+  while (rc == IMAP_CMD_CONTINUE);
+
+  if (rc != IMAP_AUTH_SUCCESS)
+    goto bail;
+  return IMAP_AUTH_SUCCESS;
+
+#if 0
+ err_abort_cmd:
+  mutt_socket_write (idata->conn, "*\r\n");
+  do
+    rc = imap_cmd_step (idata);
+  while (rc == IMAP_CMD_CONTINUE);
+#endif
+
+ bail:
+  mutt_error _("PLAIN authentication failed.");
+  mutt_sleep (2);
+  return IMAP_AUTH_FAILURE;
+}
diff -Pur mutt.orig/imap/command.c mutt/imap/command.c
--- mutt.orig/imap/command.c    Tue May 10 21:34:15 2005
+++ mutt/imap/command.c Tue May 10 21:36:33 2005
@@ -35,6 +35,7 @@
 
 #define IMAP_CMD_BUFSIZE 512
 
+#define static
 /* forward declarations */
 static void cmd_handle_fatal (IMAP_DATA* idata);
 static int cmd_handle_untagged (IMAP_DATA* idata);
@@ -53,6 +54,7 @@
   "AUTH=CRAM-MD5",
   "AUTH=GSSAPI",
   "AUTH=ANONYMOUS",
+  "AUTH=PLAIN",
   "STARTTLS",
   "LOGINDISABLED",
 
diff -Pur mutt.orig/imap/imap.h mutt/imap/imap.h
--- mutt.orig/imap/imap.h       Tue May 10 21:34:15 2005
+++ mutt/imap/imap.h    Tue May 10 21:36:33 2005
@@ -24,6 +24,15 @@
 #include "browser.h"
 #include "mailbox.h"
 
+/* Constants for ImapAuthUserStyle */
+enum {
+  DT_AUTHUSER_NONE = 0,
+  DT_AUTHUSER_UW,
+  DT_AUTHUSER_MIRA,
+  DT_AUTHUSER_END
+};
+extern const char *AuthUserStyles[DT_AUTHUSER_END+1];
+
 /* -- data structures -- */
 typedef struct
 {
diff -Pur mutt.orig/imap/imap_private.h mutt/imap/imap_private.h
--- mutt.orig/imap/imap_private.h       Tue May 10 21:34:15 2005
+++ mutt/imap/imap_private.h    Tue May 10 21:36:33 2005
@@ -110,6 +110,7 @@
   ACRAM_MD5,                   /* RFC 2195: CRAM-MD5 authentication */
   AGSSAPI,                     /* RFC 1731: GSSAPI authentication */
   AUTH_ANON,                   /* AUTH=ANONYMOUS */
+  AUTH_PLAIN,                  /* AUTH=PLAIN */
   STARTTLS,                    /* RFC 2595: STARTTLS */
   LOGINDISABLED,               /*           LOGINDISABLED */
 
diff -Pur mutt.orig/init.c mutt/init.c
--- mutt.orig/init.c    Tue May 10 21:34:14 2005
+++ mutt/init.c Tue May 10 21:36:32 2005
@@ -1101,6 +1101,9 @@
     case DT_NUM:
     case DT_SORT:
     case DT_MAGIC:
+#ifdef USE_IMAP
+    case DT_AUTHUSER:
+#endif
       *((short *) p->data) = p->init;
       break;
     case DT_RX:
@@ -1574,6 +1577,45 @@
        break;
       }
     }
+#ifdef USE_IMAP
+    else if (DTYPE (MuttVars[idx].type) == DT_AUTHUSER)
+    {
+      short *ptr = (short *) MuttVars[idx].data;
+
+      if (query)
+      {
+       snprintf (err->data, err->dsize, "%s=%s",
+                 MuttVars[idx].option, AuthUserStyles[*ptr]);
+       r = 0;
+       break;
+      }
+
+      if (*s->dptr == '=')
+      {
+       s->dptr++;
+       mutt_extract_token (tmp, s, 0);
+       for (r = DT_AUTHUSER_NONE; r < DT_AUTHUSER_END; r++)
+       {
+         if (ascii_strcasecmp (AuthUserStyles[r], tmp->data) == 0)
+         {
+           *ptr = r;
+           r = 0;
+           break;
+         }
+       }
+       if (r == DT_AUTHUSER_END)
+       {
+         snprintf (err->data, err->dsize, _("%s: invalid authuser style"), 
tmp->data);
+         r = -1;
+         break;
+       }
+      }
+      else
+      {
+       r = -1;
+      }
+    }
+#endif
     else
     {
       snprintf (err->data, err->dsize, _("%s: unknown type"), 
MuttVars[idx].option);
diff -Pur mutt.orig/init.h mutt/init.h
--- mutt.orig/init.h    Tue May 10 21:34:15 2005
+++ mutt/init.h Tue May 10 21:36:33 2005
@@ -25,6 +25,10 @@
 
 #include "buffy.h"
 
+#ifdef USE_IMAP
+# include "imap/imap.h"
+#endif
+
 #ifndef _MAKEDOC
 #define DT_MASK                0x0f
 #define DT_BOOL                1 /* boolean option */
@@ -37,6 +41,7 @@
 #define DT_MAGIC       8 /* mailbox type */
 #define DT_SYN         9 /* synonym for another variable */
 #define DT_ADDR               10 /* e-mail address */
+#define DT_AUTHUSER    11 /* IMAP authuser style */
 
 #define DTYPE(x) ((x) & DT_MASK)
 
@@ -793,6 +798,23 @@
   ** the previous methods are unavailable. If a method is available but
   ** authentication fails, mutt will not connect to the IMAP server.
   */
+  { "imap_authuser",   DT_STR, R_NONE, UL &ImapAuthUser, UL 0 },
+  /*
+  ** .pp
+  ** Sets the authuser ("superuser") username for IMAP access.  If unset,
+  ** do not use authuser mode.  If set, attempt authuser login using the
+  ** mode specified in $$imap_authuser_style.
+  */
+  { "imap_authuser_style",     DT_AUTHUSER, R_NONE, UL &ImapAuthUserStyle, 
DT_AUTHUSER_UW },
+  /*
+  ** .pp
+  ** Indicates the style of authuser authentication to use.  UW style
+  ** is to use "realuser*authuser" as the username and the authuser password
+  ** as the password, with any login method.  Mirapoint style is to base-64
+  ** encode a string of "realuser[@domain]\0authuser\0authuserpass", and to
+  ** submit that in AUTHENTICATE PLAIN.  If no $$imap_authuser is set, this
+  ** has no effect.
+  **/
   { "imap_delim_chars",                DT_STR, R_NONE, UL &ImapDelimChars, UL 
"/." },
   /*
   ** .pp