#include #include #include #include #include #include #include #include #include "gss_common.h" #if __INITIAL_POINTER_SIZE == 64 #pragma __required_pointer_size __save #pragma __required_pointer_size 32 #endif typedef char ** ArgArray; #if __INITIAL_POINTER_SIZE == 64 #pragma __required_pointer_size __restore #endif /* ** Option Data Structure */ struct _OptData { char *prog; unsigned short port; char *serv; char *host; char *msg; }; /* ** Context Data Structure */ struct _CtxData { gss_ctx_id_t context; }; /* ** Local Routine Prototypes */ void parse_command_line ( int, ArgArray, struct _OptData *); void usage ( char *); int connect_to_server ( char *, unsigned short); int initialize_client_context ( struct _OptData *, struct _CtxData *, int); int dialog_with_server ( struct _OptData *, struct _CtxData *, int); int terminate_client_context ( struct _OptData *, struct _CtxData *, int); /* ** ** main - Main processing routine for the GSS client ** ** Functional Description: ** ** This routine controls the GSS client processing. ** ** Usage: ** ** main argc, argv, envp ** ** Formal parameters: ** ** argc - (IN) argument count ** argv - (IN) address of an argument array ** envp - (IN) address of an environment string ** ** Implicit Parameters: ** ** None ** ** Routine Value: ** ** FAILURE - Completed with errors ** SUCCESS - Completed successfully ** ** Side Effects: ** ** None ** */ main ( int argc, ArgArray argv, char *envp[] ) { struct _OptData OptData = {NULL, 0, NULL, NULL}; struct _CtxData CtxData = {GSS_C_NO_CONTEXT}; int status = SUCCESS, sock = 0; /* ** Process the command line options */ parse_command_line (argc, argv, &OptData); /* ** Connect to the requested server */ sock = connect_to_server (OptData.host, OptData.port); if (sock < 0) status = FAILURE; /* ** Initialize the client context */ if (status == SUCCESS) status = initialize_client_context (&OptData, &CtxData, sock); /* ** Dialog with the server */ if (status == SUCCESS) status = dialog_with_server (&OptData, &CtxData, sock); /* ** Terminate the client context */ terminate_client_context (&OptData, &CtxData, sock); /* ** Exit */ return (status); } /* ** ** parse_command_line - Parse the command line options ** ** Functional Description: ** ** This routine parses the command line options. ** ** Usage: ** ** parse_command_line argc, argv, OptData ** ** Formal parameters: ** ** argc - (IN) argument count ** argv - (IN) address of an argument array ** OptData - (OUT) address of command option data structure ** which will contain the parsed input. ** ** Implicit Parameters: ** ** None ** ** Routine Value: ** ** None ** ** Side Effects: ** ** None ** */ void parse_command_line ( int argc, ArgArray argv, struct _OptData *OptData ) { char host_name[255 + 1]; struct servent *sp; int option, status; /* ** Parse the program name from the first argument */ parse_prog_name (argv[0], &OptData->prog); /* ** Process the command line options */ while ((option = getopt (argc, argv, "p:?")) != EOF) { switch (option) { /* ** Port Number ? */ case 'p': OptData->port = atoi (optarg); break; /* ** Invalid argument ? */ case '?': default: usage (OptData->prog); exit (1); break; } } /* ** Are there extra parameters on the command line ? */ if (argc - optind > 3) { usage (OptData->prog); exit (1); } /* ** Process the Message Text */ if (argc - optind > 0) OptData->msg = argv[optind + 0]; else OptData->msg = ""; /* ** Process the Host Name */ if (argc - optind > 1) OptData->host = argv[optind + 1]; else { status = gethostname (host_name, sizeof (host_name) - 1); if (status != 0) { perror ("gethostname"); exit (1); } OptData->host = malloc (strlen (host_name) + 1); strcpy (OptData->host, host_name); } /* ** Process the Service Name */ if (argc - optind > 2) OptData->serv = argv[optind + 2]; else OptData->serv = GSS_SERVICE; /* ** If no port was specified at the command line, then let's determine ** an appropriate default. */ if (! OptData->port) { sp = getservbyname (OptData->serv, "tcp"); if (sp) OptData->port = ntohs (sp->s_port); else OptData->port = GSS_PORT; } } /* ** ** usage - Display the acceptable unix style command usage ** ** Functional Description: ** ** This routine displays to standard output the appropriate unix style ** command usage. ** ** Usage: ** ** usage prog_name ** ** Formal parameters: ** ** prog_name - (IN) address of the program name string ** ** Implicit Parameters: ** ** None ** ** Routine Value: ** ** None ** ** Side Effects: ** ** None ** */ void usage ( char *prog_name ) { fprintf (stderr, "Usage: %s [-p port] [message] [host] [service]\n", prog_name); } /* ** ** connect_to_server - Connect to a given server ** ** Functional Description: ** ** This routine makes the appropriate TCP/IP socket calls to connect to ** the specified host name on the given port. ** ** Usage: ** ** connect_to_server host, port ** ** Formal parameters: ** ** host - (IN) address of host name string ** port - (IN) port number ** ** Implicit Parameters: ** ** None ** ** Routine Value: ** ** -1 - socket creation failure has occured ** n - The established socket number ** ** Side Effects: ** ** None ** */ int connect_to_server ( char *host, unsigned short port ) { struct sockaddr_in SockBuf; struct hostent *hp; int sock; /* ** Get the host entry by name */ if ((hp = gethostbyname (host)) == NULL) { perror ("gethostbyname"); return (-1); } /* ** Setup the socket information */ SockBuf.sin_family = hp->h_addrtype; memcpy ((char *) &SockBuf.sin_addr, hp->h_addr, sizeof (SockBuf.sin_addr)); SockBuf.sin_port = htons (port); /* ** Create a socket */ if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0) { perror ("socket"); return (-1); } /* ** Connect the socket */ if (connect (sock, (struct sockaddr *) &SockBuf, sizeof (SockBuf)) < 0) { perror ("connect"); return (-1); } /* ** Return the socket */ return (sock); } /* ** ** initialize_client_context - Initialize the GSS client context ** ** Functional Description: ** ** This routine initializes the necessary client context information. ** ** Usage: ** ** initialize_client_context OptData, CtxData, sock ** ** Formal parameters: ** ** OptData - (IN) address of the option data structure ** CtxData - (OUT) address of the context data structure ** sock - (IN) socket number ** ** Implicit Parameters: ** ** None ** ** Routine Value: ** ** SUCCESS - client context initialization succeeded ** FAILURE - client context initialization failed ** ** Side Effects: ** ** None ** */ int initialize_client_context ( struct _OptData *OptData, struct _CtxData *CtxData, int sock ) { gss_buffer_desc *data_ptr = GSS_C_NO_BUFFER; gss_buffer_desc send_data, recv_data, name_data; unsigned int minor_status; gss_name_t target_name; OM_uint32 ret_flags; int status; /* ** Import the service name into target_name. */ name_data.value = OptData->serv; name_data.length = strlen (OptData->serv) + 1; status = gss_import_name (&minor_status, &name_data, GSS_C_NT_HOSTBASED_SERVICE, &target_name); if (status != GSS_S_COMPLETE) { put_message (OptData->prog, status, minor_status, "{gss_import_name}"); return (FAILURE); } /* ** Perform the context-establishement loop. ** ** On each pass through the loop, data_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_data which is then ** transmitted to the server; every received token is stored in ** recv_data, which data_ptr is then set to, to be processed by ** the next call to gss_init_sec_context. ** ** GSS-API guarantees that send_data'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. */ do { /* ** Initialize the security context */ status = gss_init_sec_context (&minor_status, GSS_C_NO_CREDENTIAL, &CtxData->context, target_name, GSS_C_NULL_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_DELEG_FLAG, 0, NULL, data_ptr, NULL, &send_data, &ret_flags, NULL); if (data_ptr != GSS_C_NO_BUFFER) gss_release_buffer (&minor_status, &recv_data); if (status != GSS_S_COMPLETE && status != GSS_S_CONTINUE_NEEDED) { put_message (OptData->prog, status, minor_status, "{gss_init_sec_context}"); gss_release_name (&minor_status, &target_name); return (FAILURE); } if (send_data.length != 0) { /* ** Write the send data length */ if (net_write (sock, (char *) &send_data.length, sizeof (send_data.length)) < 0) { put_message (OptData->prog, 0, 0, "an error has occurred writing authorization length"); gss_release_name (&minor_status, &target_name); gss_release_buffer (&minor_status, &send_data); return (FAILURE); } /* ** Write the send data */ if (net_write (sock, send_data.value, send_data.length) < 0) { put_message (OptData->prog, 0, 0, "an error has occurred writing authorization data"); gss_release_name (&minor_status, &target_name); gss_release_buffer (&minor_status, &send_data); return (FAILURE); } } /* ** Release the send data */ gss_release_buffer (&minor_status, &send_data); /* ** Is further dialog with the server required ? */ if (status == GSS_S_CONTINUE_NEEDED) { /* ** Read the receive data length */ if (net_read (sock, (char *) &recv_data.length, sizeof (recv_data.length)) < 0) { put_message (OptData->prog, 0, 0, "an error has occurred reading authorization length"); gss_release_name (&minor_status, &target_name); gss_release_buffer (&minor_status, &recv_data); return (FAILURE); } /* ** Allocate a buffer to hold the receive data */ recv_data.value = malloc (recv_data.length); if (! recv_data.value) { put_message (OptData->prog, 0, 0, "unable to allocate authorization buffer"); gss_release_name (&minor_status, &target_name); gss_release_buffer (&minor_status, &recv_data); return (FAILURE); } /* ** Read the receive data */ if (net_read (sock, recv_data.value, recv_data.length) < 0) { put_message (OptData->prog, 0, 0, "an error has occurred reading authorization data"); gss_release_name (&minor_status, &target_name); gss_release_buffer (&minor_status, &recv_data); return (FAILURE); } /* ** Assign a pointer to the receive data. The receive data will be released ** after the initialize security context. */ data_ptr = &recv_data; } } while (status == GSS_S_CONTINUE_NEEDED); /* ** Release the target name */ gss_release_name (&minor_status, &target_name); /* ** Return success */ return (SUCCESS); } /* ** ** dialog_with_server - Dialog with the given server ** ** Functional Description: ** ** This routine sends an encrypted message to the server and then ** descripts the server's encrypted reply. ** ** Usage: ** ** dialog_with_server OptData, CtxData, sock ** ** Formal parameters: ** ** OptData - (IN) address of the option data structure ** CtxData - (IN) address of the context data structure ** sock - (IN) socket number ** ** Implicit Parameters: ** ** None ** ** Routine Value: ** ** SUCCESS - dialog with server succeeded ** FAILURE - dialog with server failed ** ** Side Effects: ** ** None ** */ int dialog_with_server ( struct _OptData *OptData, struct _CtxData *CtxData, int sock ) { unsigned int minor_status; gss_buffer_desc recv_data; gss_buffer_desc send_data; gss_buffer_desc msg_data; gss_qop_t qop_state; int status, state; /* ** Initialize the message data descriptor */ msg_data.value = OptData->msg; msg_data.length = strlen (OptData->msg); /* ** Initialize the state */ state = 0; /* ** Encrypt the send data */ status = gss_wrap (&minor_status, CtxData->context, 1, GSS_C_QOP_DEFAULT, &msg_data, &state, &send_data); if (status != GSS_S_COMPLETE) { put_message (OptData->prog, status, minor_status, "{gss_wrap}"); return (FAILURE); } else if (! state) put_message (OptData->prog, 0, 0, "{gss_wrap} Message was not encrypted."); /* ** Write the send data length */ if (net_write (sock, (char *) &send_data.length, sizeof (send_data.length)) < 0) { put_message (OptData->prog, 0, 0, "an error has occurred writing message length"); gss_release_buffer (&minor_status, &send_data); return (FAILURE); } /* ** Write the send data */ if (net_write (sock, send_data.value, send_data.length) < 0) { put_message (OptData->prog, 0, 0, "an error has occurred writing message data"); gss_release_buffer (&minor_status, &send_data); return (FAILURE); } /* ** Log the sent message */ printf ("%s: sent message '%-*.*s'\n", OptData->prog, msg_data.length, msg_data.length, (char *) msg_data.value); /* ** Release the send data */ gss_release_buffer (&minor_status, &send_data); /* ** Read the receive data length */ if (net_read (sock, (char *) &recv_data.length, sizeof (recv_data.length)) < 0) { put_message (OptData->prog, 0, 0, "an error has occurred reading message length"); return (FAILURE); } /* ** Allocate a buffer to hold the receive data */ recv_data.value = malloc (recv_data.length); if (! recv_data.value) { put_message (OptData->prog, 0, 0, "unable to allocate message buffer"); return (FAILURE); } /* ** Read the receive data */ if (net_read (sock, recv_data.value, recv_data.length) < 0) { put_message (OptData->prog, 0, 0, "an error has occurred reading message data"); gss_release_buffer (&minor_status, &recv_data); return (FAILURE); } /* ** Re-initialize the state */ state = 0; /* ** Decrypt the receive data */ status = gss_unwrap (&minor_status, CtxData->context, &recv_data, &msg_data, &state, (gss_qop_t *) NULL); if (status != GSS_S_COMPLETE) { put_message (OptData->prog, status, minor_status, "{gss_unwrap}"); gss_release_buffer (&minor_status, &recv_data); return (FAILURE); } else if (! state) put_message (OptData->prog, 0, 0, "{gss_unwrap} Message was not encrypted."); /* ** Release the receive data */ gss_release_buffer (&minor_status, &recv_data); /* ** Log the received message */ printf ("%s: received message '%-*.*s'\n", OptData->prog, msg_data.length, msg_data.length, (char *) msg_data.value); /* ** Release the message data */ gss_release_buffer (&minor_status, &msg_data); /* ** Return success */ return (SUCCESS); } /* ** ** terminate_client_context - Connect to a given server ** ** Functional Description: ** ** This routine makes the appropriate TCP/IP socket calls to connect to ** the specified host name on the given port. ** ** Usage: ** ** terminate_client_context OptData, CtxData, sock ** ** Formal parameters: ** ** OptData - (IN) address of the option data structure ** CtxData - (IN) address of the context data structure ** sock - (IN) socket number ** ** Implicit Parameters: ** ** None ** ** Routine Value: ** ** SUCCESS - terminate client context succeeded ** FAILURE - terminate client context failed ** ** Side Effects: ** ** None ** */ int terminate_client_context ( struct _OptData *OptData, struct _CtxData *CtxData, int sock ) { unsigned int minor_status; int status; /* ** Free the host name */ if (OptData->host) free (OptData->host); /* ** Delete context */ if (CtxData->context != GSS_C_NO_CONTEXT) { status = gss_delete_sec_context (&minor_status, &CtxData->context, GSS_C_NO_BUFFER); if (status != GSS_S_COMPLETE) put_message (OptData->prog, status, minor_status, "{gss_delete_sec_context}"); CtxData->context = GSS_C_NO_CONTEXT; } /* ** Shutdown and close the socket */ if (sock > 0) { shutdown (sock, 2); close (sock); } /* ** Return success */ return (SUCCESS); }