]> granicus.if.org Git - neomutt/commitdiff
Implement SASL's PLAIN mechanism as a standalone authenticator
authorPietro Cerutti <gahr@gahr.ch>
Tue, 15 Nov 2016 17:10:15 +0000 (17:10 +0000)
committerRichard Russon <rich@flatcap.org>
Thu, 19 Jan 2017 17:02:58 +0000 (17:02 +0000)
Closes: #232
Makefile.am
imap/Makefile.am
imap/auth.c
imap/auth.h
imap/auth_plain.c [new file with mode: 0644]
init.h
mutt_sasl_plain.c [new file with mode: 0644]
mutt_sasl_plain.h [new file with mode: 0644]
smtp.c

index 15c7ef0a7baa77a772756b39a4056951077f3d58..09be1a23239d406b7e7bb1c88160b98bf0cc739f 100644 (file)
@@ -41,7 +41,7 @@ mutt_SOURCES = \
        edit.c enter.c flags.c init.c filter.c from.c \
        getdomain.c group.c \
        handler.c hash.c hdrline.c headers.c help.c hook.c keymap.c \
-       main.c mbox.c menu.c mh.c mx.c pager.c parse.c pattern.c \
+       main.c mbox.c menu.c mh.c mutt_sasl_plain.c mx.c pager.c parse.c pattern.c \
        postpone.c query.c recvattach.c recvcmd.c \
        rfc822.c rfc1524.c rfc2047.c rfc2231.c rfc3676.c \
        score.c send.c sendlib.c signal.c sort.c \
@@ -78,8 +78,8 @@ EXTRA_DIST = COPYRIGHT LICENSE.md OPS OPS.PGP OPS.CRYPT OPS.SMIME TODO UPDATING
        attach.h buffy.h charset.h compress.h copy.h crypthash.h dotlock.h functions.h gen_defs \
        globals.h hash.h history.h init.h keymap.h mutt_crypt.h \
        mailbox.h mapping.h md5.h mime.h mutt.h mutt_curses.h mutt_menu.h \
-       mutt_regex.h mutt_sasl.h mutt_socket.h mutt_ssl.h mutt_tunnel.h \
-       mx.h pager.h pgp.h pop.h protos.h rfc1524.h rfc2047.h \
+       mutt_regex.h mutt_sasl.h mutt_sasl_plain.h mutt_socket.h mutt_ssl.h \
+       mutt_tunnel.h mx.h pager.h pgp.h pop.h protos.h rfc1524.h rfc2047.h \
        rfc2231.h rfc822.h rfc3676.h sha1.h sort.h mime.types \
        nntp.h ChangeLog.nntp \
        _regex.h OPS.MIX README.SECURITY remailer.c remailer.h browser.h \
index 527b044f7276e7380bf397fa7c7a59fcdb2585de..8bbbcb110c6ac16f1f4ccbc217a3254fc7cfc07c 100644 (file)
@@ -20,5 +20,5 @@ AM_CPPFLAGS = -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 \
-       message.c utf7.c util.c $(AUTHENTICATORS) $(GSSSOURCES)
+libimap_a_SOURCES = auth.c auth_plain.c auth_login.c browse.c command.c \
+       imap.c imap.h message.c utf7.c util.c $(AUTHENTICATORS) $(GSSSOURCES)
index 68dcb8647a898ddccfa76e4d8383f345fc2b9205..e2dcb3bef6b157076221492b28c6de77d2461b24 100644 (file)
@@ -29,6 +29,7 @@
 #include "auth.h"
 
 static const imap_auth_t imap_authenticators[] = {
+  { imap_auth_plain, "plain" },
 #ifdef USE_SASL
   { imap_auth_sasl, NULL },
 #else
index 63107947c9073008b8615b04271c2c1c7be92972..3c930d8e51a371811f717ab04ee6ad2af88be5f1 100644 (file)
@@ -40,6 +40,7 @@ typedef struct
 } imap_auth_t;
 
 /* external authenticator prototypes */
+imap_auth_res_t imap_auth_plain (IMAP_DATA *idata, const char *method);
 #ifndef USE_SASL
 imap_auth_res_t imap_auth_anon (IMAP_DATA* idata, const char* method);
 imap_auth_res_t imap_auth_cram_md5 (IMAP_DATA* idata, const char* method);
