GSS-API Programming Guide

Appendix A Sample C–Based GSS-API Programs

Programs Using GSS-API

This appendix shows the source code for two sample applications that use GSS-API to make a safe network connection. One application is a client, and the other is a server. The two programs display benchmarks as they run, so that a user can “see” GSS-API being used. Additionally, certain miscellaneous functions are provided for use by the client and server applications. For convenience's sake we have divided each application into its constituent functions.

These programs are examined in detail in Chapter 2, A Walk–Through of the Sample GSS-API Programs.

Client-Side Application

The following sections detail the client-side program, gss_client.

Program Headers

These are the declarations for the client program, plus a function that explains the syntax if an incorrect command line is given.


Example A–1 Client Program Headers

/*
 * Copyright 1994 by OpenVision Technologies, Inc.
 * 
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appears in all copies and
 * that both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of OpenVision not be used
 * in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission. OpenVision makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 * 
 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <error.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <gssapi/gssapi.h>
#include <gssapi/gssapi_ext.h>
#include "gss-misc.h"

/* global mech oid needed by display status, and acquire cred */
gss_OID g_mechOid = GSS_C_NULL_OID;


void usage()
{
     fprintf(stderr, "Usage: gss-client [-port port] [-d]"
                        " [-mech mechOid] host service msg\n");
     exit(1);
}

main()

This is the entrypoint to the program. The program takes the following syntax on the command line:



gss-client [-port port] [-d] [-mech mech] host service msg

After parsing the command line, main() converts the name of the appropriate security mechanism (if provided) to an OID, establishes a secure connection, and then destroys the mechanism OID, if necessary.


Note –

main() uses a nonstandard function, gss_release_oid(). This function is not supported by all implementations of the GSS-API and should not be used if possible. Since applications should use a default mechanism (specified by GSS_C_NULL_OID) instead of allocating one of their own, this function should not be needed in any case. It is included here for reasons of backward compatibility and to show the full extent of this implementation of the GSS-API.



Example A–2 main()

int main(argc, argv)
     int argc;
     char **argv;
{
    /* char *service_name, *hostname, *msg; */
     char *msg;
     char service_name[128]; 
     char hostname[128];  
     char *mechanism = 0;
     u_short port = 4444;
     int use_file = 0;
     OM_uint32 deleg_flag = 0, min_stat;
     
     display_file = stdout;

     /* Parse arguments. */

        argc--; argv++;
     while (argc) {
          if (strcmp(*argv, "-port") == 0) {
               argc--; argv++;
               if (!argc) usage();
               port = atoi(*argv);
           } else if (strcmp(*argv, "-mech") == 0) {
               argc--; argv++;
               if (!argc) usage();
               mechanism = *argv;
           } else if (strcmp(*argv, "-d") == 0) {
               deleg_flag = GSS_C_DELEG_FLAG;
          } else if (strcmp(*argv, "-f") == 0) {
               use_file = 1;
          } else
                break;
          argc--; argv++;
     }
     if (argc != 3)
          usage();

     if (argc > 1) {
                strcpy(hostname, argv[0]);
        } else if (gethostname(hostname, sizeof(hostname)) == -1) {
                        perror("gethostname");
                        exit(1);
        }


     if (argc > 2) { 
        strcpy(service_name, argv[1]);
        strcat(service_name, "@");
        strcat(service_name, hostname);
 
     }

      msg = argv[2];
     
     if (mechanism)
         parse_oid(mechanism, &g_mechOid);

     if (call_server(hostname, port, g_mechOid, service_name,
                   deleg_flag, msg, use_file) < 0)
          exit(1);

     if (g_mechOid != GSS_C_NULL_OID)
         (void) gss_release_oid(&min_stat, &gmechOid);
         
     return 0;
}

parse_oid()

Converts the name of the security mechanism provided on the command line (if any is provided) to an OID for GSS-API to work with.


Caution – Caution –

Despite this sample, applications are strongly recommended to use the default mechanism provided by the GSS-API implementation, rather than specifying one. The default mechanism can be obtained by setting the mechanism OID value to GSS_C_NULL_OID. Also, the function gss_str_to_oid() is not supported by all GSS-API implementations.



Example A–3 parse_oid()

static void parse_oid(char *mechanism, gss_OID *oid)
{
    char        *mechstr = 0, *cp;
    gss_buffer_desc tok;
    OM_uint32 maj_stat, min_stat;
   
    if (isdigit(mechanism[0])) {
        mechstr = malloc(strlen(mechanism)+5);
        if (!mechstr) {
            printf("Couldn't allocate mechanism scratch!\n");
            return;
        }
        sprintf(mechstr, "{ %s }", mechanism);
        for (cp = mechstr; *cp; cp++)
            if (*cp == '.')
                *cp = ' ';
        tok.value = mechstr;
    } else
        tok.value = mechanism;
    tok.length = strlen(tok.value);
    maj_stat = gss_str_to_oid(&min_stat, &tok, oid);
    if (maj_stat != GSS_S_COMPLETE) {
        display_status("str_to_oid", maj_stat, min_stat);
        return;
    }
    if (mechstr)
        free(mechstr);
}

call_server()

This is the centerpiece of the program.


Example A–4 call_server()

/*
 * Function: call_server
 *
 * Purpose: Call the "sign" service.
 *
 * Arguments:
 *
 *      host            (r) the host providing the service
 *      port            (r) the port to connect to on host
 *      service_name    (r) the GSS-API service name to authenticate to
 *      msg             (r) the message to have "signed"
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * call_server opens a TCP connection to <host:port> and establishes a
 * GSS-API context with service_name over the connection.  It then
 * wraps msg in a GSS-API token with gss_wrap, sends it to the server,
 * reads back a GSS-API signature block for msg from the server, and
 * verifies it with gss_verify.  -1 is returned if any step fails,
 * otherwise 0 is returned.
 */
