This code example can be used to establish a secure server using Global Secure ToolKit (GSKit) APIs.
The server opens the socket, prepares the secure environment, accepts and processes connection requests, exchanges data with the client and ends the session. The client also opens a socket, sets up the secure environment, calls the server and requests a secure connection, exchanges data with the server, and closes the session. The following diagram and description shows the server/client flow of events.
The following sequence of the socket calls provides a description of the graphic. It also describes the relationship between the server and client examples.
/* GSK Asynchronous Server Program using Application Id*/ /* "IBM grants you a nonexclusive copyright license */ /* to use all programming code examples from which */ /* you can generate similar function tailored to your */ /* own specific needs. */ /* */ /* All sample code is provided by IBM for illustrative*/ /* purposes only. These examples have not been */ /* thoroughly tested under all conditions. IBM, */ /* therefore, cannot guarantee or imply reliability, */ /* serviceability, or function of these programs. */ /* */ /* All programs contained herein are provided to you */ /* "AS IS" without any warranties of any kind. The */ /* implied warranties of non-infringement, */ /* merchantability and fitness for a particular */ /* purpose are expressly disclaimed. " */ /* Assummes that application id is already registered */ /* and a certificate has been associated with the */ /* application id. */ /* No parameters, some comments and many hardcoded */ /* values to keep it short and simple */ /* use following command to create bound program: */ /* CRTBNDC PGM(PROG/GSKSERVa) */ /* SRCFILE(PROG/CSRC) */ /* SRCMBR(GSKSERVa) */ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <gskssl.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #define _MULTI_THREADED #include "pthread.h" #include "qsoasync.h" #define Failure 0 #define Success 1 #define TRUE 1 #define FALSE 0 void *workerThread(void *arg); /********************************************************************/ /* Descriptive Name: Master thread will establish a client */ /* connection and hand processing responsibility */ /* to a worker thread. */ /* Note: Due to the thread attribute of this program, spawn() must */ /* be used to invoke. */ /********************************************************************/ int main(void) { gsk_handle my_env_handle=NULL; /* secure environment handle */ gsk_handle my_session_handle=NULL; /* secure session handle */ struct sockaddr_in address; int buf_len, on = 1, rc = 0; int sd = -1, lsd = -1, al = -1, ioCompPort = -1; int successFlag = FALSE; char buff[1024]; pthread_t thr; void *status; Qso_OverlappedIO_t ioStruct; /*********************************************/ /* Issue all of the command in a do/while */ /* loop so that clean up can happen at end */ /*********************************************/ do { /*********************************************/ /* Create an I/O completion port for this */ /* process. */ /*********************************************/ if ((ioCompPort = QsoCreateIOCompletionPort()) < 0) { perror("QsoCreateIOCompletionPort() failed"); break; } /*********************************************/ /* Create a worker thread */ /* to process all client requests. The */ /* worker thread will wait for client */ /* requests to arrive on the I/O completion */ /* port just created. */ /*********************************************/ rc = pthread_create(&thr, NULL, workerThread, &ioCompPort); if (rc < 0) { perror("pthread_create() failed"); break; } /* open a gsk environment */ rc = errno = 0; rc = gsk_environment_open(&my_env_handle); if (rc != GSK_OK) { printf("gsk_environment_open() failed with rc = %d & errno = %d.\n", rc,errno); printf("rc of %d means %s\n", rc, gsk_strerror(rc)); break; } /* set the Application ID to use */ rc = errno = 0; rc = gsk_attribute_set_buffer(my_env_handle, GSK_OS400_APPLICATION_ID, "MY_SERVER_APP", 13); if (rc != GSK_OK) { printf("gsk_attribute_set_buffer() failed with rc = %d & errno = %d.\n" ,rc,errno); printf("rc of %d means %s\n", rc, gsk_strerror(rc)); break; } /* set this side as the server */ rc = errno = 0; rc = gsk_attribute_set_enum(my_env_handle, GSK_SESSION_TYPE, GSK_SERVER_SESSION); if (rc != GSK_OK) { printf("gsk_attribute_set_enum() failed with rc = %d & errno = %d.\n", rc,errno); printf("rc of %d means %s\n", rc, gsk_strerror(rc)); break; } /* by default SSL_V2, SSL_V3, and TLS_V1 are enabled */ /* We will disable SSL_V2 for this example. */ rc = errno = 0; rc = gsk_attribute_set_enum(my_env_handle, GSK_PROTOCOL_SSLV2, GSK_PROTOCOL_SSLV2_OFF); if (rc != GSK_OK) { printf("gsk_attribute_set_enum() failed with rc = %d & errno = %d.\n", rc,errno); printf("rc of %d means %s\n", rc, gsk_strerror(rc)); break; } /* set the cipher suite to use. By default our default list */ /* of ciphers is enabled. For this example we will just use one */ rc = errno = 0; rc = gsk_attribute_set_buffer(my_env_handle, GSK_V3_CIPHER_SPECS, "05", /* SSL_RSA_WITH_RC4_128_SHA */ 2); if (rc != GSK_OK) { printf("gsk_attribute_set_buffer() failed with rc = %d & errno = %d.\n" ,rc,errno); printf("rc of %d means %s\n", rc, gsk_strerror(rc)); break; } /* Initialize the secure environment */ rc = errno = 0; rc = gsk_environment_init(my_env_handle); if (rc != GSK_OK) { printf("gsk_environment_init() failed with rc = %d & errno = %d.\n", rc,errno); printf("rc of %d means %s\n", rc, gsk_strerror(rc)); break; } /* initialize a socket to be used for listening */ lsd = socket(AF_INET, SOCK_STREAM, 0); if (lsd < 0) { perror("socket() failed"); break; } /* set socket so can be reused immediately */ rc = setsockopt(lsd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); if (rc < 0) { perror("setsockopt() failed"); break; } /* bind to the local server address */ memset((char *) &address, 0, sizeof(address)); address.sin_family = AF_INET; address.sin_port = 13333; address.sin_addr.s_addr = 0; rc = bind(lsd, (struct sockaddr *) &address, sizeof(address)); if (rc < 0) { perror("bind() failed"); break; } /* enable the socket for incoming client connections */ listen(lsd, 5); if (rc < 0) { perror("listen() failed"); break; } /* accept an incoming client connection */ al = sizeof(address); sd = accept(lsd, (struct sockaddr *) &address, &al); if (sd < 0) { perror("accept() failed"); break; } /* open a secure session */ rc = errno = 0; rc = gsk_secure_soc_open(my_env_handle, &my_session_handle); if (rc != GSK_OK) { printf("gsk_secure_soc_open() failed with rc = %d & errno = %d.\n", rc,errno); printf("rc of %d means %s\n", rc, gsk_strerror(rc)); break; } /* associate our socket with the secure session */ rc=errno=0; rc = gsk_attribute_set_numeric_value(my_session_handle, GSK_FD, sd); if (rc != GSK_OK) { printf("gsk_attribute_set_numeric_value() failed with rc = %d ", rc); printf("and errno = %d.\n", errno); printf("rc of %d means %s\n", rc, gsk_strerror(rc)); break; } /* initiate the SSL handshake */ rc = errno = 0; rc = gsk_secure_soc_init(my_session_handle); if (rc != GSK_OK) { printf("gsk_secure_soc_init() failed with rc = %d & errno = %d.\n", rc,errno); printf("rc of %d means %s\n", rc, gsk_strerror(rc)); break; } /*********************************************/ /* Issue gsk_secure_soc_startRecv() to */ /* receive client request. */ /* Note: */ /* postFlag == on denoting request should */ /* posted to the I/O completion port, even */ /* if request is immediately available. */ /* Worker thread will process client request.*/ /*********************************************/ /*********************************************/ /* initialize Qso_OverlappedIO_t structure - */ /* reserved fields must be hex 00's. */ /*********************************************/ memset(&ioStruct, '\0', sizeof(ioStruct)); memset((char *) buff, 0, sizeof(buff)); ioStruct.buffer = buff; ioStruct.bufferLength = sizeof(buff); /*********************************************/ /* Store the session handle in the */ /* Qso_OverlappedIO_t descriptorHandle field.*/ /* This area is used to house information */ /* defining the state of the client */ /* connection. Field descriptorHandle is */ /* defined as a (void *) to allow the server */ /* to address more extensive client */ /* connection state if needed. */ /*********************************************/ ioStruct.descriptorHandle = my_session_handle; ioStruct.postFlag = 1; ioStruct.fillBuffer = 0; rc = gsk_secure_soc_startRecv(my_session_handle, ioCompPort, &ioStruct); if (rc != GSK_AS400_ASYNCHRONOUS_RECV) { printf("gsk_secure_soc_startRecv() rc = %d & errno = %d.\n",rc,errno); printf("rc of %d means %s\n", rc, gsk_strerror(rc)); break; } /*********************************************/ /* This is where the server can loop back */ /* to accept a new connection. */ /*********************************************/ /*********************************************/ /* Wait for worker thread to finish */ /* processing client connection. */ /*********************************************/ rc = pthread_join(thr, &status); /* check status of the worker */ if ( rc == 0 && (rc = __INT(status)) == Success) { printf("Success.\n"); successFlag = TRUE; } else { perror("pthread_join() reported failure"); } } while(FALSE); /* disable the SSL session */ if (my_session_handle != NULL) gsk_secure_soc_close(&my_session_handle); /* disable the SSL environment */ if (my_env_handle != NULL) gsk_environment_close(&my_env_handle); /* close the listening socket */ if (lsd > -1) close(lsd); /* close the accepted socket */ if (sd > -1) close(sd); /* destroy the completion port */ if (ioCompPort > -1) QsoDestroyIOCompletionPort(ioCompPort); if (successFlag) exit(0); else exit(-1); } /********************************************************************/ /* Function Name: workerThread */ /* */ /* Descriptive Name: Process client connection. */ /* */ /* Note: To make the sample more straight forward the main routine */ /* handles all of the clean up although this function can */ /* be made responsible for the clientfd and session_handle. */ /********************************************************************/ void *workerThread(void *arg) { struct timeval waitTime; int ioCompPort = -1, clientfd = -1; Qso_OverlappedIO_t ioStruct; int rc, tID; int amtWritten; gsk_handle client_session_handle = NULL; pthread_t thr; pthread_id_np_t t_id; t_id = pthread_getthreadid_np(); tID = t_id.intId.lo; /*********************************************/ /* I/O completion port is passed to this */ /* routine. */ /*********************************************/ ioCompPort = *(int *)arg; /*********************************************/ /* Wait on the supplied I/O completion port */ /* for a client request. */ /*********************************************/ waitTime.tv_sec = 500; waitTime.tv_usec = 0; rc = QsoWaitForIOCompletion(ioCompPort, &ioStruct, &waitTime); if ((rc == 1) && (ioStruct.returnValue == GSK_OK) && (ioStruct.operationCompleted == GSKSECURESOCSTARTRECV)) /*********************************************/ /* Client request has been received. */ /*********************************************/ ; else { perror("QsoWaitForIOCompletion()/gsk_secure_soc_startRecv() failed"); printf("ioStruct.returnValue = %d.\n", ioStruct.returnValue); return __VOID(Failure); } /* write results to screen */ printf("gsk_secure_soc_startRecv() received %d bytes, here they are:\n", ioStruct.secureDataTransferSize); printf("%s\n",ioStruct.buffer); /*********************************************/ /* Obtain the session handle associated */ /* with the client connection. */ /*********************************************/ client_session_handle = ioStruct.descriptorHandle; /* get the socket associated with the secure session */ rc=errno=0; rc = gsk_attribute_get_numeric_value(client_session_handle, GSK_FD, &clientfd); if (rc != GSK_OK) { printf("gsk_attribute_get_numeric_value() rc = %d & errno = %d.\n", rc,errno); printf("rc of %d means %s\n", rc, gsk_strerror(rc)); return __VOID(Failure); } /* send the message to the client using the secure session */ amtWritten = 0; rc = gsk_secure_soc_write(client_session_handle, ioStruct.buffer, ioStruct.secureDataTransferSize, &amtWritten); if (amtWritten != ioStruct.secureDataTransferSize) { if (rc != GSK_OK) { printf("gsk_secure_soc_write() rc = %d and errno = %d.\n", rc,errno); printf("rc of %d means %s\n", rc, gsk_strerror(rc)); return __VOID(Failure); } else { printf("gsk_secure_soc_write() did not write all data.\n"); return __VOID(Failure); } } /* write results to screen */ printf("gsk_secure_soc_write() wrote %d bytes...\n", amtWritten); printf("%s\n",ioStruct.buffer); return __VOID(Success); } /* end workerThread */