diff --git a/imap/auth_plain.c b/imap/auth_plain.c
new file mode 100644 (file)
index 0000000..fe0bce0
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 1999-2001,2005,2009 Brendan Cully <brendan@kublai.com>
+ * Copyright (C) 2016 Pietro Cerutti <gahr@gahr.ch>
+ *
+ *     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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/* SASL PLAIN support */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mutt.h"
+#include "imap_private.h"
+#include "mutt_sasl_plain.h"
+#include "auth.h"
+
+/* imap_auth_plain: SASL PLAIN support */
+imap_auth_res_t imap_auth_plain(IMAP_DATA *idata, const char *method)
+{
+  char buf[STRING];
+
+  if (mutt_account_getuser(&idata->conn->account))
+    return IMAP_AUTH_FAILURE;
+  if (mutt_account_getpass(&idata->conn->account))
+    return IMAP_AUTH_FAILURE;
+
+  mutt_message _("Logging in...");
+
+  mutt_sasl_plain_msg(buf, STRING, "AUTHENTICATE PLAIN",
+                      idata->conn->account.user, idata->conn->account.user,
+                      idata->conn->account.pass);
+
+  if (!imap_exec(idata, buf, IMAP_CMD_FAIL_OK | IMAP_CMD_PASS))
+  {
+    mutt_clear_error(); /* clear "Logging in...".  fixes #3524 */
+    return IMAP_AUTH_SUCCESS;
+  }
+
+  mutt_error _("Login failed.");
+  mutt_sleep(2);
+  return IMAP_AUTH_FAILURE;
+}
+
diff --git a/init.h b/init.h
index e3a8ad754526316f746b533a47d102c7d11da0a9..2eb3d1ed7f179ad48ebc51647ed05649cc9b22a7 100644 (file)
--- a/init.h
+++ b/init.h
@@ -3555,24 +3555,24 @@ struct option_t MuttVars[] = {
   ** (S/MIME only)
   */
 #ifdef USE_SMTP
-# ifdef USE_SASL
   { "smtp_authenticators", DT_STR, R_NONE, UL &SmtpAuthenticators, UL 0 },
   /*
   ** .pp
   ** This is a colon-delimited list of authentication methods mutt may
   ** attempt to use to log in to an SMTP server, in the order mutt should
-  ** try them.  Authentication methods are any SASL mechanism, e.g.
+  ** try them.  Authentication methods are any SASL mechanism, e.g. ``plain'',
   ** ``digest-md5'', ``gssapi'' or ``cram-md5''.
   ** This option is case-insensitive. If it is ``unset''
   ** (the default) mutt will try all available methods, in order from
-  ** most-secure to least-secure.
+  ** most-secure to least-secure. Support for the ``plain'' mechanism is
+  ** bundled; other mechanisms are provided by an external SASL library (look
+  ** for +USE_SASL in the output of mutt -v).
   ** .pp
   ** Example:
   ** .ts
   ** set smtp_authenticators="digest-md5:cram-md5"
   ** .te
   */
-# endif /* USE_SASL */
   { "smtp_pass",       DT_STR,  R_NONE|F_SENSITIVE, UL &SmtpPass, UL 0 },
   /*
   ** .pp
diff --git a/mutt_sasl_plain.c b/mutt_sasl_plain.c
new file mode 100644 (file)
index 0000000..478e68a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 Pietro Cerutti <gahr@gahr.ch>
+ *
+ *     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.,
+ *     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mutt.h"
+#include "mutt_sasl_plain.h"
+
+size_t mutt_sasl_plain_msg(char *buf, size_t buflen, const char *cmd,
+                           const char *authz, const char *user,
+                           const char *pass)
+{
+  /* authz, user, and pass can each be up to 255 bytes, making up for a 765
+   * bytes string. Add the two NULL bytes in between plus one at the end and we
+   * get 768. */
+  char tmp[768];
+  size_t len;
+  size_t tmplen;
+
+  if (!user || !*user || !pass || !*pass)
+    return 0;
+
+  tmplen = snprintf(tmp, sizeof(tmp), "%s%c%s%c%s", NONULL(authz), '\0', user,
+                    '\0', pass);
+
+  len = snprintf(buf, buflen, "%s ", cmd);
+  len += mutt_to_base64(buf + len, tmp, tmplen, buflen - len);
+  return len;
+}
diff --git a/mutt_sasl_plain.h b/mutt_sasl_plain.h
new file mode 100644 (file)
index 0000000..a6744cb
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 Pietro Cerutti <gahr@gahr.ch>
+ *
+ *     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.,
+ *     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _MUTT_SASL_PLAIN_H_
+#define _MUTT_SASL_PLAIN_H_
+
+#include <stdlib.h> /* for size_t */
+
+/**
+ * mutt_sasl_plain_msg - construct a base64 encoded SASL PLAIN message
+ * @param buf    Destination buffer.
+ * @param buflen Available space in the destination buffer.
+ * @param cmd    Protocol-specific string the prepend to the PLAIN message.
+ * @param authz  Authorization identity.
+ * @param user   Authentication identity (username).
+ * @param pass   Password.
+ *
+ * @return The number of bytes written to buf.
+ *
+ * This function can be used to build a protocol-specific SASL Response message
+ * using the PLAIN mechanism. The protocol specific command is given in the cmd
+ * parameter. The function appends a space, encodes the string derived from
+ * authz\0user\0pass using base64 encoding, and stores the result in buf.
+ *
+ * Example usages for IMAP and SMTP, respectively:
+ *
+ * mutt_sasl_plain_msg(buf, sizeof(buf), "AUTHENTICATE PLAIN", user, user, pass);
+ * mutt_sasl_plain_msg(buf, sizeof(buf), "AUTH PLAIN", NULL, user, pass);
+ */
+size_t mutt_sasl_plain_msg(char *buf, size_t buflen, const char *cmd,
+                           const char *authz, const char *user,
+                           const char *pass);
+
+#endif /* _MUTT_SASL_PLAIN_H_ */
diff --git a/smtp.c b/smtp.c
index a99e50aea1effacf091e68f0243d1a89cc780aee..b504b2863edecc27ad5c75560d6611befd5b7522 100644 (file)
--- a/smtp.c
+++ b/smtp.c
@@ -34,6 +34,8 @@
 
 #include <sasl/sasl.h>
 #include <sasl/saslutil.h>
