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