#include "common.h"
static int Connect (const char *inHost, int inPort, int *outFD)
{
int err = 0;
int fd = -1;
struct hostent *hp = NULL;
struct sockaddr_in saddr;
if (!err) {
hp = gethostbyname (inHost);
if (hp == NULL) { err = errno; }
}
if (!err) {
saddr.sin_family = hp->h_addrtype;
memcpy ((char *) &saddr.sin_addr, hp->h_addr, sizeof (saddr.sin_addr));
saddr.sin_port = htons(inPort);
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0) { err = errno; }
}
if (!err) {
err = connect (fd, (struct sockaddr *) &saddr, sizeof (saddr));
if (err < 0) { err = errno; }
}
if (!err) {
printf ("connecting to host '%s' on port %d\n", inHost, inPort);
*outFD = fd;
fd = -1;
} else {
printError (err, "OpenConnection failed");
}
if (fd >= 0) { close (fd); }
return err;
}
#define kGSSExampleID "edu.mit.GSSExample"
#define kGSSExampleName "GSSExample"
#define kGSSExampleExplanantion "For GSSAPI Example authentication"
static int SelectIdentity (const char *inServiceName,
const char *inHostName,
kim_selection_hints *outSelectionHints,
const char **outClientName)
{
kim_error err = KIM_NO_ERROR;
kim_selection_hints hints = NULL;
kim_identity clientIdentity = NULL;
kim_string clientName = NULL;
if (!err) {
err = kim_selection_hints_create (&hints, kGSSExampleID);
}
if (!err) {
err = kim_selection_hints_set_explanation (hints,
kGSSExampleExplanantion);
}
if (!err) {
err = kim_selection_hints_set_hint (hints,
kim_hint_key_server,
inHostName);
}
if (!err) {
err = kim_selection_hints_set_hint (hints,
kim_hint_key_service_identity,
inServiceName);
}
if (!err) {
err = kim_selection_hints_get_identity (hints, &clientIdentity);
}
if (!err) {
err = kim_identity_get_string (clientIdentity, &clientName);
}
if (!err) {
*outSelectionHints = hints;
hints = NULL;
*outClientName = clientName;
clientName = NULL;
} else {
printError (err, "Identity selection failed");
}
kim_string_free (&clientName);
kim_identity_free (&clientIdentity);
kim_selection_hints_free (&hints);
return err;
}
static int RememberIdentity (kim_selection_hints inSelectionHints,
kim_string selectedClientName)
{
kim_error err = KIM_NO_ERROR;
kim_identity clientIdentity = NULL;
if (!err) {
err = kim_identity_create_from_string (&clientIdentity, selectedClientName);
}
if (!err) {
err = kim_selection_hints_remember_identity (inSelectionHints, clientIdentity);
}
if (err) {
printError (err, "Storing identity selection failed");
}
kim_identity_free (&clientIdentity);
return err;
}
static int Authenticate (int inSocket,
const char *inClientName,
const char *inServiceName,
gss_ctx_id_t *outGSSContext)
{
int err = 0;
OM_uint32 majorStatus;
OM_uint32 minorStatus = 0;
gss_name_t serviceName = NULL;
gss_name_t clientName = NULL;
gss_cred_id_t clientCredentials = GSS_C_NO_CREDENTIAL;
gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT;
OM_uint32 actualFlags = 0;
char *inputTokenBuffer = NULL;
size_t inputTokenBufferLength = 0;
gss_buffer_desc inputToken;
gss_buffer_t inputTokenPtr = GSS_C_NO_BUFFER;
if (inSocket < 0 ) { err = EINVAL; }
if (!inServiceName) { err = EINVAL; }
if (!outGSSContext) { err = EINVAL; }
if (!err && inClientName != NULL) {
gss_buffer_desc nameBuffer = { strlen (inClientName), (char *) inClientName };
majorStatus = gss_import_name (&minorStatus, &nameBuffer, GSS_C_NT_USER_NAME, &clientName);
if (majorStatus != GSS_S_COMPLETE) {
printGSSErrors ("gss_import_name(inClientName)", majorStatus, minorStatus);
err = minorStatus ? minorStatus : majorStatus;
}
if (!err) {
majorStatus = gss_acquire_cred (&minorStatus, clientName, GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
GSS_C_INITIATE, &clientCredentials, NULL, NULL);
if (majorStatus != GSS_S_COMPLETE) {
printGSSErrors ("gss_acquire_cred", majorStatus, minorStatus);
err = minorStatus ? minorStatus : majorStatus;
}
}
}
if (!err) {
gss_buffer_desc nameBuffer = { strlen (inServiceName), (char *) inServiceName };
majorStatus = gss_import_name (&minorStatus, &nameBuffer, (gss_OID) GSS_KRB5_NT_PRINCIPAL_NAME, &serviceName);
if (majorStatus != GSS_S_COMPLETE) {
printGSSErrors ("gss_import_name(inServiceName)", majorStatus, minorStatus);
err = minorStatus ? minorStatus : majorStatus;
}
}
majorStatus = GSS_S_CONTINUE_NEEDED;
while (!err && (majorStatus != GSS_S_COMPLETE)) {
gss_buffer_desc outputToken = { 0, NULL };
OM_uint32 requestedFlags = (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG |
GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);
printf ("Calling gss_init_sec_context...\n");
majorStatus = gss_init_sec_context (&minorStatus,
clientCredentials,
&gssContext,
serviceName,
GSS_C_NULL_OID ,
requestedFlags,
GSS_C_INDEFINITE,
GSS_C_NO_CHANNEL_BINDINGS,
inputTokenPtr,
NULL ,
&outputToken,
&actualFlags,
NULL );
if ((outputToken.length > 0) && (outputToken.value != NULL)) {
err = WriteToken (inSocket, outputToken.value, outputToken.length);
gss_release_buffer (&minorStatus, &outputToken);
}
if (!err) {
if (majorStatus == GSS_S_CONTINUE_NEEDED) {
if (inputTokenBuffer) {
free (inputTokenBuffer);
inputTokenBuffer = NULL;
}
err = ReadToken (inSocket, &inputTokenBuffer, &inputTokenBufferLength);
if (!err) {
inputToken.value = inputTokenBuffer;
inputToken.length = inputTokenBufferLength;
inputTokenPtr = &inputToken;
}
} else if (majorStatus != GSS_S_COMPLETE) {
printGSSErrors ("gss_init_sec_context", majorStatus, minorStatus);
err = minorStatus ? minorStatus : majorStatus;
}
}
}
if (!err) {
*outGSSContext = gssContext;
gssContext = NULL;
} else {
printError (err, "AuthenticateToServer failed");
}
if (inputTokenBuffer) { free (inputTokenBuffer); }
if (serviceName ) { gss_release_name (&minorStatus, &serviceName); }
if (clientName ) { gss_release_name (&minorStatus, &clientName); }
if (clientCredentials != GSS_C_NO_CREDENTIAL) {
gss_release_cred (&minorStatus, &clientCredentials); }
if (gssContext != GSS_C_NO_CONTEXT) {
gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); }
return err;
}
static void Usage (const char *argv[])
{
fprintf (stderr, "Usage: %s [--port portNumber] [--server serverHostName]\n"
"\t[--sprinc servicePrincipal] [--cprinc clientPrincipal]\n", argv[0]);
exit (1);
}
int main (int argc, const char *argv[])
{
int err = 0;
int fd = -1;
int port = kDefaultPort;
const char *server = "127.0.0.1";
const char *clientName = NULL;
const char *serviceName = "host";
kim_selection_hints hints = NULL;
kim_string selectedClientName = NULL;
gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT;
OM_uint32 minorStatus = 0;
int i = 0;
for (i = 1; (i < argc) && !err; i++) {
if ((strcmp (argv[i], "--port") == 0) && (i < (argc - 1))) {
port = strtol (argv[++i], NULL, 0);
if (port == 0) { err = errno; }
} else if ((strcmp (argv[i], "--server") == 0) && (i < (argc - 1))) {
server = argv[++i];
} else if ((strcmp(argv[i], "--cprinc") == 0) && (i < (argc - 1))) {
clientName = argv[++i];
} else if ((strcmp(argv[i], "--sprinc") == 0) && (i < (argc - 1))) {
serviceName = argv[++i];
} else {
err = EINVAL;
}
}
if (!err && !clientName) {
err = SelectIdentity (serviceName, server, &hints, &selectedClientName);
if (!err) {
clientName = selectedClientName;
}
}
if (!err) {
printf ("%s: Starting up...\n", argv[0]);
err = Connect (server, port, &fd);
}
if (!err) {
err = Authenticate (fd, clientName, serviceName, &gssContext);
}
if (!err && selectedClientName) {
err = RememberIdentity (hints, selectedClientName);
}
if (!err) {
char *buffer = NULL;
size_t bufferLength = 0;
err = ReadEncryptedToken (fd, gssContext, &buffer, &bufferLength);
if (!err) {
printf ("Server message: '%s'\n", buffer);
}
if (buffer != NULL) { free (buffer); }
}
if (err) {
if (err == EINVAL) {
Usage (argv);
} else {
printError (err, "Client failed");
}
}
if (fd >= 0) { printf ("Closing socket.\n"); close (fd); }
if (gssContext != GSS_C_NO_CONTEXT) {
gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); }
kim_selection_hints_free (&hints);
kim_string_free (&selectedClientName);
return err ? 1 : 0;
}