NeoMutt  2025-12-11-980-ge38c27
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
IMAP Authentication API

Authenticate an IMAP connection. More...

Functions

enum ImapAuthRes imap_auth_anon (struct ImapAccountData *adata, const char *method)
 Authenticate anonymously - Implements ImapAuth::authenticate() -.
 
enum ImapAuthRes imap_auth_cram_md5 (struct ImapAccountData *adata, const char *method)
 Authenticate using CRAM-MD5 - Implements ImapAuth::authenticate() -.
 
enum ImapAuthRes imap_auth_gsasl (struct ImapAccountData *adata, const char *method)
 GNU SASL authenticator - Implements ImapAuth::authenticate() -.
 
enum ImapAuthRes imap_auth_gss (struct ImapAccountData *adata, const char *method)
 GSS Authentication support - Implements ImapAuth::authenticate() -.
 
enum ImapAuthRes imap_auth_login (struct ImapAccountData *adata, const char *method)
 Plain LOGIN support - Implements ImapAuth::authenticate() -.
 
enum ImapAuthRes imap_auth_oauth (struct ImapAccountData *adata, const char *method)
 Authenticate an IMAP connection using OAUTHBEARER - Implements ImapAuth::authenticate() -.
 
enum ImapAuthRes imap_auth_xoauth2 (struct ImapAccountData *adata, const char *method)
 Authenticate an IMAP connection using XOAUTH2 - Implements ImapAuth::authenticate() -.
 
enum ImapAuthRes imap_auth_plain (struct ImapAccountData *adata, const char *method)
 SASL PLAIN support - Implements ImapAuth::authenticate() -.
 
enum ImapAuthRes imap_auth_sasl (struct ImapAccountData *adata, const char *method)
 SASL authenticator - Implements ImapAuth::authenticate() -.
 

Detailed Description

Authenticate an IMAP connection.

Parameters
adataImap Account data
methodUse this named method, or any available method if NULL
Return values
ImapAuthResResult, e.g. IMAP_AUTH_SUCCESS

Function Documentation

◆ imap_auth_anon()

enum ImapAuthRes imap_auth_anon ( struct ImapAccountData * adata,
const char * method )

Authenticate anonymously - Implements ImapAuth::authenticate() -.

This is basically a stripped-down version of the cram-md5 method.

Definition at line 42 of file auth_anon.c.

43{
44 int rc;
45
47 return IMAP_AUTH_UNAVAIL;
48
49 if (mutt_account_getuser(&adata->conn->account) < 0)
50 return IMAP_AUTH_FAILURE;
51
52 if (adata->conn->account.user[0] != '\0')
53 return IMAP_AUTH_UNAVAIL;
54
55 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
56 mutt_message(_("Authenticating (%s)..."), "anonymous");
57
58 imap_cmd_start(adata, "AUTHENTICATE ANONYMOUS");
59
60 do
61 {
62 rc = imap_cmd_step(adata);
63 } while (rc == IMAP_RES_CONTINUE);
64
65 if (rc != IMAP_RES_RESPOND)
66 {
67 mutt_debug(LL_DEBUG1, "Invalid response from server\n");
68 goto bail;
69 }
70
71 mutt_socket_send(adata->conn, "ZHVtbXkK\r\n"); /* base64 ("dummy") */
72
73 do
74 {
75 rc = imap_cmd_step(adata);
76 } while (rc == IMAP_RES_CONTINUE);
77
78 if (rc != IMAP_RES_OK)
79 {
80 mutt_debug(LL_DEBUG1, "Error receiving server response\n");
81 goto bail;
82 }
83
84 if (imap_code(adata->buf))
85 return IMAP_AUTH_SUCCESS;
86
87bail:
88 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
89 mutt_error(_("%s authentication failed"), "anonymous");
90 return IMAP_AUTH_FAILURE;
91}
@ 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
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition connaccount.c:51
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition command.c:1216
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition command.c:1230
bool imap_code(const char *s)
Was the command successful.
Definition command.c:1372
#define IMAP_RES_RESPOND
+
Definition private.h:56
#define IMAP_RES_OK
<tag> OK ...
Definition private.h:54
@ IMAP_CAP_AUTH_ANONYMOUS
AUTH=ANONYMOUS.
Definition private.h:142
#define IMAP_RES_CONTINUE
* ...
Definition private.h:55
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define _(a)
Definition message.h:28
#define mutt_socket_send(conn, buf)
Definition socket.h:56
char user[128]
Username.
Definition connaccount.h:62
struct ConnAccount account
Account details: username, password, etc.
Definition connection.h:49
ImapCapFlags capabilities
Capability flags.
Definition adata.h:56
char * buf
Command buffer.
Definition adata.h:61
struct Connection * conn
Connection to IMAP server.
Definition adata.h:41
+ Here is the call graph for this function:

