#include #include #include #include #include #include #include #include #include #include #include #include "krb_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 { krb5_context context; krb5_principal server; }; /* ** 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, krb5_context, krb5_principal); 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 = {NULL, NULL}; 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, 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 *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 = KRB_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 creation failure has occured ** n - The established socket number ** ** 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 the socket */ 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 ) { krb5_error_code status; /* ** Initialize the Kerberos context */ status = krb5_init_context (&CtxData->context); if (status) { fprintf (OptData->log, "%s: %s while initializing krb5\n", OptData->prog, krb5_get_message (status)); return (FAILURE); } /* ** Convert the service name to a Principal name */ status = krb5_sname_to_principal (CtxData->context, NULL, OptData->serv, KRB5_NT_SRV_HST, &CtxData->server); if (status) { fprintf (OptData->log, "%s: %s while generating service name %s\n", OptData->prog, krb5_get_message (status), OptData->serv); krb5_free_context (CtxData->context); return (FAILURE); } /* ** Return success */ return (SUCCESS); } /* ** ** dialog_with_server - 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->server); if (status < 0) { status = FAILURE; break; } } while (OptData->port); /* ** Return success */ return (SUCCESS); } /* ** ** accept_client_request - Accepts and processes a client request ** ** Functional Description: ** ** This routine accepts and processes a single client request. ** The authorization received by 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 client. ** ** ** Usage: ** ** accept_client_request OptData, sock, context, server ** ** Formal parameters: ** ** OptData - (IN) address of the command parameters structure ** sock - (IN) socket descriptor value ** context - (IN) Kerberos context structure ** server - (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, krb5_context context, krb5_principal server ) { struct sockaddr_in SockBuf; krb5_data recv_data, send_data, msg_data; unsigned int SockLen; int accepted_sock; char *client_name; krb5_error_code status; krb5_auth_context auth_context = 0; krb5_ticket *ticket = NULL; krb5_keytab keytab = 0; krb5_data rcache_name; krb5_rcache rcache; krb5_address kaddr; /* ** 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); /* ** Receive the client's authorization */ status = krb5_recvauth (context, &auth_context, &accepted_sock, KRB_VERSION, server, 0, keytab, &ticket); if (status) { fprintf (OptData->log, "%s: %s while receiving authorization from client\n", OptData->prog, krb5_get_message (status)); if (OptData->port) close (accepted_sock); return (FAILURE); } /* ** Extract the client name */ status = krb5_unparse_name (context, ticket->enc_part2->client, &client_name); if (status) { fprintf (OptData->log, "%s: %s while unparsing client name\n", OptData->prog, krb5_get_message (status)); client_name = NULL; } /* ** Log the accepted connection client */ fprintf (OptData->log, "%s: accepted connection from %s\n", OptData->prog, client_name ? client_name : ""); /* ** Generate local and remote addresses for encrypting & decrypting messages */ status = krb5_auth_con_genaddrs (context, auth_context, accepted_sock, KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR | KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR); if (status) { fprintf (OptData->log, "%s: %s while generating addresses for authorization\n", OptData->prog, krb5_get_message (status)); krb5_auth_con_free (context, auth_context); if (OptData->port) close (accepted_sock); return (FAILURE); } /* ** Read the receive data length */ status = net_read (accepted_sock, (char *) &recv_data.length, sizeof (recv_data.length)); if (status <= 0) { fprintf (OptData->log, "%s: %s while reading data length from client\n", OptData->prog, krb5_get_message (status)); krb5_auth_con_free (context, auth_context); if (client_name) free (client_name); if (OptData->port) close (accepted_sock); return (FAILURE); } /* ** Allocate a buffer to hold the receive data */ recv_data.data = malloc (recv_data.length); if (! recv_data.data) { fprintf (OptData->log, "%s: unable to allocate receive buffer\n", OptData->prog); if (OptData->port) close (accepted_sock); return (FAILURE); } /* ** Read the receive data */ status = net_read (accepted_sock, recv_data.data, recv_data.length); if (status <= 0) { fprintf (OptData->log, "%s: %s while reading data from client\n", OptData->prog, krb5_get_message (status)); krb5_auth_con_free (context, auth_context); if (recv_data.data) free (recv_data.data); if (client_name) free (client_name); if (OptData->port) close (accepted_sock); return (FAILURE); } /* ** Decrypt the receive data */ status = krb5_rd_priv (context, auth_context, &recv_data, &msg_data, NULL); if (status) { fprintf (OptData->log, "%s: %s while decrypting message\n", OptData->prog, krb5_get_message (status)); krb5_auth_con_free (context, auth_context); if (client_name) free (client_name); if (OptData->port) close (accepted_sock); return (FAILURE); } /* ** Log the message received */ fprintf (OptData->log, "%s: received message '%-*.*s'\n", OptData->prog, msg_data.length, msg_data.length, msg_data.data); /* ** Build the reply message */ msg_data.length = strlen ("Hello to % on node %%%.%%%.%%%.%%%") + strlen (client_name) + 1; msg_data.data = malloc (msg_data.length); sprintf (msg_data.data, "Hello to %s on node %d.%d.%d.%d", client_name ? client_name : "", 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.data); /* ** Free the client name */ if (client_name) free (client_name); /* ** Encrypt the send data */ status = krb5_mk_priv (context, auth_context, &msg_data, &send_data, NULL); if (status) { krb5_put_message (OptData->prog, status, "while encrypting message"); if (msg_data.data) free (msg_data.data); if (OptData->port) close (accepted_sock); return (FAILURE); } /* ** Write the send data length */ status = net_write (accepted_sock, (char *) &send_data.length, sizeof (send_data.length)); if (status <= 0) { fprintf (OptData->log, "%s: %s while writing len to client\n", OptData->prog, krb5_get_message (status)); krb5_auth_con_free (context, auth_context); if (msg_data.data) free (msg_data.data); if (send_data.data) free (send_data.data); if (OptData->port) close (accepted_sock); return (FAILURE); } /* ** Write the send data */ status = net_write (accepted_sock, send_data.data, send_data.length); if (status <= 0) { fprintf (OptData->log, "%s: %s while writing data to client\n", OptData->prog, krb5_get_message (status)); krb5_auth_con_free (context, auth_context); if (msg_data.data) free (msg_data.data); if (send_data.data) free (send_data.data); if (OptData->port) close (accepted_sock); return (FAILURE); } /* ** Log the message sent */ fprintf (OptData->log, "%s: sent message '%-*.*s'\n", OptData->prog, msg_data.length, msg_data.length, msg_data.data); /* ** Free the message data */ if (msg_data.data) free (msg_data.data); /* ** Free the send data */ if (send_data.data) free (send_data.data); /* ** Free the authorization context */ krb5_auth_con_free (context, auth_context); /* ** 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 ) { /* ** Disable the ctrl-c signal interrupt routine */ signal (SIGINT, ctrl_c_func); /* ** Free the server principal */ if (CtxData->server) krb5_free_principal (CtxData->context, CtxData->server); /* ** Free the Kerberos context */ if (CtxData->context) krb5_free_context (CtxData->context); /* ** 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 */ 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); }