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

IMAP GSS authentication method. More...

#include "config.h"
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include "private.h"
#include "mutt/lib.h"
#include "conn/lib.h"
#include "auth.h"
#include "mutt_logging.h"
#include "mutt_socket.h"
#include <gssapi/gssapi.h>
#include <gssapi/gssapi_generic.h>
+ Include dependency graph for auth_gss.c:

Go to the source code of this file.

Macros

#define GSS_AUTH_P_NONE   1
 
#define GSS_AUTH_P_INTEGRITY   2
 
#define GSS_AUTH_P_PRIVACY   4
 

Functions

static void print_gss_error (OM_uint32 err_maj, OM_uint32 err_min)
 Print detailed error message to the debug log. More...
 
enum ImapAuthRes imap_auth_gss (struct ImapAccountData *adata, const char *method)
 GSS Authentication support - Implements ImapAuth::authenticate() More...
 

Detailed Description

IMAP GSS 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_gss.c.

Macro Definition Documentation

◆ GSS_AUTH_P_NONE

#define GSS_AUTH_P_NONE   1

Definition at line 54 of file auth_gss.c.

◆ GSS_AUTH_P_INTEGRITY

#define GSS_AUTH_P_INTEGRITY   2

Definition at line 55 of file auth_gss.c.

◆ GSS_AUTH_P_PRIVACY

#define GSS_AUTH_P_PRIVACY   4

Definition at line 56 of file auth_gss.c.

Function Documentation

◆ print_gss_error()

static void print_gss_error ( OM_uint32  err_maj,
OM_uint32  err_min 
)
static

Print detailed error message to the debug log.

Parameters
err_majError's major number
err_minError's minor number

Definition at line 63 of file auth_gss.c.

64 {
65  OM_uint32 maj_stat, min_stat;
66  OM_uint32 msg_ctx = 0;
67  gss_buffer_desc status_string;
68  char buf_maj[512];
69  char buf_min[512];
70 
71  do
72  {
73  maj_stat = gss_display_status(&min_stat, err_maj, GSS_C_GSS_CODE,
74  GSS_C_NO_OID, &msg_ctx, &status_string);
75  if (GSS_ERROR(maj_stat))
76  break;
77  size_t status_len = status_string.length;
78  if (status_len >= sizeof(buf_maj))
79  status_len = sizeof(buf_maj) - 1;
80  strncpy(buf_maj, (char *) status_string.value, status_len);
81  buf_maj[status_len] = '\0';
82  gss_release_buffer(&min_stat, &status_string);
83 
84  maj_stat = gss_display_status(&min_stat, err_min, GSS_C_MECH_CODE,
85  GSS_C_NULL_OID, &msg_ctx, &status_string);
86  if (!GSS_ERROR(maj_stat))
87  {
88  status_len = status_string.length;
89  if (status_len >= sizeof(buf_min))
90  status_len = sizeof(buf_min) - 1;
91  strncpy(buf_min, (char *) status_string.value, status_len);
92  buf_min[status_len] = '\0';
93  gss_release_buffer(&min_stat, &status_string);
94  }
95  } while (!GSS_ERROR(maj_stat) && (msg_ctx != 0));
96 
97  mutt_debug(LL_DEBUG2, "((%s:%d )(%s:%d))\n", buf_maj, err_maj, buf_min, err_min);
98 }
Log at debug level 2.
Definition: logging.h:41
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
+ Here is the caller graph for this function:

◆ imap_auth_gss()

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

GSS Authentication support - Implements ImapAuth::authenticate()

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

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 rc, retval = 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 = mutt_buffer_pool_get();
127  struct Buffer *buf2 = mutt_buffer_pool_get();
128 
129  /* get an IMAP service ticket for the server */
130  mutt_buffer_printf(buf1, "imap@%s", adata->conn->account.host);
131  request_buf.value = buf1->data;
132  request_buf.length = mutt_buffer_len(buf1);
133 
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_DebugLevel >= 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");
190  mutt_socket_send(adata->conn, mutt_b2s(buf1));
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");
233  mutt_socket_send(adata->conn, mutt_b2s(buf1));
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);
289  mutt_buffer_addch(buf1, GSS_AUTH_P_NONE);
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");
306  mutt_socket_send(adata->conn, mutt_b2s(buf1));
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 IMAP_RES_RESPOND
+
Definition: private.h:57
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:1076
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:1062
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:185
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:189
#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: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:63
Authentication successful.
Definition: auth.h:38
bool imap_code(const char *s)
Was the command successful.
Definition: command.c:1199
#define IMAP_CAP_AUTH_GSSAPI
RFC1731: GSSAPI authentication.
Definition: private.h:128
struct Connection * conn
Definition: private.h:171
+ Here is the call graph for this function: