2 * Copyright notice from original mutt:
3 * Copyright (C) 2000-2001 Vsevolod Volkov <vvv@mutt.org.ua>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
21 #include "lib/debug.h"
28 #include <sasl/sasl.h>
29 #include <sasl/saslutil.h>
35 #include "mutt_sasl.h"
39 /* SASL authenticator */
40 static pop_auth_res_t pop_auth_sasl (POP_DATA * pop_data, const char *method)
42 sasl_conn_t *saslconn;
43 sasl_interact_t *interaction = NULL;
45 char buf[LONG_STRING];
46 char inbuf[LONG_STRING];
50 const char *pc = NULL;
54 unsigned int len, olen;
55 unsigned char client_start;
57 if (mutt_sasl_client_new (pop_data->conn, &saslconn) < 0) {
58 debug_print (1, ("Error allocating SASL connection.\n"));
63 method = pop_data->auth_list;
68 sasl_client_start (saslconn, method, &interaction, &pc, &olen, &mech);
70 rc = sasl_client_start (saslconn, method, NULL,
71 &interaction, &pc, &olen, &mech);
73 if (rc != SASL_INTERACT)
75 mutt_sasl_interact (interaction);
78 if (rc != SASL_OK && rc != SASL_CONTINUE) {
79 debug_print (1, ("Failure starting authentication exchange. No shared mechanisms?\n"));
81 /* SASL doesn't support suggested mechanisms, so fall back */
85 client_start = (olen > 0);
87 mutt_message _("Authenticating (SASL)...");
89 snprintf (buf, sizeof (buf), "AUTH %s", mech);
92 /* looping protocol */
94 strfcpy (buf + olen, "\r\n", sizeof (buf) - olen);
95 mutt_socket_write (pop_data->conn, buf);
96 if (mutt_socket_readln (inbuf, sizeof (inbuf), pop_data->conn) < 0) {
97 sasl_dispose (&saslconn);
98 pop_data->status = POP_DISCONNECTED;
102 if (rc != SASL_CONTINUE)
106 if (!safe_strncmp (inbuf, "+ ", 2)
107 && sasl_decode64 (inbuf, strlen (inbuf), buf, LONG_STRING - 1,
110 if (!safe_strncmp (inbuf, "+ ", 2)
111 && sasl_decode64 (inbuf, strlen (inbuf), buf, &len) != SASL_OK)
114 debug_print (1, ("error base64-decoding server response.\n"));
120 rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen);
121 if (rc != SASL_INTERACT)
123 mutt_sasl_interact (interaction);
128 if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK))
131 /* send out response, or line break if none needed */
133 if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK) {
134 debug_print (1, ("error base64-encoding client response.\n"));
138 /* sasl_client_st(art|ep) allocate pc with malloc, expect me to
149 if (!safe_strncmp (inbuf, "+OK", 3)) {
150 mutt_sasl_setup_conn (pop_data->conn, saslconn);
151 return POP_A_SUCCESS;
155 sasl_dispose (&saslconn);
157 /* terminate SASL sessoin if the last responce is not +OK nor -ERR */
158 if (!safe_strncmp (inbuf, "+ ", 2)) {
159 snprintf (buf, sizeof (buf), "*\r\n");
160 if (pop_query (pop_data, buf, sizeof (buf)) == PQ_NOT_CONNECTED)
164 mutt_error _("SASL authentication failed.");
168 return POP_A_FAILURE;
172 /* Get the server timestamp for APOP authentication */
173 void pop_apop_timestamp (POP_DATA * pop_data, char *buf)
177 FREE (&pop_data->timestamp);
179 if ((p1 = strchr (buf, '<')) && (p2 = strchr (p1, '>'))) {
181 pop_data->timestamp = safe_strdup (p1);
185 /* APOP authenticator */
186 static pop_auth_res_t pop_auth_apop (POP_DATA * pop_data, const char *method)
189 unsigned char digest[16];
191 char buf[LONG_STRING];
194 if (!pop_data->timestamp)
195 return POP_A_UNAVAIL;
197 mutt_message _("Authenticating (APOP)...");
199 /* Compute the authentication hash to send to the server */
200 MD5Init (&mdContext);
201 MD5Update (&mdContext, (unsigned char *) pop_data->timestamp,
202 strlen (pop_data->timestamp));
203 MD5Update (&mdContext, (unsigned char *) pop_data->conn->account.pass,
204 strlen (pop_data->conn->account.pass));
205 MD5Final (digest, &mdContext);
207 for (i = 0; i < sizeof (digest); i++)
208 sprintf (hash + 2 * i, "%02x", digest[i]);
210 /* Send APOP command to server */
211 snprintf (buf, sizeof (buf), "APOP %s %s\r\n", pop_data->conn->account.user,
214 switch (pop_query (pop_data, buf, sizeof (buf))) {
216 return POP_A_SUCCESS;
217 case PQ_NOT_CONNECTED:
221 mutt_error _("APOP authentication failed.");
225 return POP_A_FAILURE;
228 /* USER authenticator */
229 static pop_auth_res_t pop_auth_user (POP_DATA * pop_data, const char *method)
231 char buf[LONG_STRING];
232 pop_query_status ret;
234 if (pop_data->cmd_user == CMD_NOT_AVAILABLE)
235 return POP_A_UNAVAIL;
237 mutt_message _("Logging in...");
239 snprintf (buf, sizeof (buf), "USER %s\r\n", pop_data->conn->account.user);
240 ret = pop_query (pop_data, buf, sizeof (buf));
242 if (pop_data->cmd_user == CMD_UNKNOWN) {
244 pop_data->cmd_user = CMD_AVAILABLE;
246 debug_print (1, ("set USER capability\n"));
250 pop_data->cmd_user = CMD_NOT_AVAILABLE;
252 debug_print (1, ("unset USER capability\n"));
253 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
254 _("Command USER is not supported by server."));
259 snprintf (buf, sizeof (buf), "PASS %s\r\n", pop_data->conn->account.pass);
260 ret = pop_query_d (pop_data, buf, sizeof (buf),
262 /* don't print the password unless we're at the ungodly debugging level */
263 DebugLevel < M_SOCK_LOG_FULL ? "PASS *\r\n" :
270 return POP_A_SUCCESS;
271 case PQ_NOT_CONNECTED:
275 mutt_error ("%s %s", _("Login failed."), pop_data->err_msg);
278 return POP_A_FAILURE;
281 static pop_auth_t pop_authenticators[] = {
283 {pop_auth_sasl, NULL},
285 {pop_auth_apop, "apop"},
286 {pop_auth_user, "user"},
293 * -1 - conection lost,
295 * -3 - authentication canceled.
297 pop_query_status pop_authenticate (POP_DATA * pop_data)
299 ACCOUNT *acct = &pop_data->conn->account;
300 pop_auth_t *authenticator;
305 int ret = POP_A_UNAVAIL;
307 if (mutt_account_getuser (acct) || !acct->user[0] ||
308 mutt_account_getpass (acct) || !acct->pass[0])
309 return PFD_FUNCT_ERROR;
311 if (PopAuthenticators && *PopAuthenticators) {
312 /* Try user-specified list of authentication methods */
313 methods = safe_strdup (PopAuthenticators);
317 comma = strchr (method, ':');
320 debug_print (2, ("Trying method %s\n", method));
321 authenticator = pop_authenticators;
323 while (authenticator->authenticate) {
324 if (!authenticator->method ||
325 !ascii_strcasecmp (authenticator->method, method)) {
326 ret = authenticator->authenticate (pop_data, method);
327 if (ret == POP_A_SOCKET)
328 switch (pop_connect (pop_data)) {
331 ret = authenticator->authenticate (pop_data, method);
338 if (ret != POP_A_UNAVAIL)
340 if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET ||
341 (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL))) {
355 /* Fall back to default: any authenticator */
356 debug_print (2, ("Using any available method.\n"));
357 authenticator = pop_authenticators;
359 while (authenticator->authenticate) {
360 ret = authenticator->authenticate (pop_data, authenticator->method);
361 if (ret == POP_A_SOCKET)
362 switch (pop_connect (pop_data)) {
366 authenticator->authenticate (pop_data, authenticator->method);
373 if (ret != POP_A_UNAVAIL)
375 if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET ||
376 (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL)))
387 return PQ_NOT_CONNECTED;
390 mutt_error (_("No authenticators available"));