NeoMutt  2023-05-17-56-ga67199
Teaching an old dog new tricks
DOXYGEN
auth_sasl.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <stddef.h>
31#include <sasl/sasl.h>
32#include <sasl/saslutil.h>
33#include <stdbool.h>
34#include <stdio.h>
35#include <string.h>
36#include "private.h"
37#include "mutt/lib.h"
38#include "conn/lib.h"
39#include "adata.h"
40#include "auth.h"
41
45enum ImapAuthRes imap_auth_sasl(struct ImapAccountData *adata, const char *method)
46{
47 sasl_conn_t *saslconn = NULL;
48 sasl_interact_t *interaction = NULL;
49 int rc, irc;
50 char *buf = NULL;
51 size_t bufsize = 0;
52 const char *mech = NULL;
53 const char *pc = NULL;
54 unsigned int len = 0, olen = 0;
55 bool client_start;
56
57 if (mutt_sasl_client_new(adata->conn, &saslconn) < 0)
58 {
59 mutt_debug(LL_DEBUG1, "Error allocating SASL connection\n");
60 return IMAP_AUTH_FAILURE;
61 }
62
63 rc = SASL_FAIL;
64
65 /* If the user hasn't specified a method, use any available */
66 if (!method)
67 {
68 method = adata->capstr;
69
70 /* hack for SASL ANONYMOUS support:
71 * 1. Fetch username. If it's "" or "anonymous" then
72 * 2. attempt sasl_client_start with only "AUTH=ANONYMOUS" capability
73 * 3. if sasl_client_start fails, fall through... */
74
75 if (mutt_account_getuser(&adata->conn->account) < 0)
76 {
77 sasl_dispose(&saslconn);
78 return IMAP_AUTH_FAILURE;
79 }
80
82 (!adata->conn->account.user[0] ||
83 mutt_str_startswith(adata->conn->account.user, "anonymous")))
84 {
85 rc = sasl_client_start(saslconn, "AUTH=ANONYMOUS", NULL, &pc, &olen, &mech);
86 }
87 }
88 else if (mutt_istr_equal("login", method) && !strstr(NONULL(adata->capstr), "AUTH=LOGIN"))
89 {
90 /* do not use SASL login for regular IMAP login */
91 sasl_dispose(&saslconn);
92 return IMAP_AUTH_UNAVAIL;
93 }
94
95 if ((rc != SASL_OK) && (rc != SASL_CONTINUE))
96 {
97 do
98 {
99 rc = sasl_client_start(saslconn, method, &interaction, &pc, &olen, &mech);
100 if (rc == SASL_INTERACT)
101 mutt_sasl_interact(interaction);
102 } while (rc == SASL_INTERACT);
103 }
104
105 client_start = (olen > 0);
106
107 if ((rc != SASL_OK) && (rc != SASL_CONTINUE))
108 {
109 if (method)
110 {
111 mutt_debug(LL_DEBUG2, "%s unavailable\n", method);
112 }
113 else
114 {
115 mutt_debug(LL_DEBUG1, "Failure starting authentication exchange. No shared mechanisms?\n");
116 }
117 /* SASL doesn't support LOGIN, so fall back */
118
119 sasl_dispose(&saslconn);
120 return IMAP_AUTH_UNAVAIL;
121 }
122
123 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
124 mutt_message(_("Authenticating (%s)..."), mech);
125
126 bufsize = MAX((olen * 2), 1024);
127 buf = mutt_mem_malloc(bufsize);
128
129 snprintf(buf, bufsize, "AUTHENTICATE %s", mech);
130 if ((adata->capabilities & IMAP_CAP_SASL_IR) && client_start)
131 {
132 len = mutt_str_len(buf);
133 buf[len++] = ' ';
134 if (sasl_encode64(pc, olen, buf + len, bufsize - len, &olen) != SASL_OK)
135 {
136 mutt_debug(LL_DEBUG1, "#1 error base64-encoding client response\n");
137 goto bail;
138 }
139 client_start = false;
140 olen = 0;
141 }
142 imap_cmd_start(adata, buf);
143 irc = IMAP_RES_CONTINUE;
144
145 /* looping protocol */
146 while ((rc == SASL_CONTINUE) || (olen > 0))
147 {
148 do
149 {
150 irc = imap_cmd_step(adata);
151 } while (irc == IMAP_RES_CONTINUE);
152
153 if ((irc == IMAP_RES_BAD) || (irc == IMAP_RES_NO))
154 goto bail;
155
156 if (irc == IMAP_RES_RESPOND)
157 {
158 /* Exchange incorrectly returns +\r\n instead of + \r\n */
159 if (adata->buf[1] == '\0')
160 {
161 buf[0] = '\0';
162 len = 0;
163 }
164 else
165 {
166 len = strlen(adata->buf + 2);
167 if (len > bufsize)
168 {
169 bufsize = len;
170 mutt_mem_realloc(&buf, bufsize);
171 }
172 /* For sasl_decode64, the fourth parameter, outmax, doesn't
173 * include space for the trailing null */
174 if (sasl_decode64(adata->buf + 2, len, buf, bufsize - 1, &len) != SASL_OK)
175 {
176 mutt_debug(LL_DEBUG1, "error base64-decoding server response\n");
177 goto bail;
178 }
179 }
180 }
181
182 /* client-start is only available with the SASL-IR extension, but
183 * SASL 2.1 seems to want to use it regardless, at least for DIGEST
184 * fast reauth. Override if the server sent an initial continuation */
185 if (!client_start || buf[0])
186 {
187 do
188 {
189 rc = sasl_client_step(saslconn, buf, len, &interaction, &pc, &olen);
190 if (rc == SASL_INTERACT)
191 mutt_sasl_interact(interaction);
192 } while (rc == SASL_INTERACT);
193 }
194 else
195 {
196 client_start = false;
197 }
198
199 /* send out response, or line break if none needed */
200 if (olen)
201 {
202 if ((olen * 2) > bufsize)
203 {
204 bufsize = olen * 2;
205 mutt_mem_realloc(&buf, bufsize);
206 }
207 if (sasl_encode64(pc, olen, buf, bufsize, &olen) != SASL_OK)
208 {
209 mutt_debug(LL_DEBUG1, "#2 error base64-encoding client response\n");
210 goto bail;
211 }
212 }
213
214 if (irc == IMAP_RES_RESPOND)
215 {
216 mutt_str_copy(buf + olen, "\r\n", bufsize - olen);
217 mutt_socket_send(adata->conn, buf);
218 }
219
220 /* If SASL has errored out, send an abort string to the server */
221 if (rc < 0)
222 {
223 mutt_socket_send(adata->conn, "*\r\n");
224 mutt_debug(LL_DEBUG1, "sasl_client_step error %d\n", rc);
225 }
226
227 olen = 0;
228 }
229
230 while (irc != IMAP_RES_OK)
231 {
232 irc = imap_cmd_step(adata);
233 if (irc != IMAP_RES_CONTINUE)
234 break;
235 }
236
237 if (rc != SASL_OK)
238 goto bail;
239
240 if (imap_code(adata->buf))
241 {
242 mutt_sasl_setup_conn(adata->conn, saslconn);
243 FREE(&buf);
244 return IMAP_AUTH_SUCCESS;
245 }
246
247bail:
248 sasl_dispose(&saslconn);
249 FREE(&buf);
250
251 if (method)
252 {
253 mutt_debug(LL_DEBUG2, "%s failed\n", method);
254 return IMAP_AUTH_UNAVAIL;
255 }
256
257 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
258 mutt_error(_("%s authentication failed"), "SASL ");
259
260 return IMAP_AUTH_FAILURE;
261}
IMAP authenticator multiplexor.
ImapAuthRes
Results of IMAP Authentication.
Definition: auth.h:38
@ IMAP_AUTH_FAILURE
Authentication failed.
Definition: auth.h:40
@ IMAP_AUTH_SUCCESS
Authentication successful.
Definition: auth.h:39
@ IMAP_AUTH_UNAVAIL
Authentication method not permitted.
Definition: auth.h:41
enum ImapAuthRes imap_auth_sasl(struct ImapAccountData *adata, const char *method)
SASL authenticator - Implements ImapAuth::authenticate()
Definition: auth_sasl.c:45
Connection Library.
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition: connaccount.c:49
#define mutt_error(...)
Definition: logging2.h:90
#define mutt_message(...)
Definition: logging2.h:89
#define mutt_debug(LEVEL,...)
Definition: logging2.h:87
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1095
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1109
bool imap_code(const char *s)
Was the command successful.
Definition: command.c:1236
#define IMAP_RES_RESPOND
+
Definition: private.h:57
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:55
#define IMAP_CAP_AUTH_ANONYMOUS
AUTH=ANONYMOUS.
Definition: private.h:128
#define IMAP_RES_NO
<tag> NO ...
Definition: private.h:53
#define IMAP_CAP_SASL_IR
SASL initial response draft.
Definition: private.h:134
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:56
#define IMAP_RES_BAD
<tag> BAD ...
Definition: private.h:54
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:43
#define MAX(a, b)
Definition: memory.h:30
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:810
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:653
Pop-specific Account data.
int mutt_sasl_interact(sasl_interact_t *interaction)
Perform an SASL interaction with the user.
Definition: sasl.c:702
int mutt_sasl_client_new(struct Connection *conn, sasl_conn_t **saslconn)
Wrapper for sasl_client_new()
Definition: sasl.c:603
void mutt_sasl_setup_conn(struct Connection *conn, sasl_conn_t *saslconn)
Set up an SASL connection.
Definition: sasl.c:739
GUI display the mailboxes in a side panel.
#define mutt_socket_send(conn, buf)
Definition: socket.h:59
#define NONULL(x)
Definition: string2.h:37
char user[128]
Username.
Definition: connaccount.h:56
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
IMAP-specific Account data -.
Definition: adata.h:40
ImapCapFlags capabilities
Capability flags.
Definition: adata.h:55
char * capstr
Capability string from the server.
Definition: adata.h:54
char * buf
Definition: adata.h:59
struct Connection * conn
Connection to IMAP server.
Definition: adata.h:41