From: Thomas Roessler Date: Mon, 31 Jul 2000 07:18:28 +0000 (+0000) Subject: Brendan Cully's SASL patch. I hope I didn't miss any files. X-Git-Tag: mutt-1-3-7-rel~6 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=61333b001c09fb24709398d2ad2dae16b8451080;p=mutt Brendan Cully's SASL patch. I hope I didn't miss any files. --- diff --git a/Makefile.am b/Makefile.am index fb23509d..e906d61a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -63,15 +63,15 @@ non_us_sources = pgp.c pgpinvoke.c pgpkey.c pgplib.c sha1dgst.c \ contrib/pgp2.rc contrib/pgp5.rc contrib/gpg.rc \ imap/imap_ssl.c imap/imap_ssl.h README.SSL -EXTRA_mutt_SOURCES = account.c mutt_socket.c pop.c pgp.c pgpinvoke.c pgpkey.c \ - pgplib.c sha1dgst.c gnupgparse.c resize.c dotlock.c remailer.c \ - browser.h mbyte.h remailer.h +EXTRA_mutt_SOURCES = account.c mutt_sasl.c mutt_socket.c pop.c pgp.c \ + pgpinvoke.c pgpkey.c pgplib.c sha1dgst.c gnupgparse.c resize.c \ + dotlock.c remailer.c browser.h mbyte.h remailer.h EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP TODO configure acconfig.h account.h \ attach.h buffy.h charset.h copy.h dotlock.h functions.h gen_defs \ globals.h hash.h history.h init.h keymap.h \ mailbox.h mapping.h mime.h mutt.h mutt_curses.h mutt_menu.h \ - mutt_regex.h mutt_socket.h mx.h pager.h pgp.h protos.h \ + mutt_regex.h mutt_sasl.h mutt_socket.h mx.h pager.h pgp.h protos.h \ reldate.h rfc1524.h rfc2047.h rfc2231.h rfc822.h sha.h sha_locl.h \ sort.h mime.types VERSION prepare _regex.h OPS.MIX \ README.SECURITY remailer.c remailer.h browser.h \ diff --git a/acconfig.h b/acconfig.h index c31c44c3..21747e6b 100644 --- a/acconfig.h +++ b/acconfig.h @@ -46,13 +46,17 @@ /* Do you want support for the IMAP protocol? (--enable-imap) */ #undef USE_IMAP +/* Do you want to use the Cyrus SASL library for POP/IMAP authentication? + * (--with-sasl) */ +#undef USE_SASL + /* Do you want support for IMAP GSSAPI authentication? (--with-gss) */ #undef USE_GSS /* Do you have the Heimdal version of Kerberos V? (for gss support) */ #undef HAVE_HEIMDAL -/* Do you want support for SSL? (--enable-ssl) */ +/* Do you want support for SSL? (--with-ssl) */ #undef USE_SSL /* Avoid SSL routines which used patent-encumbered RC5 algorithms */ diff --git a/account.c b/account.c index 52bb58a1..f095763e 100644 --- a/account.c +++ b/account.c @@ -52,3 +52,57 @@ int mutt_account_match (const ACCOUNT* a1, const ACCOUNT* a2) return 1; } + +/* mutt_account_getuser: retrieve username into ACCOUNT, if neccessary */ +int mutt_account_getuser (ACCOUNT* account) +{ + /* already set */ + if (account->flags & M_ACCT_USER) + return 0; +#ifdef USE_IMAP + else if ((account->type == M_ACCT_TYPE_IMAP) && ImapUser) + strfcpy (account->user, ImapUser, sizeof (account->user)); +#endif +#ifdef USE_POP + else if ((account->type == M_ACCT_TYPE_POP) && PopUser) + strfcpy (account->user, PopUser, sizeof (account->user)); +#endif + /* prompt (defaults to unix username), copy into account->user */ + else + { + strfcpy (account->user, NONULL (Username), sizeof (account->user)); + if (mutt_get_field (_("Username: "), account->user, + sizeof (account->user), 0)) + return -1; + } + + account->flags |= M_ACCT_USER; + + return 0; +} + +/* mutt_account_getpass: fetch password into ACCOUNT, if neccessary */ +int mutt_account_getpass (ACCOUNT* account) +{ + if (account->flags & M_ACCT_PASS) + return 0; +#ifdef USE_IMAP + else if ((account->type == M_ACCT_TYPE_IMAP) && ImapPass) + strfcpy (account->pass, ImapPass, sizeof (account->pass)); +#endif +#ifdef USE_POP + else if ((account->type == M_ACCT_TYPE_POP) && PopPass) + strfcpy (account->pass, PopPass, sizeof (account->pass)); +#endif + else + { + account->pass[0] = '\0'; + if (mutt_get_field (_("Password: "), account->pass, + sizeof (account->pass), M_PASS)) + return -1; + } + + account->flags |= M_ACCT_PASS; + + return 0; +} diff --git a/account.h b/account.h index 20c5e63a..9e21c269 100644 --- a/account.h +++ b/account.h @@ -32,9 +32,8 @@ enum /* account flags */ #define M_ACCT_PORT (1<<0) #define M_ACCT_USER (1<<1) -#define M_ACCT_SSL (1<<2) -#define M_ACCT_CRAM (1<<3) -#define M_ACCT_PASS (1<<4) +#define M_ACCT_PASS (1<<2) +#define M_ACCT_SSL (1<<3) typedef struct { @@ -46,7 +45,8 @@ typedef struct unsigned char flags; } ACCOUNT; -/* imap_account_match: compare account info (host/port/user) */ int mutt_account_match (const ACCOUNT* a1, const ACCOUNT* m2); +int mutt_account_getuser (ACCOUNT* account); +int mutt_account_getpass (ACCOUNT* account); #endif /* _MUTT_ACCOUNT_H_ */ diff --git a/configure.in b/configure.in index 0894fbc6..a82976f6 100644 --- a/configure.in +++ b/configure.in @@ -663,6 +663,37 @@ AM_CONDITIONAL(USE_SSL, test x$need_ssl = xyes) dnl -- end imap dependencies -- +AC_ARG_WITH(sasl, [ --with-sasl[=DIR] Use Cyrus SASL library for POP/IMAP authentication], + [ + if test "$need_socket" != "yes" + then + AC_MSG_ERROR([SASL support is only useful with POP or IMAP support]) + fi + + if test "$with_sasl" != "no" + then + if test "$with_sasl" != "yes" + then + CPPFLAGS="$CPPFLAGS -I$with_sasl/include" + LDFLAGS="$LDFLAGS -L$with_sasl/lib" + fi + + saved_LIBS="$LIBS" + + AC_CHECK_LIB(sasl, sasl_client_init,, + AC_MSG_ERROR([could not find libsasl]),) + + MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS mutt_sasl.o" + MUTTLIBS="$MUTTLIBS -lsasl" + LIBS="$saved_LIBS" + AC_DEFINE(USE_SASL) + need_sasl=yes + fi + ]) +AM_CONDITIONAL(USE_SASL, test x$need_sasl = xyes) + +dnl -- end socket -- + AC_ARG_ENABLE(debug, [ --enable-debug Enable debugging support], [ if test x$enableval = xyes ; then AC_DEFINE(DEBUG) @@ -702,7 +733,7 @@ AC_ARG_ENABLE(buffy-size, [ --enable-buffy-size Use file size attribute AC_DEFINE(BUFFY_SIZE) fi]) -AC_ARG_ENABLE(mailtool, [ --enable-mailtool Enable Sun mailtool attachments support ], +AC_ARG_ENABLE(mailtool, [ --enable-mailtool Enable Sun mailtool attachments support ], [if test x$enableval = xyes; then AC_DEFINE(SUN_ATTACHMENT) fi]) @@ -717,7 +748,7 @@ AC_ARG_WITH(exec-shell, [ --with-exec-shell=SHELL Specify alternate shell (O AC_DEFINE_UNQUOTED(EXECSHELL, "$withval") fi]) -AC_ARG_ENABLE(exact-address, [ --enable-exact-address enable regeneration of email addresses], +AC_ARG_ENABLE(exact-address, [ --enable-exact-address Enable regeneration of email addresses], [if test $enableval = yes; then AC_DEFINE(EXACT_ADDRESS) fi]) diff --git a/globals.h b/globals.h index 2e403189..3fab5fe0 100644 --- a/globals.h +++ b/globals.h @@ -50,7 +50,6 @@ WHERE char *Homedir; WHERE char *Hostname; #ifdef USE_IMAP WHERE char *ImapUser INITVAL (NULL); -WHERE char *ImapCRAMKey INITVAL (NULL); WHERE char *ImapPass INITVAL (NULL); WHERE short ImapCheckTimeout; WHERE char *ImapHomeNamespace INITVAL (NULL); diff --git a/imap/Makefile.am b/imap/Makefile.am index 5ff4d388..8f5c6486 100644 --- a/imap/Makefile.am +++ b/imap/Makefile.am @@ -6,6 +6,12 @@ if USE_GSS GSSSOURCES = auth_gss.c endif +if USE_SASL +SASLSOURCES = auth_sasl.c +else +CRAMSOURCES = auth_cram.c +endif + if USE_SSL SSLSOURCES = imap_ssl.c SSLHEADERS = imap_ssl.h @@ -16,7 +22,8 @@ EXTRA_DIST = BUGS README TODO imap_ssl.c imap_ssl.h auth_gss.c INCLUDES = -I$(top_srcdir) -I../intl noinst_LIBRARIES = libimap.a -noinst_HEADERS = imap_private.h md5.h message.h $(SSLHEADERS) +noinst_HEADERS = auth.h imap_private.h md5.h message.h $(SSLHEADERS) -libimap_a_SOURCES = auth.c browse.c command.c imap.c imap.h md5c.c message.c \ - utf7.c util.c $(GSSSOURCES) $(SSLSOURCES) +libimap_a_SOURCES = auth.c auth_anon.c auth_login.c c browse.c \ + command.c imap.c imap.h md5c.c message.c utf7.c \ + util.c $(GSSSOURCES) $(SASLSOURCES) $(CRAMSOURCES) $(SSLSOURCES) diff --git a/imap/auth.c b/imap/auth.c index 0c857761..26f2feff 100644 --- a/imap/auth.c +++ b/imap/auth.c @@ -22,390 +22,37 @@ #include "mutt.h" #include "imap_private.h" -#include "md5.h" +#include "auth.h" -#define MD5_BLOCK_LEN 64 -#define MD5_DIGEST_LEN 16 - -/* external authenticator prototypes */ +static imap_auth_t imap_authenticators[] = { + imap_auth_anon, +#ifdef USE_SASL + imap_auth_sasl, +#endif #ifdef USE_GSS -int imap_auth_gss (IMAP_DATA* idata, const char* user); + imap_auth_gss, #endif + /* SASL includes CRAM-MD5 (and GSSAPI, but that's not enabled by default) */ +#ifndef USE_SASL + imap_auth_cram_md5, +#endif + imap_auth_login, -/* forward declarations */ -static void hmac_md5 (const char* password, char* challenge, - unsigned char* response); -static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user, - const char* pass); -static int imap_auth_anon (IMAP_DATA *idata); - -/* hmac_md5: produce CRAM-MD5 challenge response. */ -static void hmac_md5 (const char* password, char* challenge, - unsigned char* response) -{ - MD5_CTX ctx; - unsigned char ipad[MD5_BLOCK_LEN], opad[MD5_BLOCK_LEN]; - unsigned char secret[MD5_BLOCK_LEN+1]; - unsigned char hash_passwd[MD5_DIGEST_LEN]; - unsigned int secret_len, chal_len; - int i; - - secret_len = strlen (password); - chal_len = strlen (challenge); - - /* passwords longer than MD5_BLOCK_LEN bytes are substituted with their MD5 - * digests */ - if (secret_len > MD5_BLOCK_LEN) - { - MD5Init (&ctx); - MD5Update (&ctx, (unsigned char*) password, secret_len); - MD5Final (hash_passwd, &ctx); - strfcpy ((char*) secret, (char*) hash_passwd, MD5_DIGEST_LEN); - secret_len = MD5_DIGEST_LEN; - } - else - strfcpy ((char *) secret, password, sizeof (secret)); - - memset (ipad, 0, sizeof (ipad)); - memset (opad, 0, sizeof (opad)); - memcpy (ipad, secret, secret_len); - memcpy (opad, secret, secret_len); - - for (i = 0; i < MD5_BLOCK_LEN; i++) - { - ipad[i] ^= 0x36; - opad[i] ^= 0x5c; - } - - /* inner hash: challenge and ipadded secret */ - MD5Init (&ctx); - MD5Update (&ctx, ipad, MD5_BLOCK_LEN); - MD5Update (&ctx, (unsigned char*) challenge, chal_len); - MD5Final (response, &ctx); - - /* outer hash: inner hash and opadded secret */ - MD5Init (&ctx); - MD5Update (&ctx, opad, MD5_BLOCK_LEN); - MD5Update (&ctx, response, MD5_DIGEST_LEN); - MD5Final (response, &ctx); -} - -/* imap_auth_cram_md5: AUTH=CRAM-MD5 support. Used unconditionally if the - * server supports it */ -static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user, - const char* pass) -{ - char ibuf[LONG_STRING], obuf[LONG_STRING]; - unsigned char hmac_response[MD5_DIGEST_LEN]; - int len; - - dprint (2, (debugfile, "Attempting CRAM-MD5 login...\n")); - mutt_message _("Authenticating (CRAM-MD5)..."); - - imap_cmd_start (idata, "AUTHENTICATE CRAM-MD5"); - - /* From RFC 2195: - * The data encoded in the first ready response contains a presumptively - * arbitrary string of random digits, a timestamp, and the fully-qualified - * primary host name of the server. The syntax of the unencoded form must - * correspond to that of an RFC 822 'msg-id' [RFC822] as described in [POP3]. - */ - if (mutt_socket_readln (ibuf, sizeof (ibuf), idata->conn) < 0) - { - dprint (1, (debugfile, "Error receiving server response.\n")); - - return -1; - } - - if (ibuf[0] != '+') - { - dprint (1, (debugfile, "Invalid response from server: %s\n", ibuf)); - - return -1; - } - - if ((len = mutt_from_base64 (obuf, ibuf + 2)) == -1) - { - dprint (1, (debugfile, "Error decoding base64 response.\n")); - - return -1; - } - - obuf[len] = '\0'; - dprint (2, (debugfile, "CRAM challenge: %s\n", obuf)); - - /* The client makes note of the data and then responds with a string - * consisting of the user name, a space, and a 'digest'. The latter is - * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where the - * key is a shared secret and the digested text is the timestamp (including - * angle-brackets). - * - * Note: The user name shouldn't be quoted. Since the digest can't contain - * spaces, there is no ambiguity. Some servers get this wrong, we'll work - * around them when the bug report comes in. Until then, we'll remain - * blissfully RFC-compliant. - */ - hmac_md5 (pass, obuf, hmac_response); - /* dubious optimisation I saw elsewhere: make the whole string in one call */ - snprintf (obuf, sizeof (obuf), - "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - user, - hmac_response[0], hmac_response[1], hmac_response[2], hmac_response[3], - hmac_response[4], hmac_response[5], hmac_response[6], hmac_response[7], - hmac_response[8], hmac_response[9], hmac_response[10], hmac_response[11], - hmac_response[12], hmac_response[13], hmac_response[14], hmac_response[15]); - dprint(2, (debugfile, "CRAM response: %s\n", obuf)); - - mutt_to_base64 ((unsigned char*) ibuf, (unsigned char*) obuf, strlen (obuf)); - strcpy (ibuf + strlen (ibuf), "\r\n"); - mutt_socket_write (idata->conn, ibuf); - - if (mutt_socket_readln (ibuf, LONG_STRING, idata->conn) < 0) - { - dprint (1, (debugfile, "Error receiving server response.\n")); - - return -1; - } - - if (imap_code (ibuf)) - { - dprint (2, (debugfile, "CRAM login complete.\n")); - - return 0; - } - - dprint (2, (debugfile, "CRAM login failed.\n")); - return -1; -} - -/* this is basically a stripped-down version of the cram-md5 method. */ - -static int imap_auth_anon (IMAP_DATA* idata) -{ - char buf[LONG_STRING]; - - dprint (2, (debugfile, "Attempting anonymous login...\n")); - mutt_message _("Authenticating (anonymous)..."); - - imap_cmd_start (idata, "AUTHENTICATE ANONYMOUS"); - - if (mutt_socket_readln (buf, sizeof (buf), idata->conn) < 0) - { - dprint (1, (debugfile, "Error receiving server response.\n")); - - return -1; - } - - if (buf[0] != '+') - { - dprint (1, (debugfile, "Invalid response from server.\n")); - - return -1; - } - - strfcpy (buf, "ZHVtbXkK\r\n", sizeof (buf)); /* base64 ("dummy") */ - - mutt_socket_write (idata->conn, buf); - - if (mutt_socket_readln (buf, sizeof (buf), idata->conn) < 0) - { - dprint (1, (debugfile, "Error receiving server response.\n")); - - return -1; - } - - if (imap_code (buf)) - { - dprint (2, (debugfile, "Anonymous login complete.\n")); - - return 0; - } - - dprint (2, (debugfile, "Anonymous login failed.\n")); + NULL +}; - return -1; -} - -/* imap_authenticate: loop until success or user abort. At each loop, all - * supported authentication methods are tried, from strongest to weakest. - * Currently available: - * GSSAPI: strongest available form, requires Kerberos V infrastructure, - * or possibly alternatively Heimdal. - * CRAM-MD5: like APOP or CHAP. Safe against replay and sniffing, but - * requires that your key be stored on the server, readable by the - * server account. UW-IMAP supports this method since at least 4.5, if - * the key file exists on the server. - * LOGIN: ugh. Don't use this if you can help it. Uses cleartext password - * exchange, furthermore uses unix login techniques so this same password - * can be used to log in to the server or others that share the - * credentials database. - * Unavailable: - * KERBEROS_V4. Superceded by GSSAPI. - */ +/* imap_authenticate: oh how simple! loops through authenticators. */ int imap_authenticate (IMAP_DATA* idata) { - char buf[LONG_STRING]; - char user[SHORT_STRING], q_user[SHORT_STRING]; - char ckey[SHORT_STRING]; - char pass[SHORT_STRING], q_pass[SHORT_STRING]; - - CONNECTION* conn = idata->conn; - - int r = 1; + imap_auth_t* authenticator = imap_authenticators; + int r = -1; - while (r != 0) + while (authenticator) { - if (! (conn->account.flags & M_ACCT_USER)) - { - if (!ImapUser) - { - strfcpy (user, NONULL(Username), sizeof (user)); - if (mutt_get_field (_("IMAP Username: "), user, sizeof (user), 0) != 0) - { - user[0] = 0; - return (-1); - } - } - else - strfcpy (user, ImapUser, sizeof (user)); - } - else - strfcpy (user, conn->account.user, sizeof (user)); - - if (!user[0]) - { - if (!mutt_bit_isset (idata->capabilities, AUTH_ANON)) - { - mutt_error _("Anonymous authentication not supported."); - return -1; - } - - return imap_auth_anon (idata); - } - -#ifdef USE_GSS - /* attempt GSSAPI authentication, if available */ - if (mutt_bit_isset (idata->capabilities, AGSSAPI)) - { - if ((r = imap_auth_gss (idata, user))) - { - mutt_error _("GSSAPI authentication failed."); - sleep (1); - } - else - return 0; - } - else - dprint (2, (debugfile, "GSSAPI authentication is not available\n")); -#endif - - /* attempt CRAM-MD5 if available */ - if (mutt_bit_isset (idata->capabilities, ACRAM_MD5)) - { - if (!(conn->account.flags & M_ACCT_CRAM)) - { - if (!ImapCRAMKey) - { - ckey[0] = '\0'; - snprintf (buf, sizeof (buf), _("CRAM key for %s@%s: "), user, - conn->account.host); - if (mutt_get_field (buf, ckey, sizeof (ckey), M_PASS) != 0) - return -1; - } - else - strfcpy (ckey, ImapCRAMKey, sizeof (ckey)); - } - else - strfcpy (ckey, conn->account.pass, sizeof (ckey)); - - if (*ckey) - { - if ((r = imap_auth_cram_md5 (idata, user, ckey))) - { - mutt_error _("CRAM-MD5 authentication failed."); - sleep (1); - if (!(conn->account.flags & M_ACCT_CRAM)) - FREE (&ImapCRAMKey); - conn->account.flags &= ~M_ACCT_CRAM; - } - else - { - strfcpy (conn->account.pass, ckey, sizeof (conn->account.pass)); - conn->account.flags |= M_ACCT_CRAM; - return 0; - } - } - else - { - mutt_message _("Skipping CRAM-MD5 authentication."); - sleep (1); - } - } - else - dprint (2, (debugfile, "CRAM-MD5 authentication is not available\n")); - - if (! (conn->account.flags & M_ACCT_PASS)) - { - if (!ImapPass) - { - pass[0]=0; - snprintf (buf, sizeof (buf), _("Password for %s@%s: "), user, - conn->account.host); - if (mutt_get_field (buf, pass, sizeof (pass), M_PASS) != 0 || - !pass[0]) - return -1; - } - else - strfcpy (pass, ImapPass, sizeof (pass)); - } - else - strfcpy (pass, conn->account.pass, sizeof (pass)); - - imap_quote_string (q_user, sizeof (q_user), user); - imap_quote_string (q_pass, sizeof (q_pass), pass); - - mutt_message _("Logging in..."); - -#ifdef DEBUG - /* don't print the password unless we're at the ungodly debugging level - * of 5 or higher */ - - if (debuglevel < IMAP_LOG_PASS) - dprint (2, (debugfile, "Sending LOGIN command for %s...\n", user)); -#endif - - snprintf (buf, sizeof (buf), "LOGIN %s %s", q_user, q_pass); - r = imap_exec (buf, sizeof (buf), idata, buf, - IMAP_CMD_FAIL_OK | IMAP_CMD_PASS); - if (r == -1) - { - /* connection or protocol problem */ - imap_error ("imap_authenticate", buf); - return (-1); - } - else if (r == -2) - { - /* Login failed, try again */ - mutt_error _("Login failed."); - sleep (1); - - if (!(conn->account.flags & M_ACCT_USER)) - FREE (&ImapUser); - if (!(conn->account.flags & M_ACCT_PASS)) - FREE (&ImapPass); - conn->account.flags &= ~M_ACCT_PASS; - } - else - { - /* If they have a successful login, we may as well cache the - * user/password. */ - if (!(conn->account.flags & M_ACCT_USER)) - strfcpy (conn->account.user, user, sizeof (conn->account.user)); - if (!(conn->account.flags & M_ACCT_PASS)) - strfcpy (conn->account.pass, pass, sizeof (conn->account.pass)); - - conn->account.flags |= (M_ACCT_USER | M_ACCT_PASS); - } + if ((r = (*authenticator)(idata)) != IMAP_AUTH_UNAVAIL) + return r; + authenticator++; } - return 0; + + return r; } diff --git a/imap/auth.h b/imap/auth.h new file mode 100644 index 00000000..bf3f2c21 --- /dev/null +++ b/imap/auth.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2000 Brendan Cully + * + * 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. + */ + +/* common defs for authenticators. A good place to set up a generic callback + * system */ + +#ifndef _IMAP_AUTH_H +#define _IMAP_AUTH_H 1 + +typedef enum +{ + IMAP_AUTH_SUCCESS = 0, + IMAP_AUTH_FAILURE, + IMAP_AUTH_UNAVAIL +} imap_auth_res_t; + +typedef imap_auth_res_t (*imap_auth_t)(IMAP_DATA* idata); + +/* external authenticator prototypes */ +imap_auth_res_t imap_auth_anon (IMAP_DATA* idata); +imap_auth_res_t imap_auth_cram_md5 (IMAP_DATA* idata); +imap_auth_res_t imap_auth_login (IMAP_DATA* idata); +#ifdef USE_GSS +imap_auth_res_t imap_auth_gss (IMAP_DATA* idata); +#endif +#ifdef USE_SASL +imap_auth_res_t imap_auth_sasl (IMAP_DATA* idata); +#endif + +#endif /* _IMAP_AUTH_H */ diff --git a/imap/auth_anon.c b/imap/auth_anon.c new file mode 100644 index 00000000..09ec71db --- /dev/null +++ b/imap/auth_anon.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 1999-2000 Brendan Cully + * + * 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. + */ + +/* IMAP login/authentication code */ + +#include "mutt.h" +#include "imap_private.h" +#include "auth.h" + +/* this is basically a stripped-down version of the cram-md5 method. */ +imap_auth_res_t imap_auth_anon (IMAP_DATA* idata) +{ + char buf[LONG_STRING]; + + if (!mutt_bit_isset (idata->capabilities, AUTH_ANON)) + return IMAP_AUTH_UNAVAIL; + + if (mutt_account_getuser (&idata->conn->account)) + return IMAP_AUTH_FAILURE; + + if (idata->conn->account.user[0] != '\0') + return IMAP_AUTH_UNAVAIL; + + mutt_message _("Authenticating (anonymous)..."); + + imap_cmd_start (idata, "AUTHENTICATE ANONYMOUS"); + + if (mutt_socket_readln (buf, sizeof (buf), idata->conn) < 0) + { + dprint (1, (debugfile, "Error receiving server response.\n")); + goto bail; + } + + if (buf[0] != '+') + { + dprint (1, (debugfile, "Invalid response from server.\n")); + goto bail; + } + + strfcpy (buf, "ZHVtbXkK\r\n", sizeof (buf)); /* base64 ("dummy") */ + + mutt_socket_write (idata->conn, buf); + + if (mutt_socket_readln (buf, sizeof (buf), idata->conn) < 0) + { + dprint (1, (debugfile, "Error receiving server response.\n")); + goto bail; + } + + if (imap_code (buf)) + return IMAP_AUTH_SUCCESS; + + bail: + mutt_error _("Anonymous authentication failed."); + sleep (2); + return IMAP_AUTH_FAILURE; +} diff --git a/imap/auth_cram.c b/imap/auth_cram.c new file mode 100644 index 00000000..9736ee36 --- /dev/null +++ b/imap/auth_cram.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 1999-2000 Brendan Cully + * + * 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. + */ + +/* IMAP login/authentication code */ + +#include "mutt.h" +#include "imap_private.h" +#include "auth.h" +#include "md5.h" + +#define MD5_BLOCK_LEN 64 +#define MD5_DIGEST_LEN 16 + +/* forward declarations */ +static void hmac_md5 (const char* password, char* challenge, + unsigned char* response); + +/* imap_auth_cram_md5: AUTH=CRAM-MD5 support. */ +imap_auth_res_t imap_auth_cram_md5 (IMAP_DATA* idata) +{ + char ibuf[LONG_STRING], obuf[LONG_STRING]; + unsigned char hmac_response[MD5_DIGEST_LEN]; + int len; + + if (!mutt_bit_isset (idata->capabilities, ACRAM_MD5)) + return IMAP_AUTH_UNAVAIL; + + mutt_message _("Authenticating (CRAM-MD5)..."); + + /* get auth info */ + if (mutt_account_getuser (&idata->conn->account)) + return IMAP_AUTH_FAILURE; + if (mutt_account_getpass (&idata->conn->account)) + return IMAP_AUTH_FAILURE; + + imap_cmd_start (idata, "AUTHENTICATE CRAM-MD5"); + + /* From RFC 2195: + * The data encoded in the first ready response contains a presumptively + * arbitrary string of random digits, a timestamp, and the fully-qualified + * primary host name of the server. The syntax of the unencoded form must + * correspond to that of an RFC 822 'msg-id' [RFC822] as described in [POP3]. + */ + if (mutt_socket_readln (ibuf, sizeof (ibuf), idata->conn) < 0) + { + dprint (1, (debugfile, "Error receiving server response.\n")); + goto bail; + } + + if (ibuf[0] != '+') + { + dprint (1, (debugfile, "Invalid response from server: %s\n", ibuf)); + goto bail; + } + + if ((len = mutt_from_base64 (obuf, ibuf + 2)) == -1) + { + dprint (1, (debugfile, "Error decoding base64 response.\n")); + goto bail; + } + + obuf[len] = '\0'; + dprint (2, (debugfile, "CRAM challenge: %s\n", obuf)); + + /* The client makes note of the data and then responds with a string + * consisting of the user name, a space, and a 'digest'. The latter is + * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where the + * key is a shared secret and the digested text is the timestamp (including + * angle-brackets). + * + * Note: The user name shouldn't be quoted. Since the digest can't contain + * spaces, there is no ambiguity. Some servers get this wrong, we'll work + * around them when the bug report comes in. Until then, we'll remain + * blissfully RFC-compliant. + */ + hmac_md5 (idata->conn->account.pass, obuf, hmac_response); + /* dubious optimisation I saw elsewhere: make the whole string in one call */ + snprintf (obuf, sizeof (obuf), + "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + idata->conn->account.user, + hmac_response[0], hmac_response[1], hmac_response[2], hmac_response[3], + hmac_response[4], hmac_response[5], hmac_response[6], hmac_response[7], + hmac_response[8], hmac_response[9], hmac_response[10], hmac_response[11], + hmac_response[12], hmac_response[13], hmac_response[14], hmac_response[15]); + dprint(2, (debugfile, "CRAM response: %s\n", obuf)); + + mutt_to_base64 ((unsigned char*) ibuf, (unsigned char*) obuf, strlen (obuf)); + strcpy (ibuf + strlen (ibuf), "\r\n"); + mutt_socket_write (idata->conn, ibuf); + + if (mutt_socket_readln (ibuf, LONG_STRING, idata->conn) < 0) + { + dprint (1, (debugfile, "Error receiving server response.\n")); + goto bail; + } + + if (imap_code (ibuf)) + return IMAP_AUTH_SUCCESS; + + bail: + mutt_error _("CRAM-MD5 authentication failed."); + sleep (2); + return IMAP_AUTH_FAILURE; +} + +/* hmac_md5: produce CRAM-MD5 challenge response. */ +static void hmac_md5 (const char* password, char* challenge, + unsigned char* response) +{ + MD5_CTX ctx; + unsigned char ipad[MD5_BLOCK_LEN], opad[MD5_BLOCK_LEN]; + unsigned char secret[MD5_BLOCK_LEN+1]; + unsigned char hash_passwd[MD5_DIGEST_LEN]; + unsigned int secret_len, chal_len; + int i; + + secret_len = strlen (password); + chal_len = strlen (challenge); + + /* passwords longer than MD5_BLOCK_LEN bytes are substituted with their MD5 + * digests */ + if (secret_len > MD5_BLOCK_LEN) + { + MD5Init (&ctx); + MD5Update (&ctx, (unsigned char*) password, secret_len); + MD5Final (hash_passwd, &ctx); + strfcpy ((char*) secret, (char*) hash_passwd, MD5_DIGEST_LEN); + secret_len = MD5_DIGEST_LEN; + } + else + strfcpy ((char *) secret, password, sizeof (secret)); + + memset (ipad, 0, sizeof (ipad)); + memset (opad, 0, sizeof (opad)); + memcpy (ipad, secret, secret_len); + memcpy (opad, secret, secret_len); + + for (i = 0; i < MD5_BLOCK_LEN; i++) + { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + /* inner hash: challenge and ipadded secret */ + MD5Init (&ctx); + MD5Update (&ctx, ipad, MD5_BLOCK_LEN); + MD5Update (&ctx, (unsigned char*) challenge, chal_len); + MD5Final (response, &ctx); + + /* outer hash: inner hash and opadded secret */ + MD5Init (&ctx); + MD5Update (&ctx, opad, MD5_BLOCK_LEN); + MD5Update (&ctx, response, MD5_DIGEST_LEN); + MD5Final (response, &ctx); +} diff --git a/imap/auth_gss.c b/imap/auth_gss.c index 48a8ba1f..73e3f8b8 100644 --- a/imap/auth_gss.c +++ b/imap/auth_gss.c @@ -23,6 +23,7 @@ #include "mutt.h" #include "imap_private.h" +#include "auth.h" #include @@ -39,9 +40,8 @@ #define GSS_AUTH_P_INTEGRITY 2 #define GSS_AUTH_P_PRIVACY 4 -/* imap_auth_gss: AUTH=GSSAPI support. Used unconditionally if the server - * supports it */ -int imap_auth_gss (IMAP_DATA* idata, const char* user) +/* imap_auth_gss: AUTH=GSSAPI support. */ +imap_auth_res_t imap_auth_gss (IMAP_DATA* idata) { gss_buffer_desc request_buf, send_token; gss_buffer_t sec_token; @@ -54,8 +54,14 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user) char buf1[GSS_BUFSIZE], buf2[GSS_BUFSIZE], server_conf_flags; unsigned long buf_size; - dprint (2, (debugfile, "Attempting GSS login...\n")); + if (!mutt_bit_isset (idata->capabilities, AGSSAPI)) + return IMAP_AUTH_UNAVAIL; + mutt_message _("Authenticating (GSS)..."); + + if (mutt_account_getuser (&idata->conn->account)) + return IMAP_AUTH_FAILURE; + /* get an IMAP service ticket for the server */ snprintf (buf1, sizeof (buf1), "imap@%s", idata->conn->account.host); request_buf.value = buf1; @@ -65,7 +71,7 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user) if (maj_stat != GSS_S_COMPLETE) { dprint (2, (debugfile, "Couldn't get service name for [%s]\n", buf1)); - return -1; + goto bail; } #ifdef DEBUG else if (debuglevel >= 2) @@ -87,16 +93,14 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user) { dprint (1, (debugfile, "Error receiving server response.\n")); gss_release_name (&min_stat, &target_name); - - return -1; + goto bail; } if (buf1[0] != '+') { dprint (2, (debugfile, "Invalid response from server: %s\n", buf1)); gss_release_name (&min_stat, &target_name); - - return -1; + goto bail; } /* now start the security context initialisation loop... */ @@ -126,8 +130,7 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user) /* end authentication attempt */ mutt_socket_write (idata->conn, "*\r\n"); mutt_socket_readln (buf1, sizeof (buf1), idata->conn); - - return -1; + goto bail; } /* send token */ @@ -143,8 +146,7 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user) { dprint (1, (debugfile, "Error receiving server response.\n")); gss_release_name (&min_stat, &target_name); - - return -1; + goto bail; } request_buf.length = mutt_from_base64 (buf2, buf1 + 2); @@ -160,8 +162,7 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user) if (mutt_socket_readln (buf1, sizeof (buf1), idata->conn) < 0) { dprint (1, (debugfile, "Error receiving server response.\n")); - - return -1; + goto bail; } request_buf.length = mutt_from_base64 (buf2, buf1 + 2); request_buf.value = buf2; @@ -172,9 +173,8 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user) { dprint (2, (debugfile, "Couldn't unwrap security level data\n")); gss_release_buffer (&min_stat, &send_token); - mutt_socket_write(idata->conn, "*\r\n"); - return -1; + goto bail; } dprint (2, (debugfile, "Credential exchange complete\n")); @@ -184,9 +184,8 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user) { dprint (2, (debugfile, "Server requires integrity or privace\n")); gss_release_buffer (&min_stat, &send_token); - mutt_socket_write(idata->conn, "*\r\n"); - return -1; + goto bail; } /* we don't care about buffer size if we don't wrap content. But here it is */ @@ -204,21 +203,21 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user) memcpy (buf1, &buf_size, 4); buf1[0] = GSS_AUTH_P_NONE; /* server decides if principal can log in as user */ - strncpy (buf1 + 4, user, sizeof (buf1) - 4); + strncpy (buf1 + 4, idata->conn->account.user, sizeof (buf1) - 4); request_buf.value = buf1; - request_buf.length = 4 + strlen (user) + 1; + request_buf.length = 4 + strlen (idata->conn->account.user) + 1; maj_stat = gss_wrap (&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf, &cflags, &send_token); if (maj_stat != GSS_S_COMPLETE) { dprint (2, (debugfile, "Error creating login request\n")); - mutt_socket_write(idata->conn, "*\r\n"); - return -1; + goto bail; } mutt_to_base64 ((unsigned char*) buf1, send_token.value, send_token.length); - dprint (2, (debugfile, "Requesting authorisation as %s\n", user)); + dprint (2, (debugfile, "Requesting authorisation as %s\n", + idata->conn->account.user)); strncat (buf1, "\r\n", sizeof (buf1)); mutt_socket_write (idata->conn, buf1); @@ -226,9 +225,8 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user) if (mutt_socket_readln (buf1, GSS_BUFSIZE, idata->conn) < 0) { dprint (1, (debugfile, "Error receiving server response.\n")); - mutt_socket_write(idata->conn, "*\r\n"); - return -1; + goto bail; } if (imap_code (buf1)) { @@ -238,8 +236,7 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user) if (maj_stat != GSS_S_COMPLETE) { dprint (1, (debugfile, "Error releasing credentials\n")); - - return -1; + goto bail; } /* send_token may contain a notification to the server to flush * credentials. RFC 1731 doesn't specify what to do, and since this @@ -247,13 +244,11 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user) * enough to flush its own credentials */ gss_release_buffer (&min_stat, &send_token); - dprint (2, (debugfile, "GSS login complete\n")); - - return 0; + return IMAP_AUTH_SUCCESS; } - /* logon failed */ - dprint (2, (debugfile, "GSS login failed.\n")); - - return -1; + bail: + mutt_error _("GSS authentication failed."); + sleep (2); + return IMAP_AUTH_FAILURE; } diff --git a/imap/auth_login.c b/imap/auth_login.c new file mode 100644 index 00000000..757c3538 --- /dev/null +++ b/imap/auth_login.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 1999-2000 Brendan Cully + * + * 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. + */ + +/* plain LOGIN support */ + +#include "mutt.h" +#include "imap_private.h" +#include "auth.h" + +/* imap_auth_login: Plain LOGIN support */ +imap_auth_res_t imap_auth_login (IMAP_DATA* idata) +{ + char q_user[SHORT_STRING], q_pass[SHORT_STRING]; + char buf[LONG_STRING]; + int rc; + + 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..."); + + 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 + /* don't print the password unless we're at the ungodly debugging level + * of 5 or higher */ + + if (debuglevel < IMAP_LOG_PASS) + dprint (2, (debugfile, "Sending LOGIN command for %s...\n", + idata->conn->account.user)); +#endif + + snprintf (buf, sizeof (buf), "LOGIN %s %s", q_user, q_pass); + rc = imap_exec (buf, sizeof (buf), idata, buf, + IMAP_CMD_FAIL_OK | IMAP_CMD_PASS); + + if (!rc) + return IMAP_AUTH_SUCCESS; + + else if (rc == -1) + dprint (1, (debugfile, "imap_auth_login: Error logging in.\n")); + + mutt_error _("Login failed."); + sleep (2); + return IMAP_AUTH_FAILURE; +} diff --git a/imap/auth_sasl.c b/imap/auth_sasl.c new file mode 100644 index 00000000..ae77a17b --- /dev/null +++ b/imap/auth_sasl.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2000 Brendan Cully + * + * 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. + */ + +/* SASL login/authentication code */ + +#include "mutt.h" +#include "mutt_sasl.h" +#include "imap_private.h" +#include "auth.h" + +#include +#include + +/* imap_auth_sasl: Default authenticator if available. */ +imap_auth_res_t imap_auth_sasl (IMAP_DATA* idata) +{ + sasl_conn_t* saslconn; + int rc; + char buf[LONG_STRING]; + const char* mech; + char* pc; + unsigned int len; + + if (mutt_sasl_start () != SASL_OK) + return IMAP_AUTH_FAILURE; + + /* TODO: set fourth option to SASL_SECURITY_LAYER once we have a wrapper + * (ie more than auth code) for SASL. */ + rc = sasl_client_new ("imap", idata->conn->account.host, + mutt_sasl_get_callbacks (&idata->conn->account), 0, &saslconn); + + if (rc != SASL_OK) + { + dprint (1, (debugfile, "imap_auth_sasl: Error allocating SASL connection.\n")); + return IMAP_AUTH_FAILURE; + } + + rc = sasl_client_start (saslconn, idata->capstr, NULL, NULL, &pc, &len, + &mech); + + if (rc != SASL_OK && rc != SASL_CONTINUE) + { + dprint (1, (debugfile, "imap_auth_sasl: Failure starting authentication exchange. No shared mechanisms?\n")); + /* SASL doesn't support LOGIN, so fall back */ + + return IMAP_AUTH_UNAVAIL; + } + + mutt_message _("Authenticating (SASL)..."); + + snprintf (buf, sizeof (buf), "AUTHENTICATE %s", mech); + imap_cmd_start (idata, buf); + + /* I believe IMAP requires a server response (even if empty) before the + * client can send data, so len should always be zero at this point */ + if (len != 0) + { + dprint (1, (debugfile, "imap_auth_sasl: SASL produced invalid exchange!\n")); + goto bail; + } + + while (rc == SASL_CONTINUE) + { + if (mutt_socket_readln (buf, sizeof (buf), idata->conn) < 0) + goto bail; + + if (!mutt_strncmp (buf, "+ ", 2)) + if (sasl_decode64 (buf+2, strlen (buf+2), buf, &len) != SASL_OK) + { + dprint (1, (debugfile, "imap_auth_sasl: error base64-decoding server response.\n")); + goto bail; + } + + rc = sasl_client_step (saslconn, buf, len, NULL, &pc, &len); + + if (rc != SASL_CONTINUE) + break; + + /* send out response, or line break if none needed */ + if (sasl_encode64 (pc, len, buf, sizeof (buf), &len) != SASL_OK) + { + dprint (1, (debugfile, "imap_auth_sasl: error base64-encoding client response.\n")); + goto bail; + } + + strfcpy (buf + len, "\r\n", sizeof (buf) - len); + mutt_socket_write (idata->conn, buf); + } + + if (rc != SASL_OK) + goto bail; + + if (imap_code (buf)) + { + /* later we'll want to keep saslconn, when we support a protection layer. + * For now it shouldn't hurt to dispose of it at this point. */ + sasl_dispose (&saslconn); + return IMAP_AUTH_SUCCESS; + } + + bail: + mutt_error _("SASL authentication failed."); + sleep(2); + sasl_dispose (&saslconn); + + return IMAP_AUTH_FAILURE; +} diff --git a/imap/command.c b/imap/command.c index 12bacce2..02d2bb3c 100644 --- a/imap/command.c +++ b/imap/command.c @@ -274,6 +274,8 @@ static void cmd_parse_capabilities (IMAP_DATA* idata, char* s) { int x; + idata->capstr = safe_strdup (imap_next_word (s)); + while (*s) { for (x = 0; x < CAPMAX; x++) @@ -283,7 +285,7 @@ static void cmd_parse_capabilities (IMAP_DATA* idata, char* s) break; } s = imap_next_word (s); - } + } } /* cmd_parse_expunge: mark headers with new sequence ID and mark idata to diff --git a/imap/imap.c b/imap/imap.c index fc834d1d..991e300c 100644 --- a/imap/imap.c +++ b/imap/imap.c @@ -41,7 +41,7 @@ static int imap_get_delim (IMAP_DATA *idata); static char* imap_get_flags (LIST** hflags, char* s); static int imap_check_acl (IMAP_DATA *idata); -static int imap_check_capabilities (IMAP_DATA *idata); +static int imap_check_capabilities (IMAP_DATA* idata); static void imap_set_flag (IMAP_DATA* idata, int aclbit, int flag, const char* str, char* flags); @@ -86,7 +86,7 @@ void imap_logout_all (void) { tmp = conn; - if (conn->account.type == M_ACCT_TYPE_IMAP && conn->up) + if (conn->account.type == M_ACCT_TYPE_IMAP && conn->fd >= 0) { mutt_message (_("Closing connection to %s..."), conn->account.host); imap_logout ((IMAP_DATA*) conn->data); @@ -482,7 +482,8 @@ static int imap_check_acl (IMAP_DATA *idata) return 0; } -static int imap_check_capabilities (IMAP_DATA *idata) +/* imap_check_capabilities: make sure we can log in to this server. */ +static int imap_check_capabilities (IMAP_DATA* idata) { char buf[LONG_STRING]; @@ -491,13 +492,16 @@ static int imap_check_capabilities (IMAP_DATA *idata) imap_error ("imap_check_capabilities", buf); return -1; } + if (!(mutt_bit_isset(idata->capabilities,IMAP4) ||mutt_bit_isset(idata->capabilities,IMAP4REV1))) { mutt_error _("This IMAP server is ancient. Mutt does not work with it."); sleep (5); /* pause a moment to let the user see the error */ + return -1; } + return 0; } @@ -562,44 +566,35 @@ int imap_open_connection (IMAP_DATA* idata) idata->state = IMAP_CONNECTED; if (mutt_socket_readln (buf, sizeof (buf), idata->conn) < 0) - { - mutt_socket_close (idata->conn); - idata->state = IMAP_DISCONNECTED; - - return -1; - } + goto bail; if (mutt_strncmp ("* OK", buf, 4) == 0) { - if (imap_check_capabilities(idata) != 0 - || imap_authenticate (idata) != 0) - { - mutt_socket_close (idata->conn); - idata->state = IMAP_DISCONNECTED; - return -1; - } + if (imap_check_capabilities (idata) || imap_authenticate (idata)) + goto bail; } else if (mutt_strncmp ("* PREAUTH", buf, 9) == 0) { if (imap_check_capabilities(idata) != 0) - { - mutt_socket_close (idata->conn); - idata->state = IMAP_DISCONNECTED; - return -1; - } + goto bail; } else { imap_error ("imap_open_connection()", buf); - mutt_socket_close (idata->conn); - idata->state = IMAP_DISCONNECTED; - return -1; + goto bail; } + FREE (&idata->capstr); idata->state = IMAP_AUTHENTICATED; imap_get_delim (idata); return 0; + + bail: + FREE (&idata->capstr); + mutt_socket_close (idata->conn); + idata->state = IMAP_DISCONNECTED; + return -1; } /* imap_get_flags: Make a simple list out of a FLAGS response. diff --git a/imap/imap_private.h b/imap/imap_private.h index 3f56380f..e404f19d 100644 --- a/imap/imap_private.h +++ b/imap/imap_private.h @@ -110,7 +110,7 @@ enum TORDEREDSUBJECT, /* THREAD=ORDEREDSUBJECT */ UIDPLUS, /* RFC 2859: IMAP4 UIDPLUS extension */ AUTH_ANON, /* AUTH=ANONYMOUS */ - + CAPMAX }; @@ -145,6 +145,15 @@ typedef struct unsigned char status; unsigned char check_status; unsigned char capabilities[(CAPMAX + 7)/8]; + /* let me explain capstr: SASL needs the capability string (not bits). + * we have 3 options: + * 1. rerun CAPABILITY inside SASL function. + * 2. build appropriate CAPABILITY string by reverse-engineering from bits. + * 3. keep a copy until after authentication. + * I've chosen (3) for now. (2) might not be too bad, but it involves + * tracking all possible capabilities. bah. (1) I don't like because + * it's just no fun to get the same information twice */ + char* capstr; char seq[SEQLEN+1]; /* The following data is all specific to the currently SELECTED mbox */ diff --git a/init.h b/init.h index 6f8b6134..42d8bb87 100644 --- a/init.h +++ b/init.h @@ -685,15 +685,6 @@ struct option_t MuttVars[] = { ** .pp ** This variable defaults to your user name on the local machine. */ - { "imap_cramkey", DT_STR, R_NONE, UL &ImapCRAMKey, UL 0 }, - /* - ** .pp - ** Sets your CRAM secret, for use with the CRAM-MD5 IMAP authentication - ** method (this is the IMAP equivelent of APOP). This method will be - ** attempted automatically if the server supports it, in preference to the - ** less secure login technique. If you use CRAM-MD5, you do not need to set - ** \fIimap_pass\fP. - */ { "imap_pass", DT_STR, R_NONE, UL &ImapPass, UL 0 }, /* ** .pp diff --git a/m4/gettext.m4 b/m4/gettext.m4 index ad04c73c..52979162 100644 --- a/m4/gettext.m4 +++ b/m4/gettext.m4 @@ -17,7 +17,7 @@ AC_DEFUN(MUTT_AM_WITH_NLS, [AC_MSG_CHECKING([whether NLS is requested]) dnl Default is enabled NLS AC_ARG_ENABLE(nls, - [ --disable-nls do not use Native Language Support], + [ --disable-nls Do not use Native Language Support], USE_NLS=$enableval, USE_NLS=yes) AC_MSG_RESULT($USE_NLS) AC_SUBST(USE_NLS) @@ -29,7 +29,7 @@ AC_DEFUN(MUTT_AM_WITH_NLS, AC_DEFINE(ENABLE_NLS) AC_MSG_CHECKING([whether included gettext is requested]) AC_ARG_WITH(included-gettext, - [ --with-included-gettext use the GNU gettext library included here], + [ --with-included-gettext Use the GNU gettext library included here], nls_cv_force_use_gnu_gettext=$withval, nls_cv_force_use_gnu_gettext=no) AC_MSG_RESULT($nls_cv_force_use_gnu_gettext) @@ -84,7 +84,7 @@ AC_DEFUN(MUTT_AM_WITH_NLS, if test "$CATOBJEXT" = "NONE"; then AC_MSG_CHECKING([whether catgets can be used]) AC_ARG_WITH(catgets, - [ --with-catgets use catgets functions if available], + [ --with-catgets Use catgets functions if available], nls_cv_use_catgets=$withval, nls_cv_use_catgets=no) AC_MSG_RESULT($nls_cv_use_catgets) diff --git a/main.c b/main.c index 44d68193..64c6ce32 100644 --- a/main.c +++ b/main.c @@ -210,6 +210,12 @@ static void show_version (void) #endif ); puts ( +#ifdef USE_POP + "+USE_POP " +#else + "-USE_POP " +#endif + #ifdef USE_IMAP "+USE_IMAP " #else @@ -222,12 +228,6 @@ static void show_version (void) "-USE_GSS " #endif -#ifdef HAVE_HEIMDAHL - "+HAVE_HEIMDAHL " -#else - "-HAVE_HEIMDAHL " -#endif - #ifdef USE_SSL "+USE_SSL " @@ -235,14 +235,13 @@ static void show_version (void) "-USE_SSL " #endif - "\n" - -#ifdef USE_POP - "+USE_POP " +#ifdef USE_SASL + "+USE_SASL " #else - "-USE_POP " + "-USE_SASL " #endif - + "\n" + #ifdef HAVE_REGCOMP "+HAVE_REGCOMP " #else diff --git a/mutt_sasl.c b/mutt_sasl.c new file mode 100644 index 00000000..b02b5a2f --- /dev/null +++ b/mutt_sasl.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2000 Brendan Cully + * + * 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. + */ + +/* common SASL helper routines */ + +#include "mutt.h" +#include "account.h" +#include "mutt_sasl.h" +#include "mutt_socket.h" + +#include + +static sasl_callback_t mutt_sasl_callbacks[3]; + +/* callbacks */ +static int mutt_sasl_cb_log (void* context, int priority, const char* message); +static int mutt_sasl_cb_authname (void* context, int id, const char** result, + unsigned int* len); +static int mutt_sasl_cb_pass (sasl_conn_t* conn, void* context, int id, + sasl_secret_t** psecret); + +/* mutt_sasl_start: called before doing a SASL exchange - initialises library + * (if neccessary). */ +int mutt_sasl_start (void) +{ + static unsigned char sasl_init = 0; + + sasl_callback_t* callback, callbacks[2]; + int rc; + + if (!sasl_init) { + /* set up default logging callback */ + callback = callbacks; + + callback->id = SASL_CB_LOG; + callback->proc = mutt_sasl_cb_log; + callback->context = NULL; + callback++; + + callback->id = SASL_CB_LIST_END; + callback->proc = NULL; + callback->context = NULL; + + rc = sasl_client_init (callbacks); + + if (rc != SASL_OK) + { + dprint (1, (debugfile, "mutt_sasl_start: libsasl initialisation failed.\n")); + return SASL_FAIL; + } + + sasl_init = 1; + } + + return SASL_OK; +} + +sasl_callback_t* mutt_sasl_get_callbacks (ACCOUNT* account) +{ + sasl_callback_t* callback; + + callback = mutt_sasl_callbacks; + + callback->id = SASL_CB_AUTHNAME; + callback->proc = mutt_sasl_cb_authname; + callback->context = account; + callback++; + + callback->id = SASL_CB_PASS; + callback->proc = mutt_sasl_cb_pass; + callback->context = account; + callback++; + + callback->id = SASL_CB_LIST_END; + callback->proc = NULL; + callback->context = NULL; + + return mutt_sasl_callbacks; +} + +/* mutt_sasl_cb_log: callback to log SASL messages */ +static int mutt_sasl_cb_log (void* context, int priority, const char* message) +{ + dprint (priority, (debugfile, "SASL: %s\n", message)); + + return SASL_OK; +} + +/* mutt_sasl_cb_authname: callback to retrieve authname from ACCOUNT */ +static int mutt_sasl_cb_authname (void* context, int id, const char** result, + unsigned* len) +{ + ACCOUNT* account = (ACCOUNT*) context; + + *result = NULL; + if (len) + *len = 0; + + if (!account) + return SASL_BADPARAM; + + dprint (2, (debugfile, "mutt_sasl_cb_authname: getting user for %s:%u\n", + account->host, account->port)); + + if (mutt_account_getuser (account)) + return SASL_FAIL; + + *result = account->user; + + if (len) + *len = strlen (*result); + + return SASL_OK; +} + +static int mutt_sasl_cb_pass (sasl_conn_t* conn, void* context, int id, + sasl_secret_t** psecret) +{ + ACCOUNT* account = (ACCOUNT*) context; + int len; + + if (!account || !psecret) + return SASL_BADPARAM; + + dprint (2, (debugfile, + "mutt_sasl_cb_pass: getting password for %s@%s:%u\n", account->user, + account->host, account->port)); + + if (mutt_account_getpass (account)) + return SASL_FAIL; + + len = strlen (account->pass); + + *psecret = (sasl_secret_t*) malloc (sizeof (sasl_secret_t) + len); + (*psecret)->len = len; + strcpy ((*psecret)->data, account->pass); + + return SASL_OK; +} diff --git a/mutt_sasl.h b/mutt_sasl.h new file mode 100644 index 00000000..e979e160 --- /dev/null +++ b/mutt_sasl.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2000 Brendan Cully + * + * 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. + */ + +/* common SASL helper routines */ + +#ifndef _MUTT_SASL_H_ +#define _MUTT_SASL_H_ 1 + +#include "mutt_socket.h" + +#include + +int mutt_sasl_start (void); +sasl_callback_t* mutt_sasl_get_callbacks (ACCOUNT* account); + +#endif /* _MUTT_SASL_H_ */ diff --git a/mutt_socket.c b/mutt_socket.c index 0d2d1270..a588fe1e 100644 --- a/mutt_socket.c +++ b/mutt_socket.c @@ -22,7 +22,7 @@ #include "globals.h" #include "mutt_socket.h" #ifdef USE_SSL -#include "imap_ssl.h" +# include "imap_ssl.h" #endif #include @@ -44,20 +44,17 @@ static CONNECTION* socket_new_conn (); /* Wrappers */ int mutt_socket_open (CONNECTION* conn) { - int rc; - - rc = conn->open (conn); - if (!rc) - conn->up = 1; - - return rc; + return conn->open (conn); } int mutt_socket_close (CONNECTION* conn) { - conn->up = 0; + int rc; + + rc = conn->close (conn); + conn->fd = -1; - return conn->close (conn); + return rc; } int mutt_socket_write_d (CONNECTION *conn, const char *buf, int dbg) @@ -146,11 +143,10 @@ void mutt_socket_free (CONNECTION* conn) } /* mutt_conn_find: find a connection off the list of connections whose - * account matches account. If newconn is true, don't reuse one, but add - * it to the list. If start is not null, only search for connections after - * the given connection (allows higher level socket code to make more - * fine-grained searches than account info - eg in IMAP we may wish - * to find a connection which is not in IMAP_SELECTED state) */ + * account matches account. If start is not null, only search for + * connections after the given connection (allows higher level socket code + * to make more fine-grained searches than account info - eg in IMAP we may + * wish to find a connection which is not in IMAP_SELECTED state) */ CONNECTION* mutt_conn_find (const CONNECTION* start, const ACCOUNT* account) { CONNECTION* conn; diff --git a/mutt_socket.h b/mutt_socket.h index 6b8590da..4e151836 100644 --- a/mutt_socket.h +++ b/mutt_socket.h @@ -21,6 +21,7 @@ #define _MUTT_SOCKET_H_ 1 #include "account.h" +#include "lib.h" /* logging levels */ #define M_SOCK_LOG_CMD 2 @@ -44,10 +45,6 @@ typedef struct _connection int (*write) (struct _connection *conn, const char *buf); int (*open) (struct _connection *conn); int (*close) (struct _connection *conn); - - /* status bits */ - - int up : 1; /* is the connection up? */ } CONNECTION; int mutt_socket_open (CONNECTION* conn);