◆ imap_auth_cram_md5()

enum ImapAuthRes imap_auth_cram_md5 ( struct ImapAccountData * adata,
const char * method )

Authenticate using CRAM-MD5 - Implements ImapAuth::authenticate() -.

Definition at line 96 of file auth_cram.c.

97{
99 return IMAP_AUTH_UNAVAIL;
100
101 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
102 mutt_message(_("Authenticating (%s)..."), "CRAM-MD5");
103
104 /* get auth info */
105 if (mutt_account_getlogin(&adata->conn->account) < 0)
106 return IMAP_AUTH_FAILURE;
107 if (mutt_account_getpass(&adata->conn->account) < 0)
108 return IMAP_AUTH_FAILURE;
109
110 imap_cmd_start(adata, "AUTHENTICATE CRAM-MD5");
111
112 struct Buffer *ibuf = buf_pool_get();
113 struct Buffer *obuf = buf_pool_get();
114 unsigned char hmac_response[MD5_DIGEST_LEN];
115 int rc_step;
117
118 /* From RFC2195:
119 * The data encoded in the first ready response contains a presumptively
120 * arbitrary string of random digits, a timestamp, and the fully-qualified
121 * primary host name of the server. The syntax of the unencoded form must
122 * correspond to that of an RFC822 'msg-id' [RFC822] as described in [POP3]. */
123 do
124 {
125 rc_step = imap_cmd_step(adata);
126 } while (rc_step == IMAP_RES_CONTINUE);
127
128 if (rc_step != IMAP_RES_RESPOND)
129 {
130 mutt_debug(LL_DEBUG1, "Invalid response from server\n");
131 goto bail;
132 }
133
134 int dlen = mutt_b64_decode(adata->buf + 2, obuf->data, obuf->dsize - 1);
135 if (dlen == -1)
136 {
137 mutt_debug(LL_DEBUG1, "Error decoding base64 response\n");
138 goto bail;
139 }
140 obuf->data[dlen] = '\0';
141 buf_fix_dptr(obuf);
142
143 mutt_debug(LL_DEBUG2, "CRAM challenge: %s\n", buf_string(obuf));
144
145 /* The client makes note of the data and then responds with a string
146 * consisting of the user name, a space, and a 'digest'. The latter is
147 * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where the
148 * key is a shared secret and the digested text is the timestamp (including
149 * angle-brackets).
150 *
151 * Note: The user name shouldn't be quoted. Since the digest can't contain
152 * spaces, there is no ambiguity. Some servers get this wrong, we'll work
153 * around them when the bug report comes in. Until then, we'll remain
154 * blissfully RFC-compliant. */
155 hmac_md5(adata->conn->account.pass, buf_string(obuf), hmac_response);
156 /* dubious optimisation I saw elsewhere: make the whole string in one call */
157 int off = buf_printf(obuf, "%s ", adata->conn->account.user);
158 mutt_md5_toascii(hmac_response, obuf->data + off);
159 buf_fix_dptr(obuf);
160 mutt_debug(LL_DEBUG2, "CRAM response: %s\n", buf_string(obuf));
161
162 /* ibuf must be long enough to store the base64 encoding of obuf,
163 * plus the additional debris */
164 mutt_b64_encode(obuf->data, buf_len(obuf), ibuf->data, ibuf->dsize - 2);
165 buf_fix_dptr(ibuf);
166 buf_addstr(ibuf, "\r\n");
167 mutt_socket_send(adata->conn, buf_string(ibuf));
168
169 do
170 {
171 rc_step = imap_cmd_step(adata);
172 } while (rc_step == IMAP_RES_CONTINUE);
173
174 if (rc_step != IMAP_RES_OK)
175 {
176 mutt_debug(LL_DEBUG1, "Error receiving server response\n");
177 goto bail;
178 }
179
180 if (imap_code(adata->buf))
182
183bail:
184 if (rc != IMAP_AUTH_SUCCESS)
185 {
186 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
187 mutt_error(_("%s authentication failed"), "CRAM-MD5");
188 }
189
190 buf_pool_release(&ibuf);
191 buf_pool_release(&obuf);
192 return rc;
193}
ImapAuthRes
Results of IMAP Authentication.
Definition auth.h:39
static void hmac_md5(const char *password, const char *challenge, unsigned char *response)
Produce CRAM-MD5 challenge response.
Definition auth_cram.c:48
#define MD5_DIGEST_LEN
MD5 digest size in bytes for CRAM-MD5.
Definition auth_cram.c:40
size_t mutt_b64_encode(const char *in, size_t inlen, char *out, size_t outlen)
Convert raw bytes to a base64 string.
Definition base64.c:148
int mutt_b64_decode(const char *in, char *out, size_t olen)
Convert NUL-terminated base64 string to raw bytes.
Definition base64.c:180
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:168
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition buffer.c:497
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition buffer.c:189
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
int mutt_account_getpass(struct ConnAccount *cac)
Fetch password into ConnAccount, if necessary.
int mutt_account_getlogin(struct ConnAccount *cac)
Retrieve login info into ConnAccount, if necessary.
@ IMAP_CAP_AUTH_CRAM_MD5
RFC2195: CRAM-MD5 authentication.
Definition private.h:140
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
void mutt_md5_toascii(const void *digest, char *resbuf)
Convert a binary MD5 digest into ASCII Hexadecimal.
Definition md5.c:456
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
String manipulation buffer.
Definition buffer.h:36
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
char pass[256]
Password.
Definition connaccount.h:63
+ Here is the call graph for this function:

