2 * gssapi.c -- GSSAPI authentication (see RFC 1508)
4 * For license terms, see the file COPYING in this directory.
12 #include "fetchmail.h"
18 #include <sys/types.h>
19 #include <netinet/in.h> /* for htonl/ntohl */
25 # if defined(HAVE_GSSAPI_H) && !defined(HAVE_GSSAPI_GSSAPI_H)
28 # ifdef HAVE_GSSAPI_GSSAPI_H
29 # include <gssapi/gssapi.h>
31 # ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
32 # include <gssapi/gssapi_generic.h>
34 # ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
35 # define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
39 static void decode_subr(const char *m, uint32_t code, int type, FILE *ostrm)
41 uint32_t maj, min, context;
42 gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
46 maj = gss_display_status(&min, code, type, GSS_C_NO_OID,
49 if (maj != GSS_S_COMPLETE) {
50 report(stderr, GT_("GSSAPI error in gss_display_status called from <%s>\n"), m);
53 report(ostrm, GT_("GSSAPI error %s: %.*s\n"), m,
54 (int)msg.length, (char *)msg.value);
55 (void)gss_release_buffer(&min, &msg);
59 static void decode_status(const char *m, uint32_t major, uint32_t minor,
62 decode_subr(m, major, GSS_C_GSS_CODE, ostrm);
63 decode_subr(m, minor, GSS_C_MECH_CODE, ostrm);
66 #define GSSAUTH_P_NONE 1
67 #define GSSAUTH_P_INTEGRITY 2
68 #define GSSAUTH_P_PRIVACY 4
70 static int import_name(const char *service, const char *hostname,
71 gss_name_t *target_name, flag verbose)
75 OM_uint32 maj_stat, min_stat;
76 gss_buffer_desc request_buf;
78 /* first things first: get an imap ticket for host */
79 buf1siz = strlen(service) + 1 + strlen(hostname) + 1;
80 buf1 = (char *)xmalloc(buf1siz);
81 snprintf(buf1, buf1siz, "%s@%s", service, hostname);
82 request_buf.value = buf1;
83 request_buf.length = strlen(buf1) + 1;
84 maj_stat = gss_import_name(&min_stat, &request_buf,
85 GSS_C_NT_HOSTBASED_SERVICE, target_name);
86 if (maj_stat != GSS_S_COMPLETE) {
87 decode_status("gss_import_name", maj_stat, min_stat, stderr);
88 report(stderr, GT_("Couldn't get service name for [%s]\n"), buf1);
91 else if (outlevel >= O_DEBUG && verbose) {
92 (void)gss_display_name(&min_stat, *target_name, &request_buf, NULL);
93 report(stderr, GT_("Using service name [%s]\n"),
94 (char *)request_buf.value);
96 (void)gss_release_buffer(&min_stat, &request_buf);
101 /* If we don't have suitable credentials, don't bother trying GSSAPI, but
102 * fail right away. This is to avoid that a server - such as Microsoft
103 * Exchange 2007 - gets wedged and refuses different authentication
104 * mechanisms afterwards. */
105 int check_gss_creds(const char *service, const char *hostname)
107 OM_uint32 maj_stat, min_stat;
109 gss_name_t target_name;
111 (void)import_name(service, hostname, &target_name, FALSE);
112 (void)gss_release_name(&min_stat, &target_name);
114 maj_stat = gss_inquire_cred(&min_stat, GSS_C_NO_CREDENTIAL,
115 NULL, NULL, &cu, NULL);
116 if (maj_stat != GSS_S_COMPLETE
117 || (cu != GSS_C_INITIATE && cu != GSS_C_BOTH)) {
118 if (outlevel >= O_DEBUG) {
119 decode_status("gss_inquire_cred", maj_stat, min_stat, stdout);
120 report(stdout, GT_("No suitable GSSAPI credentials found. Skipping GSSAPI authentication.\n"));
121 report(stdout, GT_("If you want to use GSSAPI, you need credentials first, possibly from kinit.\n"));
129 int do_gssauth(int sock, const char *command, const char *service,
130 const char *hostname, const char *username)
132 gss_buffer_desc request_buf, send_token;
133 gss_buffer_t sec_token;
134 gss_name_t target_name;
135 gss_ctx_id_t context;
138 OM_uint32 maj_stat, min_stat;
139 char buf1[8192], buf2[8192], server_conf_flags;
140 unsigned long buf_size;
143 result = import_name(service, hostname, &target_name, TRUE);
147 gen_send(sock, "%s GSSAPI", command);
149 /* upon receipt of the GSSAPI authentication request, server returns
150 * a null data ready challenge to us. */
151 result = gen_recv(sock, buf1, sizeof buf1);
155 if (buf1[0] != '+' || strspn(buf1 + 1, " \t") < strlen(buf1 + 1)) {
156 if (outlevel >= O_VERBOSE) {
157 report(stdout, GT_("Received malformed challenge to \"%s GSSAPI\"!\n"), command);
163 /* now start the security context initialisation loop... */
164 sec_token = GSS_C_NO_BUFFER;
165 context = GSS_C_NO_CONTEXT;
166 if (outlevel >= O_VERBOSE)
167 report(stdout, GT_("Sending credentials\n"));
169 send_token.length = 0;
170 send_token.value = NULL;
171 maj_stat = gss_init_sec_context(&min_stat,
176 GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
178 GSS_C_NO_CHANNEL_BINDINGS,
185 if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
186 if (outlevel >= O_VERBOSE)
187 decode_status("gss_init_sec_context", maj_stat, min_stat, stdout);
188 (void)gss_release_name(&min_stat, &target_name);
191 /* wake up server and cancel authentication */
192 suppress_tags = TRUE;
194 suppress_tags = FALSE;
196 result = gen_recv(sock, buf1, sizeof buf1);
197 if (outlevel >= O_VERBOSE)
198 report(stderr, GT_("Error exchanging credentials\n"));
203 to64frombits(buf1, send_token.value, send_token.length);
204 gss_release_buffer(&min_stat, &send_token);
206 suppress_tags = TRUE;
207 gen_send(sock, "%s", buf1);
208 suppress_tags = FALSE;
210 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
211 result = gen_recv(sock, buf1, sizeof buf1);
213 gss_release_name(&min_stat, &target_name);
216 request_buf.length = from64tobits(buf2, buf1 + 2, sizeof(buf2));
217 if ((int)request_buf.length == -1) /* in case of bad data */
218 request_buf.length = 0;
219 request_buf.value = buf2;
220 sec_token = &request_buf;
223 (maj_stat == GSS_S_CONTINUE_NEEDED);
224 gss_release_name(&min_stat, &target_name);
226 /* get security flags and buffer size */
227 result = gen_recv(sock, buf1, sizeof buf1);
231 request_buf.length = from64tobits(buf2, buf1 + 2, sizeof(buf2));
232 if ((int)request_buf.length == -1) /* in case of bad data */
233 request_buf.length = 0;
234 request_buf.value = buf2;
236 maj_stat = gss_unwrap(&min_stat, context,
237 &request_buf, &send_token, &cflags, &quality);
238 if (maj_stat != GSS_S_COMPLETE) {
239 decode_status("gss_unwrap", maj_stat, min_stat, stderr);
240 report(stderr, GT_("Couldn't unwrap security level data\n"));
241 gss_release_buffer(&min_stat, &send_token);
244 if (outlevel >= O_DEBUG)
245 report(stdout, GT_("Credential exchange complete\n"));
246 /* first octet is security levels supported. We want none, for now */
247 server_conf_flags = ((char *)send_token.value)[0];
248 if ( !(((char *)send_token.value)[0] & GSSAUTH_P_NONE) ) {
249 report(stderr, GT_("Server requires integrity and/or privacy\n"));
250 gss_release_buffer(&min_stat, &send_token);
253 ((char *)send_token.value)[0] = 0;
254 buf_size = ntohl(*((long *)send_token.value));
255 /* we don't care about buffer size if we don't wrap data */
256 gss_release_buffer(&min_stat, &send_token);
257 if (outlevel >= O_DEBUG) {
258 report(stdout, GT_("Unwrapped security level flags: %s%s%s\n"),
259 server_conf_flags & GSSAUTH_P_NONE ? "N" : "-",
260 server_conf_flags & GSSAUTH_P_INTEGRITY ? "I" : "-",
261 server_conf_flags & GSSAUTH_P_PRIVACY ? "C" : "-");
262 report(stdout, GT_("Maximum GSS token size is %ld\n"),buf_size);
265 /* now respond in kind (hack!!!) */
266 buf_size = htonl(buf_size); /* do as they do... only matters if we do enc */
267 memcpy(buf1, &buf_size, 4);
268 buf1[0] = GSSAUTH_P_NONE;
269 strlcpy(buf1+4, username, sizeof(buf1) - 4); /* server decides if princ is user */
270 request_buf.length = 4 + strlen(username) + 1;
271 request_buf.value = buf1;
272 maj_stat = gss_wrap(&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf,
273 &cflags, &send_token);
274 if (maj_stat != GSS_S_COMPLETE) {
275 report(stderr, GT_("Error creating security level request\n"));
278 to64frombits(buf1, send_token.value, send_token.length);
280 suppress_tags = TRUE;
281 result = gen_transact(sock, "%s", buf1);
282 suppress_tags = FALSE;
284 /* flush security context */
285 if (outlevel >= O_DEBUG)
286 report(stdout, GT_("Releasing GSS credentials\n"));
287 maj_stat = gss_delete_sec_context(&min_stat, &context, &send_token);
288 if (maj_stat != GSS_S_COMPLETE) {
289 decode_status("gss_delete_sec_context", maj_stat, min_stat, stderr);
290 report(stderr, GT_("Error releasing credentials\n"));
293 /* send_token may contain a notification to the server to flush
294 * credentials. RFC 1731 doesn't specify what to do, and since this
295 * support is only for authentication, we'll assume the server
296 * knows enough to flush its own credentials */
297 gss_release_buffer(&min_stat, &send_token);
306 /* gssapi.c ends here */