NeoMutt  2021-10-29-43-g6b8931
Teaching an old dog new tricks
DOXYGEN
auth.h File Reference

IMAP authenticator multiplexor. More...

#include "config.h"
#include <stdbool.h>
+ Include dependency graph for auth.h:
+ This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Enumerations

enum  ImapAuthRes { IMAP_AUTH_SUCCESS = 0 , IMAP_AUTH_FAILURE , IMAP_AUTH_UNAVAIL }
 Results of IMAP Authentication. More...
 

Functions

bool imap_auth_is_valid (const char *authenticator)
 Check if string is a valid imap authentication method. More...
 
enum ImapAuthRes imap_auth_plain (struct ImapAccountData *adata, const char *method)
 SASL PLAIN support - Implements ImapAuth::authenticate() More...
 
enum ImapAuthRes imap_auth_login (struct ImapAccountData *adata, const char *method)
 Plain LOGIN support - Implements ImapAuth::authenticate() More...
 
enum ImapAuthRes imap_auth_gss (struct ImapAccountData *adata, const char *method)
 GSS Authentication support - Implements ImapAuth::authenticate() More...
 
enum ImapAuthRes imap_auth_sasl (struct ImapAccountData *adata, const char *method)
 Default authenticator if available - Implements ImapAuth::authenticate() More...
 
enum ImapAuthRes imap_auth_oauth (struct ImapAccountData *adata, const char *method)
 Authenticate an IMAP connection using OAUTHBEARER - Implements ImapAuth::authenticate() More...
 
enum ImapAuthRes imap_auth_xoauth2 (struct ImapAccountData *adata, const char *method)
 Authenticate an IMAP connection using XOAUTH2 - Implements ImapAuth::authenticate() More...
 

Detailed Description

IMAP authenticator multiplexor.

Authors
  • Brendan Cully

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file auth.h.

Enumeration Type Documentation

◆ ImapAuthRes

Results of IMAP Authentication.

Enumerator
IMAP_AUTH_SUCCESS 

Authentication successful.

IMAP_AUTH_FAILURE 

Authentication failed.

IMAP_AUTH_UNAVAIL 

Authentication method not permitted.

Definition at line 37 of file auth.h.

38 {
39  IMAP_AUTH_SUCCESS = 0,
42 };
@ 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

Function Documentation

◆ imap_auth_is_valid()

bool imap_auth_is_valid ( const char *  authenticator)

Check if string is a valid imap authentication method.

Parameters
authenticatorAuthenticator string to check
Return values
trueArgument is a valid auth method

Validate whether an input string is an accepted imap authentication method as defined by ImapAuthenticators.

Definition at line 88 of file auth.c.

89 {
90  for (size_t i = 0; i < mutt_array_size(ImapAuthenticators); i++)
91  {
92  const struct ImapAuth *auth = &ImapAuthenticators[i];
93  if (auth->method && mutt_istr_equal(auth->method, authenticator))
94  return true;
95  }
96 
97  return false;
98 }
static const struct ImapAuth ImapAuthenticators[]
Accepted authentication methods.
Definition: auth.c:59
#define mutt_array_size(x)
Definition: memory.h:33
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:727
IMAP authentication multiplexor.
Definition: auth.c:43
const char * method
Name of authentication method supported, NULL means variable.
Definition: auth.c:52
+ Here is the call graph for this function:
+ Here is the caller 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 1 of file auth_plain.c.

