Add initial GSS connection code and test client/server.
authorMargaret Wasserman <mrw@painless-security.com>
Wed, 21 Nov 2012 10:16:09 +0000 (05:16 -0500)
committerMargaret Wasserman <mrw@lilac-moonshot.suchdamage.org>
Wed, 21 Nov 2012 10:18:24 +0000 (05:18 -0500)
gsscon/gsscon.h [new file with mode: 0755]
gsscon/gsscon_active.c [new file with mode: 0755]
gsscon/gsscon_common.c [new file with mode: 0755]
gsscon/gsscon_passive.c [new file with mode: 0755]
gsscon/test/gsscon_client.c [new file with mode: 0644]
gsscon/test/gsscon_client.c~ [new file with mode: 0644]
gsscon/test/gsscon_server.c [new file with mode: 0644]
gsscon/test/gsscon_server.c~ [new file with mode: 0644]

diff --git a/gsscon/gsscon.h b/gsscon/gsscon.h
new file mode 100755 (executable)
index 0000000..70aa96e
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#ifndef GSSCON_H
+#define GSSCON_H
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+#include <krb5.h>
+#include <errno.h>
+
+#define kDefaultPort 2000
+extern const char *gServiceName;
+
+int gsscon_read_token (int      inSocket, 
+               char   **outTokenValue, 
+               size_t  *outTokenLength);
+
+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);
+
+int gsscon_connect (const char *inHost, 
+                   int inPort, 
+                   int *outFD);
+
+int 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
new file mode 100755 (executable)
index 0000000..ca7c25e
--- /dev/null
@@ -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"
+
+/* --------------------------------------------------------------------------- */
+
+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 {
+         gsscon_print_error (err, "OpenConnection failed");
+    }
+    
+    if (fd >= 0) { close (fd); }
+    
+    return err; 
+}
+
+/* --------------------------------------------------------------------------- */
+
+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) { 
+                gsscon_print_gss_errors ("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) { 
+                    gsscon_print_gss_errors ("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) { 
+            gsscon_print_gss_errors ("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 = gsscon_write_token (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 = gsscon_read_token (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) {
+                gsscon_print_gss_errors ("gss_init_sec_context", majorStatus, minorStatus);
+                err = minorStatus ? minorStatus : majorStatus; 
+            }
+        }
+    }
+    
+    if (!err) { 
+        *outGSSContext = gssContext;
+        gssContext = NULL;
+    } else {
+        gsscon_print_gss_error (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
new file mode 100755 (executable)
index 0000000..325b98b
--- /dev/null
@@ -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) { gsscon_print_error (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) { gsscon_print_error (err, "WritBuffer 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 { 
+        gsscon_print_error (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 { 
+        gsscon_print_error (err, "gsscon_write_token() 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 = gsscon_read_token (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) { 
+            gsscon_print_gss_errors("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 { 
+        gsscon_print_error (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) { 
+            gsscon_print_gss_errors ("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 = gsscon_write_token (inSocket, outputBuffer.value, outputBuffer.length);
+    }
+    
+    if (!err) {
+    } else { 
+        gsscon_print_error (err, "gsscon_write_token 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);
+}
+
+/* --------------------------------------------------------------------------- */
+/* PrintGSSAPI 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
new file mode 100755 (executable)
index 0000000..68f188a
--- /dev/null
@@ -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 = gsscon_read_token (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 = gsscon_write_token (inSocket, outputToken.value, outputToken.length);
+                
+                /* free the output token */
+                gss_release_buffer (&minorStatus, &outputToken);
+            }
+        }
+        
+        if ((majorStatus != GSS_S_COMPLETE) && (majorStatus != GSS_S_CONTINUE_NEEDED)) {
+            gsscon_print_gss_errors ("gss_accept_sec_context", majorStatus, minorStatus);
+            err = minorStatus ? minorStatus : majorStatus; 
+        }            
+    }
+    
+    if (!err) { 
+        *outGSSContext = gssContext;
+        gssContext = NULL;
+    } else {
+        gsscon_print_error (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
new file mode 100644 (file)
index 0000000..73c7858
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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 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";
+    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;
+
+        /* 
+         * 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);
+
+        if (!err) {
+            printf ("Server message: '%s'\n", buffer);
+        }
+        
+        if (buffer != NULL) { free (buffer); }
+    }
+    
+    
+    if (err) {
+        if (err == EINVAL) {
+            Usage (argv);
+        } else {
+            gsscon_print_error (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;
+}
+
+
+
diff --git a/gsscon/test/gsscon_client.c~ b/gsscon/test/gsscon_client.c~
new file mode 100644 (file)
index 0000000..f309a98
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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 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";
+    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;
+
+        /* 
+         * 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);
+
+        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;
+}
+
+
+
diff --git a/gsscon/test/gsscon_server.c b/gsscon/test/gsscon_server.c
new file mode 100644 (file)
index 0000000..999d0cd
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * 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 {
+        gsscon_print_error (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) {
+            gsscon_print_error (connectionErr, "Connection failed");
+        }
+        
+        if (connectionFD >= 0) { 
+            printf ("Closing connection.\n"); 
+            close (connectionFD); 
+        }
+    }
+    
+    if (err) { 
+        if (err == EINVAL) {
+            Usage (argv);
+        } else {
+            gsscon_print_error (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/test/gsscon_server.c~ b/gsscon/test/gsscon_server.c~
new file mode 100644 (file)
index 0000000..a7d25a9
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * 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;
+}
+