X-Git-Url: http://git.madism.org/?p=apps%2Fmadmutt.git;a=blobdiff_plain;f=imap%2Fauth.c;fp=imap%2Fauth.c;h=301c27da6711c0871fb2170ce471543f145c60f0;hp=8b328078a86a4f9c8d74ba28f79f59e931a2ab6c;hb=b314f6b74b24679a37333d74f49553b0a7577d11;hpb=6cc64ac1d90eed0c8280c3f7b4c2ab514d5224d1 diff --git a/imap/auth.c b/imap/auth.c index 8b32807..301c27d 100644 --- a/imap/auth.c +++ b/imap/auth.c @@ -13,10 +13,195 @@ #include +#include +#include + #include "mutt.h" +#include "mutt_sasl.h" #include "imap_private.h" -#include "auth.h" +typedef enum { + IMAP_AUTH_SUCCESS = 0, + IMAP_AUTH_FAILURE, + IMAP_AUTH_UNAVAIL +} imap_auth_res_t; + +typedef struct { + /* do authentication, using named method or any available if method is NULL */ + imap_auth_res_t (*authenticate) (IMAP_DATA * idata, const char *method); + /* name of authentication method supported, NULL means variable. If this + * is not null, authenticate may ignore the second parameter. */ + const char *method; +} imap_auth_t; + +/* imap_auth_sasl: Default authenticator if available. */ +static imap_auth_res_t imap_auth_sasl (IMAP_DATA * idata, const char *method) +{ + 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; + } + + 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); + + if (rc != SASL_OK && rc != SASL_CONTINUE) { + /* SASL doesn't support LOGIN, so fall back */ + return IMAP_AUTH_UNAVAIL; + } + + 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 + 2, m_strlen(idata->cmd.buf + 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; + } + + 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)) { + mutt_sasl_setup_conn (idata->conn, saslconn); + return IMAP_AUTH_SUCCESS; + } + +bail: + mutt_error _("SASL authentication failed."); + mutt_sleep (2); + sasl_dispose (&saslconn); + + return IMAP_AUTH_FAILURE; +} +/* imap_auth_login: Plain LOGIN support */ +static imap_auth_res_t imap_auth_login(IMAP_DATA *idata, const char *method) +{ + char q_user[STRING], q_pass[STRING]; + char buf[STRING]; + int rc; + + if (mutt_bit_isset (idata->capabilities, LOGINDISABLED)) { + mutt_message _("LOGIN disabled on this server."); + + return IMAP_AUTH_UNAVAIL; + } + + if (mutt_account_getlogin (&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.login); + imap_quote_string(q_pass, sizeof(q_pass), idata->conn->account.pass); + + snprintf(buf, sizeof(buf), "LOGIN %s %s", q_user, q_pass); + rc = imap_exec(idata, buf, IMAP_CMD_FAIL_OK | IMAP_CMD_PASS); + + if (!rc) + return IMAP_AUTH_SUCCESS; + + mutt_error _("Login failed."); + + mutt_sleep (2); + return IMAP_AUTH_FAILURE; +} static imap_auth_t imap_authenticators[] = { {imap_auth_sasl, NULL}, {imap_auth_login, "login"},