X-Git-Url: http://git.madism.org/?p=apps%2Fmadmutt.git;a=blobdiff_plain;f=imap%2Fauth.c;h=fd3b8ceb7cfb4df3704cba9d8b08163bc3c36d25;hp=23473e192c5d56589a2a0a00075f2e5bc27dba36;hb=819c071fa7efc8dffb4dd92f36f0111227ff692f;hpb=bbc4fd52516a8afefbd14c77e34f8389d6f0a6ed diff --git a/imap/auth.c b/imap/auth.c index 23473e1..fd3b8ce 100644 --- a/imap/auth.c +++ b/imap/auth.c @@ -11,93 +11,185 @@ /* IMAP login/authentication code */ -#if HAVE_CONFIG_H -# include "config.h" -#endif +#include -#include "lib/mem.h" -#include "lib/intl.h" -#include "lib/debug.h" +#include +#include #include "mutt.h" -#include "ascii.h" +#include "mutt_sasl.h" #include "imap_private.h" -#include "auth.h" - -static imap_auth_t imap_authenticators[] = { -#ifdef USE_SASL - {imap_auth_sasl, NULL}, -#else - {imap_auth_anon, "anonymous"}, -#endif -#ifdef USE_GSS - {imap_auth_gss, "gssapi"}, -#endif - /* SASL includes CRAM-MD5 (and GSSAPI, but that's not enabled by default) */ -#ifndef USE_SASL - {imap_auth_cram_md5, "cram-md5"}, -#endif - {imap_auth_login, "login"}, - - {NULL} + +enum { + IMAP_AUTH_SUCCESS = 0, + IMAP_AUTH_FAILURE, + IMAP_AUTH_UNAVAIL }; -/* imap_authenticate: Attempt to authenticate using either user-specified - * authentication method if specified, or any. */ -int imap_authenticate (IMAP_DATA * idata) +/* imap_auth_sasl: Default authenticator if available. */ +static int imap_auth_sasl(IMAP_DATA * idata, const char *method) { - imap_auth_t *authenticator; - char *methods; - char *method; - char *delim; - int r = -1; - - if (ImapAuthenticators && *ImapAuthenticators) { - /* Try user-specified list of authentication methods */ - methods = str_dup (ImapAuthenticators); - - for (method = methods; method; method = delim) { - delim = strchr (method, ':'); - if (delim) - *delim++ = '\0'; - if (!method[0]) - continue; - - debug_print (2, ("Trying method %s\n", method)); - authenticator = imap_authenticators; - - while (authenticator->authenticate) { - if (!authenticator->method || - !ascii_strcasecmp (authenticator->method, method)) - if ((r = authenticator->authenticate (idata, method)) != - IMAP_AUTH_UNAVAIL) { - mem_free (&methods); - return r; - } + sasl_conn_t *saslconn; + sasl_interact_t *interaction = NULL; + int rc, irc; + char buf[HUGE_STRING]; + const char *mech; + + const char *pc = NULL; + unsigned int len, olen; + unsigned char client_start; + + if (mutt_sasl_client_new (idata->conn, &saslconn) < 0) { + return IMAP_AUTH_FAILURE; + } - authenticator++; - } + rc = SASL_FAIL; + + /* If the user hasn't specified a method, use any available */ + if (!method) { + method = idata->capstr; + + /* hack for SASL ANONYMOUS support: + * 1. Fetch username. If it's "" or "anonymous" then + * 2. attempt sasl_client_start with only "AUTH=ANONYMOUS" capability + * 3. if sasl_client_start fails, fall through... */ + + if (mutt_account_getuser (&idata->conn->account)) + return IMAP_AUTH_FAILURE; + + if (mutt_bit_isset (idata->capabilities, AUTH_ANON) && + (!idata->conn->account.user[0] || + !m_strncmp(idata->conn->account.user, "anonymous", 9))) + rc = sasl_client_start (saslconn, "AUTH=ANONYMOUS", NULL, &pc, &olen, + &mech); + } + + if (rc != SASL_OK && rc != SASL_CONTINUE) + do { + rc = sasl_client_start (saslconn, method, &interaction, + &pc, &olen, &mech); + if (rc == SASL_INTERACT) + mutt_sasl_interact (interaction); } + while (rc == SASL_INTERACT); + + client_start = (olen > 0); - mem_free (&methods); + if (rc != SASL_OK && rc != SASL_CONTINUE) { + /* SASL doesn't support LOGIN, so fall back */ + return IMAP_AUTH_UNAVAIL; } - else { - /* Fall back to default: any authenticator */ - debug_print (2, ("Using any available method.\n")); - authenticator = imap_authenticators; - - while (authenticator->authenticate) { - if ((r = - authenticator->authenticate (idata, NULL)) != IMAP_AUTH_UNAVAIL) - return r; - authenticator++; + + mutt_message (_("Authenticating (%s)..."), mech); + + snprintf (buf, sizeof (buf), "AUTHENTICATE %s", mech); + imap_cmd_start (idata, buf); + irc = IMAP_CMD_CONTINUE; + + /* looping protocol */ + while (rc == SASL_CONTINUE || olen > 0) { + do + irc = imap_cmd_step (idata); + while (irc == IMAP_CMD_CONTINUE); + + if (method && irc == IMAP_CMD_NO) { + sasl_dispose (&saslconn); + return IMAP_AUTH_UNAVAIL; + } + + if (irc == IMAP_CMD_BAD || irc == IMAP_CMD_NO) + goto bail; + + if (irc == IMAP_CMD_RESPOND) { + if (sasl_decode64(idata->cmd.buf.data + 2, idata->cmd.buf.len - 2, buf, + LONG_STRING - 1, &len) != SASL_OK) + { + goto bail; + } + } + + /* client-start is only available with the SASL-IR extension, but + * SASL 2.1 seems to want to use it regardless, at least for DIGEST + * fast reauth. Override if the server sent an initial continuation */ + if (!client_start || buf[0]) { + do { + rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen); + if (rc == SASL_INTERACT) + mutt_sasl_interact (interaction); + } + while (rc == SASL_INTERACT); } + else + client_start = 0; + + /* send out response, or line break if none needed */ + if (olen) { + if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK) { + goto bail; + } + } + + if (irc == IMAP_CMD_RESPOND) { + m_strcpy(buf + olen, sizeof(buf) - olen, "\r\n"); + mutt_socket_write (idata->conn, buf); + } + + /* If SASL has errored out, send an abort string to the server */ + if (rc < 0) { + mutt_socket_write (idata->conn, "*\r\n"); + } + + olen = 0; } - if (r == IMAP_AUTH_UNAVAIL) { - mutt_error (_("No authenticators available")); - mutt_sleep (1); + while (irc != IMAP_CMD_OK) + if ((irc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE) + break; + + if (rc != SASL_OK) + goto bail; + + if (imap_code(idata->cmd.buf.data)) { + mutt_sasl_setup_conn (idata->conn, saslconn); + return IMAP_AUTH_SUCCESS; } - return r; +bail: + mutt_error _("SASL authentication failed."); + mutt_sleep (2); + sasl_dispose (&saslconn); + + return IMAP_AUTH_FAILURE; +} + +int imap_authenticate (IMAP_DATA * idata) +{ + int r = -1; + + if (!m_strisempty(ImapAuthenticators)) { + const char *p, *q; + char buf[STRING]; + + for (p = ImapAuthenticators;; p = q) { + while (*p == ':') + p++; + if (!*p) + break; + + q = strchrnul(p, ':'); + m_strncpy(buf, sizeof(buf), p, q - p); + + if ((r = imap_auth_sasl(idata, buf)) != IMAP_AUTH_UNAVAIL) { + return r; + } + } + } else { + if ((r = imap_auth_sasl(idata, NULL)) != IMAP_AUTH_UNAVAIL) { + return r; + } + } + + mutt_error (_("No authenticators available")); + mutt_sleep (1); + return r; }