From: Margaret Wasserman Date: Wed, 21 Nov 2012 11:59:23 +0000 (-0500) Subject: Moved gsscon.h include file to a newly created trust_router/include directory. X-Git-Tag: debian/1.3.1-1~69 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=trust_router.git;a=commitdiff_plain;h=93fe5c774eac24da93dbb99b51ce13d604cf3bf5 Moved gsscon.h include file to a newly created trust_router/include directory. --- diff --git a/gsscon/.#gsscon_test.c b/gsscon/.#gsscon_test.c new file mode 120000 index 0000000..f036edb --- /dev/null +++ b/gsscon/.#gsscon_test.c @@ -0,0 +1 @@ +mrw@lilac-moonshot.suchdamage.org.13738:1346036289 \ No newline at end of file diff --git a/gsscon/test/gsscon_client.c~ b/gsscon/gsscon.h~ old mode 100644 new mode 100755 similarity index 53% rename from gsscon/test/gsscon_client.c~ rename to gsscon/gsscon.h~ index f309a98..3fc0bde --- a/gsscon/test/gsscon_client.c~ +++ b/gsscon/gsscon.h~ @@ -52,86 +52,65 @@ * or implied warranty. */ -#include "../gsscon.h" +#ifndef GSSCON_H +#define GSSCON_H -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); -} +#include +#include +#include +#include +#include +#include +#include +#include +#include -/* --------------------------------------------------------------------------- */ +#include +#include +#include +#include -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"; - 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) { - printf ("%s: Starting up...\n", argv[0]); - - err = gsscon_connect (server, port, &fd); - } - - if (!err) { - err = gsscon_active_authenticate (fd, clientName, serviceName, &gssContext); - } - - if (!err) { - char *buffer = NULL; - size_t bufferLength = 0; +#define kDefaultPort 2000 - /* - * Here is where your protocol would go. This sample client just - * reads a nul terminated string from the server. - */ - err = gsscon_read_encrypted_token (fd, gssContext, &buffer, &bufferLength); +int gsscon_read_token (int inSocket, + char **outTokenValue, + size_t *outTokenLength); - 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); } - - return err ? 1 : 0; -} +int gsscon_write_token (int inSocket, + const char *inTokenValue, + size_t inTokenLength); +int gsscon_read_encrypted_token (int inSocket, + const gss_ctx_id_t inContext, + char **outTokenValue, + size_t *outTokenLength); +int gsscon_write_encrypted_token (int inSocket, + const gss_ctx_id_t inContext, + const char *inToken, + size_t inTokenLength); +void gsscon_print_error (int inError, + const char *inString); + +void gsscon_print_gss_errors (const char *inRoutineName, + OM_uint32 inMajorStatus, + OM_uint32 inMinorStatus); + +void gsscon_connect (const char *inHost, + int inPort, + int *outFD); + +void gsscon_active_authenticate (int inSocket, + const char *inClientName, + const char *inServiceName, + gss_ctx_id_t *outGSSContext); + +int gsscon_passive_authenticate (int inSocket, + gss_ctx_id_t *outGSSContext); + +int gsscon_authorize (gss_ctx_id_t inContext, + int *outAuthorized, + int *outAuthorizationError); + +#endif diff --git a/gsscon/gsscon_active.c b/gsscon/gsscon_active.c index d933944..a71f1f4 100755 --- a/gsscon/gsscon_active.c +++ b/gsscon/gsscon_active.c @@ -52,7 +52,7 @@ * or implied warranty. */ -#include "gsscon.h" +#include /* --------------------------------------------------------------------------- */ diff --git a/gsscon/gsscon_active.c~ b/gsscon/gsscon_active.c~ new file mode 100755 index 0000000..bd8e315 --- /dev/null +++ b/gsscon/gsscon_active.c~ @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2011, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This code was adapted from the MIT Kerberos Consortium's + * GSS example code, which was distributed under the following + * license: + * + * Copyright 2004-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "gsscon.h" + +/* --------------------------------------------------------------------------- */ + +static int gsscon_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; /* takes ownership */ + } else { + printError (err, "OpenConnection failed"); + } + + if (fd >= 0) { close (fd); } + + return err; +} + +/* --------------------------------------------------------------------------- */ + +static int gsscon_active_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; /* buffer received from the server */ + gss_buffer_t inputTokenPtr = GSS_C_NO_BUFFER; + + if (inSocket < 0 ) { err = EINVAL; } + if (!inServiceName) { err = EINVAL; } + if (!outGSSContext) { err = EINVAL; } + + /* + * Here is where the client picks the client principal it wants to use. + * If your application knows what the user's client principal should be, + * it should set that here. Otherwise leave clientCredentials set to NULL + * and Kerberos will attempt to use the client principal in the default + * ccache. + */ + + if (!err) { + if (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; + } + } + } + } + + /* + * Here is where the client picks the service principal it will try to use to + * connect to the server. In the case of the gssClientSample, the service + * principal is passed in on the command line, however, in a real world example, + * this would be unacceptable from a user interface standpoint since the user + * shouldn't need to know the server's service principal. + * + * In traditional Kerberos setups, the service principal would be constructed from + * the type of the service (eg: "imap"), the DNS hostname of the server + * (eg: "mailserver.domain.com") and the client's local realm (eg: "DOMAIN.COM") + * to form a full principal string (eg: "imap/mailserver.domain.com@DOMAIN.COM"). + * + * Now that many sites do not have DNS, this setup is becoming less common. + * However you decide to generate the service principal, you need to adhere + * to the following constraint: The service principal must be constructed + * by the client, typed in by the user or administrator, or transmitted to + * the client in a secure manner from a trusted third party -- such as + * through an encrypted connection to a directory server. You should not + * have the server send the client the service principal name as part of + * the authentication negotiation. + * + * The reason you can't let the server tell the client which principal to + * use is that many machines at a site will have their own service principal + * and keytab which identifies the machine -- in a Windows Active Directory + * environment all machines have a service principal and keytab. Some of these + * machines (such as a financial services server) will be more trustworthy than + * others (such as a random machine on a coworker's desk). If the owner of + * one of these untrustworthy machines can trick the client into using the + * untrustworthy machine's principal instead of the financial services + * server's principal, then he can trick the client into authenticating + * and connecting to the untrustworthy machine. The untrustworthy machine can + * then harvest any confidential information the client sends to it, such as + * credit card information or social security numbers. + * + * If your protocol already involves sending the service principal as part of + * your authentication negotiation, your client should cache the name it gets + * after the first successful authentication so that the problem above can + * only happen on the first connection attempt -- similar to what ssh does with + * host keys. + */ + + 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; + } + } + + /* + * The main authentication loop: + * + * GSS is a multimechanism API. Because the number of packet exchanges required to + * authenticate can vary between mechanisms, we need to loop calling + * gss_init_sec_context, passing the "input tokens" received from the server and + * send the resulting "output tokens" back until we get GSS_S_COMPLETE or an error. + */ + + majorStatus = GSS_S_CONTINUE_NEEDED; + + gss_OID_desc EAP_OID = { 10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01\x11" }; + + while (!err && (majorStatus != GSS_S_COMPLETE)) { + gss_buffer_desc outputToken = { 0, NULL }; /* buffer to send to the server */ + 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, + &EAP_OID /* mech_type */, + requestedFlags, + GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, + inputTokenPtr, + NULL /* actual_mech_type */, + &outputToken, + &actualFlags, + NULL /* time_rec */); + + /* Send the output token to the server (even on error) */ + if ((outputToken.length > 0) && (outputToken.value != NULL)) { + err = WriteToken (inSocket, outputToken.value, outputToken.length); + + /* free the output token */ + gss_release_buffer (&minorStatus, &outputToken); + } + + if (!err) { + if (majorStatus == GSS_S_CONTINUE_NEEDED) { + /* Protocol requires another packet exchange */ + + /* Clean up old input buffer */ + if (inputTokenBuffer) { + free (inputTokenBuffer); + inputTokenBuffer = NULL; /* don't double-free */ + } + + /* Read another input token from the server */ + err = ReadToken (inSocket, &inputTokenBuffer, &inputTokenBufferLength); + + if (!err) { + /* Set up input buffers for the next run through the loop */ + 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; +} + diff --git a/gsscon/gsscon_common.c b/gsscon/gsscon_common.c index 325b98b..d067979 100755 --- a/gsscon/gsscon_common.c +++ b/gsscon/gsscon_common.c @@ -52,7 +52,7 @@ * or implied warranty. */ -#include "gsscon.h" +#include /* --------------------------------------------------------------------------- */ /* Display the contents of the buffer in hex and ascii */ diff --git a/gsscon/gsscon_common.c~ b/gsscon/gsscon_common.c~ new file mode 100755 index 0000000..5f65826 --- /dev/null +++ b/gsscon/gsscon_common.c~ @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2011, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This code was adapted from the MIT Kerberos Consortium's + * GSS example code, which was distributed under the following + * license: + * + * Copyright 2004-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "gsscon.h" + +/* --------------------------------------------------------------------------- */ +/* Display the contents of the buffer in hex and ascii */ + +static void PrintBuffer (const char *inBuffer, + size_t inLength) +{ + int i; + + for (i = 0; i < inLength; i += 16) { + int l; + for (l = i; l < (i + 16); l++) { + if (l >= inLength) { + printf (" "); + } else { + u_int8_t *byte = (u_int8_t *) inBuffer + l; + printf ("%2.2x", *byte); + } + if ((l % 4) == 3) { printf (" "); } + } + printf (" "); + for (l = i; l < (i + 16) && l < inLength; l++) { + printf ("%c", ((inBuffer[l] > 0x1f) && + (inBuffer[l] < 0x7f)) ? inBuffer[l] : '.'); + } + printf ("\n"); + } + printf ("\n"); +} + +/* --------------------------------------------------------------------------- */ +/* Standard network read loop, accounting for EINTR, EOF and incomplete reads */ + +static int ReadBuffer (int inSocket, + size_t inBufferLength, + char *ioBuffer) +{ + int err = 0; + ssize_t bytesRead = 0; + + if (!ioBuffer) { err = EINVAL; } + + if (!err) { + char *ptr = ioBuffer; + do { + ssize_t count = read (inSocket, ptr, inBufferLength - bytesRead); + if (count < 0) { + /* Try again on EINTR */ + if (errno != EINTR) { err = errno; } + } else if (count == 0) { + err = ECONNRESET; /* EOF and we expected data */ + } else { + ptr += count; + bytesRead += count; + } + } while (!err && (bytesRead < inBufferLength)); + } + + if (err) { printError (err, "ReadBuffer failed"); } + + return err; +} + +/* --------------------------------------------------------------------------- */ +/* Standard network write loop, accounting for EINTR and incomplete writes */ + +static int WriteBuffer (int inSocket, + const char *inBuffer, + size_t inBufferLength) +{ + int err = 0; + ssize_t bytesWritten = 0; + + if (!inBuffer) { err = EINVAL; } + + if (!err) { + const char *ptr = inBuffer; + do { + ssize_t count = write (inSocket, ptr, inBufferLength - bytesWritten); + if (count < 0) { + /* Try again on EINTR */ + if (errno != EINTR) { err = errno; } + } else { + ptr += count; + bytesWritten += count; + } + } while (!err && (bytesWritten < inBufferLength)); + } + + if (err) { printError (err, "WriteBuffer failed"); } + + return err; +} + +/* --------------------------------------------------------------------------- */ +/* Read a GSS token (length + data) off the network */ + +int gsscon_read_token (int inSocket, + char **outTokenValue, + size_t *outTokenLength) +{ + int err = 0; + char *token = NULL; + u_int32_t tokenLength = 0; + + if (!outTokenValue ) { err = EINVAL; } + if (!outTokenLength) { err = EINVAL; } + + if (!err) { + err = ReadBuffer (inSocket, 4, (char *) &tokenLength); + } + + if (!err) { + tokenLength = ntohl (tokenLength); + token = malloc (tokenLength); + memset (token, 0, tokenLength); + + err = ReadBuffer (inSocket, tokenLength, token); + } + + if (!err) { + printf ("Read token:\n"); + PrintBuffer (token, tokenLength); + + *outTokenLength = tokenLength; + *outTokenValue = token; + token = NULL; /* only free on error */ + } else { + printError (err, "ReadToken failed"); + } + + if (token) { free (token); } + + return err; +} + +/* --------------------------------------------------------------------------- */ +/* Write a GSS token (length + data) onto the network */ + +int gsscon_write_token (int inSocket, + const char *inTokenValue, + size_t inTokenLength) +{ + int err = 0; + u_int32_t tokenLength = htonl (inTokenLength); + + if (!inTokenValue) { err = EINVAL; } + + if (!err) { + err = WriteBuffer (inSocket, (char *) &tokenLength, 4); + } + + if (!err) { + err = WriteBuffer (inSocket, inTokenValue, inTokenLength); + } + + if (!err) { + printf ("Wrote token:\n"); + PrintBuffer (inTokenValue, inTokenLength); + } else { + printError (err, "WriteToken failed"); + } + + return err; +} + +/* --------------------------------------------------------------------------- */ +/* Read an encrypted GSS token (length + encrypted data) off the network */ + + +int gsscon_read_encrypted_token (int inSocket, + const gss_ctx_id_t inContext, + char **outTokenValue, + size_t *outTokenLength) +{ + int err = 0; + char *token = NULL; + size_t tokenLength = 0; + OM_uint32 majorStatus; + OM_uint32 minorStatus = 0; + gss_buffer_desc outputBuffer = { 0 , NULL}; + char *unencryptedToken = NULL; + + if (!inContext ) { err = EINVAL; } + if (!outTokenValue ) { err = EINVAL; } + if (!outTokenLength) { err = EINVAL; } + + if (!err) { + err = ReadToken (inSocket, &token, &tokenLength); + } + + if (!err) { + gss_buffer_desc inputBuffer = { tokenLength, token}; + int encrypted = 0; /* did mechanism encrypt/integrity protect? */ + + majorStatus = gss_unwrap (&minorStatus, + inContext, + &inputBuffer, + &outputBuffer, + &encrypted, + NULL /* qop_state */); + if (majorStatus != GSS_S_COMPLETE) { + printGSSErrors ("gss_unwrap", majorStatus, minorStatus); + err = minorStatus ? minorStatus : majorStatus; + } else if (!encrypted) { + fprintf (stderr, "WARNING! Mechanism not using encryption!"); + err = EINVAL; /* You may not want to fail here. */ + } + } + + if (!err) { + unencryptedToken = malloc (outputBuffer.length); + if (unencryptedToken == NULL) { err = ENOMEM; } + } + + if (!err) { + memcpy (unencryptedToken, outputBuffer.value, outputBuffer.length); + + printf ("Unencrypted token:\n"); + PrintBuffer (unencryptedToken, outputBuffer.length); + + *outTokenLength = outputBuffer.length; + *outTokenValue = unencryptedToken; + unencryptedToken = NULL; /* only free on error */ + + } else { + printError (err, "ReadToken failed"); + } + + if (token ) { free (token); } + if (outputBuffer.value) { gss_release_buffer (&minorStatus, &outputBuffer); } + if (unencryptedToken ) { free (unencryptedToken); } + + return err; +} + +/* --------------------------------------------------------------------------- */ +/* Write an encrypted GSS token (length + encrypted data) onto the network */ + +int gsscon_write_encrypted_token (int inSocket, + const gss_ctx_id_t inContext, + const char *inToken, + size_t inTokenLength) +{ + int err = 0; + OM_uint32 majorStatus; + OM_uint32 minorStatus = 0; + gss_buffer_desc outputBuffer = { 0, NULL }; + + if (!inContext) { err = EINVAL; } + if (!inToken ) { err = EINVAL; } + + if (!err) { + gss_buffer_desc inputBuffer = { inTokenLength, (char *) inToken }; + int encrypt = 1; /* do encryption and integrity protection */ + int encrypted = 0; /* did mechanism encrypt/integrity protect? */ + + majorStatus = gss_wrap (&minorStatus, + inContext, + encrypt, + GSS_C_QOP_DEFAULT, + &inputBuffer, + &encrypted, + &outputBuffer); + if (majorStatus != GSS_S_COMPLETE) { + printGSSErrors ("gss_wrap", majorStatus, minorStatus); + err = minorStatus ? minorStatus : majorStatus; + } else if (!encrypted) { + fprintf (stderr, "WARNING! Mechanism does not support encryption!"); + err = EINVAL; /* You may not want to fail here. */ + } + } + + if (!err) { + printf ("Unencrypted token:\n"); + PrintBuffer (inToken, inTokenLength); + err = WriteToken (inSocket, outputBuffer.value, outputBuffer.length); + } + + if (!err) { + } else { + printError (err, "WriteToken failed"); + } + + if (outputBuffer.value) { gss_release_buffer (&minorStatus, &outputBuffer); } + + return err; +} + +/* --------------------------------------------------------------------------- */ +/* Print BSD error */ + +void gsscon_print_error (int inError, + const char *inString) +{ + fprintf (stderr, "%s: %s (err = %d)\n", + inString, error_message (inError), inError); +} + +/* --------------------------------------------------------------------------- */ +/* Print GSSAPI errors */ + +void gsscon_print_gss_errors (const char *inRoutineName, + OM_uint32 inMajorStatus, + OM_uint32 inMinorStatus) +{ + OM_uint32 minorStatus; + OM_uint32 majorStatus; + gss_buffer_desc errorBuffer; + + OM_uint32 messageContext = 0; /* first message */ + int count = 1; + + fprintf (stderr, "Error returned by %s:\n", inRoutineName); + + do { + majorStatus = gss_display_status (&minorStatus, + inMajorStatus, + GSS_C_GSS_CODE, + GSS_C_NULL_OID, + &messageContext, + &errorBuffer); + if (majorStatus == GSS_S_COMPLETE) { + fprintf (stderr," major error <%d> %s\n", + count, (char *) errorBuffer.value); + gss_release_buffer (&minorStatus, &errorBuffer); + } + ++count; + } while (messageContext != 0); + + count = 1; + messageContext = 0; + do { + majorStatus = gss_display_status (&minorStatus, + inMinorStatus, + GSS_C_MECH_CODE, + GSS_C_NULL_OID, + &messageContext, + &errorBuffer); + fprintf (stderr," minor error <%d> %s\n", + count, (char *) errorBuffer.value); + ++count; + } while (messageContext != 0); +} + diff --git a/gsscon/gsscon_passive.c b/gsscon/gsscon_passive.c index 68f188a..5fc7030 100755 --- a/gsscon/gsscon_passive.c +++ b/gsscon/gsscon_passive.c @@ -52,7 +52,7 @@ * or implied warranty. */ -#include "gsscon.h" +#include const char *gServiceName = NULL; diff --git a/gsscon/gsscon_passive.c~ b/gsscon/gsscon_passive.c~ new file mode 100755 index 0000000..7b3f661 --- /dev/null +++ b/gsscon/gsscon_passive.c~ @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2011, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This code was adapted from the MIT Kerberos Consortium's + * GSS example code, which was distributed under the following + * license: + * + * Copyright 2004-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "gsscon.h" + +const char *gServiceName = NULL; + +int gsscon_passive_authenticate (int inSocket, + gss_ctx_id_t *outGSSContext) +{ + int err = 0; + OM_uint32 majorStatus; + OM_uint32 minorStatus = 0; + gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT; + + char *inputTokenBuffer = NULL; + size_t inputTokenBufferLength = 0; + gss_buffer_desc inputToken; /* buffer received from the server */ + + if (inSocket < 0 ) { err = EINVAL; } + if (!outGSSContext) { err = EINVAL; } + + /* + * The main authentication loop: + * + * GSS is a multimechanism API. The number of packet exchanges required to + * authenticatevaries between mechanisms. As a result, we need to loop reading + * input tokens from the client, calling gss_accept_sec_context on the input + * tokens and send the resulting output tokens back to the client until we + * get GSS_S_COMPLETE or an error. + * + * When we are done, save the client principal so we can make authorization + * checks. + */ + + majorStatus = GSS_S_CONTINUE_NEEDED; + while (!err && (majorStatus != GSS_S_COMPLETE)) { + /* Clean up old input buffer */ + if (inputTokenBuffer != NULL) { + free (inputTokenBuffer); + inputTokenBuffer = NULL; /* don't double-free */ + } + + err = ReadToken (inSocket, &inputTokenBuffer, &inputTokenBufferLength); + + if (!err) { + /* Set up input buffers for the next run through the loop */ + inputToken.value = inputTokenBuffer; + inputToken.length = inputTokenBufferLength; + } + + if (!err) { + /* buffer to send to the server */ + gss_buffer_desc outputToken = { 0, NULL }; + + /* + * accept_sec_context does the actual work of taking the client's + * request and generating an appropriate reply. Note that we pass + * GSS_C_NO_CREDENTIAL for the service principal. This causes the + * server to accept any service principal in the server's keytab, + * which enables you to support multihomed hosts by having one key + * in the keytab for each host identity the server responds on. + * + * However, since we may have more keys in the keytab than we want + * the server to actually use, we will need to check which service + * principal the client used after authentication succeeds. See + * ServicePrincipalIsValidForService() for where you would put these + * checks. We don't check here since if we stopped responding in the + * middle of the authentication negotiation, the client would get an + * EOF, and the user wouldn't know what went wrong. + */ + + printf ("Calling gss_accept_sec_context...\n"); + majorStatus = gss_accept_sec_context (&minorStatus, + &gssContext, + GSS_C_NO_CREDENTIAL, + &inputToken, + GSS_C_NO_CHANNEL_BINDINGS, + NULL /* client_name */, + NULL /* actual_mech_type */, + &outputToken, + NULL /* req_flags */, + NULL /* time_rec */, + NULL /* delegated_cred_handle */); + + if ((outputToken.length > 0) && (outputToken.value != NULL)) { + /* Send the output token to the client (even on error) */ + err = WriteToken (inSocket, outputToken.value, outputToken.length); + + /* free the output token */ + 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) { + *outGSSContext = gssContext; + gssContext = NULL; + } else { + printError (err, "Authenticate failed"); + } + + if (inputTokenBuffer) { free (inputTokenBuffer); } + if (gssContext != GSS_C_NO_CONTEXT) { + gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); } + + return err; +} + +/* --------------------------------------------------------------------------- */ + +static int ServicePrincipalIsValidForService (const char *inServicePrincipal) +{ + int err = 0; + krb5_context context = NULL; + krb5_principal principal = NULL; + + if (!inServicePrincipal) { err = EINVAL; } + + if (!err) { + err = krb5_init_context (&context); + } + + if (!err) { + err = krb5_parse_name (context, inServicePrincipal, &principal); + } + + if (!err) { + /* + * Here is where we check to see if the service principal the client + * used is valid. Typically we would just check that the first component + * is the name of the service provided by the server. This check exists + * to make sure the server is using the correct key in its keytab since + * we passed GSS_C_NO_CREDENTIAL into gss_accept_sec_context(). + */ + if (gServiceName && strcmp (gServiceName, + krb5_princ_name (context, principal)->data) != 0) { + err = KRB5KRB_AP_WRONG_PRINC; + } + } + + if (principal) { krb5_free_principal (context, principal); } + if (context ) { 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) { err = EINVAL; } + + if (!err) { + err = krb5_init_context (&context); + } + + if (!err) { + err = krb5_parse_name (context, inClientPrincipal, &principal); + } + + if (!err) { + /* + * Here is where the server checks to see if the client principal should + * be allowed to use your service. Typically it should check both the name + * and the realm, since with cross-realm shared keys, a user at another + * realm may be trying to contact your service. + */ + err = 0; + } + + if (principal) { krb5_free_principal (context, principal); } + if (context ) { krb5_free_context (context); } + + return err; +} + +/* --------------------------------------------------------------------------- */ + +int gsscon_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 (!inContext ) { err = EINVAL; } + if (!outAuthorized ) { err = EINVAL; } + if (!outAuthorizationError) { err = EINVAL; } + + if (!err) { + /* Get the client and service principals used to authenticate */ + majorStatus = gss_inquire_context (&minorStatus, + inContext, + &clientName, + &serviceName, + NULL, NULL, NULL, NULL, NULL); + if (majorStatus != GSS_S_COMPLETE) { + err = minorStatus ? minorStatus : majorStatus; + } + } + + if (!err) { + /* Pull the client principal string out of the gss name */ + 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) { gss_release_buffer (&minorStatus, &nameToken); } + } + + if (!err) { + /* Pull the service principal string out of the gss name */ + 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) { 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 (serviceName ) { gss_release_name (&minorStatus, &serviceName); } + if (clientName ) { gss_release_name (&minorStatus, &clientName); } + if (clientPrincipal ) { free (clientPrincipal); } + if (servicePrincipal) { free (servicePrincipal); } + + return err; +} + + diff --git a/gsscon/test/gsscon_client.c b/gsscon/test/gsscon_client.c index 73c7858..9e3cca2 100644 --- a/gsscon/test/gsscon_client.c +++ b/gsscon/test/gsscon_client.c @@ -52,7 +52,7 @@ * or implied warranty. */ -#include "../gsscon.h" +#include static void Usage (const char *argv[]) { diff --git a/gsscon/test/gsscon_server.c b/gsscon/test/gsscon_server.c index 999d0cd..c4170c0 100644 --- a/gsscon/test/gsscon_server.c +++ b/gsscon/test/gsscon_server.c @@ -52,7 +52,7 @@ * or implied warranty. */ -#include "../gsscon.h" +#include /* --------------------------------------------------------------------------- */ diff --git a/gsscon/test/gsscon_server.c~ b/gsscon/test/gsscon_server.c~ deleted file mode 100644 index a7d25a9..0000000 --- a/gsscon/test/gsscon_server.c~ +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2011, JANET(UK) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of JANET(UK) nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This code was adapted from the MIT Kerberos Consortium's - * GSS example code, which was distributed under the following - * license: - * - * Copyright 2004-2006 Massachusetts Institute of Technology. - * All Rights Reserved. - * - * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and - * distribute this software and its documentation for any purpose and - * without fee is hereby granted, provided that the above copyright - * notice appear in all copies and that both that copyright notice and - * this permission notice appear in supporting documentation, and that - * the name of M.I.T. not be used in advertising or publicity pertaining - * to distribution of the software without specific, written prior - * permission. Furthermore if you modify this software you must label - * your software as modified software and not distribute it in such a - * fashion that it might be confused with the original M.I.T. software. - * M.I.T. makes no representations about the suitability of - * this software for any purpose. It is provided "as is" without express - * or implied warranty. - */ - -#include "../gsscon.h" - -/* --------------------------------------------------------------------------- */ - -static int SetupListeningSocket (int inPort, - int *outFD) -{ - int err = 0; - int fd = -1; - - if (!outFD) { err = EINVAL; } - - 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, sizeof(struct sockaddr_in)); - 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; /* only close on error */ - } else { - printError (err, "SetupListeningSocket failed"); - } - - if (fd >= 0) { close (fd); } - - return err; -} - -/* --------------------------------------------------------------------------- */ - -static void Usage (const char *argv[]) -{ - fprintf (stderr, "Usage: %s [--port portNumber] [--service 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; - 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], "--service") == 0) && (i < (argc - 1))) { - gServiceName = argv[++i]; - } else { - err = EINVAL; - } - } - - if (!err) { - printf ("%s: Starting up...\n", argv[0]); - - err = SetupListeningSocket (port, &listenFD); - } - - while (!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; - } - continue; /* Try again */ - } - - printf ("Accepting new connection...\n"); - connectionErr = gsscon_passive_authenticate (connectionFD, &gssContext); - - if (!connectionErr) { - connectionErr = gsscon_authorize (gssContext, - &authorized, - &authorizationError); - } - - if (!connectionErr) { - char buffer[1024]; - memset (buffer, 0, sizeof (buffer)); - - /* - * Here is where your protocol would go. This sample server just - * writes a nul terminated string to the client telling whether - * it was authorized. - */ - if (authorized) { - snprintf (buffer, sizeof (buffer), "SUCCESS!"); - } else { - snprintf (buffer, sizeof(buffer), "FAILURE! %s (err = %d)", - error_message (authorizationError), - authorizationError); - } - connectionErr = gsscon_write_encrypted_token (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 != GSS_C_NO_CONTEXT) { - gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); } - - return err ? -1 : 0; -} - diff --git a/gsscon/gsscon.h b/include/gsscon.h similarity index 100% rename from gsscon/gsscon.h rename to include/gsscon.h