int call_server(host, port, oid, service_name, deleg_flag, msg, use_file)
     char *host;
     u_short port;
     gss_OID oid;
     char *service_name;
     OM_uint32 deleg_flag;
     char *msg;
     int use_file;
{
     gss_ctx_id_t context;
     gss_buffer_desc in_buf, out_buf, context_token;
     int s, state;
     OM_uint32 ret_flags;
     OM_uint32 maj_stat, min_stat;
     gss_name_t         src_name, targ_name;
     gss_buffer_desc    sname, tname;
     OM_uint32          lifetime;
     gss_OID            mechanism, name_type;
     int                is_local;
     OM_uint32          context_flags;
     int                is_open;
     gss_qop_t          qop_state;
     gss_OID_set        mech_names;
     gss_buffer_desc    oid_name;
     int        i;
     int conf_req_flag = 0;
     int req_output_size = 1012;
     OM_uint32 max_input_size = 0;
        char *mechStr;

/* Open connection */
     if ((s = connect_to_server(host, port)) < 0)
          return -1;

     /* Establish context */
     if (client_establish_context(s, service_name, deleg_flag, oid, &context,
                                  &ret_flags) < 0) {
          (void) close(s);
          return -1;
     }

 /* Save and then restore the context */
     maj_stat = gss_export_sec_context(&min_stat,
                                           &context,
                                           &context_token);
     if (maj_stat != GSS_S_COMPLETE) {
             display_status("exporting context", maj_stat, min_stat);
             return -1;
     }
     maj_stat = gss_import_sec_context(&min_stat,
                                           &context_token,
                                           &context);
     if (maj_stat != GSS_S_COMPLETE) {
        display_status("importing context", maj_stat, min_stat);
        return -1;
     }
     (void) gss_release_buffer(&min_stat, &context_token);

     /* display the flags */
     display_ctx_flags(ret_flags);

     /* Get context information */
     maj_stat = gss_inquire_context(&min_stat, context,
                                    &src_name, &targ_name, &lifetime,
                                    &mechanism, &context_flags,
                                    &is_local,
                                    &is_open);
     if (maj_stat != GSS_S_COMPLETE) {
         display_status("inquiring context", maj_stat, min_stat);
         return -1;
     }

     if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
     printf(" context expired\n");
         display_status("Context is expired", maj_stat, min_stat);
         return -1;
     }

