#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 <sys/types.h>
#include <netinet/in.h>
#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;
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);
if (result = gen_recv(sock, buf1, sizeof buf1)) {
return result;
}
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);
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);
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"));
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));
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);
}
buf_size = htonl(buf_size);
memcpy(buf1, &buf_size, 4);
buf1[0] = GSSAUTH_P_NONE;
strcpy(buf1+4, username);
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;
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;
}
gss_release_buffer(&min_stat, &send_token);
if (result)
return(result);
else
return(PS_SUCCESS);
}
#endif