#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <Kerberos/Kerberos.h>
#include "test-gss-common.h"
const char *gServiceName = NULL;
static int SetupListeningSocket (int inPort, int *outFD)
{
int err = 0;
int fd = -1;
if (!err) {
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0) { err = errno; }
}
if (!err) {
struct sockaddr_storage addressStorage;
struct sockaddr_in *saddr = (struct sockaddr_in *) &addressStorage;
saddr->sin_port = htons (inPort);
saddr->sin_len = sizeof (struct sockaddr_in);
saddr->sin_family = AF_INET;
saddr->sin_addr.s_addr = INADDR_ANY;
err = bind (fd, (struct sockaddr *) saddr, saddr->sin_len);
if (err < 0) { err = errno; }
}
if (!err) {
err = listen (fd, 5);
if (err < 0) { err = errno; }
}
if (!err) {
printf ("listening on port %d\n", inPort);
*outFD = fd;
fd = -1;
} else {
printError (err, "SetupListeningSocket failed");
}
if (fd >= 0) { close (fd); }
return err;
}
static int Authenticate (int inSocket, gss_ctx_id_t *outContext)
{
int err = 0;
OM_uint32 majorStatus;
OM_uint32 minorStatus = 0;
gss_ctx_id_t context = GSS_C_NO_CONTEXT;
char *inputTokenBuffer = NULL;
size_t inputTokenBufferLength = 0;
gss_buffer_desc inputToken;
if (inSocket < 0) { err = EINVAL; }
if (outContext == NULL) { err = EINVAL; }
majorStatus = GSS_S_CONTINUE_NEEDED;
while (!err && (majorStatus != GSS_S_COMPLETE)) {
if (inputTokenBuffer != NULL) {
free (inputTokenBuffer);
inputTokenBuffer = NULL;
}
err = ReadToken (inSocket, &inputTokenBuffer, &inputTokenBufferLength);
if (!err) {
inputToken.value = inputTokenBuffer;
inputToken.length = inputTokenBufferLength;
}
if (!err) {
gss_buffer_desc outputToken = { 0, NULL };
printf ("Calling gss_accept_sec_context...\n");
majorStatus = gss_accept_sec_context (&minorStatus, &context, GSS_C_NO_CREDENTIAL,
&inputToken, GSS_C_NO_CHANNEL_BINDINGS, NULL ,
NULL , &outputToken, NULL ,
NULL , NULL );
if ((outputToken.length > 0) && (outputToken.value != NULL)) {
err = WriteToken (inSocket, outputToken.value, outputToken.length);
gss_release_buffer (&minorStatus, &outputToken);
}
}
if ((majorStatus != GSS_S_COMPLETE) && (majorStatus != GSS_S_CONTINUE_NEEDED)) {
printGSSErrors ("gss_accept_sec_context", majorStatus, minorStatus);
err = minorStatus ? minorStatus : majorStatus;
}
}
if (!err) {
*outContext = context;
} else {
printError (err, "Authenticate failed");
}
return err;
}
static int ServicePrincipalIsValidForService (const char *inServicePrincipal)
{
int err = 0;
krb5_context context = NULL;
krb5_principal principal = NULL;
if (inServicePrincipal == NULL) { err = EINVAL; }
if (!err) {
err = krb5_init_context (&context);
}
if (!err) {
err = krb5_parse_name (context, inServicePrincipal, &principal);
}
if (!err) {
if ((gServiceName != NULL) && (strcmp (gServiceName, krb5_princ_name (context, principal)->data) != 0)) {
err = KRB5KRB_AP_WRONG_PRINC;
}
}
if (principal != NULL) { krb5_free_principal (context, principal); }
if (context != NULL) { krb5_free_context (context); }
return err;
}
static int ClientPrincipalIsAuthorizedForService (const char *inClientPrincipal)
{
int err = 0;
krb5_context context = NULL;
krb5_principal principal = NULL;
if (inClientPrincipal == NULL) { err = EINVAL; }
if (!err) {
err = krb5_init_context (&context);
}
if (!err) {
err = krb5_parse_name (context, inClientPrincipal, &principal);
}
if (!err) {
err = 0;
}
if (principal != NULL) { krb5_free_principal (context, principal); }
if (context != NULL) { krb5_free_context (context); }
return err;
}
static int Authorize (gss_ctx_id_t *inContext, int *outAuthorized, int *outAuthorizationError)
{
int err = 0;
OM_uint32 majorStatus;
OM_uint32 minorStatus = 0;
gss_name_t clientName = NULL;
gss_name_t serviceName = NULL;
char *clientPrincipal = NULL;
char *servicePrincipal = NULL;
if (outAuthorized == NULL) { err = EINVAL; }
if (outAuthorizationError == NULL) { err = EINVAL; }
if (!err) {
majorStatus = gss_inquire_context (&minorStatus, *inContext, &clientName, &serviceName,
NULL, NULL, NULL, NULL, NULL);
if (majorStatus != GSS_S_COMPLETE) { err = minorStatus ? minorStatus : majorStatus; }
}
if (!err) {
gss_buffer_desc nameToken;
majorStatus = gss_display_name (&minorStatus, clientName, &nameToken, NULL);
if (majorStatus != GSS_S_COMPLETE) { err = minorStatus ? minorStatus : majorStatus; }
if (!err) {
clientPrincipal = malloc (nameToken.length + 1);
if (clientPrincipal == NULL) { err = ENOMEM; }
}
if (!err) {
memcpy (clientPrincipal, nameToken.value, nameToken.length);
clientPrincipal[nameToken.length] = '\0';
}
if (nameToken.value != NULL) { gss_release_buffer (&minorStatus, &nameToken); }
}
if (!err) {
gss_buffer_desc nameToken;
majorStatus = gss_display_name (&minorStatus, serviceName, &nameToken, NULL);
if (majorStatus != GSS_S_COMPLETE) { err = minorStatus ? minorStatus : majorStatus; }
if (!err) {
servicePrincipal = malloc (nameToken.length + 1);
if (servicePrincipal == NULL) { err = ENOMEM; }
}
if (!err) {
memcpy (servicePrincipal, nameToken.value, nameToken.length);
servicePrincipal[nameToken.length] = '\0';
}
if (nameToken.value != NULL) { gss_release_buffer (&minorStatus, &nameToken); }
}
if (!err) {
int authorizationErr = ServicePrincipalIsValidForService (servicePrincipal);
if (!authorizationErr) {
authorizationErr = ClientPrincipalIsAuthorizedForService (clientPrincipal);
}
printf ("'%s' is%s authorized for service '%s'\n",
clientPrincipal, authorizationErr ? " NOT" : "", servicePrincipal);
*outAuthorized = !authorizationErr;
*outAuthorizationError = authorizationErr;
}
if (clientPrincipal == NULL) { free (clientPrincipal); }
if (servicePrincipal == NULL) { free (servicePrincipal); }
return err;
}
static void Usage (const char *argv[])
{
fprintf (stderr, "Usage: %s [--port portNumber] [--sname serviceName]\n", argv[0]);
exit (1);
}
int main (int argc, const char *argv[])
{
int err = 0;
OM_uint32 minorStatus;
int port = kDefaultPort;
int listenFD = -1;
gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT;
gss_buffer_desc outputToken = { 0, NULL };
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], "--sname") == 0) && (i < (argc - 1))) {
gServiceName = argv[++i];
} else {
err = EINVAL;
}
}
if (!err) {
printf ("%s: Starting up...\n", argv[0]);
err = SetupListeningSocket (port, &listenFD);
}
if (!err) {
int connectionErr = 0;
int connectionFD = -1;
int authorized = 0;
int authorizationError = 0;
connectionFD = accept (listenFD, NULL, NULL);
if (connectionFD < 0) {
if (errno != EINTR) {
err = errno;
}
}
printf ("Accepting new connection...\n");
connectionErr = Authenticate (connectionFD, &gssContext);
if (!connectionErr) {
connectionErr = Authorize (&gssContext, &authorized, &authorizationError);
}
if (!connectionErr) {
char buffer[1024];
memset (buffer, 0, sizeof (buffer));
if (authorized) {
snprintf (buffer, sizeof (buffer), "SUCCESS!");
} else {
snprintf (buffer, sizeof(buffer), "FAILURE! %s (err = %d)",
error_message (authorizationError), authorizationError);
}
connectionErr = WriteEncryptedToken (connectionFD, gssContext, buffer, strlen (buffer) + 1);
}
if (connectionErr) {
printError (connectionErr, "Connection failed");
}
if (connectionFD >= 0) { printf ("Closing connection.\n"); close (connectionFD); }
}
if (err) {
if (err == EINVAL) {
Usage (argv);
} else {
printError (err, "Server failed");
}
}
if (listenFD >= 0) { close (listenFD); }
if (gssContext != NULL) { gss_delete_sec_context (&minorStatus, &gssContext, &outputToken); }
if (outputToken.value != NULL) { gss_release_buffer (&minorStatus, &outputToken); }
return err ? -1 : 0;
}