This appendix shows the source code for two sample applications that use GSS-API to make a safe network connection. The first application is a typical client. The second application demonstrates how a server works in GSS-API. The two programs display benchmarks in the course of being run. A user can thus view GSS-API in action. Additionally, certain miscellaneous functions are provided for use by the client and server applications.
These programs are examined in detail in the Chapter 5, GSS-API Client Example and Chapter 6, GSS-API Server Example.
The source code for the client-side program, gss_client, is provided in the following example.
The source code for this example is also available through the Sun download center. See http://www.sun.com/download/products.xml?id=41912db5.
/*
* 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;
}
The source code for the server-side program, gss_server, is provided in the following example.
The source code for this example is also available through the Sun download center. See http://www.sun.com/download/products.xml?id=41912db5.
/*
* 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;
}
To make the client and server programs work as shown, a number of other functions are required. These functions are used to display values. The functions are not otherwise needed. The functions in this category are as follows:
send_token() – Transfers tokens and messages to a recipient
recv_token() – Accepts tokens and messages from a sender
display_status() – Shows the status returned by the last GSS-API function called
write_all() – Writes a buffer to a file
read_all() – Reads a file into a buffer
display_ctx_flags() – Shows in a readable form information about the current context, such as whether confidentiality or mutual authentication is allowed
print_token() – Prints out a token's value
The code for these functions is shown in the following example.
The source code for this example is also available through the Sun download center. See http://www.sun.com/download/products.xml?id=41912db5.
/*
* 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);
}