Solaris 开发者安全性指南

附录 A 基于 C 的 GSS-API 样例程序

本附录给出了使用 GSS-API 建立安全网络连接的两个样例应用程序的源代码。第一个应用程序为典型客户机。第二个应用程序说明服务器如何在 GSS-API 中工作。在运行的过程中,这两个程序都会显示基准测试程序。因此,用户可以查看使用中的 GSS-API。此外,还提供了可供客户机和服务器应用程序使用的各种特定函数。

第 5 章,GSS-API 客户机示例第 6 章,GSS-API 服务器示例中详细检查了这些程序。

客户端应用程序

以下示例提供了客户端程序 gss_client 的源代码。


注 –

此示例的源代码也可以通过 Sun 下载中心获得。请访问 http://www.sun.com/download/products.xml?id=41912db5



示例 A–1 gss-client.c 样例程序的完整代码

/*

 * 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: /cvs/krbdev/krb5/src/appl/gss-sample/gss-client.c,\

v 1.16 1998/10/30 02:52:03 marc Exp $";

#endif



#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 <errno.h>

#include <sys/stat.h>

#include <fcntl.h>



#include <gssapi/gssapi.h>

#include <gssapi/gssapi_ext.h>

#include <gss-misc.h>



void usage()

{

     fprintf(stderr, "Usage: gss-client [-port port] [-d] host service \

msg\n");

     exit(1);

}



/*

 * 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 descriptor, 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;

}



/*

 * 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, init_sec_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(&init_sec_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 (token_ptr != GSS_C_NO_BUFFER)

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



          if (send_tok.length != 0) {

               printf("Sending init_sec_context token (size=%d)...",

                     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_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {

               display_status("initializing context", maj_stat,

                              init_sec_min_stat);

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

               if (*gss_context == GSS_C_NO_CONTEXT)

                       gss_delete_sec_context(&min_stat, gss_context,

                                              GSS_C_NO_BUFFER);

               return -1;

          }

          

          if (maj_stat == GSS_S_CONTINUE_NEEDED) {

               printf("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;

}



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;



    if (in_buf->length == 0) {

        in_buf->value = NULL;

        return;

    }



    if ((in_buf->value = malloc(in_buf->length)) == 0) {

        fprintf(stderr, \

            "Couldn't allocate %d byte buffer for reading file\n",

            in_buf->length);

        exit(1);

    }



    /* this code used to check for incomplete reads, but you can't get

       an incomplete read on any file for which fstat() is meaningful */



    count = read(fd, in_buf->value, in_buf->length);

    if (count < 0) {

        perror("read");

        exit(1);

    }

    if (count < in_buf->length)

        fprintf(stderr, "Warning, only read in %d bytes, expected %d\n",

                count, in_buf->length);

}



/*

 * 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(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;

     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;

     size_t     i;



     /* 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;

     }



     /* 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;

     }



     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 %d, 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;

     }

     fprintf(stderr, "Mechanism %.*s supports %d names\n",

             (int) oid_name.length, (char *) oid_name.value,

             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);

     }



     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("sealing 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;

}



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);

}



int main(argc, argv)

     int argc;

     char **argv;

{

     char *service_name, *server_host, *msg;

     char *mechanism = 0;

     u_short port = 4444;

     int use_file = 0;

     OM_uint32 deleg_flag = 0, min_stat;

     gss_OID oid = GSS_C_NULL_OID;

     

     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();



     server_host = *argv++;

     service_name = *argv++;

     msg = *argv++;



     if (mechanism)

         parse_oid(mechanism, &oid);



     if (call_server(server_host, port, oid, service_name,

                     deleg_flag, msg, use_file) < 0)

          exit(1);



     if (oid != GSS_C_NULL_OID)

         (void) gss_release_oid(&min_stat, &oid);

         

     return 0;

}

服务器端应用程序

以下示例提供了服务器端程序 gss_server 的源代码。


注 –

此示例的源代码也可以通过 Sun 下载中心获得。请访问 http://www.sun.com/download/products.xml?id=41912db5



示例 A–2 gss-server.c 样例程序的完整代码

/*

 * 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: /cvs/krbdev/krb5/src/appl/gss-sample/gss-server.c, \

    v 1.21 1998/12/22 \

04:10:08 tytso Exp $";

#endif



#include <stdio.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/time.h>

#include <netinet/in.h>

#include <unistd.h>

#include <stdlib.h>

#include <ctype.h>



#include <gssapi/gssapi.h>

#include <gssapi/gssapi_ext.h>

#include <gss-misc.h>



#include <string.h>



void usage()

{

     fprintf(stderr, "Usage: gss-server [-port port] [-verbose]\n");

     fprintf(stderr, "       [-inetd] [-logfile file] [service_name]\n");

     exit(1);

}



FILE *log;



int verbose = 0;



/*

 * 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 operation

 * fails, an error message is displayed and -1 is returned; otherwise,

 * 0 is returned.

 */

