-/* 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;
-}
-