NeoMutt  2025-01-09-144-gb44c67
Teaching an old dog new tricks
DOXYGEN
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
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:52
#define mutt_error(...)
Definition: logging2.h:93
#define mutt_message(...)
Definition: logging2.h:92
#define mutt_debug(LEVEL,...)
Definition: logging2.h:90
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_CONTINUE
* ...
Definition: private.h:56
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:44
#define _(a)
Definition: message.h:28
#define mutt_socket_send(conn, buf)
Definition: socket.h:57
char user[128]
Username.
Definition: connaccount.h:56
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:49
ImapCapFlags capabilities
Capability flags.
Definition: adata.h:55
char * buf
Definition: adata.h:59
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 97 of file auth_cram.c.

98{
100 return IMAP_AUTH_UNAVAIL;
101
102 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
103 mutt_message(_("Authenticating (%s)..."), "CRAM-MD5");
104
105 /* get auth info */
106 if (mutt_account_getlogin(&adata->conn->account) < 0)
107 return IMAP_AUTH_FAILURE;
108 if (mutt_account_getpass(&adata->conn->account) < 0)
109 return IMAP_AUTH_FAILURE;
110
111 imap_cmd_start(adata, "AUTHENTICATE CRAM-MD5");
112
113 struct Buffer *ibuf = buf_pool_get();
114 struct Buffer *obuf = buf_pool_get();
115 unsigned char hmac_response[MD5_DIGEST_LEN];
116 int rc_step;
118
119 /* From RFC2195:
120 * The data encoded in the first ready response contains a presumptively
121 * arbitrary string of random digits, a timestamp, and the fully-qualified
122 * primary host name of the server. The syntax of the unencoded form must
123 * correspond to that of an RFC822 'msg-id' [RFC822] as described in [POP3]. */
124 do
125 {
126 rc_step = imap_cmd_step(adata);
127 } while (rc_step == IMAP_RES_CONTINUE);
128
129 if (rc_step != IMAP_RES_RESPOND)
130 {
131 mutt_debug(LL_DEBUG1, "Invalid response from server\n");
132 goto bail;
133 }
134
135 if (mutt_b64_decode(adata->buf + 2, obuf->data, obuf->dsize) == -1)
136 {
137 mutt_debug(LL_DEBUG1, "Error decoding base64 response\n");
138 goto bail;
139 }
140
141 mutt_debug(LL_DEBUG2, "CRAM challenge: %s\n", buf_string(obuf));
142
143 /* The client makes note of the data and then responds with a string
144 * consisting of the user name, a space, and a 'digest'. The latter is
145 * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where the
146 * key is a shared secret and the digested text is the timestamp (including
147 * angle-brackets).
148 *
149 * Note: The user name shouldn't be quoted. Since the digest can't contain
150 * spaces, there is no ambiguity. Some servers get this wrong, we'll work
151 * around them when the bug report comes in. Until then, we'll remain
152 * blissfully RFC-compliant. */
153 hmac_md5(adata->conn->account.pass, buf_string(obuf), hmac_response);
154 /* dubious optimisation I saw elsewhere: make the whole string in one call */
155 int off = buf_printf(obuf, "%s ", adata->conn->account.user);
156 mutt_md5_toascii(hmac_response, obuf->data + off);
157 mutt_debug(LL_DEBUG2, "CRAM response: %s\n", buf_string(obuf));
158
159 /* ibuf must be long enough to store the base64 encoding of obuf,
160 * plus the additional debris */
161 mutt_b64_encode(obuf->data, obuf->dsize, ibuf->data, ibuf->dsize - 2);
162 buf_addstr(ibuf, "\r\n");
163 mutt_socket_send(adata->conn, buf_string(ibuf));
164
165 do
166 {
167 rc_step = imap_cmd_step(adata);
168 } while (rc_step == IMAP_RES_CONTINUE);
169
170 if (rc_step != IMAP_RES_OK)
171 {
172 mutt_debug(LL_DEBUG1, "Error receiving server response\n");
173 goto bail;
174 }
175
176 if (imap_code(adata->buf))
178
179bail:
180 if (rc != IMAP_AUTH_SUCCESS)
181 {
182 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
183 mutt_error(_("%s authentication failed"), "CRAM-MD5");
184 }
185
186 buf_pool_release(&ibuf);
187 buf_pool_release(&obuf);
188 return rc;
189}
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:49
#define MD5_DIGEST_LEN
Definition: auth_cram.c:41
size_t mutt_b64_encode(const char *in, size_t inlen, char *out, size_t outlen)
Convert raw bytes to NUL-terminated base64 string.
Definition: base64.c:87
int mutt_b64_decode(const char *in, char *out, size_t olen)
Convert NUL-terminated base64 string to raw bytes.
Definition: base64.c:135
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:161
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:226
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.
Definition: connaccount.c:131
int mutt_account_getlogin(struct ConnAccount *cac)
Retrieve login info into ConnAccount, if necessary.
Definition: connaccount.c:101
#define IMAP_CAP_AUTH_CRAM_MD5
RFC2195: CRAM-MD5 authentication.
Definition: private.h:126
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:45
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:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
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:57
+ 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:241
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:395
const char * mutt_gsasl_get_mech(const char *requested_mech, const char *server_mechlist)
Pick a connection mechanism.
Definition: gsasl.c:164
int mutt_gsasl_client_new(struct Connection *conn, const char *mech, Gsasl_session **sctx)
Create a new GNU SASL client.
Definition: gsasl.c:199
void mutt_gsasl_client_finish(Gsasl_session **sctx)
Free a GNU SASL client.
Definition: gsasl.c:220
#define IMAP_RES_NO
<tag> NO ...
Definition: private.h:53
#define IMAP_CAP_SASL_IR
SASL initial response draft.
Definition: private.h:134
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:824
#define IMAP_RES_BAD
<tag> BAD ...
Definition: private.h:54
char * capstr
Capability string from the server.
Definition: adata.h:54
+ 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, send_token;
109 gss_buffer_t sec_token;
110 gss_name_t target_name;
111 gss_ctx_id_t context;
112 gss_OID mech_name;
113 char server_conf_flags;
114 gss_qop_t quality;
115 int cflags;
116 OM_uint32 maj_stat, min_stat;
117 unsigned long buf_size;
118 int rc2, rc = IMAP_AUTH_FAILURE;
119
120 if (!(adata->capabilities & IMAP_CAP_AUTH_GSSAPI))
121 return IMAP_AUTH_UNAVAIL;
122
123 if (mutt_account_getuser(&adata->conn->account) < 0)
124 return IMAP_AUTH_FAILURE;
125
126 struct Buffer *buf1 = buf_pool_get();
127 struct Buffer *buf2 = buf_pool_get();
128
129 /* get an IMAP service ticket for the server */
130 buf_printf(buf1, "imap@%s", adata->conn->account.host);
131 request_buf.value = buf1->data;
132 request_buf.length = buf_len(buf1);
133
134 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
135 maj_stat = gss_import_name(&min_stat, &request_buf, gss_nt_service_name, &target_name);
136 if (maj_stat != GSS_S_COMPLETE)
137 {
138 mutt_debug(LL_DEBUG2, "Couldn't get service name for [%s]\n", buf1->data);
140 goto cleanup;
141 }
142 else if (c_debug_level >= 2)
143 {
144 gss_display_name(&min_stat, target_name, &request_buf, &mech_name);
145 mutt_debug(LL_DEBUG2, "Using service name [%s]\n", (char *) request_buf.value);
146 gss_release_buffer(&min_stat, &request_buf);
147 }
148 /* Acquire initial credentials - without a TGT GSSAPI is UNAVAIL */
149 sec_token = GSS_C_NO_BUFFER;
150 context = GSS_C_NO_CONTEXT;
151
152 /* build token */
153 maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &context, target_name,
154 GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
155 0, GSS_C_NO_CHANNEL_BINDINGS, sec_token, NULL,
156 &send_token, (unsigned int *) &cflags, NULL);
157 if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED))
158 {
159 print_gss_error(maj_stat, min_stat);
160 mutt_debug(LL_DEBUG1, "Error acquiring credentials - no TGT?\n");
161 gss_release_name(&min_stat, &target_name);
162
164 goto cleanup;
165 }
166
167 /* now begin login */
168 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
169 mutt_message(_("Authenticating (%s)..."), "GSSAPI");
170
171 imap_cmd_start(adata, "AUTHENTICATE GSSAPI");
172
173 /* expect a null continuation response ("+") */
174 do
175 {
176 rc2 = imap_cmd_step(adata);
177 } while (rc2 == IMAP_RES_CONTINUE);
178
179 if (rc2 != IMAP_RES_RESPOND)
180 {
181 mutt_debug(LL_DEBUG2, "Invalid response from server: %s\n", buf1->data);
182 gss_release_name(&min_stat, &target_name);
183 goto bail;
184 }
185
186 /* now start the security context initialisation loop... */
187 mutt_debug(LL_DEBUG2, "Sending credentials\n");
188 mutt_b64_buffer_encode(buf1, send_token.value, send_token.length);
189 gss_release_buffer(&min_stat, &send_token);
190 buf_addstr(buf1, "\r\n");
191 mutt_socket_send(adata->conn, buf_string(buf1));
192
193 while (maj_stat == GSS_S_CONTINUE_NEEDED)
194 {
195 /* Read server data */
196 do
197 {
198 rc2 = imap_cmd_step(adata);
199 } while (rc2 == IMAP_RES_CONTINUE);
200
201 if (rc2 != IMAP_RES_RESPOND)
202 {
203 mutt_debug(LL_DEBUG1, "#1 Error receiving server response\n");
204 gss_release_name(&min_stat, &target_name);
205 goto bail;
206 }
207
208 if (mutt_b64_buffer_decode(buf2, adata->buf + 2) < 0)
209 {
210 mutt_debug(LL_DEBUG1, "Invalid base64 server response\n");
211 gss_release_name(&min_stat, &target_name);
212 goto err_abort_cmd;
213 }
214 request_buf.value = buf2->data;
215 request_buf.length = buf_len(buf2);
216 sec_token = &request_buf;
217
218 /* Write client data */
219 maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &context, target_name,
220 GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
221 0, GSS_C_NO_CHANNEL_BINDINGS, sec_token, NULL,
222 &send_token, (unsigned int *) &cflags, NULL);
223 if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED))
224 {
225 print_gss_error(maj_stat, min_stat);
226 mutt_debug(LL_DEBUG1, "Error exchanging credentials\n");
227 gss_release_name(&min_stat, &target_name);
228
229 goto err_abort_cmd;
230 }
231 mutt_b64_buffer_encode(buf1, send_token.value, send_token.length);
232 gss_release_buffer(&min_stat, &send_token);
233 buf_addstr(buf1, "\r\n");
234 mutt_socket_send(adata->conn, buf_string(buf1));
235 }
236
237 gss_release_name(&min_stat, &target_name);
238
239 /* get security flags and buffer size */
240 do
241 {
242 rc2 = imap_cmd_step(adata);
243 } while (rc2 == IMAP_RES_CONTINUE);
244
245 if (rc2 != IMAP_RES_RESPOND)
246 {
247 mutt_debug(LL_DEBUG1, "#2 Error receiving server response\n");
248 goto bail;
249 }
250 if (mutt_b64_buffer_decode(buf2, adata->buf + 2) < 0)
251 {
252 mutt_debug(LL_DEBUG1, "Invalid base64 server response\n");
253 goto err_abort_cmd;
254 }
255 request_buf.value = buf2->data;
256 request_buf.length = buf_len(buf2);
257
258 maj_stat = gss_unwrap(&min_stat, context, &request_buf, &send_token, &cflags, &quality);
259 if (maj_stat != GSS_S_COMPLETE)
260 {
261 print_gss_error(maj_stat, min_stat);
262 mutt_debug(LL_DEBUG2, "Couldn't unwrap security level data\n");
263 gss_release_buffer(&min_stat, &send_token);
264 goto err_abort_cmd;
265 }
266 mutt_debug(LL_DEBUG2, "Credential exchange complete\n");
267
268 /* first byte is security levels supported. We want NONE */
269 server_conf_flags = ((char *) send_token.value)[0];
270 if (!(((char *) send_token.value)[0] & GSS_AUTH_P_NONE))
271 {
272 mutt_debug(LL_DEBUG2, "Server requires integrity or privacy\n");
273 gss_release_buffer(&min_stat, &send_token);
274 goto err_abort_cmd;
275 }
276
277 /* we don't care about buffer size if we don't wrap content. But here it is */
278 ((char *) send_token.value)[0] = '\0';
279 buf_size = ntohl(*((long *) send_token.value));
280 gss_release_buffer(&min_stat, &send_token);
281 mutt_debug(LL_DEBUG2, "Unwrapped security level flags: %c%c%c\n",
282 (server_conf_flags & GSS_AUTH_P_NONE) ? 'N' : '-',
283 (server_conf_flags & GSS_AUTH_P_INTEGRITY) ? 'I' : '-',
284 (server_conf_flags & GSS_AUTH_P_PRIVACY) ? 'P' : '-');
285 mutt_debug(LL_DEBUG2, "Maximum GSS token size is %ld\n", buf_size);
286
287 /* agree to terms (hack!) */
288 buf_size = htonl(buf_size); /* not relevant without integrity/privacy */
289 buf_reset(buf1);
291 buf_addstr_n(buf1, ((char *) &buf_size) + 1, 3);
292 /* server decides if principal can log in as user */
293 buf_addstr(buf1, adata->conn->account.user);
294 request_buf.value = buf1->data;
295 request_buf.length = buf_len(buf1);
296 maj_stat = gss_wrap(&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf,
297 &cflags, &send_token);
298 if (maj_stat != GSS_S_COMPLETE)
299 {
300 mutt_debug(LL_DEBUG2, "Error creating login request\n");
301 goto err_abort_cmd;
302 }
303
304 mutt_b64_buffer_encode(buf1, send_token.value, send_token.length);
305 mutt_debug(LL_DEBUG2, "Requesting authorisation as %s\n", adata->conn->account.user);
306 buf_addstr(buf1, "\r\n");
307 mutt_socket_send(adata->conn, buf_string(buf1));
308
309 /* Joy of victory or agony of defeat? */
310 do
311 {
312 rc2 = imap_cmd_step(adata);
313 } while (rc2 == IMAP_RES_CONTINUE);
314 if (rc2 == IMAP_RES_RESPOND)
315 {
316 mutt_debug(LL_DEBUG1, "Unexpected server continuation request\n");
317 goto err_abort_cmd;
318 }
319 if (imap_code(adata->buf))
320 {
321 /* flush the security context */
322 mutt_debug(LL_DEBUG2, "Releasing GSS credentials\n");
323 maj_stat = gss_delete_sec_context(&min_stat, &context, &send_token);
324 if (maj_stat != GSS_S_COMPLETE)
325 mutt_debug(LL_DEBUG1, "Error releasing credentials\n");
326
327 /* send_token may contain a notification to the server to flush
328 * credentials. RFC1731 doesn't specify what to do, and since this
329 * support is only for authentication, we'll assume the server knows
330 * enough to flush its own credentials */
331 gss_release_buffer(&min_stat, &send_token);
332
334 goto cleanup;
335 }
336 else
337 {
338 goto bail;
339 }
340
341err_abort_cmd:
342 mutt_socket_send(adata->conn, "*\r\n");
343 do
344 {
345 rc2 = imap_cmd_step(adata);
346 } while (rc2 == IMAP_RES_CONTINUE);
347
348bail:
349 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
350 mutt_error(_("%s authentication failed"), "GSSAPI");
352
353cleanup:
354 buf_pool_release(&buf1);
355 buf_pool_release(&buf2);
356
357 return rc;
358}
#define GSS_AUTH_P_NONE
Definition: auth_gss.c:57
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:66
#define GSS_AUTH_P_PRIVACY
Definition: auth_gss.c:59
#define GSS_AUTH_P_INTEGRITY
Definition: auth_gss.c:58
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:198
int mutt_b64_buffer_decode(struct Buffer *buf, const char *in)
Convert NUL-terminated base64 string to raw bytes.
Definition: base64.c:216
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:96
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:491
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:76
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:143
#define IMAP_CAP_AUTH_GSSAPI
RFC1731: GSSAPI authentication.
Definition: private.h:127
char host[128]
Server to login to.
Definition: connaccount.h:54
Container for Accounts, Notifications.
Definition: neomutt.h:43
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:47
+ 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:1303
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
Quote string according to IMAP rules.
Definition: util.c:886
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition: private.h:72
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h:82
#define IMAP_LOG_PASS
Definition: private.h:50
#define IMAP_CAP_LOGINDISABLED
RFC2595: LOGINDISABLED.
Definition: private.h:132
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
+ 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:53
+ 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 47 of file auth_sasl.c.

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}
#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
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:673
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:231
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:497
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:582
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
#define NONULL(x)
Definition: string2.h:37
+ Here is the call graph for this function: