From: Pierre Habouzit Date: Mon, 14 May 2007 21:33:01 +0000 (+0200) Subject: Simplify pop code further. X-Git-Url: http://git.madism.org/?a=commitdiff_plain;h=c05e2953d8c688f8e1321bbf717298c9177fe5c5;p=apps%2Fmadmutt.git Simplify pop code further. Signed-off-by: Pierre Habouzit --- diff --git a/init.h b/init.h index 895bc0a..b8dfcc3 100644 --- a/init.h +++ b/init.h @@ -1681,14 +1681,6 @@ struct option_t MuttVars[] = { ** .pp ** Example: \fTset pop_authenticators="digest-md5:apop:user"\fP */ - {"pop_auth_try_all", DT_BOOL, R_NONE, OPTPOPAUTHTRYALL, "yes" }, - /* - ** .pp - ** If \fIset\fP, Madmutt will try all available methods. When \fIunset\fP, Madmutt will - ** only fall back to other authentication methods if the previous - ** methods are unavailable. If a method is available but authentication - ** fails, Madmutt will not connect to the POP server. - */ {"pop_mail_check", DT_NUM, R_NONE, UL &PopCheckTimeout, "60" }, /* ** .pp diff --git a/mutt.h b/mutt.h index 310c012..815b091 100644 --- a/mutt.h +++ b/mutt.h @@ -265,7 +265,6 @@ enum { OPTPAGERSTOP, OPTPIPEDECODE, OPTPIPESPLIT, - OPTPOPAUTHTRYALL, OPTPOPLAST, OPTPRINTDECODE, OPTPRINTSPLIT, diff --git a/pop.c b/pop.c index 55943c5..43b8959 100644 --- a/pop.c +++ b/pop.c @@ -68,7 +68,6 @@ typedef struct { unsigned use_stls : 2; cmd_status cmd_capa : 2; /* optional command CAPA */ cmd_status cmd_stls : 2; /* optional command STLS */ - cmd_status cmd_user : 2; /* optional command USER */ cmd_status cmd_uidl : 2; /* optional command UIDL */ cmd_status cmd_top : 2; /* optional command TOP */ unsigned resp_codes : 1; /* server supports extended response codes */ @@ -189,6 +188,283 @@ static void pop_logout (CONTEXT * ctx) return; } +/* }}} */ +/* Authentication {{{ */ + +/* SASL authenticator */ +static pop_auth_res_t pop_auth_sasl(pop_data_t *pop_data, const char *method) +{ + sasl_conn_t *saslconn; + sasl_interact_t *interaction = NULL; + + char buf[LONG_STRING], inbuf[LONG_STRING]; + const char *mech, *pc = NULL; + unsigned int len, olen; + unsigned char client_start; + int rc; + + if (mutt_sasl_client_new(pop_data->conn, &saslconn) < 0) { + return POP_A_FAILURE; + } + + if (!method) + method = pop_data->auth_list; + + for (;;) { + rc = sasl_client_start(saslconn, method, &interaction, &pc, &olen, + &mech); + if (rc != SASL_INTERACT) + break; + mutt_sasl_interact(interaction); + } + + if (rc != SASL_OK && rc != SASL_CONTINUE) { + return POP_A_UNAVAIL; + } + + client_start = (olen > 0); + mutt_message(_("Authenticating (SASL)...")); + snprintf(buf, sizeof (buf), "AUTH %s", mech); + olen = strlen (buf); + + /* looping protocol */ + for (;;) { + m_strcpy(buf + olen, sizeof(buf) - olen, "\r\n"); + mutt_socket_write(pop_data->conn, buf); + if (mutt_socket_readln(inbuf, sizeof(inbuf), pop_data->conn) < 0) { + sasl_dispose(&saslconn); + pop_data->status = POP_DISCONNECTED; + return POP_A_SOCKET; + } + + if (rc != SASL_CONTINUE) + break; + + if (!m_strncmp(inbuf, "+ ", 2) + && sasl_decode64(inbuf, strlen(inbuf), + buf, sizeof(buf) - 1, &len) != SASL_OK) + { + goto bail; + } + + if (!client_start) { + for (;;) { + rc = sasl_client_step(saslconn, buf, len, &interaction, &pc, + &olen); + if (rc != SASL_INTERACT) + break; + mutt_sasl_interact(interaction); + } + } else { + client_start = 0; + } + + if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK)) + break; + + /* send out response, or line break if none needed */ + if (pc) { + if (sasl_encode64(pc, olen, buf, sizeof(buf), &olen) != SASL_OK) { + goto bail; + } + p_delete((char **)&pc); + } + } + + if (rc != SASL_OK) + goto bail; + + if (!m_strncmp(inbuf, "+OK", 3)) { + mutt_sasl_setup_conn(pop_data->conn, saslconn); + return POP_A_SUCCESS; + } + + bail: + sasl_dispose(&saslconn); + p_delete((char **)&pc); + + /* terminate SASL session if the last responce is not +OK nor -ERR */ + if (!m_strncmp(inbuf, "+ ", 2)) { + snprintf(buf, sizeof(buf), "*\r\n"); + if (pop_query(pop_data, buf, sizeof(buf)) == PQ_NOT_CONNECTED) + return POP_A_SOCKET; + } + + mutt_error(_("SASL authentication failed.")); + mutt_sleep(2); + + return POP_A_FAILURE; +} + +/* APOP authenticator */ +static pop_auth_res_t pop_auth_apop(pop_data_t *pop_data, const char *method) +{ + MD5_CTX mdContext; + unsigned char digest[16]; + char hash[33]; + char buf[LONG_STRING]; + int i; + + if (!pop_data->timestamp) + return POP_A_UNAVAIL; + + mutt_message _("Authenticating (APOP)..."); + + /* Compute the authentication hash to send to the server */ + MD5Init(&mdContext); + MD5Update(&mdContext, (unsigned char *)pop_data->timestamp, + strlen(pop_data->timestamp)); + MD5Update(&mdContext, (unsigned char *)pop_data->conn->account.pass, + strlen(pop_data->conn->account.pass)); + MD5Final(digest, &mdContext); + + for (i = 0; i < countof(digest); i++) + sprintf(hash + 2 * i, "%02x", digest[i]); + + /* Send APOP command to server */ + snprintf(buf, sizeof(buf), "APOP %s %s\r\n", pop_data->conn->account.user, + hash); + + switch (pop_query(pop_data, buf, sizeof(buf))) { + case PQ_OK: + return POP_A_SUCCESS; + case PQ_NOT_CONNECTED: + return POP_A_SOCKET; + case PFD_FUNCT_ERROR: + case PQ_ERR: + default: + break; + } + + mutt_error("%s %s", _("APOP authentication failed."), pop_data->err_msg); + mutt_sleep(2); + + return POP_A_FAILURE; +} + +/* USER authenticator */ +static pop_auth_res_t pop_auth_user(pop_data_t *pop_data, const char *method) +{ + char buf[LONG_STRING]; + pop_query_status ret; + + mutt_message(_("Logging in...")); + snprintf(buf, sizeof(buf), "USER %s\r\n", pop_data->conn->account.user); + ret = pop_query(pop_data, buf, sizeof (buf)); + + if (ret == PQ_ERR) { + snprintf(pop_data->err_msg, sizeof(pop_data->err_msg), + _("Command USER is not supported by server.")); + } + + if (ret == PQ_OK) { + snprintf(buf, sizeof(buf), "PASS %s\r\n", + pop_data->conn->account.pass); + ret = pop_query(pop_data, buf, sizeof(buf)); + } + + switch (ret) { + case PQ_OK: + return POP_A_SUCCESS; + case PQ_NOT_CONNECTED: + return POP_A_SOCKET; + default: + mutt_error ("%s %s", _("Login failed."), pop_data->err_msg); + mutt_sleep (2); + return POP_A_FAILURE; + } +} + +typedef struct { + pop_auth_res_t (*do_auth)(pop_data_t *, const char *); + const char *method; +} pop_auth_t; + +static pop_query_status pop_authenticate (pop_data_t * pop_data) +{ + static pop_auth_t const pop_authenticators[] = { + {pop_auth_sasl, NULL}, + {pop_auth_apop, "apop"}, + {pop_auth_user, "user"}, + {NULL, NULL} + }; + + ACCOUNT *act = &pop_data->conn->account; + int attempts = 0, ret = POP_A_UNAVAIL; + const pop_auth_t *auth; + + if (mutt_account_getuser(act) || !act->user[0] + || mutt_account_getpass(act) || !act->pass[0]) + { + return PFD_FUNCT_ERROR; + } + + if (!m_strisempty(PopAuthenticators)) { + const char *p, *q; + char buf[STRING]; + + for (p = PopAuthenticators; ; p = q) { + while (*p == ':') + p++; + if (!*p) + break; + + q = strchrnul(p, ':'); + m_strncpy(buf, sizeof(buf), p, q - p); + + for (auth = pop_authenticators; auth->do_auth; auth++) { + if (auth->method && ascii_strcasecmp(auth->method, buf)) + continue; + + ret = auth->do_auth(pop_data, buf); + if (ret == POP_A_SOCKET) { + if (pop_connect(pop_data) == PQ_OK) { + ret = auth->do_auth(pop_data, buf); + } else { + ret = POP_A_FAILURE; + } + } + + if (ret != POP_A_UNAVAIL) + attempts++; + if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET) { + goto ok; + } + } + } + } else { + /* Fall back to default: any authenticator */ + for (auth = pop_authenticators; auth->do_auth; auth++) { + ret = auth->do_auth(pop_data, auth->method); + if (ret == POP_A_SOCKET) { + if (pop_connect(pop_data) == PQ_OK) { + ret = auth->do_auth(pop_data, auth->method); + } else { + ret = POP_A_FAILURE; + } + } + + if (ret != POP_A_UNAVAIL) + attempts++; + if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET) + break; + } + } + + ok: + switch (ret) { + case POP_A_SUCCESS: + return PQ_OK; + case POP_A_SOCKET: + return PQ_NOT_CONNECTED; + case POP_A_UNAVAIL: + if (!attempts) + mutt_error(_("No authenticators available")); + } + + return PQ_ERR; +} + /* }}} */ /* @@ -269,9 +545,6 @@ static int fetch_capa (char *line, void *data) else if (!ascii_strncasecmp (line, "STLS", 4)) pop_data->cmd_stls = CMD_AVAILABLE; - else if (!ascii_strncasecmp (line, "USER", 4)) - pop_data->cmd_user = CMD_AVAILABLE; - else if (!ascii_strncasecmp (line, "UIDL", 4)) pop_data->cmd_uidl = CMD_AVAILABLE; @@ -317,7 +590,6 @@ static pop_query_status pop_capabilities (pop_data_t * pop_data, int mode) if (mode == 0) { pop_data->cmd_capa = CMD_NOT_AVAILABLE; pop_data->cmd_stls = CMD_NOT_AVAILABLE; - pop_data->cmd_user = CMD_NOT_AVAILABLE; pop_data->cmd_uidl = CMD_NOT_AVAILABLE; pop_data->cmd_top = CMD_NOT_AVAILABLE; pop_data->resp_codes = 0; @@ -331,16 +603,12 @@ static pop_query_status pop_capabilities (pop_data_t * pop_data, int mode) m_strcpy(buf, sizeof(buf), "CAPA\r\n"); switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data)) { case PQ_OK: - { - pop_data->cmd_capa = CMD_AVAILABLE; - break; - } + pop_data->cmd_capa = CMD_AVAILABLE; + break; case PFD_FUNCT_ERROR: case PQ_ERR: - { - pop_data->cmd_capa = CMD_NOT_AVAILABLE; - break; - } + pop_data->cmd_capa = CMD_NOT_AVAILABLE; + break; case PQ_NOT_CONNECTED: return PQ_NOT_CONNECTED; } @@ -348,7 +616,6 @@ static pop_query_status pop_capabilities (pop_data_t * pop_data, int mode) /* CAPA not supported, use defaults */ if (mode == 0 && pop_data->cmd_capa == CMD_NOT_AVAILABLE) { - pop_data->cmd_user = CMD_UNKNOWN; pop_data->cmd_uidl = CMD_UNKNOWN; pop_data->cmd_top = CMD_UNKNOWN; @@ -377,328 +644,6 @@ static pop_query_status pop_capabilities (pop_data_t * pop_data, int mode) return PQ_OK; } -/* Authentication {{{ */ - -/* SASL authenticator */ -static pop_auth_res_t pop_auth_sasl (pop_data_t * pop_data, const char *method) -{ - sasl_conn_t *saslconn; - sasl_interact_t *interaction = NULL; - int rc; - char buf[LONG_STRING]; - char inbuf[LONG_STRING]; - const char *mech; - - const char *pc = NULL; - unsigned int len, olen; - unsigned char client_start; - - if (mutt_sasl_client_new (pop_data->conn, &saslconn) < 0) { - return POP_A_FAILURE; - } - - if (!method) - method = pop_data->auth_list; - - for (;;) { - rc = sasl_client_start (saslconn, method, &interaction, &pc, &olen, - &mech); - if (rc != SASL_INTERACT) - break; - mutt_sasl_interact (interaction); - } - - if (rc != SASL_OK && rc != SASL_CONTINUE) { - /* SASL doesn't support suggested mechanisms, so fall back */ - return POP_A_UNAVAIL; - } - - client_start = (olen > 0); - - mutt_message _("Authenticating (SASL)..."); - - snprintf (buf, sizeof (buf), "AUTH %s", mech); - olen = strlen (buf); - - /* looping protocol */ - for (;;) { - m_strcpy(buf + olen, sizeof(buf) - olen, "\r\n"); - mutt_socket_write (pop_data->conn, buf); - if (mutt_socket_readln (inbuf, sizeof (inbuf), pop_data->conn) < 0) { - sasl_dispose (&saslconn); - pop_data->status = POP_DISCONNECTED; - return POP_A_SOCKET; - } - - if (rc != SASL_CONTINUE) - break; - - if (!m_strncmp(inbuf, "+ ", 2) - && sasl_decode64 (inbuf, strlen (inbuf), buf, LONG_STRING - 1, - &len) != SASL_OK) - { - goto bail; - } - - if (!client_start) - for (;;) { - rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen); - if (rc != SASL_INTERACT) - break; - mutt_sasl_interact (interaction); - } - else - client_start = 0; - - if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK)) - break; - - /* send out response, or line break if none needed */ - if (pc) { - if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK) { - goto bail; - } - - /* sasl_client_st(art|ep) allocate pc with malloc, expect me to - * free it */ - p_delete((char **)&pc); - } - } - - if (rc != SASL_OK) - goto bail; - - if (!m_strncmp(inbuf, "+OK", 3)) { - mutt_sasl_setup_conn (pop_data->conn, saslconn); - return POP_A_SUCCESS; - } - -bail: - sasl_dispose (&saslconn); - - /* terminate SASL sessoin if the last responce is not +OK nor -ERR */ - if (!m_strncmp(inbuf, "+ ", 2)) { - snprintf (buf, sizeof (buf), "*\r\n"); - if (pop_query (pop_data, buf, sizeof (buf)) == PQ_NOT_CONNECTED) - return POP_A_SOCKET; - } - - mutt_error _("SASL authentication failed."); - - mutt_sleep (2); - - return POP_A_FAILURE; -} - -/* APOP authenticator */ -static pop_auth_res_t pop_auth_apop (pop_data_t * pop_data, - const char *method __attribute__ ((unused))) -{ - MD5_CTX mdContext; - unsigned char digest[16]; - char hash[33]; - char buf[LONG_STRING]; - int i; - - if (!pop_data->timestamp) - return POP_A_UNAVAIL; - - mutt_message _("Authenticating (APOP)..."); - - /* Compute the authentication hash to send to the server */ - MD5Init (&mdContext); - MD5Update (&mdContext, (unsigned char *) pop_data->timestamp, - strlen (pop_data->timestamp)); - MD5Update (&mdContext, (unsigned char *) pop_data->conn->account.pass, - strlen (pop_data->conn->account.pass)); - MD5Final (digest, &mdContext); - - for (i = 0; i < ssizeof(digest); i++) - sprintf (hash + 2 * i, "%02x", digest[i]); - - /* Send APOP command to server */ - snprintf(buf, sizeof(buf), "APOP %s %s\r\n", pop_data->conn->account.user, - hash); - - switch (pop_query (pop_data, buf, sizeof (buf))) { - case PQ_OK: - return POP_A_SUCCESS; - case PQ_NOT_CONNECTED: - return POP_A_SOCKET; - case PFD_FUNCT_ERROR: - case PQ_ERR: - default: - break; - } - - mutt_error ("%s %s", _("APOP authentication failed."), pop_data->err_msg); - mutt_sleep (2); - - return POP_A_FAILURE; -} - -/* USER authenticator */ -static pop_auth_res_t pop_auth_user (pop_data_t * pop_data, - const char *method __attribute__ ((unused))) -{ - char buf[LONG_STRING]; - pop_query_status ret; - - if (pop_data->cmd_user == CMD_NOT_AVAILABLE) - return POP_A_UNAVAIL; - - mutt_message _("Logging in..."); - - snprintf (buf, sizeof (buf), "USER %s\r\n", pop_data->conn->account.user); - ret = pop_query (pop_data, buf, sizeof (buf)); - - if (pop_data->cmd_user == CMD_UNKNOWN) { - if (ret == PQ_OK) { - pop_data->cmd_user = CMD_AVAILABLE; - } - - if (ret == PQ_ERR) { - pop_data->cmd_user = CMD_NOT_AVAILABLE; - - snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), - _("Command USER is not supported by server.")); - } - } - - if (ret == PQ_OK) { - snprintf (buf, sizeof (buf), "PASS %s\r\n", pop_data->conn->account.pass); - ret = pop_query (pop_data, buf, sizeof (buf)); - } - - switch (ret) { - case PQ_OK: - return POP_A_SUCCESS; - case PQ_NOT_CONNECTED: - return POP_A_SOCKET; - case PFD_FUNCT_ERROR: - case PQ_ERR: - default: - break; - } - - mutt_error ("%s %s", _("Login failed."), pop_data->err_msg); - mutt_sleep (2); - - return POP_A_FAILURE; -} - -typedef struct { - /* do authentication, using named method or any available if method is NULL */ - pop_auth_res_t (*authenticate) (pop_data_t *, const char *); - /* name of authentication method supported, NULL means variable. If this - * is not null, authenticate may ignore the second parameter. */ - const char *method; -} pop_auth_t; - -/* - * Authentication - * 0 - successful, - * -1 - conection lost, - * -2 - login failed, - * -3 - authentication canceled. -*/ -static pop_query_status pop_authenticate (pop_data_t * pop_data) -{ - static pop_auth_t const pop_authenticators[] = { - {pop_auth_sasl, NULL}, - {pop_auth_apop, "apop"}, - {pop_auth_user, "user"}, - {NULL, NULL} - }; - - ACCOUNT *act = &pop_data->conn->account; - const pop_auth_t *authenticator; - char *methods; - char *comma; - char *method; - int attempts = 0; - int ret = POP_A_UNAVAIL; - - if (mutt_account_getuser (act) || !act->user[0] || - mutt_account_getpass (act) || !act->pass[0]) - return PFD_FUNCT_ERROR; - - if (PopAuthenticators && *PopAuthenticators) { - /* Try user-specified list of authentication methods */ - methods = m_strdup(PopAuthenticators); - method = methods; - - while (method) { - comma = strchr (method, ':'); - if (comma) - *comma++ = '\0'; - authenticator = pop_authenticators; - - while (authenticator->authenticate) { - if (!authenticator->method || - !ascii_strcasecmp (authenticator->method, method)) { - ret = authenticator->authenticate (pop_data, method); - if (ret == POP_A_SOCKET) { - if (pop_connect(pop_data) == PQ_OK) { - ret = authenticator->authenticate (pop_data, method); - } else { - ret = POP_A_FAILURE; - } - } - - if (ret != POP_A_UNAVAIL) - attempts++; - if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET || - (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL))) { - comma = NULL; - break; - } - } - authenticator++; - } - - method = comma; - } - - p_delete(&methods); - } - else { - /* Fall back to default: any authenticator */ - authenticator = pop_authenticators; - - while (authenticator->authenticate) { - ret = authenticator->authenticate (pop_data, authenticator->method); - if (ret == POP_A_SOCKET) { - if (pop_connect(pop_data) == PQ_OK) { - ret = authenticator->authenticate(pop_data, authenticator->method); - } else { - ret = POP_A_FAILURE; - } - } - - if (ret != POP_A_UNAVAIL) - attempts++; - if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET || - (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL))) - break; - - authenticator++; - } - } - - switch (ret) { - case POP_A_SUCCESS: - return PQ_OK; - case POP_A_SOCKET: - return PQ_NOT_CONNECTED; - case POP_A_UNAVAIL: - if (!attempts) - mutt_error (_("No authenticators available")); - } - - return PQ_ERR; -} - /* given an POP mailbox name, return host, port, username and password */ static int pop_parse_path (const char *path, ACCOUNT * act) { @@ -728,8 +673,6 @@ static int pop_parse_path (const char *path, ACCOUNT * act) return ret; } -/* }}} */ - /* * Open connection and authenticate * 0 - successful, @@ -740,7 +683,7 @@ static int pop_parse_path (const char *path, ACCOUNT * act) static pop_query_status pop_open_connection (pop_data_t * pop_data) { pop_query_status ret; - unsigned int n, size; + int n, size; char buf[LONG_STRING]; ret = pop_connect(pop_data);