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