int server_acquire_creds(service_name, server_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_OID) GSS_C_NT_HOSTBASED_SERVICE, &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: establishes 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, acc_sec_min_stat;

     gss_buffer_desc    oid_name;



     *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(&acc_sec_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 (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 (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {

               display_status("accepting context", maj_stat,

                              acc_sec_min_stat);

               if (*context == GSS_C_NO_CONTEXT)

                       gss_delete_sec_context(&min_stat, context,

                                              GSS_C_NO_BUFFER);

               return -1;

          }



          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;

         }

         fprintf(log, "Accepted connection using mechanism OID %.*s.\n",

                 (int) oid_name.length, (char *) oid_name.value);

         (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;

     }

     maj_stat = gss_release_name(&min_stat, &client);

     if (maj_stat != GSS_S_COMPLETE) {

          display_status("releasing name", maj_stat, min_stat);

          return -1;

     }

     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 is 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;

}



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);

}



/*

 * Yes, yes, this isn't the best place for doing this test.

 * DO NOT REMOVE THIS UNTIL A BETTER TEST HAS BEEN WRITTEN, THOUGH.

 *                                      -TYT

 */

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;

        }

        free(copied_token.value);

        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;

}



/*

 * 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 sealed token.  The token is

 * unsealed and a signature block, produced with gss_sign, is returned

 * to the sender.  The context is then 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 sealed message token */

     if (recv_token(s, &xmit_buf) < 0)

        return(-1);

          

     if (verbose && log) {

        fprintf(log, "Sealed 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("unsealing 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]) || isspace(cp[0])) &&

         (isprint(cp[1]) || isspace(cp[1]))) {

        fprintf(log, "\"%.*s\"\n", msg_buf.length, msg_buf.value);

     } 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);

}



int

main(argc, argv)

     int argc;

     char **argv;

{

     char *service_name;

     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, "-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 (server_acquire_creds(service_name, &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)) >= 0) {

             do {

                 /* Accept a TCP connection */

                 if ((s = accept(stmp, NULL, 0)) < 0) {

                     perror("accepting connection");

                     continue;

                 }

                 /* this return value is not checked, because there's

                    not really anything to do if it fails */

                 sign_server(s, server_creds);

                 close(s);

             } while (!once);



             close(stmp);

         }

     }



     (void) gss_release_cred(&min_stat, &server_creds);



     /*NOTREACHED*/

     (void) close(s);

     return 0;

}

各种 GSS-API 样例函数

要使客户机和服务器程序按所示方式工作,还需要大量其他函数。这些函数用于显示值。此外不需要这些函数。以下是此类别的函数:

以下示例中给出了这些函数的代码。


注 –

此示例的源代码也可以通过 Sun 下载中心获得。请访问 http://www.sun.com/download/products.xml?id=41912db5



示例 A–3 各种 GSS-API 函数的代码

/*

 * 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: /cvs/krbdev/krb5/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>

#include <unistd.h>

#include <string.h>



#include <gssapi/gssapi.h>

#include <gssapi/gssapi_ext.h>

#include <gss-misc.h>



#include <stdlib.h>



FILE *display_file;



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);

}



/*

 * 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(tok->length);



     ret = write_all(s, (char *) &len, 4);

     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, 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;

}



/*

 * 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;



     ret = read_all(s, (char *) &tok->length, 4);

     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(tok->length);

     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, 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;

}



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;

     OM_uint32 msg_ctx;

     

     msg_ctx = 0;

     while (1) {

          maj_stat = gss_display_status(&min_stat, code,

                                       type, GSS_C_NULL_OID,

                                       &msg_ctx, &msg);

          if (display_file)

              fprintf(display_file, "GSS-API error %s: %s\n", m,

                      (char *)msg.value); 

          (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 preceded 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 initiation 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);

}