#include #include #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 void (*ctrl_c_func) (); static jmp_buf ctrl_c_jump; /* ** Option Data Structure */ struct _OptData { char *prog; unsigned short port; char *serv; FILE *log; }; /* ** Context Data Structure */ struct _CtxData { gss_ctx_id_t context; gss_cred_id_t creds; }; /* ** Local Routine Prototypes */ void parse_command_line ( int, ArgArray, struct _OptData *); void usage ( char *); int setup_network_connection ( int); int initialize_server_context ( struct _OptData *, struct _CtxData *); int dialog_with_client ( struct _OptData *, struct _CtxData *, int); int accept_client_request ( struct _OptData *, int, gss_ctx_id_t, gss_cred_id_t); int terminate_server_context ( struct _OptData *, struct _CtxData *, int); void ctrl_c_rtn ( int); /* ** ** main - Main processing routine for the Kerberos server ** ** Functional Description: ** ** This routine controls the Kerberos server 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: ** ** ctrl_c_jump - (OUT) The Ctrl-C exception address ** ctrl_c_func - (OUT) The Ctrl-C exception handle ** ** 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, GSS_C_NO_CREDENTIAL}; int status = SUCCESS, sock = 0; /* ** Print a message to indicate that Control-C will terminate processing */ printf ("Press Ctrl-C to terminate processing\n"); /* ** Setup a setjmp to unwind the stack and return here if we're ** interrupted with a ctrl-c during server processing. */ if (setjmp (ctrl_c_jump)) { printf ("%s: Ctrl-C has terminated processing\n", OptData.prog); terminate_server_context (&OptData, &CtxData, sock); exit (SUCCESS); } /* ** Establish the ctrl-c interrupt function */ ctrl_c_func = signal (SIGINT, ctrl_c_rtn); /* ** Parse the command line options */ parse_command_line (argc, argv, &OptData); /* ** Setup the network connection for clients */ sock = setup_network_connection (OptData.port); if (sock < 0) status = FAILURE; /* ** Initialize the server context */ if (status == SUCCESS) status = initialize_server_context (&OptData, &CtxData); /* ** Dialog with the client */ if (status == SUCCESS) status = dialog_with_client (&OptData, &CtxData, sock); /* ** Terminate the client context */ terminate_server_context (&OptData, &CtxData, sock); /* ** Exit */ exit (status); } /* ** ** parse_command_line - Parse the command line options ** ** Functional Description: ** ** This routine parses the command line options. ** ** Usage: ** ** parse_command_line argc, argv, envp ** ** Formal parameters: ** ** argc - (IN) argument count ** ** argv - (IN) address of an argument array ** ** OptData - (OUT) address of command line data structure to ** contain parsed input. ** ** Implicit Parameters: ** ** None ** ** Routine Value: ** ** None ** ** Side Effects: ** ** None ** */ void parse_command_line ( int argc, ArgArray argv, struct _OptData *OptData ) { char *logName = NULL; struct servent *sp; int option; /* ** Parse the program name from the first parameter */ parse_prog_name (argv[0], &OptData->prog); /* ** Process the command line options */ while ((option = getopt (argc, argv, "k:l:p:s?")) != EOF) { switch (option) { /* ** Log File ? */ case 'l': logName = optarg; break; /* ** 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 > 1) { usage (OptData->prog); exit (1); } /* ** If a log file was specified, then open it here, otherwise use the standard ** output file for logging. */ if (logName) OptData->log = fopen (logName,"w"); else OptData->log = stdout; /* ** If a service name was specified, then use it, otherwise use the default ** service name for this server. */ if (argc - optind > 0) OptData->serv = argv[optind]; else OptData->serv = GSS_SERVICE; } /* ** ** 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] [-l logfile] [service]\n", prog_name); } /* ** ** setup_network_connection - Setup the network connection ** ** Functional Description: ** ** This routine setups the network connection to receive client requests. ** ** Usage: ** ** setup_network_connection port ** ** Formal parameters: ** ** port - (IN) port number ** ** Implicit Parameters: ** ** None ** ** Routine Value: ** ** -1 - Socket failure has occured ** socket - The established socket file descriptor ** ** Side Effects: ** ** None ** */ int setup_network_connection ( int port ) { struct sockaddr_in sin; int sock; int status, on = 1; /* ** If the caller specified a port, then listen on that port; otherwise, assume ** we've been started as a TCP/IP service. */ if (port == 0) { sock = socket (INET$C_AUXS, 0, 0); if (sock < 0) { perror ("socket"); return (-1); } else return (sock); } /* ** Create a socket */ sock = socket (PF_INET, SOCK_STREAM, 0); if (sock < 0) { perror ("socket"); return (-1); } /* ** Set the socket options so that the socket can be reused. */ status = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)); if (status < 0) { perror ("setsockopt"); return (-1); } /* ** Setup the socket information */ sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; sin.sin_port = htons (port); /* ** Let the socket be reused */ status = bind (sock, (struct sockaddr *) &sin, sizeof (sin)); if (status != 0) { perror ("bind"); return (-1); } /* ** Listen on the socket with a backlog of 1 */ status = listen (sock, 1); if (status < 0) { perror ("listen"); return (-1); } return (sock); } /* ** ** initialize_server_context - Initialize the server's context ** ** Functional Description: ** ** This routine initializes the necessary server context information. ** ** Usage: ** ** initialize_client_context OptData, CtxData ** ** Formal parameters: ** ** OptData - (IN) address of the option data structure ** CtxData - (OUT) address of the context data structure ** ** Implicit Parameters: ** ** None ** ** Routine Value: ** ** SUCCESS - client context initialization succeeded ** FAILURE - client context initialization failed ** ** Side Effects: ** ** None ** */ int initialize_server_context ( struct _OptData *OptData, struct _CtxData *CtxData ) { gss_buffer_desc service_desc; gss_name_t service_name; OM_uint32 status, minor_status; /* ** Initialize service descriptor */ service_desc.value = OptData->serv; service_desc.length = strlen (OptData->serv) + 1; /* ** Import the service name */ status = gss_import_name (&minor_status, &service_desc, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &service_name); if (status != GSS_S_COMPLETE) { put_message (OptData->prog, status, minor_status, "{gss_import_name}"); return (-1); } /* ** Acquire the service credentials */ status = gss_acquire_cred (&minor_status, service_name, 0, GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &CtxData->creds, NULL, NULL); if (status != GSS_S_COMPLETE) { put_message (OptData->prog, status, minor_status, "{gss_acquire_cred}"); return (-1); } /* ** Release the service name */ gss_release_name (&minor_status, &service_name); /* ** Return Success */ return (SUCCESS); } /* ** ** dialog_with_client - Dialog with client(s) ** ** Functional Description: ** ** This routine accepts client's request as long as the server was started with ** a port number. This indicates that the server was intended to hand more than ** one client. ** ** Usage: ** ** dialog_with_client OptData, CtxData, sock ** ** Formal parameters: ** ** OptData - (IN) address of the option data structure ** CtxData - (IN/OUT) address of the context data structure ** sock - (IN) socket number ** ** Implicit Parameters: ** ** None ** ** Routine Value: ** ** -1 - socket connection error has occured ** SUCCESS - dialog with client succeeded ** FAILURE - dialog with client failed ** ** Side Effects: ** ** None ** */ int dialog_with_client ( struct _OptData *OptData, struct _CtxData *CtxData, int sock ) { int status; /* ** Process client requests */ do { status = accept_client_request (OptData, sock, CtxData->context, CtxData->creds); if (status < 0) { status = FAILURE; break; } } while (OptData->port); /* ** Return status */ return (status); } /* ** ** accept_client_request - Accepts and processes a client request ** ** Functional Description: ** ** This routine accepts and processes a single client request. ** The authorization is sent to the server and if it is accepted the ** authorization context is modified with the components necessary to ** encrypt and decrypt messages to and from the server. ** ** ** Usage: ** ** accept_client_request OptData, sock, context, creds ** ** Formal parameters: ** ** OptData - (IN) address of the command parameters structure ** sock - (IN) socket descriptor value ** context - (IN) Kerberos context structure ** creds - (IN) server principal information ** ** Implicit Parameters: ** ** None ** ** Routine Value: ** ** SUCCESS - accept client request succeeded ** FAILURE - accept client request failed ** ** Side Effects: ** ** None ** */ int accept_client_request ( struct _OptData *OptData, int sock, gss_ctx_id_t context, gss_cred_id_t creds ) { struct sockaddr_in SockBuf; gss_buffer_desc recv_data, send_data, msg_data; unsigned int SockLen; int accepted_sock; gss_buffer_desc client_name; OM_uint32 status, ret_flags, minor_status; gss_name_t client; int conf_state; gss_OID doid; /* ** Initialize the appropriate variables */ memset (&SockBuf, 0, sizeof (SockBuf)); SockLen = sizeof (SockBuf); /* ** If the user specified a port, then wait on that port to accept a connection, ** otherwise, get the socket provided by TCP/IP services. */ if (OptData->port) { accepted_sock = accept (sock, (struct sockaddr *) &SockBuf, &SockLen); if (accepted_sock < 0) { perror ("accept"); return (-1); } } else { accepted_sock = sock; if (getpeername (sock, (struct sockaddr *) &SockBuf, &SockLen) < 0) { perror ("getpeername"); return (-1); } } /* ** Log the accepted connection address */ fprintf (OptData->log, "%s: received connection from %d.%d.%d.%d\n", OptData->prog, SockBuf.sin_addr.s_net, SockBuf.sin_addr.s_host, SockBuf.sin_addr.s_lh, SockBuf.sin_addr.s_impno); /* ** Perform the context-establishement loop. */ do { /* ** Read the receive data length */ if (net_read (accepted_sock, (char *) &recv_data.length, sizeof (recv_data.length)) < 0) { fprintf (OptData->log, "%s: error while writing data length to client\n", OptData->prog); if (OptData->port) close (accepted_sock); return (-1); } /* ** Allocate a buffer to hold the receive data */ recv_data.value = malloc (recv_data.length); if (! recv_data.value) { fprintf (OptData->log, "%s: unable to allocate receive buffer\n", OptData->prog); if (OptData->port) close (accepted_sock); return (-1); } /* ** Read the receive data */ if (net_read (accepted_sock, recv_data.value, recv_data.length) < 0) { fprintf (OptData->log, "%s: error while reading data from client\n", OptData->prog); gss_release_buffer (&minor_status, &recv_data); if (OptData->port) close (accepted_sock); return (-1); } /* ** Accept the security context */ status = gss_accept_sec_context (&minor_status, &context, creds, &recv_data, GSS_C_NO_CHANNEL_BINDINGS, &client, &doid, &send_data, &ret_flags, NULL, /* ignore time_rec */ NULL); /* ignore del_cred_handle */ if (status != GSS_S_COMPLETE && status != GSS_S_CONTINUE_NEEDED) { put_message (OptData->prog, status, minor_status, "{gss_accept_sec_context}"); gss_release_buffer (&minor_status, &recv_data); if (OptData->port) close (accepted_sock); return (SUCCESS); } /* ** Release the receive data */ gss_release_buffer (&minor_status, &recv_data); /* ** Check to see if we we need to dialog with the client further */ if (send_data.length != 0) { /* ** Write the send data length */ if (net_write (accepted_sock, (char *) &send_data.length, sizeof (send_data.length)) < 0) { fprintf (OptData->log, "%s: error while writing data length to client\n", OptData->prog); gss_release_buffer (&minor_status, &send_data); if (OptData->port) close (accepted_sock); return (-1); } /* ** Write the send data */ if (net_write (accepted_sock, send_data.value, send_data.length) < 0) { fprintf (OptData->log, "%s: error while writing data to client\n", OptData->prog); gss_release_buffer (&minor_status, &send_data); if (OptData->port) close (accepted_sock); return (-1); } /* ** Release the send data */ gss_release_buffer (&minor_status, &send_data); } } while (status == GSS_S_CONTINUE_NEEDED); /* ** Extract the client name */ status = gss_display_name (&minor_status, client, &client_name, &doid); if (status != GSS_S_COMPLETE) { put_message (OptData->prog, status, minor_status, "{gss_display_name}"); if (OptData->port) close (accepted_sock); return (-1); } /* ** Log the accepted connection client */ fprintf (OptData->log, "%s: accepted connection from %s\n", OptData->prog, client_name.length ? (char *) client_name.value : ""); /* ** Release the client name */ status = gss_release_name (&minor_status, &client); if (status != GSS_S_COMPLETE) { put_message (OptData->prog, status, minor_status, "{gss_release_name}"); if (OptData->port) close (accepted_sock); return (-1); } /* ** Read the receive data length */ if (net_read (accepted_sock, (char *) &recv_data.length, sizeof (recv_data.length)) < 0) { fprintf (OptData->log, "%s: error while reading data length from client\n", OptData->prog); if (OptData->port) close (accepted_sock); return (-1); } /* ** Allocate a buffer to hold the receive data */ recv_data.value = malloc (recv_data.length); if (! recv_data.value) { fprintf (OptData->log, "%s: unable to allocate receive buffer\n", OptData->prog); if (OptData->port) close (accepted_sock); return (-1); } /* ** Read the receive the data */ if (net_read (accepted_sock, recv_data.value, recv_data.length) < 0) { fprintf (OptData->log, "%s: error while reading data from client\n", OptData->prog); if (OptData->port) close (accepted_sock); return (-1); } /* ** Decrypt the receive data */ status = gss_unwrap (&minor_status, context, &recv_data, &msg_data, &conf_state, (gss_qop_t *) NULL); if (status != GSS_S_COMPLETE) { put_message (OptData->prog, status, minor_status, "{gss_unwrap}"); if (OptData->port) close (accepted_sock); return (-1); } else if (! conf_state) fprintf (stderr, "Warning! Message not encrypted.\n"); /* ** Release the receive data */ gss_release_buffer (&minor_status, &recv_data); /* ** Log the message received */ fprintf (OptData->log, "%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); /* ** Build the reply message */ msg_data.value = malloc (255 + 1); sprintf (msg_data.value, "Hello to %s on node %d.%d.%d.%d", client_name.length ? (char *) client_name.value : "", SockBuf.sin_addr.s_net, SockBuf.sin_addr.s_host, SockBuf.sin_addr.s_lh, SockBuf.sin_addr.s_impno); msg_data.length = strlen (msg_data.value); /* ** Encrypt the send data */ status = gss_wrap (&minor_status, context, 1, GSS_C_QOP_DEFAULT, &msg_data, &conf_state, &send_data); if (status != GSS_S_COMPLETE) { put_message (OptData->prog, status, minor_status, "{gss_wrap}"); gss_release_buffer (&minor_status, &msg_data); if (OptData->port) close (accepted_sock); return (FAILURE); } else if (! conf_state) fprintf (stderr, "Warning! Message not encrypted.\n"); /* ** Write the send data length */ if (net_write (accepted_sock, (char *) &send_data.length, sizeof (send_data.length)) < 0) { gss_release_buffer (&minor_status, &msg_data); gss_release_buffer (&minor_status, &send_data); if (OptData->port) close (accepted_sock); return (-1); } /* ** Write the send data */ if (net_write (accepted_sock, send_data.value, send_data.length) < 0) { gss_release_buffer (&minor_status, &msg_data); gss_release_buffer (&minor_status, &send_data); if (OptData->port) close (accepted_sock); return (-1); } /* ** Log the message sent */ fprintf (OptData->log, "%s: sent message '%-*.*s'\n", OptData->prog, msg_data.length, msg_data.length, (char *) msg_data.value); /* ** Release the message and send data */ gss_release_buffer (&minor_status, &msg_data); gss_release_buffer (&minor_status, &send_data); /* ** Delete the security context */ status = gss_delete_sec_context (&minor_status, &context, NULL); if (status != GSS_S_COMPLETE) { put_message (OptData->prog, status, minor_status, "{gss_delete_sec_context}"); if (OptData->port) close (accepted_sock); return (-1); } /* ** Close the accepted socket */ if (OptData->port) close (accepted_sock); /* ** Return success */ return (SUCCESS); } /* ** ** terminate_server_context - Terminate the server context ** ** Functional Description: ** ** This routine terminates the server's context information and ** closes it's socket. ** ** Usage: ** ** terminate_server_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 server context succeeded ** FAILURE - terminate server context failed ** ** Side Effects: ** ** None ** */ int terminate_server_context ( struct _OptData *OptData, struct _CtxData *CtxData, int sock ) { OM_uint32 status, minor_status; /* ** Disable the ctrl-c signal interrupt routine */ signal (SIGINT, ctrl_c_func); /* ** Free the GSS server credentials */ if (CtxData->creds != GSS_C_NO_CREDENTIAL) gss_release_cred (&minor_status, &CtxData->creds); /* ** Close the log file */ if (OptData->log) fclose (OptData->log); /* ** Shutdown and close the socket */ #if !defined(__VAX) /* socket calls accvio on VAX */ if (sock > 0) { shutdown (sock, 2); close (sock); } #endif return (SUCCESS); } /* ** ** ctrl_c_rtn - This routine captures any Ctrl-C interupts ** ** Functional Description: ** ** This routine is Ctrl-C interupt routine. It simply executes a ** "longjmp" to the previously saved registers and causes the ** "setjmp" conditional to be evaluated with the second parameter ** of the the "longjmp". ** ** Usage: ** ** ctrl_c_rtn signo ** ** Formal parameters: ** ** signo - (IN) The signal argument for this interupt ** ** Implicit Parameters: ** ** ctrl_c_jump - (IN) The "setjmp" address ** ** Routine Value: ** ** None ** ** Side Effects: ** ** None ** */ void ctrl_c_rtn ( int signo ) { /* ** Unwind back to the spot where the setjmp was executed */ longjmp (ctrl_c_jump, 1); }