As submitted from Openvision --- a sample GSSAPI client/server application!
authorTheodore Tso <tytso@mit.edu>
Wed, 15 Jun 1994 04:52:28 +0000 (04:52 +0000)
committerTheodore Tso <tytso@mit.edu>
Wed, 15 Jun 1994 04:52:28 +0000 (04:52 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@3794 dc483132-0cff-0310-8789-dd5450dbe970

src/appl/gss-sample/Imakefile [new file with mode: 0644]
src/appl/gss-sample/Makefile.in [new file with mode: 0644]
src/appl/gss-sample/README [new file with mode: 0644]
src/appl/gss-sample/configure.in [new file with mode: 0644]
src/appl/gss-sample/gss-client.c [new file with mode: 0644]
src/appl/gss-sample/gss-misc.c [new file with mode: 0644]
src/appl/gss-sample/gss-server.c [new file with mode: 0644]

diff --git a/src/appl/gss-sample/Imakefile b/src/appl/gss-sample/Imakefile
new file mode 100644 (file)
index 0000000..ae71ec1
--- /dev/null
@@ -0,0 +1,39 @@
+#      $Source$
+#      $Author$
+#      $Id$
+#
+#  Copyright 1990,1991 by the Massachusetts Institute of Technology.
+#  All Rights Reserved.
+# 
+# Export of this software from the United States of America may
+#   require a specific license from the United States Government.
+#   It is the responsibility of any person or organization contemplating
+#   export to obtain such a license before exporting.
+# 
+# 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.  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.
+# 
+# 
+
+SRCS= gss-client.c gss-misc.c gss-server.c
+
+DEPLIBS= $(TOP)/lib/gssapi/libgssapi_krb5.a $(DEPKLIB) 
+LIBS= $(TOP)/lib/gssapi/libgssapi_krb5.a $(KLIB) 
+
+all:: gss-client gss-server
+
+NormalProgramTarget(gss-client,gss-client.o gss-misc.o,$(DEPLIBS),$(LIBS),)
+Krb5InstallClientProgram(gss-client)
+
+NormalProgramTarget(gss-server,gss-server.o gss-misc.o,$(DEPLIBS),$(LIBS),)
+Krb5InstallServerProgram(gss-server)
+
+DependTarget()
diff --git a/src/appl/gss-sample/Makefile.in b/src/appl/gss-sample/Makefile.in
new file mode 100644 (file)
index 0000000..732dc7c
--- /dev/null
@@ -0,0 +1,43 @@
+srcdir = @srcdir@
+VPATH = @srcdir@
+DEFS = @DEFS@
+LIBS = @LIBS@
+CFLAGS = -g $(DEFS) $(LOCALINCLUDE)
+LDFLAGS = -g
+
+BUILDTOP=../..
+SRCTOP = $(srcdir)/$(BUILDTOP)
+TOPLIBD = $(BUILDTOP)/lib
+ISODELIB=$(TOPLIBD)/libisode.a
+COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
+DBMLIB=
+
+all::
+
+KLIB = $(TOPLIBD)/gssapi/libgssapi_krb5.a $(TOPLIBD)/libkrb5.a \
+               $(TOPLIBD)/libcrypto.a $(ISODELIB) $(COMERRLIB) $(DBMLIB)
+
+gss-server: gss-server.o gss-misc.o
+       $(CC) $(CFLAGS) -o gss-server gss-server.o gss-misc.o $(KLIB) $(LIBS)
+
+gss-client: gss-client.o gss-misc.o
+       $(CC) $(CFLAGS) -o gss-client gss-client.o gss-misc.o $(KLIB) $(LIBS)
+
+gss-client.o:  $(srcdir)/gss-client.o
+
+gss-misc.o:    $(srcdir)/gss-misc.o
+
+gss-server.o:  $(srcdir)/gss-server.o
+
+
+all:: gss-server gss-client
+
+
+clean::
+       $(RM) kinit.o kinit
+
+install::
+       $(INSTALLPROG) kinit ${DESTDIR}$(CLIENT_BINDIR)/kinit
+       $(INSTALLPROG) kinit.M ${DESTDIR}$(CLIENT_MANDIR)/kinit.$(CLIENT_MANSUFFIX)
diff --git a/src/appl/gss-sample/README b/src/appl/gss-sample/README
new file mode 100644 (file)
index 0000000..5afa0ed
--- /dev/null
@@ -0,0 +1,83 @@
+# Copyright 1993 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.
+
+This directory contains a sample GSS-API client and server
+application.  Each invocation of the client performs the following
+exchange with the server:
+
+       1.  The client and server establish a GSS-API context.  The
+       server prints the identity of the client.
+
+       2.  The client sends a sealed (encrypted) message to the
+       server.
+
+       3.  The server decrypts the message and prints it.
+
+       4.  The server produces a signature block for the message and
+       sends it to the client.
+
+       5.  The client verifies the signature block.
+
+Obviously, this exchange does not perform a tremendously valuable
+function; however, it demostrates the use of primary GSS-API
+interfaces.
+
+The server's command line usage is
+
+       gss-server [-port port] service_name
+
+where service_name is a GSS-API service name of the form
+"service@host".  The server will accept TCP connections on port
+(default 4444) and establish contexts as service_name.
+
+The client's command line usage is
+
+       gss-client [-port port] host service_name msg
+
+where host is the host running the server, service_name is the service
+name that the server will establish connections as, and msg is the
+message.  The client connects to the TCP on <host:port> (default 4444)
+and performs the exchange.
+
+If you are using this sample application with OpenVision's Kerberos 5
+GSS-API mechanism:
+
+1.  Link the client and server with -lgssapi_krb5 -lkrb5 -lcrypto
+-lisode -lcom_err.
+
+2.  Make sure that the principal corresponding to service_name is in
+the default keytab on the server host, and that the gss-server process
+can read the keytab.  For example, the service name "host@server"
+corresponds to the Kerberos principal "host/server.domain.com@REALM".
+
+This sample application uses the following GSS-API functions:
+
+       gss_accept_sec_context          gss_release_buffer
+       gss_acquire_cred                gss_release_cred
+       gss_delete_sec_context          gss_release_name
+       gss_display_name                gss_seal
+       gss_display_status              gss_sign
+       gss_import_name                 gss_unseal
+       gss_init_sec_context            gss_verify
+
+Barry Jaspan, bjaspan@security.ov.com
+OpenVision Technologies, Inc.
+
+$Id$
diff --git a/src/appl/gss-sample/configure.in b/src/appl/gss-sample/configure.in
new file mode 100644 (file)
index 0000000..959036a
--- /dev/null
@@ -0,0 +1,7 @@
+AC_INIT(klist.c)
+AC_HAVE_LIBRARY(socket)
+AC_HAVE_LIBRARY(nsl)
+CONFIG_RULES
+KRB_INCLUDE
+ISODE_INCLUDE
+AC_OUTPUT(Makefile,[EXTRA_RULES])
diff --git a/src/appl/gss-sample/gss-client.c b/src/appl/gss-sample/gss-client.c
new file mode 100644 (file)
index 0000000..264f451
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * 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$";
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+
+int establish_context(int s, char *service_name, gss_ctx_id_t *gss_context);
+int connect_to_server(char *host, u_short port);
+int call_server(char *host, u_short port, char *service_name, char *msg);
+
+int send_token(int s, gss_buffer_t tok);
+int recv_token(int s, gss_buffer_t tok);
+void display_status(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat);
+
+usage()
+{
+     fprintf(stderr, "Usage: gss-client [-port port] host service msg\n");
+     exit(1);
+}
+
+main(int argc, char **argv)
+{
+     char *service_name, *server_host, *msg;
+     u_short port = 4444;
+     
+     /* Parse arguments. */
+     argc--; argv++;
+     while (argc) {
+         if (strcmp(*argv, "-port") == 0) {
+              argc--; argv++;
+              if (!argc) usage();
+              port = atoi(*argv);
+         } else 
+              break;
+         argc--; argv++;
+     }
+     if (argc != 3)
+         usage();
+
+     server_host = *argv++;
+     service_name = *argv++;
+     msg = *argv++;
+
+     if (call_server(server_host, port, service_name, msg) < 0)
+         exit(1);
+
+     return 0;
+}
+
+/*
+ * 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
+ * seals msg in a GSS-API token with gss_seal, 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(char *host, u_short port, char *service_name, char *msg)
+{
+     gss_ctx_id_t context;
+     gss_buffer_desc in_buf, out_buf;
+     int s, state;
+     OM_uint32 maj_stat, min_stat;
+
+     /* Open connection */
+     if ((s = connect_to_server(host, port)) < 0)
+         return -1;
+
+     /* Establish context */
+     if (client_establish_context(s, service_name, &context) < 0)
+         return -1;
+
+     /* Seal the message */
+     in_buf.value = msg;
+     in_buf.length = strlen(msg) + 1;
+     maj_stat = gss_seal(&min_stat, context, 1, GSS_C_QOP_DEFAULT,
+                        &in_buf, &state, &out_buf);
+     if (maj_stat != GSS_S_COMPLETE) {
+         display_status("sealing message", maj_stat, min_stat);
+         return -1;
+     } else if (! state) {
+         fprintf(stderr, "Warning!  Message not encrypted.\n");
+     }
+
+     /* Send to server */
+     if (send_token(s, &out_buf) < 0)
+         return -1;
+     (void) gss_release_buffer(&min_stat, &out_buf);
+
+     /* Read signature block into out_buf */
+     if (recv_token(s, &out_buf) < 0)
+         return -1;
+
+     /* Verify signature block */
+     maj_stat = gss_verify(&min_stat, context, &in_buf, &out_buf, &state);
+     if (maj_stat != GSS_S_COMPLETE) {
+         display_status("verifying signature", maj_stat, min_stat);
+         return -1;
+     }
+     (void) gss_release_buffer(&min_stat, &out_buf);
+
+     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);
+         return -1;
+     }
+     (void) gss_release_buffer(&min_stat, &out_buf);
+     
+     return 0;
+}
+
+/*
+ * 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(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, hp->h_length);
+     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");
+         return -1;
+     }
+     return s;
+}
+
+/*
+ * 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
+ *
+ * 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(int s, char *service_name,
+                            gss_ctx_id_t *gss_context) 
+{
+     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_nt_service_name, &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,
+                                   GSS_C_NULL_OID,
+                                   GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
+                                   0,
+                                   NULL,       /* no channel bindings */
+                                   token_ptr,
+                                   NULL,       /* ignore mech type */
+                                   &send_tok,
+                                   NULL,       /* ignore ret_flags */
+                                   NULL);      /* ignore time_rec */
+
+         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) {
+              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) {
+              if (recv_token(s, &recv_tok) < 0) {
+                   (void) gss_release_name(&min_stat, &target_name);
+                   return -1;
+              }
+              token_ptr = &recv_tok;
+         }
+     } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+     (void) gss_release_name(&min_stat, &target_name);
+     return 0;
+}
diff --git a/src/appl/gss-sample/gss-misc.c b/src/appl/gss-sample/gss-misc.c
new file mode 100644 (file)
index 0000000..24705ab
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * 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$";
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+
+static void display_status_1(char *m, OM_uint32 code, int type);
+
+/*
+ * 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(int s, gss_buffer_t tok)
+{
+     int len, ret;
+
+     len = htonl(tok->length);
+
+     ret = write(s, (char *) &len, 4);
+     if (ret < 0) {
+         perror("sending token length");
+         return -1;
+     } else if (ret != 4) {
+         fprintf(stderr, "sending token length: %d of %d bytes written\n", 
+                 ret, 4);
+         return -1;
+     }
+
+     ret = write(s, tok->value, tok->length);
+     if (ret < 0) {
+         perror("sending token data");
+         return -1;
+     } else if (ret != tok->length) {
+         fprintf(stderr, "sending token data: %d of %d bytes written\n", 
+                 ret, tok->length);
+         return -1;
+     }
+     
+     return 0;
+}
+
+/*
+ * 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(int s, gss_buffer_t tok)
+{
+     int ret;
+
+     ret = read(s, (char *) &tok->length, 4);
+     if (ret < 0) {
+         perror("reading token length");
+         return -1;
+     } else if (ret != 4) {
+         fprintf(stderr, "reading token length: %d of %d bytes read\n", 
+                 ret, 4);
+         return -1;
+     }
+         
+     tok->length = ntohl(tok->length);
+     tok->value = (char *) malloc(tok->length);
+     if (tok->value == NULL) {
+         fprintf(stderr, "Out of memory allocating token data\n");
+         return -1;
+     }
+
+     ret = read(s, (char *) tok->value, 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;
+}
+
+/*
+ * 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(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);
+}
+
+static void display_status_1(char *m, OM_uint32 code, int type)
+{
+     OM_uint32 maj_stat, min_stat;
+     gss_buffer_desc msg;
+     int msg_ctx;
+     
+     msg_ctx = 0;
+     while (1) {
+         maj_stat = gss_display_status(&min_stat, code,
+                                      type, GSS_C_NULL_OID,
+                                      &msg_ctx, &msg);
+         fprintf(stderr, "GSS-API error %s: %s\n", m,
+                 (char *)msg.value); 
+         (void) gss_release_buffer(&min_stat, &msg);
+         
+         if (!msg_ctx)
+              break;
+     }
+}
diff --git a/src/appl/gss-sample/gss-server.c b/src/appl/gss-sample/gss-server.c
new file mode 100644 (file)
index 0000000..523b2bd
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * 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$";
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+
+int create_socket(u_short port);
+
+int send_token(int s, gss_buffer_t tok);
+int recv_token(int s, gss_buffer_t tok);
+void display_status(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat);
+
+usage()
+{
+     fprintf(stderr, "Usage: gss-server [-port port] service_name\n");
+     exit(1);
+}
+
+main(int argc, char **argv)
+{
+     struct sockaddr_in saddr;
+     char *service_name;
+     u_short port = 4444;
+     int s;
+
+     argc--; argv++;
+     while (argc) {
+         if (strcmp(*argv, "-port") == 0) {
+              argc--; argv++;
+              if (!argc) usage();
+              port = atoi(*argv);
+         } else
+              break;
+         argc--; argv++;
+     }
+     if (argc != 1)
+         usage();
+
+     service_name = *argv;
+
+     if ((s = create_socket(port)) < 0)
+         exit(1);
+
+     if (sign_server(s, service_name) < 0)
+         exit(1);
+     
+     /*NOTREACHED*/
+     return 0;
+}
+
+/*
+ * 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(u_short port)
+{
+     struct sockaddr_in saddr;
+     int s;
+     
+     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;
+     }
+     if (bind(s, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
+         perror("binding socket");
+         return -1;
+     }
+     if (listen(s, 5) < 0) {
+         perror("listening on socket");
+         return -1;
+     }
+     return s;
+}
+
+/*
+ * Function: sign_server
+ *
+ * Purpose: Performs the "sign" service.
+ *
+ * Arguments:
+ *
+ *     s               (r) a TCP socket on which to listen for connections
+ *     service_name    (r) the ASCII name of the GSS-API service to
+ *                     establish a context as
+ *
+ * Returns: -1 on error
+ *
+ * Effects:
+ *
+ * sign_server acquires GSS-API credentials for service_name and then
+ * loops forever accepting TCP connections on s, establishing a
+ * context, and performing a single sign request.
+ *
+ * A sign request is a single GSS-API sealed token.  The token is
+ * unsealed and a signature block, produced with gss_sign, is returned
+ * to the sender.  The context is the destroyed and the connection
+ * closed.
+ *
+ * If any error occurs, -1 is returned.
+ */
+int sign_server(int s, char *service_name)
+{
+     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 s2;
+     
+     if (server_acquire_creds(service_name, &server_creds) < 0)
+         return -1;
+     
+     while (1) {
+         /* Accept a TCP connection */
+         if ((s2 = accept(s, NULL, 0)) < 0) {
+              perror("accepting connection");
+              exit(1);
+         }
+
+         /* Establish a context with the client */
+         if (server_establish_context(s2, server_creds, &context,
+                                      &client_name) < 0)
+              break;
+         
+         printf("Accepted connection: \"%s\"\n", client_name.value);
+         (void) gss_release_buffer(&min_stat, &client_name);
+
+         /* Receive the sealed message token */
+         if (recv_token(s2, &xmit_buf) < 0)
+              break;
+
+         /* Unseal the message token */
+         maj_stat = gss_unseal(&min_stat, context, &xmit_buf,
+                               &msg_buf, NULL, NULL);
+         if (maj_stat != GSS_S_COMPLETE) {
+              display_status("unsealing message", maj_stat, min_stat);
+              break;
+         }
+
+         (void) gss_release_buffer(&min_stat, &xmit_buf);
+
+         printf("Received message: \"%s\"\n", msg_buf.value);
+
+         /* Produce a signature block for the message */
+         maj_stat = gss_sign(&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);
+              break;
+         }
+
+         (void) gss_release_buffer(&min_stat, &msg_buf);
+
+         /* Send the signature block to the client */
+         if (send_token(s2, &xmit_buf) < 0)
+              break;
+
+         (void) gss_release_buffer(&min_stat, &xmit_buf);
+
+         /* Delete context */
+         maj_stat = gss_delete_sec_context(&min_stat, &context, &xmit_buf);
+         if (maj_stat != GSS_S_COMPLETE) {
+              display_status("deleting context", maj_stat, min_stat);
+              break;
+         }
+
+         (void) gss_release_buffer(&min_stat, &xmit_buf);
+
+         /* Close TCP connection */
+         close(s2);
+     }
+
+     /*NOTREACHED*/
+     (void) gss_release_cred(&min_stat, &server_creds);
+     return -1;
+}
+
+/*
+ * Function: server_acquire_creds
+ *
+ * Purpose: imports a service name and acquires credentials for it
+ *
+ * Arguments:
+ *
+ *     service_name    (r) the ASCII service name
+ *     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 opertion
+ * fails, an error message is displayed and -1 is returned; otherwise,
+ * 0 is returned.
+ */
+int server_acquire_creds(char *service_name, gss_cred_id_t *server_creds)
+{
+     gss_buffer_desc name_buf;
+     gss_name_t server_name;
+     OM_uint32 maj_stat, min_stat;
+
+     name_buf.value = service_name;
+     name_buf.length = strlen(name_buf.value) + 1;
+     maj_stat = gss_import_name(&min_stat, &name_buf, gss_nt_service_name,
+                               &server_name);
+     if (maj_stat != GSS_S_COMPLETE) {
+         display_status("importing name", maj_stat, min_stat);
+         return -1;
+     }
+
+     maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
+                                GSS_C_NULL_OID_SET, 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;
+}
+
+/*
+ * 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(int s, gss_cred_id_t server_creds,
+                            gss_ctx_id_t *context, gss_buffer_t
+                            client_name)
+{
+     gss_buffer_desc send_tok, recv_tok;
+     gss_name_t client;
+     gss_OID doid;
+     OM_uint32 maj_stat, min_stat, ret_flags;
+
+     *context = GSS_C_NO_CONTEXT;
+     
+     do {
+         if (recv_token(s, &recv_tok) < 0)
+              return -1;
+
+         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 */
+
+         (void) gss_release_buffer(&min_stat, &recv_tok);
+         
+         if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
+              display_status("accepting context", maj_stat, min_stat);
+              return -1;
+         }
+
+         if (send_tok.length != 0) {
+              if (send_token(s, &send_tok) < 0) {
+                   fprintf(stderr, "failure sending token\n");
+                   return -1;
+              }
+
+              (void) gss_release_buffer(&min_stat, &send_tok);
+         }
+     } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+     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;
+}
+
+         
+