43 {
44  int rc = IMAP_RES_CONTINUE;
45  enum ImapAuthRes res = IMAP_AUTH_SUCCESS;
46  static const char auth_plain_cmd[] = "AUTHENTICATE PLAIN";
47  char buf[256] = { 0 };
48 
49  if (mutt_account_getuser(&adata->conn->account) < 0)
50  return IMAP_AUTH_FAILURE;
51  if (mutt_account_getpass(&adata->conn->account) < 0)
52  return IMAP_AUTH_FAILURE;
53 
54  mutt_message(_("Logging in..."));
55 
56  /* Prepare full AUTHENTICATE PLAIN message */
57  mutt_sasl_plain_msg(buf, sizeof(buf), auth_plain_cmd, adata->conn->account.user,
58  adata->conn->account.user, adata->conn->account.pass);
59 
60  if (adata->capabilities & IMAP_CAP_SASL_IR)
61  {
62  imap_cmd_start(adata, buf);
63  }
64  else
65  {
66  /* Split the message so we send AUTHENTICATE PLAIN first, and the
67  * credentials after the first command continuation request */
68  buf[sizeof(auth_plain_cmd) - 1] = '\0';
69  imap_cmd_start(adata, buf);
70  while (rc == IMAP_RES_CONTINUE)
71  {
72  rc = imap_cmd_step(adata);
73  }
74  if (rc == IMAP_RES_RESPOND)
75  {
76  mutt_str_cat(buf + sizeof(auth_plain_cmd),
77  sizeof(buf) - sizeof(auth_plain_cmd), "\r\n");
78  mutt_socket_send(adata->conn, buf + sizeof(auth_plain_cmd));
79  rc = IMAP_RES_CONTINUE;
80  }
81  }
82 
83  while (rc == IMAP_RES_CONTINUE)
84  {
85  rc = imap_cmd_step(adata);
86  }
87 
88  if (rc == IMAP_RES_BAD)
89  {
90  res = IMAP_AUTH_UNAVAIL;
91  }
92  else if (rc == IMAP_RES_NO)
93  {
94  mutt_error(_("Login failed"));
95  res = IMAP_AUTH_FAILURE;
96  }
97 
99  return res;
100 }
ImapAuthRes
Results of IMAP Authentication.
Definition: auth.h:38
int mutt_account_getpass(struct ConnAccount *cac)
Fetch password into ConnAccount, if necessary.
Definition: connaccount.c:111
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition: connaccount.c:48
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1070
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1084
#define IMAP_RES_RESPOND
+
Definition: private.h:58
#define IMAP_RES_NO
<tag> NO ...
Definition: private.h:54
#define IMAP_CAP_SASL_IR
SASL initial response draft.
Definition: private.h:136
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
#define IMAP_RES_BAD
<tag> BAD ...
Definition: private.h:55
#define _(a)
Definition: message.h:28
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:196
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
#define mutt_socket_send(conn, buf)
Definition: mutt_socket.h:37
size_t mutt_sasl_plain_msg(char *buf, size_t buflen, const char *cmd, const char *authz, const char *user, const char *pass)
Construct a base64 encoded SASL PLAIN message.
Definition: sasl_plain.c:55
char user[128]
Username.
Definition: connaccount.h:56
char pass[256]
Password.
Definition: connaccount.h:57
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
ImapCapFlags capabilities
Capability flags.
Definition: adata.h:55
struct Connection * conn
Connection to IMAP server.
Definition: adata.h:41
+ 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 1 of file auth_login.c.

