/*
* gssapi.c -- GSSAPI authentication (see RFC 1508)
*
* For license terms, see the file COPYING in this directory.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#if defined(STDC_HEADERS)
#include <stdlib.h>
#endif
#include "fetchmail.h"
#include "socket.h"
#include "i18n.h"
#include "md5.h"
#include <netinet/in.h> /* for htonl/ntohl */
#ifdef GSSAPI
# ifdef HAVE_GSSAPI_H
# include <gssapi.h>
# endif
# ifdef HAVE_GSSAPI_GSSAPI_H
# include <gssapi/gssapi.h>
# endif
# ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
# include <gssapi/gssapi_generic.h>
# endif
# ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
# define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
# endif
#define GSSAUTH_P_NONE 1
#define GSSAUTH_P_INTEGRITY 2
#define GSSAUTH_P_PRIVACY 4
int do_gssauth(int sock, char *command, char *hostname, char *username)
{
gss_buffer_desc request_buf, send_token;
gss_buffer_t sec_token;
gss_name_t target_name;
gss_ctx_id_t context;
gss_OID mech_name;
gss_qop_t quality;
int cflags;
OM_uint32 maj_stat, min_stat;
char buf1[8192], buf2[8192], server_conf_flags;
unsigned long buf_size;
int result;
/* first things first: get an imap ticket for host */
sprintf(buf1, "imap@%s", hostname);
request_buf.value = buf1;
request_buf.length = strlen(buf1) + 1;
maj_stat = gss_import_name(&min_stat, &request_buf, GSS_C_NT_HOSTBASED_SERVICE,
&target_name);
if (maj_stat != GSS_S_COMPLETE) {
report(stderr, _("Couldn't get service name for [%s]\n"), buf1);
return PS_AUTHFAIL;
}
else if (outlevel >= O_DEBUG) {
maj_stat = gss_display_name(&min_stat, target_name, &request_buf,
&mech_name);
report(stderr, _("Using service name [%s]\n"),request_buf.value);
maj_stat = gss_release_buffer(&min_stat, &request_buf);
}
gen_send(sock, "%s GSSAPI", command);
/* upon receipt of the GSSAPI authentication request, server returns
* null data ready response. */
if (result = gen_recv(sock, buf1, sizeof buf1)) {
return result;
}
/* now start the security context initialisation loop... */
sec_token = GSS_C_NO_BUFFER;
context = GSS_C_NO_CONTEXT;
if (outlevel >= O_VERBOSE)
report(stdout, _("Sending credentials\n"));
do {
send_token.length = 0;
send_token.value = NULL;
maj_stat = gss_init_sec_context(&min_stat,
GSS_C_NO_CREDENTIAL,
&context,
target_name,
GSS_C_NO_OID,
GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
0,
GSS_C_NO_CHANNEL_BINDINGS,
sec_token,
NULL,
&send_token,
NULL,
NULL);
if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
report(stderr, _("Error exchanging credentials\n"));
gss_release_name(&min_stat, &target_name);
/* wake up server and await NO response */
SockWrite(sock, "\r\n", 2);
if (result = gen_recv(sock, buf1, sizeof buf1))
return result;
return PS_AUTHFAIL;
}
to64frombits(buf1, send_token.value, send_token.length);
gss_release_buffer(&min_stat, &send_token);
suppress_tags = TRUE;
gen_send(sock, buf1, strlen(buf1));
suppress_tags = FALSE;
if (maj_stat == GSS_S_CONTINUE_NEEDED) {
if (result = gen_recv(sock, buf1, sizeof buf1)) {
gss_release_name(&min_stat, &target_name);
return result;
}
request_buf.length = from64tobits(buf2, buf1 + 2);
request_buf.value = buf2;
sec_token = &request_buf;
}
} while
(maj_stat == GSS_S_CONTINUE_NEEDED);
gss_release_name(&min_stat, &target_name);
/* get security flags and buffer size */
if (result = gen_recv(sock, buf1, sizeof buf1))
return result;
request_buf.length = from64tobits(buf2, buf1 + 2);
request_buf.value = buf2;
maj_stat = gss_unwrap(&min_stat, context,
&request_buf, &send_token, &cflags, &quality);
if (maj_stat != GSS_S_COMPLETE) {
report(stderr, _("Couldn't unwrap security level data\n"));
gss_release_buffer(&min_stat, &send_token);
return PS_AUTHFAIL;
}
if (outlevel >= O_DEBUG)
report(stdout, _("Credential exchange complete\n"));
/* first octet is security levels supported. We want none, for now */
server_conf_flags = ((char *)send_token.value)[0];
if ( !(((char *)send_token.value)[0] & GSSAUTH_P_NONE) ) {
report(stderr, _("Server requires integrity and/or privacy\n"));
gss_release_buffer(&min_stat, &send_token);
return PS_AUTHFAIL;
}
((char *)send_token.value)[0] = 0;
buf_size = ntohl(*((long *)send_token.value));
/* we don't care about buffer size if we don't wrap data */
gss_release_buffer(&min_stat, &send_token);
if (outlevel >= O_DEBUG) {
report(stdout, _("Unwrapped security level flags: %s%s%s\n"),
server_conf_flags & GSSAUTH_P_NONE ? "N" : "-",
server_conf_flags & GSSAUTH_P_INTEGRITY ? "I" : "-",
server_conf_flags & GSSAUTH_P_PRIVACY ? "C" : "-");
report(stdout, _("Maximum GSS token size is %ld\n"),buf_size);
}
/* now respond in kind (hack!!!) */
buf_size = htonl(buf_size); /* do as they do... only matters if we do enc */
memcpy(buf1, &buf_size, 4);
buf1[0] = GSSAUTH_P_NONE;
strcpy(buf1+4, username); /* server decides if princ is user */
request_buf.length = 4 + strlen(username) + 1;
request_buf.value = buf1;
maj_stat = gss_wrap(&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf,
&cflags, &send_token);
if (maj_stat != GSS_S_COMPLETE) {
report(stderr, _("Error creating security level request\n"));
return PS_AUTHFAIL;
}
to64frombits(buf1, send_token.value, send_token.length);
suppress_tags = TRUE;
result = gen_transact(sock, buf1, strlen(buf1));
suppress_tags = FALSE;
/* flush security context */
if (outlevel >= O_DEBUG)
report(stdout, _("Releasing GSS credentials\n"));
maj_stat = gss_delete_sec_context(&min_stat, &context, &send_token);
if (maj_stat != GSS_S_COMPLETE) {
report(stderr, _("Error releasing credentials\n"));
return PS_AUTHFAIL;
}
/* send_token may contain a notification to the server to flush
* credentials. RFC 1731 doesn't specify what to do, and since this
* support is only for authentication, we'll assume the server
* knows enough to flush its own credentials */
gss_release_buffer(&min_stat, &send_token);
if (result)
return(result);
else
return(PS_SUCCESS);
}
#endif /* GSSAPI */
/* gssapi.c ends here */