further unclutter.
[apps/madmutt.git] / imap / auth.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-8 Michael R. Elkins <me@mutt.org>
4  * Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
5  * Copyright (C) 1999-2001 Brendan Cully <brendan@kublai.com>
6  *
7  * This file is part of mutt-ng, see http://www.muttng.org/.
8  * It's licensed under the GNU General Public License,
9  * please see the file GPL in the top level source directory.
10  */
11
12 /* IMAP login/authentication code */
13
14 #include <lib-lib/lib-lib.h>
15
16 #include <sasl/sasl.h>
17 #include <sasl/saslutil.h>
18
19 #include "mutt.h"
20 #include "mutt_sasl.h"
21 #include "imap_private.h"
22
23 typedef enum {
24   IMAP_AUTH_SUCCESS = 0,
25   IMAP_AUTH_FAILURE,
26   IMAP_AUTH_UNAVAIL
27 } imap_auth_res_t;
28
29 typedef struct {
30   /* do authentication, using named method or any available if method is NULL */
31   imap_auth_res_t (*authenticate) (IMAP_DATA * idata, const char *method);
32   /* name of authentication method supported, NULL means variable. If this
33    * is not null, authenticate may ignore the second parameter. */
34   const char *method;
35 } imap_auth_t;
36
37 /* imap_auth_sasl: Default authenticator if available. */
38 static imap_auth_res_t imap_auth_sasl (IMAP_DATA * idata, const char *method)
39 {
40   sasl_conn_t *saslconn;
41   sasl_interact_t *interaction = NULL;
42   int rc, irc;
43   char buf[HUGE_STRING];
44   const char *mech;
45
46   const char *pc = NULL;
47   unsigned int len, olen;
48   unsigned char client_start;
49
50   if (mutt_sasl_client_new (idata->conn, &saslconn) < 0) {
51     return IMAP_AUTH_FAILURE;
52   }
53
54   rc = SASL_FAIL;
55
56   /* If the user hasn't specified a method, use any available */
57   if (!method) {
58     method = idata->capstr;
59
60     /* hack for SASL ANONYMOUS support:
61      * 1. Fetch username. If it's "" or "anonymous" then
62      * 2. attempt sasl_client_start with only "AUTH=ANONYMOUS" capability
63      * 3. if sasl_client_start fails, fall through... */
64
65     if (mutt_account_getuser (&idata->conn->account))
66       return IMAP_AUTH_FAILURE;
67
68     if (mutt_bit_isset (idata->capabilities, AUTH_ANON) &&
69         (!idata->conn->account.user[0] ||
70          !m_strncmp(idata->conn->account.user, "anonymous", 9)))
71       rc = sasl_client_start (saslconn, "AUTH=ANONYMOUS", NULL, &pc, &olen,
72                               &mech);
73   }
74
75   if (rc != SASL_OK && rc != SASL_CONTINUE)
76     do {
77       rc = sasl_client_start (saslconn, method, &interaction,
78                               &pc, &olen, &mech);
79       if (rc == SASL_INTERACT)
80         mutt_sasl_interact (interaction);
81     }
82     while (rc == SASL_INTERACT);
83
84   client_start = (olen > 0);
85
86   if (rc != SASL_OK && rc != SASL_CONTINUE) {
87     /* SASL doesn't support LOGIN, so fall back */
88     return IMAP_AUTH_UNAVAIL;
89   }
90
91   mutt_message (_("Authenticating (%s)..."), mech);
92
93   snprintf (buf, sizeof (buf), "AUTHENTICATE %s", mech);
94   imap_cmd_start (idata, buf);
95   irc = IMAP_CMD_CONTINUE;
96
97   /* looping protocol */
98   while (rc == SASL_CONTINUE || olen > 0) {
99     do
100       irc = imap_cmd_step (idata);
101     while (irc == IMAP_CMD_CONTINUE);
102
103     if (method && irc == IMAP_CMD_NO) {
104       sasl_dispose (&saslconn);
105       return IMAP_AUTH_UNAVAIL;
106     }
107
108     if (irc == IMAP_CMD_BAD || irc == IMAP_CMD_NO)
109       goto bail;
110
111     if (irc == IMAP_CMD_RESPOND) {
112       if (sasl_decode64(idata->cmd.buf + 2, m_strlen(idata->cmd.buf + 2), buf,
113                         LONG_STRING - 1, &len) != SASL_OK) {
114         goto bail;
115       }
116     }
117
118     /* client-start is only available with the SASL-IR extension, but
119      * SASL 2.1 seems to want to use it regardless, at least for DIGEST
120      * fast reauth. Override if the server sent an initial continuation */
121     if (!client_start || buf[0]) {
122       do {
123         rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen);
124         if (rc == SASL_INTERACT)
125           mutt_sasl_interact (interaction);
126       }
127       while (rc == SASL_INTERACT);
128     }
129     else
130       client_start = 0;
131
132     /* send out response, or line break if none needed */
133     if (olen) {
134       if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK) {
135         goto bail;
136       }
137     }
138
139     if (irc == IMAP_CMD_RESPOND) {
140       m_strcpy(buf + olen, sizeof(buf) - olen, "\r\n");
141       mutt_socket_write (idata->conn, buf);
142     }
143
144     /* If SASL has errored out, send an abort string to the server */
145     if (rc < 0) {
146       mutt_socket_write (idata->conn, "*\r\n");
147     }
148
149     olen = 0;
150   }
151
152   while (irc != IMAP_CMD_OK)
153     if ((irc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
154       break;
155
156   if (rc != SASL_OK)
157     goto bail;
158
159   if (imap_code (idata->cmd.buf)) {
160     mutt_sasl_setup_conn (idata->conn, saslconn);
161     return IMAP_AUTH_SUCCESS;
162   }
163
164 bail:
165   mutt_error _("SASL authentication failed.");
166   mutt_sleep (2);
167   sasl_dispose (&saslconn);
168
169   return IMAP_AUTH_FAILURE;
170 }
171 /* imap_auth_login: Plain LOGIN support */
172 static imap_auth_res_t imap_auth_login(IMAP_DATA *idata, const char *method)
173 {
174     char q_user[STRING], q_pass[STRING];
175     char buf[STRING];
176     int rc;
177
178     if (mutt_bit_isset (idata->capabilities, LOGINDISABLED)) {
179         mutt_message _("LOGIN disabled on this server.");
180
181         return IMAP_AUTH_UNAVAIL;
182     }
183
184     if (mutt_account_getlogin (&idata->conn->account))
185         return IMAP_AUTH_FAILURE;
186     if (mutt_account_getpass (&idata->conn->account))
187         return IMAP_AUTH_FAILURE;
188
189     mutt_message _("Logging in...");
190
191     imap_quote_string(q_user, sizeof(q_user), idata->conn->account.login);
192     imap_quote_string(q_pass, sizeof(q_pass), idata->conn->account.pass);
193
194     snprintf(buf, sizeof(buf), "LOGIN %s %s", q_user, q_pass);
195     rc = imap_exec(idata, buf, IMAP_CMD_FAIL_OK | IMAP_CMD_PASS);
196
197     if (!rc)
198         return IMAP_AUTH_SUCCESS;
199
200     mutt_error _("Login failed.");
201
202     mutt_sleep (2);
203     return IMAP_AUTH_FAILURE;
204 }
205 static imap_auth_t imap_authenticators[] = {
206   {imap_auth_sasl, NULL},
207   {imap_auth_login, "login"},
208   {NULL, NULL}
209 };
210
211 /* imap_authenticate: Attempt to authenticate using either user-specified
212  *   authentication method if specified, or any. */
213 int imap_authenticate (IMAP_DATA * idata)
214 {
215   imap_auth_t *authenticator;
216   char *methods;
217   char *method;
218   char *delim;
219   int r = -1;
220
221   if (ImapAuthenticators && *ImapAuthenticators) {
222     /* Try user-specified list of authentication methods */
223     methods = m_strdup(ImapAuthenticators);
224
225     for (method = methods; method; method = delim) {
226       delim = strchr (method, ':');
227       if (delim)
228         *delim++ = '\0';
229       if (!method[0])
230         continue;
231
232       authenticator = imap_authenticators;
233
234       while (authenticator->authenticate) {
235         if (!authenticator->method ||
236             !ascii_strcasecmp (authenticator->method, method))
237           if ((r = authenticator->authenticate (idata, method)) !=
238               IMAP_AUTH_UNAVAIL) {
239             p_delete(&methods);
240             return r;
241           }
242
243         authenticator++;
244       }
245     }
246
247     p_delete(&methods);
248   }
249   else {
250     /* Fall back to default: any authenticator */
251     authenticator = imap_authenticators;
252
253     while (authenticator->authenticate) {
254       if ((r =
255            authenticator->authenticate (idata, NULL)) != IMAP_AUTH_UNAVAIL)
256         return r;
257       authenticator++;
258     }
259   }
260
261   if (r == IMAP_AUTH_UNAVAIL) {
262     mutt_error (_("No authenticators available"));
263     mutt_sleep (1);
264   }
265
266   return r;
267 }