◆ imap_auth_gsasl()

enum ImapAuthRes imap_auth_gsasl ( struct ImapAccountData * adata,
const char * method )

GNU SASL authenticator - Implements ImapAuth::authenticate() -.

Definition at line 41 of file auth_gsasl.c.

42{
43 Gsasl_session *gsasl_session = NULL;
44 struct Buffer *output_buf = NULL;
45 char *imap_step_output = NULL;
46 int rc = IMAP_AUTH_FAILURE;
47 int gsasl_rc = GSASL_OK;
48 int imap_step_rc = IMAP_RES_CONTINUE;
49
50 const char *chosen_mech = mutt_gsasl_get_mech(method, adata->capstr);
51 if (!chosen_mech)
52 {
53 mutt_debug(LL_DEBUG2, "mutt_gsasl_get_mech() returned no usable mech\n");
54 return IMAP_AUTH_UNAVAIL;
55 }
56
57 mutt_debug(LL_DEBUG2, "using mech %s\n", chosen_mech);
58
59 if (mutt_gsasl_client_new(adata->conn, chosen_mech, &gsasl_session) < 0)
60 {
61 mutt_debug(LL_DEBUG1, "Error allocating GSASL connection\n");
62 return IMAP_AUTH_UNAVAIL;
63 }
64
65 mutt_message(_("Authenticating (%s)..."), chosen_mech);
66
67 output_buf = buf_pool_get();
68 buf_printf(output_buf, "AUTHENTICATE %s", chosen_mech);
69 if (adata->capabilities & IMAP_CAP_SASL_IR)
70 {
71 char *gsasl_step_output = NULL;
72 gsasl_rc = gsasl_step64(gsasl_session, "", &gsasl_step_output);
73 if ((gsasl_rc != GSASL_NEEDS_MORE) && (gsasl_rc != GSASL_OK))
74 {
75 mutt_debug(LL_DEBUG1, "gsasl_step64() failed (%d): %s\n", gsasl_rc,
76 gsasl_strerror(gsasl_rc));
78 goto bail;
79 }
80
81 buf_addch(output_buf, ' ');
82 buf_addstr(output_buf, gsasl_step_output);
83 gsasl_free(gsasl_step_output);
84 }
85 imap_cmd_start(adata, buf_string(output_buf));
86
87 do
88 {
89 do
90 {
91 imap_step_rc = imap_cmd_step(adata);
92 } while (imap_step_rc == IMAP_RES_CONTINUE);
93
94 if ((imap_step_rc == IMAP_RES_BAD) || (imap_step_rc == IMAP_RES_NO))
95 goto bail;
96
97 if (imap_step_rc != IMAP_RES_RESPOND)
98 break;
99
100 imap_step_output = imap_next_word(adata->buf);
101
102 char *gsasl_step_output = NULL;
103 gsasl_rc = gsasl_step64(gsasl_session, imap_step_output, &gsasl_step_output);
104 if ((gsasl_rc == GSASL_NEEDS_MORE) || (gsasl_rc == GSASL_OK))
105 {
106 buf_strcpy(output_buf, gsasl_step_output);
107 gsasl_free(gsasl_step_output);
108 }
109 else
110 {
111 // sasl error occurred, send an abort string
112 mutt_debug(LL_DEBUG1, "gsasl_step64() failed (%d): %s\n", gsasl_rc,
113 gsasl_strerror(gsasl_rc));
114 buf_strcpy(output_buf, "*");
115 }
116
117 buf_addstr(output_buf, "\r\n");
118 mutt_socket_send(adata->conn, buf_string(output_buf));
119 } while ((gsasl_rc == GSASL_NEEDS_MORE) || (gsasl_rc == GSASL_OK));
120
121 if (imap_step_rc != IMAP_RES_OK)
122 {
123 do
124 imap_step_rc = imap_cmd_step(adata);
125 while (imap_step_rc == IMAP_RES_CONTINUE);
126 }
127
128 if (imap_step_rc == IMAP_RES_RESPOND)
129 {
130 mutt_socket_send(adata->conn, "*\r\n");
131 goto bail;
132 }
133
134 if ((gsasl_rc != GSASL_OK) || (imap_step_rc != IMAP_RES_OK))
135 goto bail;
136
137 if (imap_code(adata->buf))
139
140bail:
141 buf_pool_release(&output_buf);
142 mutt_gsasl_client_finish(&gsasl_session);
143
144 if (rc == IMAP_AUTH_FAILURE)
145 {
146 mutt_debug(LL_DEBUG2, "%s failed\n", chosen_mech);
147 mutt_error(_("SASL authentication failed"));
148 }
149
150 return rc;
151}
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
const char * mutt_gsasl_get_mech(const char *requested_mech, const char *server_mechlist)
Pick a connection mechanism.
Definition gsasl.c:166
int mutt_gsasl_client_new(struct Connection *conn, const char *mech, Gsasl_session **sctx)
Create a new GNU SASL client.
Definition gsasl.c:203
void mutt_gsasl_client_finish(Gsasl_session **sctx)
Free a GNU SASL client.
Definition gsasl.c:225
#define IMAP_RES_NO
<tag> NO ...
Definition private.h:52
@ IMAP_CAP_SASL_IR
SASL initial response draft.
Definition private.h:148
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition util.c:832
#define IMAP_RES_BAD
<tag> BAD ...
Definition private.h:53
char * capstr
Capability string from the server.
Definition adata.h:55
+ Here is the call graph for this function:

