NeoMutt  2022-04-29-249-gaae397
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 "config/lib.h"
#include "core/lib.h"
#include "conn/lib.h"
#include "adata.h"
#include "auth.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 55 of file auth_gss.c.

◆ GSS_AUTH_P_INTEGRITY

#define GSS_AUTH_P_INTEGRITY   2

Definition at line 56 of file auth_gss.c.

◆ GSS_AUTH_P_PRIVACY

#define GSS_AUTH_P_PRIVACY   4

Definition at line 57 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 64 of file auth_gss.c.

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

Definition at line 104 of file auth_gss.c.

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