45 {
46  char q_user[256], q_pass[256];
47  char buf[1024];
48 
49  if ((adata->capabilities & IMAP_CAP_LOGINDISABLED))
50  {
51  mutt_message(_("LOGIN disabled on this server"));
52  return IMAP_AUTH_UNAVAIL;
53  }
54 
55  if (mutt_account_getuser(&adata->conn->account) < 0)
56  return IMAP_AUTH_FAILURE;
57  if (mutt_account_getpass(&adata->conn->account) < 0)
58  return IMAP_AUTH_FAILURE;
59 
60  mutt_message(_("Logging in..."));
61 
62  imap_quote_string(q_user, sizeof(q_user), adata->conn->account.user, false);
63  imap_quote_string(q_pass, sizeof(q_pass), adata->conn->account.pass, false);
64 
65  /* don't print the password unless we're at the ungodly debugging level
66  * of 5 or higher */
67 
68  const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
69  if (c_debug_level < IMAP_LOG_PASS)
70  mutt_debug(LL_DEBUG2, "Sending LOGIN command for %s\n", adata->conn->account.user);
71 
72  snprintf(buf, sizeof(buf), "LOGIN %s %s", q_user, q_pass);
73  if (imap_exec(adata, buf, IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS)
74  {
76  return IMAP_AUTH_SUCCESS;
77  }
78 
79  mutt_error(_("Login failed"));
80  return IMAP_AUTH_FAILURE;
81 }
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
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:1255
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
Quote string according to IMAP rules.
Definition: util.c:840
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition: private.h:74
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h:84
#define IMAP_LOG_PASS
Definition: private.h:51
#define IMAP_CAP_LOGINDISABLED
RFC2595: LOGINDISABLED.
Definition: private.h:134
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ 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 65 of file auth_gss.c.

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

◆ imap_auth_sasl()

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

Default authenticator if available - Implements ImapAuth::authenticate()

Definition at line 1 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 
82  if ((adata->capabilities & IMAP_CAP_AUTH_ANONYMOUS) &&
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  mutt_debug(LL_DEBUG2, "%s unavailable\n", method);
112  else
113  {
114  mutt_debug(
115  LL_DEBUG1,
116  "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);
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);
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  client_start = false;
197 
198  /* send out response, or line break if none needed */
199  if (olen)
200  {
201  if ((olen * 2) > bufsize)
202  {
203  bufsize = olen * 2;
204  mutt_mem_realloc(&buf, bufsize);
205  }
206  if (sasl_encode64(pc, olen, buf, bufsize, &olen) != SASL_OK)
207  {
208  mutt_debug(LL_DEBUG1, "#2 error base64-encoding client response\n");
209  goto bail;
210  }
211  }
212 
213  if (irc == IMAP_RES_RESPOND)
214  {
215  mutt_str_copy(buf + olen, "\r\n", bufsize - olen);
216  mutt_socket_send(adata->conn, buf);
217  }
218 
219  /* If SASL has errored out, send an abort string to the server */
220  if (rc < 0)
221  {
222  mutt_socket_send(adata->conn, "*\r\n");
223  mutt_debug(LL_DEBUG1, "sasl_client_step error %d\n", rc);
224  }
225 
226  olen = 0;
227  }
228 
229  while (irc != IMAP_RES_OK)
230  {
231  irc = imap_cmd_step(adata);
232  if (irc != IMAP_RES_CONTINUE)
233  break;
234  }
235 
236  if (rc != SASL_OK)
237  goto bail;
238 
239  if (imap_code(adata->buf))
240  {
241  mutt_sasl_setup_conn(adata->conn, saslconn);
242  FREE(&buf);
243  return IMAP_AUTH_SUCCESS;
244  }
245 
246 bail:
247  sasl_dispose(&saslconn);
248  FREE(&buf);
249 
250  if (method)
251  {
252  mutt_debug(LL_DEBUG2, "%s failed\n", method);
253  return IMAP_AUTH_UNAVAIL;
254  }
255 
256  // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
257  mutt_error(_("%s authentication failed"), "SASL ");
258 
259  return IMAP_AUTH_FAILURE;
260 }
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
#define IMAP_CAP_AUTH_ANONYMOUS
AUTH=ANONYMOUS.
Definition: private.h:130
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:40
#define MAX(a, b)
Definition: memory.h:30
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:158
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:475
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:560
int mutt_sasl_interact(sasl_interact_t *interaction)
Perform an SASL interaction with the user.
Definition: sasl.c:701
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:737
#define NONULL(x)
Definition: string2.h:37
char * capstr
Capability string from the server.
Definition: adata.h:54
+ 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 1 of file auth_oauth.c.

115 {
116  return imap_auth_oauth_xoauth2(adata, method, false);
117 }
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:50
+ 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 1 of file auth_oauth.c.

123 {
124  return imap_auth_oauth_xoauth2(adata, method, true);
125 }