◆ imap_auth_gss()

enum ImapAuthRes imap_auth_gss ( struct ImapAccountData * adata,
const char * method )

GSS Authentication support - Implements ImapAuth::authenticate() -.

Definition at line 106 of file auth_gss.c.

107{
108 gss_buffer_desc request_buf = { 0 };
109 gss_buffer_desc send_token = { 0 };
110 gss_buffer_t sec_token = NULL;
111 gss_name_t target_name = NULL;
112 gss_ctx_id_t context = NULL;
113 gss_OID mech_name = NULL;
114 char server_conf_flags;
115 gss_qop_t quality = 0;
116 int cflags = 0;
117 OM_uint32 maj_stat = 0;
118 OM_uint32 min_stat = 0;
119 unsigned long buf_size = 0;
120 int rc2;
121 int rc = IMAP_AUTH_FAILURE;
122
123 if (!(adata->capabilities & IMAP_CAP_AUTH_GSSAPI))
124 return IMAP_AUTH_UNAVAIL;
125
126 if (mutt_account_getuser(&adata->conn->account) < 0)
127 return IMAP_AUTH_FAILURE;
128
129 struct Buffer *buf1 = buf_pool_get();
130 struct Buffer *buf2 = buf_pool_get();
131
132 /* get an IMAP service ticket for the server */
133 buf_printf(buf1, "imap@%s", adata->conn->account.host);
134 request_buf.value = buf1->data;
135 request_buf.length = buf_len(buf1);
136
137 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
138 maj_stat = gss_import_name(&min_stat, &request_buf, gss_nt_service_name, &target_name);
139 if (maj_stat != GSS_S_COMPLETE)
140 {
141 mutt_debug(LL_DEBUG2, "Couldn't get service name for [%s]\n", buf1->data);
143 goto cleanup;
144 }
145 else if (c_debug_level >= 2)
146 {
147 gss_display_name(&min_stat, target_name, &request_buf, &mech_name);
148 mutt_debug(LL_DEBUG2, "Using service name [%s]\n", (char *) request_buf.value);
149 gss_release_buffer(&min_stat, &request_buf);
150 }
151 /* Acquire initial credentials - without a TGT GSSAPI is UNAVAIL */
152 sec_token = GSS_C_NO_BUFFER;
153 context = GSS_C_NO_CONTEXT;
154
155 /* build token */
156 maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &context, target_name,
157 GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
158 0, GSS_C_NO_CHANNEL_BINDINGS, sec_token, NULL,
159 &send_token, (unsigned int *) &cflags, NULL);
160 if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED))
161 {
162 print_gss_error(maj_stat, min_stat);
163 mutt_debug(LL_DEBUG1, "Error acquiring credentials - no TGT?\n");
164 gss_release_name(&min_stat, &target_name);
165
167 goto cleanup;
168 }
169
170 /* now begin login */
171 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
172 mutt_message(_("Authenticating (%s)..."), "GSSAPI");
173
174 imap_cmd_start(adata, "AUTHENTICATE GSSAPI");
175
176 /* expect a null continuation response ("+") */
177 do
178 {
179 rc2 = imap_cmd_step(adata);
180 } while (rc2 == IMAP_RES_CONTINUE);
181
182 if (rc2 != IMAP_RES_RESPOND)
183 {
184 mutt_debug(LL_DEBUG2, "Invalid response from server: %s\n", buf1->data);
185 gss_release_name(&min_stat, &target_name);
186 goto bail;
187 }
188
189 /* now start the security context initialisation loop... */
190 mutt_debug(LL_DEBUG2, "Sending credentials\n");
191 mutt_b64_buffer_encode(buf1, send_token.value, send_token.length);
192 gss_release_buffer(&min_stat, &send_token);
193 buf_addstr(buf1, "\r\n");
194 mutt_socket_send(adata->conn, buf_string(buf1));
195
196 while (maj_stat == GSS_S_CONTINUE_NEEDED)
197 {
198 /* Read server data */
199 do
200 {
201 rc2 = imap_cmd_step(adata);
202 } while (rc2 == IMAP_RES_CONTINUE);
203
204 if (rc2 != IMAP_RES_RESPOND)
205 {
206 mutt_debug(LL_DEBUG1, "#1 Error receiving server response\n");
207 gss_release_name(&min_stat, &target_name);
208 goto bail;
209 }
210
211 if (mutt_b64_buffer_decode(buf2, adata->buf + 2) < 0)
212 {
213 mutt_debug(LL_DEBUG1, "Invalid base64 server response\n");
214 gss_release_name(&min_stat, &target_name);
215 goto err_abort_cmd;
216 }
217 request_buf.value = buf2->data;
218 request_buf.length = buf_len(buf2);
219 sec_token = &request_buf;
220
221 /* Write client data */
222 maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &context, target_name,
223 GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
224 0, GSS_C_NO_CHANNEL_BINDINGS, sec_token, NULL,
225 &send_token, (unsigned int *) &cflags, NULL);
226 if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED))
227 {
228 print_gss_error(maj_stat, min_stat);
229 mutt_debug(LL_DEBUG1, "Error exchanging credentials\n");
230 gss_release_name(&min_stat, &target_name);
231
232 goto err_abort_cmd;
233 }
234 mutt_b64_buffer_encode(buf1, send_token.value, send_token.length);
235 gss_release_buffer(&min_stat, &send_token);
236 buf_addstr(buf1, "\r\n");
237 mutt_socket_send(adata->conn, buf_string(buf1));
238 }
239
240 gss_release_name(&min_stat, &target_name);
241
242 /* get security flags and buffer size */
243 do
244 {
245 rc2 = imap_cmd_step(adata);
246 } while (rc2 == IMAP_RES_CONTINUE);
247
248 if (rc2 != IMAP_RES_RESPOND)
249 {
250 mutt_debug(LL_DEBUG1, "#2 Error receiving server response\n");
251 goto bail;
252 }
253 if (mutt_b64_buffer_decode(buf2, adata->buf + 2) < 0)
254 {
255 mutt_debug(LL_DEBUG1, "Invalid base64 server response\n");
256 goto err_abort_cmd;
257 }
258 request_buf.value = buf2->data;
259 request_buf.length = buf_len(buf2);
260
261 maj_stat = gss_unwrap(&min_stat, context, &request_buf, &send_token, &cflags, &quality);
262 if (maj_stat != GSS_S_COMPLETE)
263 {
264 print_gss_error(maj_stat, min_stat);
265 mutt_debug(LL_DEBUG2, "Couldn't unwrap security level data\n");
266 gss_release_buffer(&min_stat, &send_token);
267 goto err_abort_cmd;
268 }
269 mutt_debug(LL_DEBUG2, "Credential exchange complete\n");
270
271 /* The unwrapped token must be at least 4 bytes: 1 byte of security flags
272 * followed by 3 bytes of maximum buffer size. Reject tokens that are
273 * too short to prevent an out-of-bounds read. */
274 if (send_token.length < 4)
275 {
276 mutt_debug(LL_DEBUG2, "Unwrapped security token too short (%zu bytes)\n",
277 send_token.length);
278 gss_release_buffer(&min_stat, &send_token);
279 goto err_abort_cmd;
280 }
281
282 /* first byte is security levels supported. We want NONE */
283 server_conf_flags = ((char *) send_token.value)[0];
284 if (!(((char *) send_token.value)[0] & GSS_AUTH_P_NONE))
285 {
286 mutt_debug(LL_DEBUG2, "Server requires integrity or privacy\n");
287 gss_release_buffer(&min_stat, &send_token);
288 goto err_abort_cmd;
289 }
290
291 /* we don't care about buffer size if we don't wrap content. But here it is */
292 ((char *) send_token.value)[0] = '\0';
293 buf_size = ntohl(*((uint32_t *) send_token.value));
294 gss_release_buffer(&min_stat, &send_token);
295 mutt_debug(LL_DEBUG2, "Unwrapped security level flags: %c%c%c\n",
296 (server_conf_flags & GSS_AUTH_P_NONE) ? 'N' : '-',
297 (server_conf_flags & GSS_AUTH_P_INTEGRITY) ? 'I' : '-',
298 (server_conf_flags & GSS_AUTH_P_PRIVACY) ? 'P' : '-');
299 mutt_debug(LL_DEBUG2, "Maximum GSS token size is %ld\n", buf_size);
300
301 /* agree to terms (hack!) */
302 buf_size = htonl(buf_size); /* not relevant without integrity/privacy */
303 buf_reset(buf1);
305 buf_addstr_n(buf1, ((char *) &buf_size) + 1, 3);
306 /* server decides if principal can log in as user */
307 buf_addstr(buf1, adata->conn->account.user);
308 request_buf.value = buf1->data;
309 request_buf.length = buf_len(buf1);
310 maj_stat = gss_wrap(&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf,
311 &cflags, &send_token);
312 if (maj_stat != GSS_S_COMPLETE)
313 {
314 mutt_debug(LL_DEBUG2, "Error creating login request\n");
315 goto err_abort_cmd;
316 }
317
318 mutt_b64_buffer_encode(buf1, send_token.value, send_token.length);
319 mutt_debug(LL_DEBUG2, "Requesting authorisation as %s\n", adata->conn->account.user);
320 buf_addstr(buf1, "\r\n");
321 mutt_socket_send(adata->conn, buf_string(buf1));
322
323 /* Joy of victory or agony of defeat? */
324 do
325 {
326 rc2 = imap_cmd_step(adata);
327 } while (rc2 == IMAP_RES_CONTINUE);
328 if (rc2 == IMAP_RES_RESPOND)
329 {
330 mutt_debug(LL_DEBUG1, "Unexpected server continuation request\n");
331 goto err_abort_cmd;
332 }
333 if (imap_code(adata->buf))
334 {
335 /* flush the security context */
336 mutt_debug(LL_DEBUG2, "Releasing GSS credentials\n");
337 maj_stat = gss_delete_sec_context(&min_stat, &context, &send_token);
338 if (maj_stat != GSS_S_COMPLETE)
339 mutt_debug(LL_DEBUG1, "Error releasing credentials\n");
340
341 /* send_token may contain a notification to the server to flush
342 * credentials. RFC1731 doesn't specify what to do, and since this
343 * support is only for authentication, we'll assume the server knows
344 * enough to flush its own credentials */
345 gss_release_buffer(&min_stat, &send_token);
346
348 goto cleanup;
349 }
350 else
351 {
352 goto bail;
353 }
354
355err_abort_cmd:
356 mutt_socket_send(adata->conn, "*\r\n");
357 do
358 {
359 rc2 = imap_cmd_step(adata);
360 } while (rc2 == IMAP_RES_CONTINUE);
361
362bail:
363 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
364 mutt_error(_("%s authentication failed"), "GSSAPI");
366
367cleanup:
368 buf_pool_release(&buf1);
369 buf_pool_release(&buf2);
370
371 return rc;
372}
#define GSS_AUTH_P_NONE
No protection (authentication only)
Definition auth_gss.c:56
static void print_gss_error(OM_uint32 err_maj, OM_uint32 err_min)
Print detailed error message to the debug log.
Definition auth_gss.c:65
#define GSS_AUTH_P_PRIVACY
Privacy protection (encryption)
Definition auth_gss.c:58
#define GSS_AUTH_P_INTEGRITY
Integrity protection.
Definition auth_gss.c:57
size_t mutt_b64_buffer_encode(struct Buffer *buf, const char *in, size_t len)
Convert raw bytes to NUL-terminated base64 string.
Definition base64.c:243
int mutt_b64_buffer_decode(struct Buffer *buf, const char *in)
Convert NUL-terminated base64 string to raw bytes.
Definition base64.c:261
size_t buf_addstr_n(struct Buffer *buf, const char *s, size_t len)
Add a string to a Buffer, expanding it if necessary.
Definition buffer.c:109
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
@ IMAP_CAP_AUTH_GSSAPI
RFC1731: GSSAPI authentication.
Definition private.h:141
char host[128]
Server to login to.
Definition connaccount.h:60
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
+ Here is the call graph for this function:

