From: Pietro Cerutti Date: Tue, 15 Nov 2016 17:10:15 +0000 (+0000) Subject: Implement SASL's PLAIN mechanism as a standalone authenticator X-Git-Tag: neomutt-20170128~26 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4bed3e86f7ed195503f22b30c2c896d7d3cef361;p=neomutt Implement SASL's PLAIN mechanism as a standalone authenticator Closes: #232 --- diff --git a/Makefile.am b/Makefile.am index 15c7ef0a7..09be1a232 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/imap/Makefile.am b/imap/Makefile.am index 527b044f7..8bbbcb110 100644 --- a/imap/Makefile.am +++ b/imap/Makefile.am @@ -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) diff --git a/imap/auth.c b/imap/auth.c index 68dcb8647..e2dcb3bef 100644 --- a/imap/auth.c +++ b/imap/auth.c @@ -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 diff --git a/imap/auth.h b/imap/auth.h index 63107947c..3c930d8e5 100644 --- a/imap/auth.h +++ b/imap/auth.h @@ -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 index 000000000..fe0bce061 --- /dev/null +++ b/imap/auth_plain.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 1999-2001,2005,2009 Brendan Cully + * Copyright (C) 2016 Pietro Cerutti + * + * 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 e3a8ad754..2eb3d1ed7 100644 --- 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 index 000000000..478e68abb --- /dev/null +++ b/mutt_sasl_plain.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Pietro Cerutti + * + * 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 index 000000000..a6744cb77 --- /dev/null +++ b/mutt_sasl_plain.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Pietro Cerutti + * + * 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 /* 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 a99e50aea..b504b2863 100644 --- a/smtp.c +++ b/smtp.c @@ -34,6 +34,8 @@ #include #include +#else +#include "mutt_sasl_plain.h" #endif #include @@ -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 */