]> git.llucax.com Git - software/mutt-debian.git/blob - pop_auth.c
doc update: clarify what attach_charset does (Closes: 502628)
[software/mutt-debian.git] / pop_auth.c
1 /*
2  * Copyright (C) 2000-2001 Vsevolod Volkov <vvv@mutt.org.ua>
3  * 
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  * 
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  * 
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18
19 #if HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include "mutt.h"
24 #include "mx.h"
25 #include "md5.h"
26 #include "pop.h"
27
28 #include <string.h>
29 #include <unistd.h>
30
31 #ifdef USE_SASL
32 #include <sasl/sasl.h>
33 #include <sasl/saslutil.h>
34
35 #include "mutt_sasl.h"
36 #endif
37
38 #ifdef USE_SASL
39 /* SASL authenticator */
40 static pop_auth_res_t pop_auth_sasl (POP_DATA *pop_data, const char *method)
41 {
42   sasl_conn_t *saslconn;
43   sasl_interact_t *interaction = NULL;
44   int rc;
45   char buf[LONG_STRING];
46   char inbuf[LONG_STRING];
47   const char* mech;
48   const char *pc = NULL;
49   unsigned int len, olen, client_start;
50
51   if (mutt_sasl_client_new (pop_data->conn, &saslconn) < 0)
52   {
53     dprint (1, (debugfile, "pop_auth_sasl: Error allocating SASL connection.\n"));
54     return POP_A_FAILURE;
55   }
56
57   if (!method)
58     method = pop_data->auth_list;
59
60   FOREVER
61   {
62     rc = sasl_client_start(saslconn, method, &interaction, &pc, &olen, &mech);
63     if (rc != SASL_INTERACT)
64       break;
65     mutt_sasl_interact (interaction);
66   }
67
68   if (rc != SASL_OK && rc != SASL_CONTINUE)
69   {
70     dprint (1, (debugfile, "pop_auth_sasl: Failure starting authentication exchange. No shared mechanisms?\n"));
71
72     /* SASL doesn't support suggested mechanisms, so fall back */
73     return POP_A_UNAVAIL;
74   }
75
76   client_start = olen;
77
78   mutt_message _("Authenticating (SASL)...");
79
80   snprintf (buf, sizeof (buf), "AUTH %s", mech);
81   olen = strlen (buf);
82
83   /* looping protocol */
84   FOREVER
85   {
86     strfcpy (buf + olen, "\r\n", sizeof (buf) - olen);
87     mutt_socket_write (pop_data->conn, buf);
88     if (mutt_socket_readln (inbuf, sizeof (inbuf), pop_data->conn) < 0)
89     {
90       sasl_dispose (&saslconn);
91       pop_data->status = POP_DISCONNECTED;
92       return POP_A_SOCKET;
93     }
94
95     if (!client_start && rc != SASL_CONTINUE)
96       break;
97
98     if (!mutt_strncmp (inbuf, "+ ", 2)
99         && sasl_decode64 (inbuf+2, strlen (inbuf+2), buf, LONG_STRING-1, &len) != SASL_OK)
100     {
101       dprint (1, (debugfile, "pop_auth_sasl: error base64-decoding server response.\n"));
102       goto bail;
103     }
104
105     if (!client_start)
106       FOREVER
107       {
108         rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen);
109         if (rc != SASL_INTERACT)
110           break;
111         mutt_sasl_interact (interaction);
112       }
113     else
114     {
115       olen = client_start;
116       client_start = 0;
117     }
118
119     if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK))
120       break;
121
122     /* send out response, or line break if none needed */
123     if (pc)
124     {
125       if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK)
126       {
127         dprint (1, (debugfile, "pop_auth_sasl: error base64-encoding client response.\n"));
128         goto bail;
129       }
130     }
131   }
132
133   if (rc != SASL_OK)
134     goto bail;
135
136   if (!mutt_strncmp (inbuf, "+OK", 3))
137   {
138     mutt_sasl_setup_conn (pop_data->conn, saslconn);
139     return POP_A_SUCCESS;
140   }
141
142 bail:
143   sasl_dispose (&saslconn);
144
145   /* terminate SASL sessoin if the last responce is not +OK nor -ERR */
146   if (!mutt_strncmp (inbuf, "+ ", 2))
147   {
148     snprintf (buf, sizeof (buf), "*\r\n");
149     if (pop_query (pop_data, buf, sizeof (buf)) == -1)
150       return POP_A_SOCKET;
151   }
152
153   mutt_error _("SASL authentication failed.");
154   mutt_sleep (2);
155
156   return POP_A_FAILURE;
157 }
158 #endif
159
160 /* Get the server timestamp for APOP authentication */
161 void pop_apop_timestamp (POP_DATA *pop_data, char *buf)
162 {
163   char *p1, *p2;
164
165   FREE (&pop_data->timestamp);
166
167   if ((p1 = strchr (buf, '<')) && (p2 = strchr (p1, '>')))
168   {
169     p2[1] = '\0';
170     pop_data->timestamp = safe_strdup (p1);
171   }
172 }
173
174 /* APOP authenticator */
175 static pop_auth_res_t pop_auth_apop (POP_DATA *pop_data, const char *method)
176 {
177   struct md5_ctx ctx;
178   unsigned char digest[16];
179   char hash[33];
180   char buf[LONG_STRING];
181   int i;
182
183   if (!pop_data->timestamp)
184     return POP_A_UNAVAIL;
185
186   if (rfc822_valid_msgid (pop_data->timestamp) < 0)
187   {
188     mutt_error _("POP timestamp is invalid!");
189     mutt_sleep (2);
190     return POP_A_UNAVAIL;
191   }
192
193   mutt_message _("Authenticating (APOP)...");
194
195   /* Compute the authentication hash to send to the server */
196   md5_init_ctx (&ctx);
197   md5_process_bytes (pop_data->timestamp, strlen (pop_data->timestamp), &ctx);
198   md5_process_bytes (pop_data->conn->account.pass,
199                      strlen (pop_data->conn->account.pass), &ctx);
200   md5_finish_ctx (&ctx, digest);
201
202   for (i = 0; i < sizeof (digest); i++)
203     sprintf (hash + 2 * i, "%02x", digest[i]);
204
205   /* Send APOP command to server */
206   snprintf (buf, sizeof (buf), "APOP %s %s\r\n", pop_data->conn->account.user, hash);
207
208   switch (pop_query (pop_data, buf, sizeof (buf)))
209   {
210     case 0:
211       return POP_A_SUCCESS;
212     case -1:
213       return POP_A_SOCKET;
214   }
215
216   mutt_error _("APOP authentication failed.");
217   mutt_sleep (2);
218
219   return POP_A_FAILURE;
220 }
221
222 /* USER authenticator */
223 static pop_auth_res_t pop_auth_user (POP_DATA *pop_data, const char *method)
224 {
225   char buf[LONG_STRING];
226   int ret;
227
228   if (!pop_data->cmd_user)
229     return POP_A_UNAVAIL;
230
231   mutt_message _("Logging in...");
232
233   snprintf (buf, sizeof (buf), "USER %s\r\n", pop_data->conn->account.user);
234   ret = pop_query (pop_data, buf, sizeof (buf));
235
236   if (pop_data->cmd_user == 2)
237   {
238     if (ret == 0)
239     {
240       pop_data->cmd_user = 1;
241
242       dprint (1, (debugfile, "pop_auth_user: set USER capability\n"));
243     }
244
245     if (ret == -2)
246     {
247       pop_data->cmd_user = 0;
248
249       dprint (1, (debugfile, "pop_auth_user: unset USER capability\n"));
250       snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
251               _("Command USER is not supported by server."));
252     }
253   }
254
255   if (ret == 0)
256   {
257     snprintf (buf, sizeof (buf), "PASS %s\r\n", pop_data->conn->account.pass);
258     ret = pop_query_d (pop_data, buf, sizeof (buf), 
259 #ifdef DEBUG
260         /* don't print the password unless we're at the ungodly debugging level */
261         debuglevel < M_SOCK_LOG_FULL ? "PASS *\r\n" :
262 #endif
263         NULL);
264   }
265
266   switch (ret)
267   {
268     case 0:
269       return POP_A_SUCCESS;
270     case -1:
271       return POP_A_SOCKET;
272   }
273
274   mutt_error ("%s %s", _("Login failed."), pop_data->err_msg);
275   mutt_sleep (2);
276
277   return POP_A_FAILURE;
278 }
279
280 static pop_auth_t pop_authenticators[] = {
281 #ifdef USE_SASL
282   { pop_auth_sasl, NULL },
283 #endif
284   { pop_auth_apop, "apop" },
285   { pop_auth_user, "user" },
286   { NULL }
287 };
288
289 /*
290  * Authentication
291  *  0 - successful,
292  * -1 - conection lost,
293  * -2 - login failed,
294  * -3 - authentication canceled.
295 */
296 int pop_authenticate (POP_DATA* pop_data)
297 {
298   ACCOUNT *acct = &pop_data->conn->account;
299   pop_auth_t* authenticator;
300   char* methods;
301   char* comma;
302   char* method;
303   int attempts = 0;
304   int ret = POP_A_UNAVAIL;
305
306   if (mutt_account_getuser (acct) || !acct->user[0] ||
307       mutt_account_getpass (acct) || !acct->pass[0])
308     return -3;
309
310   if (PopAuthenticators && *PopAuthenticators)
311   {
312     /* Try user-specified list of authentication methods */
313     methods = safe_strdup (PopAuthenticators);
314     method = methods;
315
316     while (method)
317     {
318       comma = strchr (method, ':');
319       if (comma)
320         *comma++ = '\0';
321       dprint (2, (debugfile, "pop_authenticate: Trying method %s\n", method));
322       authenticator = pop_authenticators;
323
324       while (authenticator->authenticate)
325       {
326         if (!authenticator->method ||
327             !ascii_strcasecmp (authenticator->method, method))
328         {
329           ret = authenticator->authenticate (pop_data, method);
330           if (ret == POP_A_SOCKET)
331             switch (pop_connect (pop_data))
332             {
333               case 0:
334               {
335                 ret = authenticator->authenticate (pop_data, method);
336                 break;
337               }
338               case -2:
339                 ret = POP_A_FAILURE;
340             }
341
342           if (ret != POP_A_UNAVAIL)
343             attempts++;
344           if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET ||
345               (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL)))
346           {
347             comma = NULL;
348             break;
349           }
350         }
351         authenticator++;
352       }
353
354       method = comma;
355     }
356
357     FREE (&methods);
358   }
359   else
360   {
361     /* Fall back to default: any authenticator */
362     dprint (2, (debugfile, "pop_authenticate: Using any available method.\n"));
363     authenticator = pop_authenticators;
364
365     while (authenticator->authenticate)
366     {
367       ret = authenticator->authenticate (pop_data, authenticator->method);
368       if (ret == POP_A_SOCKET)
369         switch (pop_connect (pop_data))
370         {
371           case 0:
372           {
373             ret = authenticator->authenticate (pop_data, authenticator->method);
374             break;
375           }
376           case -2:
377             ret = POP_A_FAILURE;
378         }
379
380       if (ret != POP_A_UNAVAIL)
381         attempts++;
382       if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET ||
383           (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL)))
384         break;
385
386       authenticator++;
387     }
388   }
389
390   switch (ret)
391   {
392     case POP_A_SUCCESS:
393       return 0;
394     case POP_A_SOCKET:
395       return -1;
396     case POP_A_UNAVAIL:
397       if (!attempts)
398         mutt_error (_("No authenticators available"));
399   }
400
401   return -2;
402 }