NeoMutt  2020-08-07-1-gab41a1
Teaching an old dog new tricks
DOXYGEN
auth_sasl.c File Reference

IMAP SASL authentication method. More...

#include "config.h"
#include <stddef.h>
#include <sasl/sasl.h>
#include <sasl/saslutil.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "private.h"
#include "mutt/lib.h"
#include "conn/lib.h"
#include "auth.h"
#include "mutt_socket.h"
+ Include dependency graph for auth_sasl.c:

Go to the source code of this file.

Functions

enum ImapAuthRes imap_auth_sasl (struct ImapAccountData *adata, const char *method)
 Default authenticator if available - Implements ImapAuth::authenticate() More...
 

Detailed Description

IMAP SASL authentication method.

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

Function Documentation

◆ imap_auth_sasl()

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

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

Parameters
adataImap Account data
methodName of this authentication method
Return values
ImapAuthResResult, e.g. IMAP_AUTH_SUCCESS

Definition at line 48 of file auth_sasl.c.

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