+#else
+#include "mutt_sasl_plain.h"
 #endif
 
 #include <netdb.h>
@@ -69,6 +71,8 @@ enum {
 #ifdef USE_SASL
 static int smtp_auth (CONNECTION* conn);
 static int smtp_auth_sasl (CONNECTION* conn, const char* mechanisms);
+#else
+static int smtp_auth_plain (CONNECTION* conn);
 #endif
 
 static int smtp_fill_account (ACCOUNT* account);
@@ -498,9 +502,7 @@ static int smtp_open (CONNECTION* conn)
 #ifdef USE_SASL
     return smtp_auth (conn);
 #else
-    mutt_error (_("SMTP authentication requires SASL"));
-    mutt_sleep (1);
-    return -1;
+    return smtp_auth_plain (conn);
 #endif /* USE_SASL */
   }
 
@@ -662,4 +664,61 @@ fail:
   FREE (&buf);
   return SMTP_AUTH_FAIL;
 }
+#else /* USE_SASL */
+
+static int smtp_auth_plain(CONNECTION* conn)
+{
+  char buf[LONG_STRING];
+  size_t len;
+  const char *method;
+  const char *delim;
+  const char *error = _("SASL authentication failed");
+
+  if (!SmtpAuthenticators || !*SmtpAuthenticators)
+  {
+    goto error;
+  }
+
+  /* Check if any elements in SmtpAuthenticators is "plain" */
+  for (method = delim = SmtpAuthenticators;
+       *delim && (delim = mutt_strchrnul(method, ':'));
+       method = delim + 1)
+  {
+    if (ascii_strncasecmp(method, "plain", 5) == 0)
+    {
+      /* Get username and password. Bail out of any cannot be retrieved. */
+      if (mutt_account_getuser(&conn->account) ||
+          mutt_account_getpass(&conn->account))
+      {
+        goto error;
+      }
+
+      /* Build the initial client response. */
+      len = mutt_sasl_plain_msg(buf, sizeof(buf), "AUTH PLAIN",
+          conn->account.user, conn->account.user, conn->account.pass);
+
+      /* Terminate as per SMTP protocol. Bail out if there's no room left. */
+      if (snprintf(buf + len, sizeof(buf) - len, "\r\n") != 2)
+      {
+        goto error;
+      }
+
+      /* Send request, receive response (with a check for OK code). */
+      if ((mutt_socket_write(conn, buf) < 0) || smtp_get_resp(conn))
+      {
+        goto error;
+      }
+
+      /* If we got here, auth was successful. */
+      return 0;
+    }
+  }
+
+  error = _("No authenticators available");
+
+error:
+  mutt_error(error);
+  mutt_sleep(1);
+  return -1;
+}
 #endif /* USE_SASL */