NeoMutt  2020-08-21-74-g346364
Teaching an old dog new tricks
DOXYGEN
auth.h File Reference

IMAP authenticator multiplexor. More...

#include "config.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

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...
 

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 36 of file auth.h.

37 {
38  IMAP_AUTH_SUCCESS = 0,
41 };
Authentication method not permitted.
Definition: auth.h:40
Authentication failed.
Definition: auth.h:39
Authentication successful.
Definition: auth.h:38

Function Documentation

◆ imap_auth_plain()

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

SASL PLAIN support - Implements ImapAuth::authenticate()

Definition at line 41 of file auth_plain.c.

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

42 {
43  char q_user[256], q_pass[256];
44  char buf[1024];
45 
46  if ((adata->capabilities & IMAP_CAP_LOGINDISABLED))
47  {
48  mutt_message(_("LOGIN disabled on this server"));
49  return IMAP_AUTH_UNAVAIL;
50  }
51 
52  if (mutt_account_getuser(&adata->conn->account) < 0)
53  return IMAP_AUTH_FAILURE;
54  if (mutt_account_getpass(&adata->conn->account) < 0)
55  return IMAP_AUTH_FAILURE;
56 
57  mutt_message(_("Logging in..."));
58 
59  imap_quote_string(q_user, sizeof(q_user), adata->conn->account.user, false);
60  imap_quote_string(q_pass, sizeof(q_pass), adata->conn->account.pass, false);
61 
62  /* don't print the password unless we're at the ungodly debugging level
63  * of 5 or higher */
64 
66  mutt_debug(LL_DEBUG2, "Sending LOGIN command for %s\n", adata->conn->account.user);
67 
68  snprintf(buf, sizeof(buf), "LOGIN %s %s", q_user, q_pass);
69  if (imap_exec(adata, buf, IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS)
70  {
72  return IMAP_AUTH_SUCCESS;
73  }
74 
75  mutt_error(_("Login failed"));
76  return IMAP_AUTH_FAILURE;
77 }
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition: connaccount.c:48
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:36
#define mutt_message(...)
Definition: logging.h:83
char user[128]
Username.
Definition: connaccount.h:55
#define _(a)
Definition: message.h:28
#define IMAP_LOG_PASS
Definition: private.h:51
Authentication method not permitted.
Definition: auth.h:40
Imap command executed or queued successfully.
Definition: private.h:84
Log at debug level 2.
Definition: logging.h:41
char pass[256]
Password.
Definition: connaccount.h:56
Authentication failed.
Definition: auth.h:39
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
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:1242
short C_DebugLevel
Config: Logging level for debug logs.
Definition: mutt_logging.c:48
#define IMAP_CAP_LOGINDISABLED
RFC2595: LOGINDISABLED.
Definition: private.h:133
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition: private.h:74
ImapCapFlags capabilities
Definition: private.h:186
#define mutt_error(...)
Definition: logging.h:84
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Authentication successful.
Definition: auth.h:38
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
quote string according to IMAP rules
Definition: util.c:971
int mutt_account_getpass(struct ConnAccount *cac)
Fetch password into ConnAccount, if necessary.
Definition: connaccount.c:111
struct Connection * conn
Definition: private.h:172
+ 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 103 of file auth_gss.c.

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

◆ imap_auth_sasl()

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

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

Definition at line 45 of file auth_sasl.c.

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

44 {
45  char *ibuf = NULL;
46  char *oauthbearer = NULL;
47  int ilen;
48  int rc;
49 
50  /* For now, we only support SASL_IR also and over TLS */
51  if (!(adata->capabilities & IMAP_CAP_AUTH_OAUTHBEARER) ||
52  !(adata->capabilities & IMAP_CAP_SASL_IR) || (adata->conn->ssf == 0))
53  {
54  return IMAP_AUTH_UNAVAIL;
55  }
56 
57  /* If they did not explicitly request or configure oauth then fail quietly */
58  if (!method && !C_ImapOauthRefreshCommand)
59  return IMAP_AUTH_UNAVAIL;
60 
61  // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
62  mutt_message(_("Authenticating (%s)..."), "OAUTHBEARER");
63 
64  /* We get the access token from the imap_oauth_refresh_command */
65  oauthbearer = mutt_account_getoauthbearer(&adata->conn->account);
66  if (!oauthbearer)
67  return IMAP_AUTH_FAILURE;
68 
69  ilen = mutt_str_len(oauthbearer) + 30;
70  ibuf = mutt_mem_malloc(ilen);
71  snprintf(ibuf, ilen, "AUTHENTICATE OAUTHBEARER %s", oauthbearer);
72 
73  /* This doesn't really contain a password, but the token is good for
74  * an hour, so suppress it anyways. */
75  rc = imap_exec(adata, ibuf, IMAP_CMD_PASS);
76 
77  FREE(&oauthbearer);
78  FREE(&ibuf);
79 
80  if (rc != IMAP_EXEC_SUCCESS)
81  {
82  /* The error response was in SASL continuation, so continue the SASL
83  * to cause a failure and exit SASL input. See RFC7628 3.2.3 */
84  mutt_socket_send(adata->conn, "\001");
85  rc = imap_exec(adata, ibuf, IMAP_CMD_NO_FLAGS);
86  }
87 
88  if (rc == IMAP_EXEC_SUCCESS)
89  {
91  return IMAP_AUTH_SUCCESS;
92  }
93 
94  // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
95  mutt_error(_("%s authentication failed"), "OAUTHBEARER");
96  return IMAP_AUTH_FAILURE;
97 }
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:36
#define mutt_socket_send(conn, buf)
Definition: mutt_socket.h:37
unsigned int ssf
Security strength factor, in bits (see below)
Definition: connection.h:37
#define mutt_message(...)
Definition: logging.h:83
#define IMAP_CAP_SASL_IR
SASL initial response draft.
Definition: private.h:135
#define _(a)
Definition: message.h:28
char * C_ImapOauthRefreshCommand
Config: (imap) External command to generate OAUTH refresh token.
Definition: config.c:50
Authentication method not permitted.
Definition: auth.h:40
Imap command executed or queued successfully.
Definition: private.h:84
Authentication failed.
Definition: auth.h:39
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
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:1242
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition: private.h:74
ImapCapFlags capabilities
Definition: private.h:186
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:73
#define IMAP_CAP_AUTH_OAUTHBEARER
RFC7628: AUTH=OAUTHBEARER.
Definition: private.h:131
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:631
#define mutt_error(...)
Definition: logging.h:84
#define FREE(x)
Definition: memory.h:40
char * mutt_account_getoauthbearer(struct ConnAccount *cac)
Get an OAUTHBEARER token.
Definition: connaccount.c:158
Authentication successful.
Definition: auth.h:38
struct Connection * conn
Definition: private.h:172
+ Here is the call graph for this function: