diff options
-rw-r--r-- | Makefile.am | 8 | ||||
-rw-r--r-- | acconfig.h | 6 | ||||
-rw-r--r-- | account.c | 54 | ||||
-rw-r--r-- | account.h | 8 | ||||
-rw-r--r-- | configure.in | 35 | ||||
-rw-r--r-- | globals.h | 1 | ||||
-rw-r--r-- | imap/Makefile.am | 13 | ||||
-rw-r--r-- | imap/auth.c | 399 | ||||
-rw-r--r-- | imap/auth.h | 45 | ||||
-rw-r--r-- | imap/auth_anon.c | 72 | ||||
-rw-r--r-- | imap/auth_cram.c | 170 | ||||
-rw-r--r-- | imap/auth_gss.c | 65 | ||||
-rw-r--r-- | imap/auth_login.c | 64 | ||||
-rw-r--r-- | imap/auth_sasl.c | 122 | ||||
-rw-r--r-- | imap/command.c | 4 | ||||
-rw-r--r-- | imap/imap.c | 43 | ||||
-rw-r--r-- | imap/imap_private.h | 11 | ||||
-rw-r--r-- | init.h | 9 | ||||
-rw-r--r-- | m4/gettext.m4 | 6 | ||||
-rw-r--r-- | main.c | 23 | ||||
-rw-r--r-- | mutt_sasl.c | 154 | ||||
-rw-r--r-- | mutt_sasl.h | 31 | ||||
-rw-r--r-- | mutt_socket.c | 26 | ||||
-rw-r--r-- | mutt_socket.h | 5 |
24 files changed, 879 insertions, 495 deletions
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 \ @@ -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 */ @@ -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; +} @@ -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]) @@ -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 <brendan@kublai.com> + * + * 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 <brendan@kublai.com> + * + * 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 <brendan@kublai.com> + * + * 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 |