◆ imap_auth_login()

enum ImapAuthRes imap_auth_login ( struct ImapAccountData * adata,
const char * method )

Plain LOGIN support - Implements ImapAuth::authenticate() -.

Definition at line 45 of file auth_login.c.

46{
47 char q_user[256] = { 0 };
48 char q_pass[256] = { 0 };
49 char buf[1024] = { 0 };
50
52 {
53 mutt_message(_("LOGIN disabled on this server"));
54 return IMAP_AUTH_UNAVAIL;
55 }
56
57 if (mutt_account_getuser(&adata->conn->account) < 0)
58 return IMAP_AUTH_FAILURE;
59 if (mutt_account_getpass(&adata->conn->account) < 0)
60 return IMAP_AUTH_FAILURE;
61
62 mutt_message(_("Logging in..."));
63
64 imap_quote_string(q_user, sizeof(q_user), adata->conn->account.user, false);
65 imap_quote_string(q_pass, sizeof(q_pass), adata->conn->account.pass, false);
66
67 /* don't print the password unless we're at the ungodly debugging level
68 * of 5 or higher */
69
70 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
71 if (c_debug_level < IMAP_LOG_PASS)
72 mutt_debug(LL_DEBUG2, "Sending LOGIN command for %s\n", adata->conn->account.user);
73
74 snprintf(buf, sizeof(buf), "LOGIN %s %s", q_user, q_pass);
75 if (imap_exec(adata, buf, IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS)
76 {
78 return IMAP_AUTH_SUCCESS;
79 }
80
81 mutt_error(_("Login failed"));
82 return IMAP_AUTH_FAILURE;
83}
int imap_exec(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Execute a command and wait for the response from the server.
Definition command.c:1420
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
Quote string according to IMAP rules.
Definition util.c:894
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition private.h:95
#define IMAP_LOG_PASS
Log passwords (dangerous!)
Definition private.h:49
@ IMAP_CAP_LOGINDISABLED
RFC2595: LOGINDISABLED.
Definition private.h:146
@ IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition private.h:83
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
+ Here is the call graph for this function:

◆ imap_auth_oauth()

enum ImapAuthRes imap_auth_oauth ( struct ImapAccountData * adata,
const char * method )

Authenticate an IMAP connection using OAUTHBEARER - Implements ImapAuth::authenticate() -.

Definition at line 111 of file auth_oauth.c.

112{
113 return imap_auth_oauth_xoauth2(adata, method, false);
114}
static enum ImapAuthRes imap_auth_oauth_xoauth2(struct ImapAccountData *adata, const char *method, bool xoauth2)
Authenticate an IMAP connection using OAUTHBEARER or XOAUTH2.
Definition auth_oauth.c:51
+ Here is the call graph for this function:

◆ imap_auth_xoauth2()

enum ImapAuthRes imap_auth_xoauth2 ( struct ImapAccountData * adata,
const char * method )

Authenticate an IMAP connection using XOAUTH2 - Implements ImapAuth::authenticate() -.

Definition at line 119 of file auth_oauth.c.

120{
121 return imap_auth_oauth_xoauth2(adata, method, true);
122}
+ Here is the call graph for this function:

◆ imap_auth_plain()

enum ImapAuthRes imap_auth_plain ( struct ImapAccountData * adata,
const char * method )

SASL PLAIN support - Implements ImapAuth::authenticate() -.

Definition at line 42 of file auth_plain.c.

43{
44 static const char auth_plain_cmd[] = "AUTHENTICATE PLAIN";
45 // Subtract 1 (for the \0) to get the string length
46 static const size_t apc_len = sizeof(auth_plain_cmd) - 1;
47
48 struct ConnAccount *cac = &adata->conn->account;
49
50 if (mutt_account_getuser(cac) < 0)
51 return IMAP_AUTH_FAILURE;
52 if (mutt_account_getpass(cac) < 0)
53 return IMAP_AUTH_FAILURE;
54
55 mutt_message(_("Logging in..."));
56
57 int rc_step = IMAP_RES_CONTINUE;
59 struct Buffer *buf = buf_pool_get();
60
61 /* Prepare full AUTHENTICATE PLAIN message */
62 mutt_sasl_plain_msg(buf, auth_plain_cmd, cac->user, cac->user, cac->pass);
63
64 if (adata->capabilities & IMAP_CAP_SASL_IR)
65 {
66 imap_cmd_start(adata, buf_string(buf));
67 }
68 else
69 {
70 /* Split the message so we send AUTHENTICATE PLAIN first, and the
71 * credentials after the first command continuation request */
72 buf->data[apc_len] = '\0';
73 imap_cmd_start(adata, buf_string(buf));
74 while (rc_step == IMAP_RES_CONTINUE)
75 {
76 rc_step = imap_cmd_step(adata);
77 }
78 if (rc_step == IMAP_RES_RESPOND)
79 {
80 buf_addstr(buf, "\r\n");
81 mutt_socket_send(adata->conn, buf->data + apc_len + 1);
82 rc_step = IMAP_RES_CONTINUE;
83 }
84 }
85
86 while (rc_step == IMAP_RES_CONTINUE)
87 {
88 rc_step = imap_cmd_step(adata);
89 }
90
91 if (rc_step == IMAP_RES_BAD)
92 {
94 }
95 else if (rc_step == IMAP_RES_NO)
96 {
97 mutt_error(_("Login failed"));
99 }
100
102 buf_pool_release(&buf);
103 return rc;
104}
size_t mutt_sasl_plain_msg(struct Buffer *buf, const char *cmd, const char *authz, const char *user, const char *pass)
Construct a base64 encoded SASL PLAIN message.
Definition sasl_plain.c:50
Login details for a remote server.
Definition connaccount.h:59
+ Here is the call graph for this function:

◆ imap_auth_sasl()

enum ImapAuthRes imap_auth_sasl ( struct ImapAccountData * adata,
const char * method )

SASL authenticator - Implements ImapAuth::authenticate() -.

Definition at line 46 of file auth_sasl.c.

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}
#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
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:678
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:503
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:587
int mutt_sasl_interact(sasl_interact_t *interaction)
Perform an SASL interaction with the user.
Definition sasl.c:710
int mutt_sasl_client_new(struct Connection *conn, sasl_conn_t **saslconn)
Wrapper for sasl_client_new()
Definition sasl.c:612
void mutt_sasl_setup_conn(struct Connection *conn, sasl_conn_t *saslconn)
Set up an SASL connection.
Definition sasl.c:746
#define NONULL(x)
Definition string2.h:44
+ Here is the call graph for this function: