#pragma module ssl$tcp_server_qio \ "V5.5-00" /* ** Copyright 2004 Hewlett-Packard Development Company, L.P. ** ** Confidential computer software. Valid license from HP and/or its ** subsidiaries required for possession, use, or copying. ** ** Consistent with FAR 12.211 and 12.212, Commercial Computer Software, ** Computer Software Documentation, and Technical Data for Commercial ** Items are licensed to the U.S. Government under vendor's standard ** commercial license. ** ** Neither HP nor any of its subsidiaries shall be liable for technical ** or editorial errors or omissions contained herein. The information ** in this document is provided "as is" without warranty of any kind ** and is subject to change without notice. The warranties for HP ** products are set forth in the express limited warranty statements ** accompanying such products. Nothing herein should be construed as ** constituting an additional warranty. ** ** **++ ** FACILITY: ** ** SSL EXAMPLES ** ** ABSTRACT: ** ** This is an example of a TCP/IP IPv4 server using OpenVMS ** QIO system services to handle network I/O operations with SSL ** to secure the data with encryption. ** ** Refer to 'Build, Configuration, and Run Instructions' for ** details on how to build, configure, and run this program. ** ** ENVIRONMENT: (Minimal Versions) ** ** OpenVMS V7.2-2 Alpha, VAX V7.3 ** TCP/IP Services V5.3 ** SSL for OpenVMS V1.1-B ** ** AUTHORS: ** ** TCPIP Development Group, ** ** CREATION DATE: 23-May-1989 ** ** DESIGN ISSUES: ** ** None ** ** MODIFICATION HISTORY: ** ** OpenVMS Engineering - Security Group, ** ** V5.5-00 01-Dec-2003 Paul Mosteika ** Modified to use SSL to verify server certificate and encrypt data. ** **-- */ /* Build, Configuration, and Run Instructions */ /* * BUILD INSTRUCTIONS: * * To build this example program use commands of the form, * * using the "DECC" compiler: * * To Create a 32-bit Application Using 32-bit SSL APIs * ---------------------------------------------------- * * $ CC /POINTER=32 /PREFIX=ALL SSL$TCP_SERVER_QIO_SSL.C * $ LINK SSL$TCP_SERVER_QIO_SSL.OBJ , SYS$INPUT:/OPT * SYS$LIBRARY:SSL$LIBCRYPTO_SHR32.EXE /SHARE * SYS$LIBRARY:SSL$LIBSSL_SHR32.EXE /SHARE * $ EXIT * * Note: The source code would need to be modified for 64 bit support. * * CONFIGURATION INSTRUCTIONS: * * No special configuration required, although, TCPIP and SSL must be * installed and started * * * RUN INSTRUCTIONS: * * To run this example program: * * 1) Start this server program as shown below: * * $ run SSL$TCP_SERVER_QIO_SSL * . . . Waiting for a client connection on port: m * * 2) After the server program blocks, start the client program, * entering the server host as shown below: * * $ run SSL$TCP_CLIENT_QIO_SSL * Enter remote host: * * Note: You can specify a server host by using either an IPv4 * address in dotted-decimal notation (e.g. 16.20.10.56) * or a host domain name (e.g. serverhost.hp.com). * * To eliminate user prompting for the Server node, define * SERVER_ADDR for the default Server node for the Client * connection, for example: * * #define SERVER_ADDR "TCPIP-Nodename" * * See SSL_EXAMPLES.H for more information and further debug * information and callbacks. * * 3) The server program then displays client connection information * and client data as shown below: * * . . . Accepted connection from host: a.b.c.d, port: n * Data sent: Hello, world! * */ /* * INCLUDE FILES: */ #include /* define OpenVMS descriptors */ #include /* define error definitions */ #include /* define 'EFN$C_ENF' event flag */ #include /* define internet related constants, */ /* functions, and structures */ #include /* define network address info */ #include /* define i/o function codes */ #include /* define network database library info */ #include /* define system service status codes */ #include /* define system service calls */ #include /* define standard i/o functions */ #include /* define standard library functions */ #include /* define string handling functions */ #include /* define condition value fields */ #include /* define tcp/ip network constants, */ /* structures, and functions */ #include /* Posix definitions */ /* * NAMED CONSTANTS: */ #define SERV_BACKLOG 1 /* server backlog */ #define SERV_PORTNUM 5555 /* server port number */ /* ++ SSL */ #include #include #define openssl "SSL$INCLUDE" #include #include #include #include "SSL$EXAMPLES:ssl_examples.h" /* SSL callbacks and error handling */ #undef openssl #define RSA_SERVER_CERT "SSL$CERTS:server.crt" #define RSA_SERVER_KEY "SSL$CERTS:server.key" #define RSA_SERVER_CA_CERT "SSL$CERTS:server_ca.crt" #define ON 1 #define OFF 0 /* -- SSL */ /* Global Data */ /* * STRUCTURE DEFINITIONS: */ struct iosb { /* i/o status block */ unsigned short status; /* i/o completion status */ unsigned short bytcnt; /* bytes transferred if read/write */ void *details; /* address of buffer or parameter */ }; struct itemlst_2 { /* item-list 2 descriptor/element */ unsigned short length; /* length */ unsigned short type; /* parameter type */ void *address; /* address of item list */ }; struct itemlst_3 { /* item-list 3 descriptor/element */ unsigned short length; /* length */ unsigned short type; /* parameter type */ void *address; /* address of item list */ unsigned int *retlen; /* address of returned length */ }; struct sockchar { /* socket characteristics buffer */ unsigned short prot; /* protocol */ unsigned char type; /* type */ unsigned char af; /* address format */ }; /* ++ SSL */ char buf[4096]; /* Global server read data buffer */ /* -- SSL */ /* * FORWARD REFERENCES: */ int main( void ); /* server main */ /* Server Main */ /* * FUNCTIONAL DESCRIPTION: * * This is the server's main-line code. It handles all the tasks of the * server including: socket creation, accepting and/or rejecting client * connections, writing client connection data, and terminating client * connections. * * This example program implements a typical TCP IPv4 server using QIO * system services to handle network i/o operations as shown below: * * 1) To create a socket and set REUSEADDR option: * * sys$assign() and sys$qiow(IO$_SETMODE) * * 2) To bind internet address and port number to a socket: * * sys$qiow(IO$_SETMODE) * * 3) To accept a connection request: * * sys$qiow(IO$_ACCESS|IO$M_ACCEPT) * * 4) To transfer data: * * sys$qiow(IO$_WRITEVBLK) * * 5) To shutdown a socket: * * sys$qiow(IO$_DEACCESS|IO$M_SHUTDOWN) * * 6) To close and delete a socket: * * sys$qiow(IO$_DEACCESS) and sys$dassgn() * * This function is invoked by the DCL "RUN" command (see below); the * function's completion status is interpreted by DCL and if needed, * an error message is displayed. * * SYNOPSIS: * * int main( void ) * * FORMAL PARAMETERS: * * ** None ** * * IMPLICIT INPUTS: * * ** None ** * * IMPLICIT OUTPUTS: * * ** None ** * * FUNCTION VALUE: * * completion status * * SIDE EFFECTS: * * ** None ** * */ int main( void ) { /* ++ SSL */ int err; int verify_client = OFF; /* To verify a client certificate, set ON */ int listen_sock; int sock; struct sockaddr_in sa_serv; struct sockaddr_in sa_cli; size_t client_len; char *str; SSL_CTX *ctx; SSL *ssl; /* Pointer to SSL Object */ SSL_METHOD *meth; X509 *client_cert = NULL; short int s_port = 5555; /* -- SSL */ int one = 1; /* reuseaddr option value */ struct iosb iosb; /* i/o status block */ unsigned int status; /* system service return status */ unsigned short conn_channel; /* connect inet device i/o channel */ unsigned short listen_channel; /* listen inet device i/o channel */ struct sockchar listen_sockchar; /* listen socket char buffer */ unsigned int client_retlen; /* returned length of client socket */ /* address structure */ struct sockaddr_in client_addr; /* client socket address structure */ struct itemlst_3 client_itemlst; /* client item-list 3 descriptor */ struct sockaddr_in serv_addr; /* server socket address structure */ struct itemlst_2 serv_itemlst; /* server item-list 2 descriptor */ struct itemlst_2 sockopt_itemlst; /* sockopt item-list 2 descriptor */ struct itemlst_2 reuseaddr_itemlst; /* reuseaddr item-list 2 element */ $DESCRIPTOR( inet_device, /* string descriptor with logical */ "TCPIP$DEVICE:" ); /* name of internet pseudodevice */ /* ++ SSL */ /*-----------------------------------------------------------------------------------------*/ /* Load encryption & hashing algorithms for the SSL program */ SSL_library_init(); /* Load the error strings for SSL & CRYPTO APIs */ SSL_load_error_strings(); /* Create a SSL_METHOD structure (choose a SSL/TLS protocol version) */ meth = SSLv3_method(); /* Create a SSL_CTX structure */ ctx = SSL_CTX_new(meth); RETURN_NULL(ctx); /* Load the server certificate into the SSL_CTX structure */ if (SSL_CTX_use_certificate_file(ctx, RSA_SERVER_CERT, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); exit(1); } /* Load the private-key corresponding to the server certificate */ if (SSL_CTX_use_PrivateKey_file(ctx, RSA_SERVER_KEY, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); exit(1); } /* Check if the server certificate and private-key matches */ if (!SSL_CTX_check_private_key(ctx)) { fprintf(stderr,"Private key does not match the certificate public key\n"); exit(1); } if(verify_client == ON) { /* Load the RSA CA certificate into the SSL_CTX structure */ if (!SSL_CTX_load_verify_locations(ctx, RSA_SERVER_CA_CERT, NULL)) { ERR_print_errors_fp(stderr); exit(1); } /* Set to require peer (client) certificate verification */ SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL); /* Set the verification depth to 1 */ SSL_CTX_set_verify_depth(ctx,1); } /* ++ SSL */ /* A SSL structure is created */ ssl = SSL_new(ctx); RETURN_NULL(ssl); /* Set informational callback routine */ // SSL_set_info_callback( ssl , get_info ); /* -- SSL */ /* * init listen socket characteristics buffer */ listen_sockchar.prot = TCPIP$C_TCP; listen_sockchar.type = TCPIP$C_STREAM; listen_sockchar.af = TCPIP$C_AF_INET; /* * init reuseaddr's item-list element */ reuseaddr_itemlst.length = sizeof( one ); reuseaddr_itemlst.type = TCPIP$C_REUSEADDR; reuseaddr_itemlst.address = &one; /* * init sockopt's item-list descriptor */ sockopt_itemlst.length = sizeof( reuseaddr_itemlst ); sockopt_itemlst.type = TCPIP$C_SOCKOPT; sockopt_itemlst.address = &reuseaddr_itemlst; /* * init client's item-list descriptor */ memset( &client_itemlst, 0, sizeof(client_itemlst) ); client_itemlst.length = sizeof( client_addr ); client_itemlst.address = &client_addr; client_itemlst.retlen = &client_retlen; /* * init client's socket address structure */ memset( &client_addr, 0, sizeof(client_addr) ); /* * init server's item-list descriptor */ serv_itemlst.length = sizeof( serv_addr ); serv_itemlst.type = TCPIP$C_SOCK_NAME; serv_itemlst.address = &serv_addr; /* * init server's socket address structure */ memset( &serv_addr, 0, sizeof(serv_addr) ); serv_addr.sin_family = TCPIP$C_AF_INET; serv_addr.sin_port = htons( SERV_PORTNUM ); serv_addr.sin_addr.s_addr = TCPIP$C_INADDR_ANY; /* * assign device sockets */ status = sys$assign( &inet_device, /* device name */ &listen_channel, /* i/o channel */ 0, /* access mode */ 0 /* not used */ ); if ( (status & STS$M_SUCCESS) ) status = sys$assign( &inet_device, /* device name */ &conn_channel, /* i/o channel */ 0, /* access mode */ 0 /* not used */ ); if ( !(status & STS$M_SUCCESS) ) { printf( "Failed to assign i/o channel(s) to TCPIP device\n" ); exit( status ); } /* * create a listen socket */ status = sys$qiow( EFN$C_ENF, /* event flag */ listen_channel, /* i/o channel */ IO$_SETMODE, /* i/o function code */ &iosb, /* i/o status block */ 0, /* ast service routine */ 0, /* ast parameter */ &listen_sockchar, /* p1 - socket char buffer */ 0, /* p2 */ 0, /* p3 */ 0, /* p4 */ &sockopt_itemlst, /* p5 - socket options */ 0 /* p6 */ ); if ( status & STS$M_SUCCESS ) status = iosb.status; if ( !(status & STS$M_SUCCESS) ) { printf( "Failed to create socket\n" ); exit( status ); } /* * bind server's internet address and port number to * listen socket; set socket as a passive socket */ status = sys$qiow( EFN$C_ENF, /* event flag */ listen_channel, /* i/o channel */ IO$_SETMODE, /* i/o function code */ &iosb, /* i/o status block */ 0, /* ast service routine */ 0, /* ast parameter */ 0, /* p1 */ 0, /* p2 */ &serv_itemlst, /* p3 - local socket name */ SERV_BACKLOG, /* p4 - connection backlog */ 0, /* p5 */ 0 /* p6 */ ); if ( status & STS$M_SUCCESS ) { status = iosb.status; } if ( !(status & STS$M_SUCCESS) ) { printf( "Failed to bind socket\n" ); exit( status ); } /* * accept a connection from a client */ printf( " . . . Waiting for a client connection on port: %d\n", ntohs(serv_addr.sin_port) ); status = sys$qiow( EFN$C_ENF, /* event flag */ listen_channel, /* i/o channel */ IO$_ACCESS|IO$M_ACCEPT, /* i/o function code */ &iosb, /* i/o status block */ 0, /* ast service routine */ 0, /* ast parameter */ 0, /* p1 */ 0, /* p2 */ &client_itemlst, /* p3 - remote socket name */ &conn_channel, /* p4 - i/o channel for new */ /* connection */ 0, /* p5 */ 0 /* p6 */ ); if ( status & STS$M_SUCCESS ) { status = iosb.status; } if ( !(status & STS$M_SUCCESS) ) { printf( "Failed to accept client connection\n" ); exit( status ); } printf( " . . . Accepted connection from host: %s, port: %d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port) ); /* ++ SSL */ /* ** Assign the socket into the SSL structure ** (SSL and socket without BIO) */ sock = decc$socket_fd ( conn_channel ); err = SSL_set_fd( ssl , sock ); if ( err == -1 ) check_error( ssl , err , \ "- ERROR - during SERVER assigning socket to SSL object"); /* ** Perform SSL Handshake on the SSL server */ err = SSL_accept(ssl); if ( err == -1 ) check_error( ssl , err , \ "- ERROR - during SERVER accepting CLIENT connection"); /* ** Informational output (optional) */ printf(" SSL connection using %s\n", SSL_get_cipher (ssl)); if (verify_client == ON) { /* ** Get the client's certificate (optional) */ client_cert = SSL_get_peer_certificate(ssl); if (client_cert != NULL) { printf ("Client certificate:\n"); str = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0); RETURN_NULL(str); printf ("\t subject: %s\n", str); free (str); str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0); RETURN_NULL(str); printf ("\t issuer: %s\n", str); free (str); X509_free(client_cert); } else printf("The SSL client does not have certificate.\n"); } /* End - verify client certificate */ /*--------------- DATA EXCHANGE - Receive message and send reply. ---------------*/ /* ** Receive data from the SSL client */ err = SSL_read(ssl, buf, sizeof(buf) - 1); if ( err == -1 ) check_error( ssl , err , \ "- ERROR - during SERVER read operation from Client"); buf[err] = '\0'; printf (" Received %d chars from client:'%s'\n", err, buf); /* ** Send data to the SSL client */ err = SSL_write( ssl, "This message is from the SSL QIO server", strlen("This message is from the SSL QIO server")); if ( err == -1 ) check_error( ssl , err , \ "- ERROR - during SERVER write operation to CLIENT"); /*--------------- SSL closure ---------------*/ /* ** Shutdown this side (server) of the connection. */ err = SSL_shutdown(ssl); if ( err == -1 ) check_error( ssl , err , \ "- ERROR - during SERVER shutting down SSL connection"); /* -- SSL */ /* * shutdown connection socket */ status = sys$qiow( EFN$C_ENF, /* event flag */ conn_channel, /* i/o channel */ IO$_DEACCESS|IO$M_SHUTDOWN, /* i/o function code */ &iosb, /* i/o status block */ 0, /* ast service routine */ 0, /* ast parameter */ 0, /* p1 */ 0, /* p2 */ 0, /* p3 */ TCPIP$C_DSC_ALL, /* p4 - discard all packets */ 0, /* p5 */ 0 /* p6 */ ); if ( status & STS$M_SUCCESS ) status = iosb.status; if ( !(status & STS$M_SUCCESS) ) { printf( "Failed to shutdown client connection\n" ); exit( status ); } /* * close connection socket */ status = sys$qiow( EFN$C_ENF, /* event flag */ conn_channel, /* i/o channel */ IO$_DEACCESS, /* i/o function code */ &iosb, /* i/o status block */ 0, /* ast service routine */ 0, /* ast parameter */ 0, /* p1 */ 0, /* p2 */ 0, /* p3 */ 0, /* p4 */ 0, /* p5 */ 0 /* p6 */ ); if ( status & STS$M_SUCCESS ) status = iosb.status; if ( !(status & STS$M_SUCCESS) ) { printf( "Failed to close socket\n" ); exit( status ); } /* * close listen socket */ status = sys$qiow( EFN$C_ENF, /* event flag */ listen_channel, /* i/o channel */ IO$_DEACCESS, /* i/o function code */ &iosb, /* i/o status block */ 0, /* ast service routine */ 0, /* ast parameter */ 0, /* p1 */ 0, /* p2 */ 0, /* p3 */ 0, /* p4 */ 0, /* p5 */ 0 /* p6 */ ); if ( status & STS$M_SUCCESS ) status = iosb.status; if ( !(status & STS$M_SUCCESS) ) { printf( "Failed to close socket\n" ); exit( status ); } /* ++ SSL */ /* ** Free the SSL structure */ SSL_free(ssl); /* ** Free the SSL_CTX structure */ SSL_CTX_free(ctx); /* -- SSL */ /* * deassign all device sockets */ status = sys$dassgn( conn_channel ); if ( (status & STS$M_SUCCESS) ) status = sys$dassgn( listen_channel ); if ( !(status & STS$M_SUCCESS) ) { printf( "Failed to deassign i/o channel(s) to TCPIP device\n" ); exit( status ); } exit( EXIT_SUCCESS ); } /* End Main */