2 * Copyright notice from original mutt:
3 * Copyright (C) 1999-2000 Brendan Cully <brendan@kublai.com>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
10 /* GSS login/authentication code */
17 #include "imap_private.h"
20 #include <netinet/in.h>
23 # include <gssapi/gssapi.h>
24 # define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
26 # include <gssapi/gssapi.h>
27 # include <gssapi/gssapi_generic.h>
30 #define GSS_BUFSIZE 8192
32 #define GSS_AUTH_P_NONE 1
33 #define GSS_AUTH_P_INTEGRITY 2
34 #define GSS_AUTH_P_PRIVACY 4
36 /* imap_auth_gss: AUTH=GSSAPI support. */
37 imap_auth_res_t imap_auth_gss (IMAP_DATA * idata, const char *method)
39 gss_buffer_desc request_buf, send_token;
40 gss_buffer_t sec_token;
41 gss_name_t target_name;
46 OM_uint32 maj_stat, min_stat;
47 char buf1[GSS_BUFSIZE], buf2[GSS_BUFSIZE], server_conf_flags;
48 unsigned long buf_size;
51 if (!mutt_bit_isset (idata->capabilities, AGSSAPI))
52 return IMAP_AUTH_UNAVAIL;
54 if (mutt_account_getuser (&idata->conn->account))
55 return IMAP_AUTH_FAILURE;
57 /* get an IMAP service ticket for the server */
58 snprintf (buf1, sizeof (buf1), "imap@%s", idata->conn->account.host);
59 request_buf.value = buf1;
60 request_buf.length = mutt_strlen (buf1) + 1;
61 maj_stat = gss_import_name (&min_stat, &request_buf, gss_nt_service_name,
63 if (maj_stat != GSS_S_COMPLETE) {
64 dprint (2, (debugfile, "Couldn't get service name for [%s]\n", buf1));
65 return IMAP_AUTH_UNAVAIL;
68 else if (debuglevel >= 2) {
69 maj_stat = gss_display_name (&min_stat, target_name, &request_buf,
71 dprint (2, (debugfile, "Using service name [%s]\n",
72 (char *) request_buf.value));
73 maj_stat = gss_release_buffer (&min_stat, &request_buf);
76 /* Acquire initial credentials - without a TGT GSSAPI is UNAVAIL */
77 sec_token = GSS_C_NO_BUFFER;
78 context = GSS_C_NO_CONTEXT;
81 maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &context,
82 target_name, GSS_C_NO_OID,
83 GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG, 0,
84 GSS_C_NO_CHANNEL_BINDINGS, sec_token, NULL,
85 &send_token, (unsigned int *) &cflags,
87 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
88 dprint (1, (debugfile, "Error acquiring credentials - no TGT?\n"));
89 gss_release_name (&min_stat, &target_name);
91 return IMAP_AUTH_UNAVAIL;
95 mutt_message _("Authenticating (GSSAPI)...");
97 imap_cmd_start (idata, "AUTHENTICATE GSSAPI");
99 /* expect a null continuation response ("+") */
101 rc = imap_cmd_step (idata);
102 while (rc == IMAP_CMD_CONTINUE);
104 if (rc != IMAP_CMD_RESPOND) {
105 dprint (2, (debugfile, "Invalid response from server: %s\n", buf1));
106 gss_release_name (&min_stat, &target_name);
110 /* now start the security context initialisation loop... */
111 dprint (2, (debugfile, "Sending credentials\n"));
112 mutt_to_base64 ((unsigned char *) buf1, send_token.value, send_token.length,
114 gss_release_buffer (&min_stat, &send_token);
115 safe_strcat (buf1, sizeof (buf1), "\r\n");
116 mutt_socket_write (idata->conn, buf1);
118 while (maj_stat == GSS_S_CONTINUE_NEEDED) {
119 /* Read server data */
121 rc = imap_cmd_step (idata);
122 while (rc == IMAP_CMD_CONTINUE);
124 if (rc != IMAP_CMD_RESPOND) {
125 dprint (1, (debugfile, "Error receiving server response.\n"));
126 gss_release_name (&min_stat, &target_name);
130 request_buf.length = mutt_from_base64 (buf2, idata->cmd.buf + 2);
131 request_buf.value = buf2;
132 sec_token = &request_buf;
134 /* Write client data */
135 maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &context,
136 target_name, GSS_C_NO_OID,
137 GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
138 0, GSS_C_NO_CHANNEL_BINDINGS, sec_token,
140 (unsigned int *) &cflags, NULL);
141 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
142 dprint (1, (debugfile, "Error exchanging credentials\n"));
143 gss_release_name (&min_stat, &target_name);
147 mutt_to_base64 ((unsigned char *) buf1, send_token.value,
148 send_token.length, sizeof (buf1) - 2);
149 gss_release_buffer (&min_stat, &send_token);
150 safe_strcat (buf1, sizeof (buf1), "\r\n");
151 mutt_socket_write (idata->conn, buf1);
154 gss_release_name (&min_stat, &target_name);
156 /* get security flags and buffer size */
158 rc = imap_cmd_step (idata);
159 while (rc == IMAP_CMD_CONTINUE);
161 if (rc != IMAP_CMD_RESPOND) {
162 dprint (1, (debugfile, "Error receiving server response.\n"));
165 request_buf.length = mutt_from_base64 (buf2, idata->cmd.buf + 2);
166 request_buf.value = buf2;
168 maj_stat = gss_unwrap (&min_stat, context, &request_buf, &send_token,
170 if (maj_stat != GSS_S_COMPLETE) {
171 dprint (2, (debugfile, "Couldn't unwrap security level data\n"));
172 gss_release_buffer (&min_stat, &send_token);
175 dprint (2, (debugfile, "Credential exchange complete\n"));
177 /* first octet is security levels supported. We want NONE */
178 server_conf_flags = ((char *) send_token.value)[0];
179 if (!(((char *) send_token.value)[0] & GSS_AUTH_P_NONE)) {
180 dprint (2, (debugfile, "Server requires integrity or privacy\n"));
181 gss_release_buffer (&min_stat, &send_token);
185 /* we don't care about buffer size if we don't wrap content. But here it is */
186 ((char *) send_token.value)[0] = 0;
187 buf_size = ntohl (*((long *) send_token.value));
188 gss_release_buffer (&min_stat, &send_token);
189 dprint (2, (debugfile, "Unwrapped security level flags: %c%c%c\n",
190 server_conf_flags & GSS_AUTH_P_NONE ? 'N' : '-',
191 server_conf_flags & GSS_AUTH_P_INTEGRITY ? 'I' : '-',
192 server_conf_flags & GSS_AUTH_P_PRIVACY ? 'P' : '-'));
193 dprint (2, (debugfile, "Maximum GSS token size is %ld\n", buf_size));
195 /* agree to terms (hack!) */
196 buf_size = htonl (buf_size); /* not relevant without integrity/privacy */
197 memcpy (buf1, &buf_size, 4);
198 buf1[0] = GSS_AUTH_P_NONE;
199 /* server decides if principal can log in as user */
200 strncpy (buf1 + 4, idata->conn->account.user, sizeof (buf1) - 4);
201 request_buf.value = buf1;
202 request_buf.length = 4 + mutt_strlen (idata->conn->account.user) + 1;
203 maj_stat = gss_wrap (&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf,
204 &cflags, &send_token);
205 if (maj_stat != GSS_S_COMPLETE) {
206 dprint (2, (debugfile, "Error creating login request\n"));
210 mutt_to_base64 ((unsigned char *) buf1, send_token.value, send_token.length,
212 dprint (2, (debugfile, "Requesting authorisation as %s\n",
213 idata->conn->account.user));
214 safe_strcat (buf1, sizeof (buf1), "\r\n");
215 mutt_socket_write (idata->conn, buf1);
217 /* Joy of victory or agony of defeat? */
219 rc = imap_cmd_step (idata);
220 while (rc == IMAP_CMD_CONTINUE);
221 if (rc == IMAP_CMD_RESPOND) {
222 dprint (1, (debugfile, "Unexpected server continuation request.\n"));
225 if (imap_code (idata->cmd.buf)) {
226 /* flush the security context */
227 dprint (2, (debugfile, "Releasing GSS credentials\n"));
228 maj_stat = gss_delete_sec_context (&min_stat, &context, &send_token);
229 if (maj_stat != GSS_S_COMPLETE)
230 dprint (1, (debugfile, "Error releasing credentials\n"));
232 /* send_token may contain a notification to the server to flush
233 * credentials. RFC 1731 doesn't specify what to do, and since this
234 * support is only for authentication, we'll assume the server knows
235 * enough to flush its own credentials */
236 gss_release_buffer (&min_stat, &send_token);
238 return IMAP_AUTH_SUCCESS;
244 mutt_socket_write (idata->conn, "*\r\n");
246 rc = imap_cmd_step (idata);
247 while (rc == IMAP_CMD_CONTINUE);
250 mutt_error _("GSSAPI authentication failed.");
252 return IMAP_AUTH_FAILURE;