/* Test gss_wrap_size_limit */
     maj_stat = gss_wrap_size_limit(&min_stat, context,
                                conf_req_flag,
                                GSS_C_QOP_DEFAULT,
                                req_output_size,
                                &max_input_size
                                );
     if (maj_stat != GSS_S_COMPLETE) {
           display_status("wrap_size_limit call", maj_stat, min_stat);
     } else
           fprintf (stderr, "gss_wrap_size_limit returned "
                "max input size = %d \n"
                "for req_output_size = %d with Integrity only\n",
                max_input_size , req_output_size , conf_req_flag);

     conf_req_flag = 1;
     maj_stat = gss_wrap_size_limit(&min_stat, context,
                                conf_req_flag,
                                GSS_C_QOP_DEFAULT,
                                req_output_size,
                                &max_input_size
                                );
     if (maj_stat != GSS_S_COMPLETE) {
           display_status("wrap_size_limit call", maj_stat, min_stat);
     } else
           fprintf (stderr, "gss_wrap_size_limit returned "
                " max input size = %d \n"
                "for req_output_size = %d with "
                "Integrity & Privacy \n",
                max_input_size , req_output_size );

     maj_stat = gss_display_name(&min_stat, src_name, &sname,
                                 &name_type);
     if (maj_stat != GSS_S_COMPLETE) {
         display_status("displaying source name", maj_stat, min_stat);
         return -1;
     }
     maj_stat = gss_display_name(&min_stat, targ_name, &tname,
                                 (gss_OID *) NULL);
     if (maj_stat != GSS_S_COMPLETE) {
         display_status("displaying target name", maj_stat, min_stat);
         return -1;
     }
     fprintf(stderr, "\"%.*s\" to \"%.*s\", lifetime %u, flags %x, %s, %s\n",
             (int) sname.length, (char *) sname.value,
             (int) tname.length, (char *) tname.value, lifetime,
             context_flags,
             (is_local) ? "locally initiated" : "remotely initiated",
             (is_open) ? "open" : "closed");

     (void) gss_release_name(&min_stat, &src_name);
     (void) gss_release_name(&min_stat, &targ_name);
     (void) gss_release_buffer(&min_stat, &sname);
     (void) gss_release_buffer(&min_stat, &tname);


     maj_stat = gss_oid_to_str(&min_stat,
                               name_type,
                               &oid_name);
     if (maj_stat != GSS_S_COMPLETE) {
         display_status("converting oid->string", maj_stat, min_stat);
         return -1;
     }
     fprintf(stderr, "Name type of source name is %.*s.\n",
             (int) oid_name.length, (char *) oid_name.value);
     (void) gss_release_buffer(&min_stat, &oid_name);

     /* Now get the names supported by the mechanism */
     maj_stat = gss_inquire_names_for_mech(&min_stat,
                                           mechanism,
                                           &mech_names);
     if (maj_stat != GSS_S_COMPLETE) {
         display_status("inquiring mech names", maj_stat, min_stat);
         return -1;
     }

     maj_stat = gss_oid_to_str(&min_stat,
                               mechanism,
                               &oid_name);
     if (maj_stat != GSS_S_COMPLETE) {
         display_status("converting oid->string", maj_stat, min_stat);
         return -1;
     }
        mechStr = (char *)__gss_oid_to_mech(mechanism);
     fprintf(stderr, "Mechanism %.*s (%s) supports %d names\n",
                (int) oid_name.length, (char *) oid_name.value,
                (mechStr == NULL ? "NULL" : mechStr),
                mech_names->count);
     (void) gss_release_buffer(&min_stat, &oid_name);

     for (i=0; i < mech_names->count; i++) {
         maj_stat = gss_oid_to_str(&min_stat,
                                   &mech_names->elements[i],
                                   &oid_name);
         if (maj_stat != GSS_S_COMPLETE) {
             display_status("converting oid->string", maj_stat, min_stat);
             return -1;
         }
         fprintf(stderr, "  %d: %.*s\n", i,
                 (int) oid_name.length, (char *) oid_name.value);

         (void) gss_release_buffer(&min_stat, &oid_name);
     }
     (void) gss_release_oid_set(&min_stat, &mech_names);

     if (use_file) {
         read_file(msg, &in_buf);
     } else {
         /* Seal the message */
         in_buf.value = msg;
         in_buf.length = strlen(msg) + 1;
     }

     if (ret_flag & GSS_C_CONF_FLAG) {
          state = 1;
     else
          state = 0;
     }

     maj_stat = gss_wrap(&min_stat, context, 1, GSS_C_QOP_DEFAULT,
                         &in_buf, &state, &out_buf);
     if (maj_stat != GSS_S_COMPLETE) {
          display_status("wrapping message", maj_stat, min_stat);
          (void) close(s);
          (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
          return -1;
     } else if (! state) {
          fprintf(stderr, "Warning!  Message not encrypted.\n");
     }

     /* Send to server */
     if (send_token(s, &out_buf) < 0) {
          (void) close(s);
          (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
          return -1;
     }
     (void) gss_release_buffer(&min_stat, &out_buf);

     /* Read signature block into out_buf */
     if (recv_token(s, &out_buf) < 0) {
          (void) close(s);
          (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
          return -1;
     }

     /* Verify signature block */
     maj_stat = gss_verify_mic(&min_stat, context, &in_buf,
                               &out_buf, &qop_state);
     if (maj_stat != GSS_S_COMPLETE) {
          display_status("verifying signature", maj_stat, min_stat);
          (void) close(s);
          (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
          return -1;
     }
     (void) gss_release_buffer(&min_stat, &out_buf);

     if (use_file)
         free(in_buf.value);

     printf("Signature verified.\n");

     /* Delete context */
     maj_stat = gss_delete_sec_context(&min_stat, &context, &out_buf);
     if (maj_stat != GSS_S_COMPLETE) {
          display_status("deleting context", maj_stat, min_stat);
          (void) close(s);
          (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
          return -1;
     }

     (void) gss_release_buffer(&min_stat, &out_buf);
     (void) close(s);
     return 0;
}

read_file()

In the case that the message to be transferred is contained in a file, this function, called by call_server(), opens and reads the file.


Example A–5 read_file()

void read_file(file_name, in_buf)
    char                *file_name;
    gss_buffer_t        in_buf;
{
    int fd, bytes_in, count;
    struct stat stat_buf;

    if ((fd = open(file_name, O_RDONLY, 0)) < 0) {
        perror("open");
        fprintf(stderr, "Couldn't open file %s\n", file_name);
        exit(1);
    }
    if (fstat(fd, &stat_buf) < 0) {
        perror("fstat");
        exit(1);
    }
    in_buf->length = stat_buf.st_size;
    in_buf->value = malloc(in_buf->length);
    if (in_buf->value == 0) {
        fprintf(stderr, "Couldn't allocate %ld byte buffer for reading file\n",
                in_buf->length);
        exit(1);
    }
    memset(in_buf->value, 0, in_buf->length);
    for (bytes_in = 0; bytes_in < in_buf->length; bytes_in += count) {
        count = read(fd, in_buf->value, (OM_uint32)in_buf->length);
        if (count < 0) {
            perror("read");
            exit(1);
        }
        if (count == 0)
            break;
    }
    if (bytes_in != count)
        fprintf(stderr, "Warning, only read in %d bytes, expected %d\n",
                bytes_in, count);
}

client_establish_context()

Calls gss_init_sec_context() to establish a context with the server.


Example A–6 client_establish_context()

/*
 * Function: client_establish_context
 *
 * Purpose: establishes a GSS-API context with a specified service and
 * returns the context handle
 *
 * Arguments:
 *
 *      s               (r) an established TCP connection to the service
 *      service_name    (r) the ASCII service name of the service
 *      context         (w) the established GSS-API context
 *      ret_flags       (w) the returned flags from init_sec_context
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * service_name is imported as a GSS-API name and a GSS-API context is
 * established with the corresponding service; the service should be
 * listening on the TCP connection s.  The default GSS-API mechanism
 * is used, and mutual authentication and replay detection are
 * requested.
 *
 * If successful, the context handle is returned in context.  If
 * unsuccessful, the GSS-API error messages are displayed on stderr
 * and -1 is returned.
 */
     int client_establish_context(s, service_name, deleg_flag, oid,
                             gss_context, ret_flags)
     int s;
     char *service_name;
     gss_OID oid;
     OM_uint32 deleg_flag;
     gss_ctx_id_t *gss_context;
     OM_uint32 *ret_flags;
{
     gss_buffer_desc send_tok, recv_tok, *token_ptr;
     gss_name_t target_name;
     OM_uint32 maj_stat, min_stat;

     /*
      * Import the name into target_name.  Use send_tok to save
      * local variable space.
      */

     send_tok.value = service_name;
     send_tok.length = strlen(service_name) + 1;
     maj_stat = gss_import_name(&min_stat, &send_tok,
                        (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &target_name);
     if (maj_stat != GSS_S_COMPLETE) {
          display_status("parsing name", maj_stat, min_stat);
          return -1;
     }


     /*
      * Perform the context-establishement loop.
      *
      * On each pass through the loop, token_ptr points to the token
      * to send to the server (or GSS_C_NO_BUFFER on the first pass).
      * Every generated token is stored in send_tok which is then
      * transmitted to the server; every received token is stored in
      * recv_tok, which token_ptr is then set to, to be processed by
      * the next call to gss_init_sec_context.
      *
      * GSS-API guarantees that send_tok's length will be non-zero
      * if and only if the server is expecting another token from us,
      * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
      * and only if the server has another token to send us.
      */

     token_ptr = GSS_C_NO_BUFFER;
     *gss_context = GSS_C_NO_CONTEXT;

     do {
          maj_stat =
               gss_init_sec_context(&min_stat,
                                    GSS_C_NO_CREDENTIAL,
                                    gss_context,
                                    target_name,
                                    oid,
                                    GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
                                                        deleg_flag,
                                    0,
                                    NULL,       /* no channel bindings */
                                    token_ptr,
                                    NULL,       /* ignore mech type */
                                    &send_tok,
                                    ret_flags,
                                    NULL);      /* ignore time_rec */
          if (gss_context == NULL){
               printf("Cannot create context\n");
               return GSS_S_NO_CONTEXT;
          }
          if (token_ptr != GSS_C_NO_BUFFER)
               (void) gss_release_buffer(&min_stat, &recv_tok);
          if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
               display_status("initializing context", maj_stat, min_stat);
               (void) gss_release_name(&min_stat, &target_name);
               return -1;
          }

          if (send_tok.length != 0) {
               fprintf(stdout, "Sending init_sec_context token (size=%ld)...",
                        send_tok.length);
               if (send_token(s, &send_tok) < 0) {
                    (void) gss_release_buffer(&min_stat, &send_tok);
                    (void) gss_release_name(&min_stat, &target_name);
                    return -1;
               }
          }
          (void) gss_release_buffer(&min_stat, &send_tok);

          if (maj_stat == GSS_S_CONTINUE_NEEDED) {
               fprintf(stdout, "continue needed...");
               if (recv_token(s, &recv_tok) < 0) {
                    (void) gss_release_name(&min_stat, &target_name);
                    return -1;
               }
               token_ptr = &recv_tok;
          }
          printf("\n");
     } while (maj_stat == GSS_S_CONTINUE_NEEDED);

     (void) gss_release_name(&min_stat, &target_name);
     return 0;
}

connect_to_server()

This offers a basic, no-frills function that creates a TCP connection.


Example A–7 connect_to_server()

/*
 * Function: connect_to_server
 *
 * Purpose: Opens a TCP connection to the name host and port.
 *
 * Arguments:
 *
 *      host            (r) the target host name
 *      port            (r) the target port, in host byte order
 *
 * Returns: the established socket file desciptor, or -1 on failure
 *
 * Effects:
 *
 * The host name is resolved with gethostbyname(), and the socket is
 * opened and connected.  If an error occurs, an error message is
 * displayed and -1 is returned.
 */
int connect_to_server(host, port)
     char *host;
     u_short port;
{
     struct sockaddr_in saddr;
     struct hostent *hp;
     int s;
    
     if ((hp = gethostbyname(host)) == NULL) {
          fprintf(stderr, "Unknown host: %s\n", host);
          return -1;
     }

     saddr.sin_family = hp->h_addrtype;
     memcpy((char *)&saddr.sin_addr, hp->h_addr, sizeof(saddr.sin_addr));
     saddr.sin_port = htons(port);

     if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
          perror("creating socket");
          return -1;
     }
     if (connect(s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
          perror("connecting to server");
          (void) close(s);
          return -1;
     }

     return s;
}

Server-Side Application

This is the application that receives messages from the client function described earlier.

Program Headers

These are the declarations for the server program, plus a function that explains the syntax if an incorrect command line is given. Here the security mechanism is set to be the GSS-API-provided default.


Example A–8 Program Headers

/*
 * Copyright 1994 by OpenVision Technologies, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appears in all copies and
 * that both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of OpenVision not be used
 * in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission. OpenVision makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#if !defined(lint) && !defined(__CODECENTER__)
static char *rcsid = "$Header: /afs/athena.mit.edu/astaff/project/krbdev/.cvsroot
/src/appl/gss-sample/gss-server.c,v 1.17 1996/10/22 00:07:59 tytso Exp $";
#endif

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <ctype.h>

#include <gssapi/gssapi.h>
#include <gssapi/gssapi_ext.h>
#include "gss-misc.h"

#ifdef USE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif

/* global mechanism oid used in acquire cred and display status */
gss_OID g_mechOid = GSS_C_NULL_OID;

void usage()
{
     fprintf(stderr, "Usage: gss-server [-port port] [-verbose]\n");
     fprintf(stderr, "       [-inetd] [-logfile file]");
     fprintf(stderr, " [-mech mechoid] [service_name]\n");
     exit(1);
}

FILE *log;

int verbose = 0;

main()

This is the entrypoint to the program. The program takes the following syntax on the command line:



gss-server [-port port] [-d] [-mech mech] host service msg

After parsing the command line, main() converts the name of the desired security mechanism (if provided) to an OID, acquires credentials, establishes a context and receives data, and then destroys the mechanism OID if necessary.


Note –

Applications should normally not set the mechanism, but use defaults provided by the GSS-API.



Example A–9 main()

int
main(argc, argv)
     int argc;
     char **argv;
{
     char *service_name, *mechType = NULL;
     gss_cred_id_t server_creds;
     OM_uint32 min_stat;
     u_short port = 4444;
     int s;
     int once = 0;
     int do_inetd = 0;

     log = stdout;
     display_file = stdout;
     argc--; argv++;
     while (argc) {
          if (strcmp(*argv, "-port") == 0) {
               argc--; argv++;
               if (!argc) usage();
               port = atoi(*argv);
          } else if (strcmp(*argv, "-verbose") == 0) {
              verbose = 1;
          } else if (strcmp(*argv, "-once") == 0) {
              once = 1;
          } else if (strcmp(*argv, "-inetd") == 0) {
              do_inetd = 1;
          } else if (strcmp(*argv, "-mech") == 0) {
                argc--; argv++;
                if (!argc)      usage();
                mechType = *argv;
          } else if (strcmp(*argv, "-logfile") == 0) {
              argc--; argv++;
              if (!argc) usage();
              log = fopen(*argv, "a");
              display_file = log;
              if (!log) {
                  perror(*argv);
                  exit(1);
              }
          } else
               break;
          argc--; argv++;
     }
     if (argc != 1)
          usage();

     if ((*argv)[0] == '-')
          usage();

     service_name = *argv;

     if (mechType != NULL) {
             if ((g_mechOid = createMechOid(mechType)) == NULL) {
                     usage();
                     exit(-1);
             }
     }

     if (server_acquire_creds(service_name, g_mechOid, &server_creds) < 0)
         return -1;

     if (do_inetd) {
         close(1);
         close(2);

         sign_server(0, server_creds);
         close(0);
     } else {
         int stmp;

         if ((stmp = create_socket(port))) {
             do {
                 /* Accept a TCP connection */
                 if ((s = accept(stmp, NULL, 0)) < 0) {
                     perror("accepting connection");
                 } else {
                     /* this return value is not checked, because there's
                        not really anything to do if it fails */
                     sign_server(s, server_creds);
                 }
             } while (!once);
         }

         close(stmp);
     }

     (void) gss_release_cred(&min_stat, &server_creds);
     if (g_mechOid != GSS_C_NULL_OID)
             gss_release_oid(&min_stat, &g_mechOid);

     /*NOTREACHED*/
     (void) close(s);
     return 0;
}

createMechOid()

This function is shown for completeness' sake. Normally, you should use the default mechanism (specified by GSS_C_NULL_OID).


Example A–10 createMechOid()

gss_OID createMechOid(const char *mechStr)
{
        gss_buffer_desc mechDesc;
        gss_OID mechOid;
        OM_uint32 minor;

        if (mechStr == NULL)
                return (GSS_C_NULL_OID);

        mechDesc.length = strlen(mechStr);
        mechDesc.value = (void *) mechStr;

        if (gss_str_to_oid(&minor, &mechDesc, &mechOid) !
= GSS_S_COMPLETE) {
                fprintf(stderr, "Invalid mechanism oid specified <%s>",
                                mechStr);
                return (GSS_C_NULL_OID);
        }

        return (mechOid);
}

server_acquire_creds()

Gets the credentials for the requested network service.


Example A–11 server_acquire_creds()

/*
 * Function: server_acquire_creds
 *
 * Purpose: imports a service name and acquires credentials for it
 *
 * Arguments:
 *
 *      service_name    (r) the ASCII service name
        mechType        (r) the mechanism type to use
 *      server_creds    (w) the GSS-API service credentials
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * The service name is imported with gss_import_name, and service
 * credentials are acquired with gss_acquire_cred.  If either operation
 * fails, an error message is displayed and -1 is returned; otherwise,
 * 0 is returned.
 */
int server_acquire_creds(service_name, mechOid, server_creds)
     char *service_name;
     gss_OID mechOid;
     gss_cred_id_t *server_creds;
{
     gss_buffer_desc name_buf;
     gss_name_t server_name;
     OM_uint32 maj_stat, min_stat;
     gss_OID_set_desc mechOidSet;
     gss_OID_set desiredMechs = GSS_C_NULL_OID_SET;

     if (mechOid != GSS_C_NULL_OID) {
                desiredMechs = &mechOidSet;
                mechOidSet.count = 1;
                mechOidSet.elements = mechOid;
     } else
                desiredMechs = GSS_C_NULL_OID_SET;


     name_buf.value = service_name;
     name_buf.length = strlen(name_buf.value) + 1;
     maj_stat = gss_import_name(&min_stat, &name_buf,
                (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &server_name);
     if (maj_stat != GSS_S_COMPLETE) {
          display_status("importing name", maj_stat, min_stat);
          if (mechOid != GSS_C_NO_OID)
                gss_release_oid(&min_stat, &mechOid);
          return -1;
     }

     maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
                                 desiredMechs, GSS_C_ACCEPT,
                                 server_creds, NULL, NULL);

     if (maj_stat != GSS_S_COMPLETE) {
          display_status("acquiring credentials", maj_stat, min_stat);
          return -1;
     }

     (void) gss_release_name(&min_stat, &server_name);

     return 0;
}

sign_server()

This is the “guts” of the program. Calls server_establish_context() to accept the context, receives the data, unwraps it, verifies it, then generates a MIC to send back to the client. Finally, it deletes the context.


Example A–12 sign_server()

/*
 * Function: sign_server
 *
 * Purpose: Performs the "sign" service.
 *
 * Arguments:
 *
 *      s               (r) a TCP socket on which a connection has been
 *                      accept()ed
 *      service_name    (r) the ASCII name of the GSS-API service to
 *                      establish a context as
 *
 * Returns: -1 on error
 *
 * Effects:
 *
 * sign_server establishes a context, and performs a single sign request.
 *
 * A sign request is a single GSS-API wrapped token.  The token is
 * unwrapped and a signature block, produced with gss_get_mic, is returned
 * to the sender.  The context is the destroyed and the connection
 * closed.
 *
 * If any error occurs, -1 is returned.
 */
int sign_server(s, server_creds)
     int s;
     gss_cred_id_t server_creds;
{
     gss_buffer_desc client_name, xmit_buf, msg_buf;
     gss_ctx_id_t context;
     OM_uint32 maj_stat, min_stat;
     int i, conf_state, ret_flags;
     char       *cp;

     /* Establish a context with the client */
     if (server_establish_context(s, server_creds, &context,
                                  &client_name, &ret_flags) < 0)
        return(-1);

     printf("Accepted connection: \"%.*s\"\n",
            (int) client_name.length, (char *) client_name.value);
     (void) gss_release_buffer(&min_stat, &client_name);

     for (i=0; i < 3; i++)
             if (test_import_export_context(&context))
                     return -1;

     /* Receive the wrapped message token */
     if (recv_token(s, &xmit_buf) < 0)
        return(-1);

     if (verbose && log) {
        fprintf(log, "Wrapped message token:\n");
        print_token(&xmit_buf);
     }

     maj_stat = gss_unwrap(&min_stat, context, &xmit_buf, &msg_buf,
                           &conf_state, (gss_qop_t *) NULL);
     if (maj_stat != GSS_S_COMPLETE) {
        display_status("unwrapping message", maj_stat, min_stat);
        return(-1);
     } else if (! conf_state) {
        fprintf(stderr, "Warning!  Message not encrypted.\n");
     }

     (void) gss_release_buffer(&min_stat, &xmit_buf);

     fprintf(log, "Received message: ");
     cp = msg_buf.value;
     if (isprint(cp[0]) && isprint(cp[1]))
        fprintf(log, "\"%s\"\n", cp);
     else {
        printf("\n");
        print_token(&msg_buf);
     }

     /* Produce a signature block for the message */
     maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT,
                            &msg_buf, &xmit_buf);
     if (maj_stat != GSS_S_COMPLETE) {
        display_status("signing message", maj_stat, min_stat);
        return(-1);
     }

     (void) gss_release_buffer(&min_stat, &msg_buf);

     /* Send the signature block to the client */
     if (send_token(s, &xmit_buf) < 0)
        return(-1);

     (void) gss_release_buffer(&min_stat, &xmit_buf);

     /* Delete context */
     maj_stat = gss_delete_sec_context(&min_stat, &context, NULL);
     if (maj_stat != GSS_S_COMPLETE) {
        display_status("deleting context", maj_stat, min_stat);
        return(-1);
     }

     fflush(log);

     return(0);
}

server_establish_context()

This calls gss_accept_sec_context() as part of a context-establishment loop.


Example A–13 server_establish_context()

/*
 * Function: server_establish_context
 *
 * Purpose: establishses a GSS-API context as a specified service with
 * an incoming client, and returns the context handle and associated
 * client name
 *
 * Arguments:
 *
 *      s               (r) an established TCP connection to the client
 *      service_creds   (r) server credentials, from gss_acquire_cred
 *      context         (w) the established GSS-API context
 *      client_name     (w) the client's ASCII name
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * Any valid client request is accepted.  If a context is established,
 * its handle is returned in context and the client name is returned
 * in client_name and 0 is returned.  If unsuccessful, an error
 * message is displayed and -1 is returned.
 */
int server_establish_context(s, server_creds, context, client_name, ret_flags)
     int s;
     gss_cred_id_t server_creds;
     gss_ctx_id_t *context;
     gss_buffer_t client_name;
     OM_uint32 *ret_flags;
{
     gss_buffer_desc send_tok, recv_tok;
     gss_name_t client;
     gss_OID doid;
     OM_uint32 maj_stat, min_stat;
     gss_buffer_desc    oid_name;
        char *mechStr;

     *context = GSS_C_NO_CONTEXT;

     do {
          if (recv_token(s, &recv_tok) < 0)
               return -1;

          if (verbose && log) {
              fprintf(log, "Received token (size=%d): \n", recv_tok.length);
              print_token(&recv_tok);
          }

          maj_stat =
               gss_accept_sec_context(&min_stat,
                                      context,
                                      server_creds,
                                      &recv_tok,
                                      GSS_C_NO_CHANNEL_BINDINGS,
                                      &client,
                                      &doid,
                                      &send_tok,
                                      ret_flags,
                                      NULL,     /* ignore time_rec */
                                      NULL);    /* ignore del_cred_handle */

          if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
               display_status("accepting context", maj_stat, min_stat);
               (void) gss_release_buffer(&min_stat, &recv_tok);
               return -1;
          }

          (void) gss_release_buffer(&min_stat, &recv_tok);

          if (send_tok.length != 0) {
              if (verbose && log) {
                  fprintf(log,
                          "Sending accept_sec_context token (size=%d):\n",
                          send_tok.length);
                  print_token(&send_tok);
              }
               if (send_token(s, &send_tok) < 0) {
                    fprintf(log, "failure sending token\n");
                    return -1;
               }

               (void) gss_release_buffer(&min_stat, &send_tok);
          }
          if (verbose && log) {
              if (maj_stat == GSS_S_CONTINUE_NEEDED)
                  fprintf(log, "continue needed...\n");
              else
                  fprintf(log, "\n");
              fflush(log);
          }
     } while (maj_stat == GSS_S_CONTINUE_NEEDED);

     /* display the flags */
     display_ctx_flags(*ret_flags);

     if (verbose && log) {
         maj_stat = gss_oid_to_str(&min_stat, doid, &oid_name);
         if (maj_stat != GSS_S_COMPLETE) {
             display_status("converting oid->string", maj_stat, min_stat);
             return -1;
         }
                mechStr = (char *)__gss_oid_to_mech(doid);
         fprintf(log, "Accepted connection using mechanism OID %.*s (%s).\n",
                 (int) oid_name.length, (char *) oid_name.value,
                        (mechStr == NULL ? "NULL" : mechStr));
         (void) gss_release_buffer(&min_stat, &oid_name);
     }

     maj_stat = gss_display_name(&min_stat, client, client_name, &doid);
     if (maj_stat != GSS_S_COMPLETE) {
          display_status("displaying name", maj_stat, min_stat);
          return -1;
     }
     return 0;
}

create_a_socket()

This is a no-frills function for creating a transport connection with the client.


Example A–14 create_a_socket()

/*
 * Function: create_socket
 *
 * Purpose: Opens a listening TCP socket.
 *
 * Arguments:
 *
 *      port            (r) the port number on which to listen
 *
 * Returns: the listening socket file descriptor, or -1 on failure
 *
 * Effects:
 *
 * A listening socket on the specified port and created and returned.
 * On error, an error message is displayed and -1 is returned.
 */
int create_socket(port)
     u_short port;
{
     struct sockaddr_in saddr;
     int s;
     int on = 1;

     saddr.sin_family = AF_INET;
     saddr.sin_port = htons(port);
     saddr.sin_addr.s_addr = INADDR_ANY;

     if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
          perror("creating socket");
          return -1;
     }
     /* Let the socket be reused right away */
     (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
     if (bind(s, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
{
          perror("binding socket");
          (void) close(s);
          return -1;
     }
     if (listen(s, 5) < 0) {
          perror("listening on socket");
          (void) close(s);
          return -1;
     }
     return s;
}

test_import_export_context()

Finally, this is a small function to show how gss_export_sec_context() and gss_import_sec_context() work. Of limited practicality, this function is here mostly to indicate how these GSS-API functions can be used.


Example A–15 test_import_export_context()

int test_import_export_context(context)
        gss_ctx_id_t *context;
{
        OM_uint32       min_stat, maj_stat;
        gss_buffer_desc context_token, copied_token;
        struct timeval tm1, tm2;

        /*
         * Attempt to save and then restore the context.
         */
        gettimeofday(&tm1, (struct timezone *)0);
        maj_stat = gss_export_sec_context(&min_stat, context, &context_token);
        if (maj_stat != GSS_S_COMPLETE) {
                display_status("exporting context", maj_stat, min_stat);
                return 1;
        }
        gettimeofday(&tm2, (struct timezone *)0);
        if (verbose && log)
                fprintf(log, "Exported context: %d bytes, %7.4f seconds\n",
                        context_token.length, timeval_subtract(&tm2, &tm1));
        copied_token.length = context_token.length;
        copied_token.value = malloc(context_token.length);
        if (copied_token.value == 0) {
            fprintf(log, "Couldn't allocate memory to copy context token.\n");
            return 1;
        }
        memcpy(copied_token.value, context_token.value, copied_token.length);
        maj_stat = gss_import_sec_context(&min_stat, &copied_token, context);
        if (maj_stat != GSS_S_COMPLETE) {
                display_status("importing context", maj_stat, min_stat);
                return 1;
        }
        gettimeofday(&tm1, (struct timezone *)0);
        if (verbose && log)
                fprintf(log, "Importing context: %7.4f seconds\n",
                        timeval_subtract(&tm1, &tm2));
        (void) gss_release_buffer(&min_stat, &context_token);
        return 0;
}

timeval_subtract()

This is a convenience function used by test_import_export_context().


Example A–16 timeval_subtract()

static float timeval_subtract(tv1, tv2)
        struct timeval *tv1, *tv2;
{
        return ((tv1->tv_sec - tv2->tv_sec) +
                ((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
}

Ancillary Functions

To make the client and server programs work as shown, a number of other functions are required. These are mostly for displaying values, and are not necessary to the basic functioning of the programs. They are shown here for completeness.

Two functions, however, are significant: send_token() and recv_token(), which do the actual transfer of context tokens and messages. They are actually plain “vanilla” functions that open up a file descriptor and read to or write from it. Although ordinary, and not directly related to the GSS-API, they are sufficiently important to call out separately.

Miscellaneous Support Functions

These functions include:


Example A–17

/*
 * Copyright 1994 by OpenVision Technologies, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appears in all copies and
 * that both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of OpenVision not be used
 * in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission. OpenVision makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#if !defined(lint) && !defined(__CODECENTER__)
static char *rcsid = "$Header: /afs/athena.mit.edu/astaff/project/krbdev/.cvsroot
/src/appl/gss-sample/gss-misc.c,v 1.15 1996/07/22 20:21:20 marc Exp $";
#endif

#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>

#include <gssapi/gssapi.h>
#include "gss-misc.h"
#include <stdlib.h>

FILE *display_file;
extern gss_OID g_mechOid;


static void display_status_1(char *m, OM_uint32 code, int type);

static int write_all(int fildes, char *buf, unsigned int nbyte)
{
     int ret;
     char *ptr;

     for (ptr = buf; nbyte; ptr += ret, nbyte -= ret) {
          ret = write(fildes, ptr, nbyte);
          if (ret < 0) {
               if (errno == EINTR)
                    continue;
               return(ret);
          } else if (ret == 0) {
               return(ptr-buf);
          }
     }

     return(ptr-buf);
}

static int read_all(int fildes, char *buf, unsigned int nbyte)
{
     int ret;
     char *ptr;

     for (ptr = buf; nbyte; ptr += ret, nbyte -= ret) {
          ret = read(fildes, ptr, nbyte);
          if (ret < 0) {
               if (errno == EINTR)
                    continue;
               return(ret);
          } else if (ret == 0) {
               return(ptr-buf);
          }
     }

     return(ptr-buf);
}

static void display_status_1(m, code, type)
     char *m;
     OM_uint32 code;
     int type;
{
     OM_uint32 maj_stat, min_stat;
     gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
     OM_uint32 msg_ctx;

     msg_ctx = 0;
     while (1) {
          maj_stat = gss_display_status(&min_stat, code,
                                       type, g_mechOid,
                                       &msg_ctx, &msg);
          if (maj_stat != GSS_S_COMPLETE) {
                  if (display_file) {
                        fprintf(display_file, "error in gss_display_status"
                                        " called from <%s>\n", m);
                  }
                  break;
          }
          else if (display_file)
                fprintf(display_file, "GSS-API error %s: %s\n", m,
                      (char *)msg.value);
          if (msg.length != 0)
                (void) gss_release_buffer(&min_stat, &msg);

          if (!msg_ctx)
               break;
     }
}

/*
 * Function: display_status
 *
 * Purpose: displays GSS-API messages
 *
 * Arguments:
 *
 *      msg             a string to be displayed with the message
 *      maj_stat        the GSS-API major status code
 *      min_stat        the GSS-API minor status code
 *
 * Effects:
 *
 * The GSS-API messages associated with maj_stat and min_stat are
 * displayed on stderr, each preceeded by "GSS-API error <msg>:
" and
 * followed by a newline.
 */
void display_status(msg, maj_stat, min_stat)
     char *msg;
     OM_uint32 maj_stat;
     OM_uint32 min_stat;
{
     display_status_1(msg, maj_stat, GSS_C_GSS_CODE);
     display_status_1(msg, min_stat, GSS_C_MECH_CODE);
}


/*
 * Function: display_ctx_flags
 *
 * Purpose: displays the flags returned by context initation in
 *          a human-readable form
 *
 * Arguments:
 *
 *      int             ret_flags
 *
 * Effects:
 *
 * Strings corresponding to the context flags are printed on
 * stdout, preceded by "context flag: " and followed by a newline
 */

void display_ctx_flags(flags)
     OM_uint32 flags;
{
     if (flags & GSS_C_DELEG_FLAG)
          fprintf(display_file, "context flag: GSS_C_DELEG_FLAG\n");
     if (flags & GSS_C_MUTUAL_FLAG)
          fprintf(display_file, "context flag: GSS_C_MUTUAL_FLAG\n");
     if (flags & GSS_C_REPLAY_FLAG)
          fprintf(display_file, "context flag: GSS_C_REPLAY_FLAG\n");
     if (flags & GSS_C_SEQUENCE_FLAG)
          fprintf(display_file, "context flag: GSS_C_SEQUENCE_FLAG\n");
     if (flags & GSS_C_CONF_FLAG )
          fprintf(display_file, "context flag: GSS_C_CONF_FLAG \n");
     if (flags & GSS_C_INTEG_FLAG )
          fprintf(display_file, "context flag: GSS_C_INTEG_FLAG \n");
}

void print_token(tok)
     gss_buffer_t tok;
{
    int i;
    unsigned char *p = tok->value;

    if (!display_file)
        return;
    for (i=0; i < tok->length; i++, p++) {
        fprintf(display_file, "%02x ", *p);
        if ((i % 16) == 15) {
            fprintf(display_file, "\n");
        }
    }
    fprintf(display_file, "\n");
    fflush(display_file);
}

send_token() and recv_token()

These functions send and receive data between the client and the server. (In a multiprocess application they could do the same between processes.) They are slightly misnamed, since they send and receive messages as well as tokens. They are oblivious to the content they handle.

send_token()

This function sends a token or message.


Example A–18 send_token()

/*
 * Function: send_token
 *
 * Purpose: Writes a token to a file descriptor.
 *
 * Arguments:
 *
 *      s               (r) an open file descriptor
 *      tok             (r) the token to write
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * send_token writes the token length (as a network long) and then the
 * token data to the file descriptor s.  It returns 0 on success, and
 * -1 if an error occurs or if it could not write all the data.
 */
int send_token(s, tok)
     int s;
     gss_buffer_t tok;
{
     int len, ret;

     len = htonl((OM_uint32)tok->length);
     ret = write_all(s, (char *) &len, sizeof(int));
     if (ret < 0) {
          perror("sending token length");
          return -1;
     } else if (ret != 4) {
         if (display_file)
             fprintf(display_file,
                     "sending token length: %d of %d bytes written\n",
                     ret, 4);
          return -1;
     }

     ret = write_all(s, tok->value, (OM_uint32)tok->length);
     if (ret < 0) {
          perror("sending token data");
          return -1;
     } else if (ret != tok->length) {
         if (display_file)
             fprintf(display_file,
                     "sending token data: %d of %d bytes written\n",
                     ret, tok->length);
         return -1;
     }

     return 0;
}

recv_token()

This function receives a token or message.


Example A–19 recv_token()

/*
 * Function: recv_token
 *
 * Purpose: Reads a token from a file descriptor.
 *
 * Arguments:
 *
 *      s               (r) an open file descriptor
 *      tok             (w) the read token
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * recv_token reads the token length (as a network long), allocates
 * memory to hold the data, and then reads the token data from the
 * file descriptor s.  It blocks to read the length and data, if
 * necessary.  On a successful return, the token should be freed with
 * gss_release_buffer.  It returns 0 on success, and -1 if an error
 * occurs or if it could not read all the data.
 */
int recv_token(s, tok)
     int s;
     gss_buffer_t tok;
{
     int ret, len;

     ret = read_all(s, (char *) &len, sizeof(int));
     if (ret < 0) {
          perror("reading token length");
          return -1;
     } else if (ret != 4) {
         if (display_file)
             fprintf(display_file,
                     "reading token length: %d of %d bytes read\n",
                     ret, 4);
         return -1;
     }

     tok->length = ntohl(len);
     tok->value = (char *) malloc(tok->length);
     if (tok->value == NULL) {
         if (display_file)
             fprintf(display_file,
                     "Out of memory allocating token data\n");
          return -1;
     }

     ret = read_all(s, (char *) tok->value, (OM_uint32)tok->length);
     if (ret < 0) {
          perror("reading token data");
          free(tok->value);
          return -1;
     } else if (ret != tok->length) {
          fprintf(stderr, "sending token data: %d of %d bytes written\n",
                  ret, tok->length);
          free(tok->value);
          return -1;
     }

     return 0;
}