TCP/IP

28. TCP/IP SSL通信サーバー

TCP/IP の Socket通信は、それほど難しいものではなくサイト検索や一般書籍、
あるいは iSeries の TCP/IP解説書などにサンプル・ソースの例はいくらでもある。

しかし SSL となるとサイトでも SSL の原理の説明ばかりで肝心のソースの例は少ない。
そこでここでは SSL 通信を行うサーバーとクライアントの例をシンプルでかつ、
わかりやすい解説を加えてみよう。

Socket 通信では Socket識別子というハンドルによって通信を識別して通信を行っていたが
SSL では Socket識別子の代わりに アプリケーション識別とSocket識別子から得られる
SSLハンドルによって通信するのである。
つまり Socket の代わりに SSLハンドルを使うものと理解するだけでよい。
たったこれだけのことである。

SSLサーバーの手順としては、最初に SSL環境をセットしておいてから通常の TCP/IPサーバーの
処理に加えて、「CREATE AND HANDSHAKE」 という処理によって
クライアントとの認証作業を行う。
認証が成功すると 正しい SSLハンドルを取得することができるので、後はこのSSLハンドルによって
通信するだけである。

そのような理解のもとに下記のソースを参照されたい。

【 SSLSVR: SSL による TCP/IPサーバー・デーモン ( C/400) 】
/********************************************************************** 
/*                                                                      
/*   SSLSVR  : TEST SSL SERVER                                          
/*                                                                      
/********************************************************************** 
#define _MULTI_THREADED                                                 
#include <pthread.h>                                                    
#include <stdio.h>                                                      
#include <stdlib.h>                                                     
#include <string.h>                                                     
#include <sys/socket.h>                                                 
#include <sys/types.h>                                                  
#include <netinet/in.h>                                                 
#include <netinet/tcp.h>                                                
#include <arpa/inet.h>                                                  
#include <ssl.h>                                                        
#include <errno.h>                                                      
                                                                        
#define TRUE         0                                                  
#define FALSE       -1                                                  
SSLHandle*  sslh;                                                    
SSLInitApp  sslinit;                                                 
void * malloc_ptr = (void*)NULL;                                     
unsigned int malloc_size = 8192;                                     
unsigned short int cipher = SSL_RSA_WITH_RC4_128_SHA;                
char ssl_msg[256];                                                   
typedef struct {                                                     
   int  BYTESPRO;                                                    
   int  BYTESAVL;                                                    
   char MSGID[7];                                                    
   char RESRVD;                                                      
   char EXCPDATA[100];                                               
} ERRSTRUCTURE;     /* Define the error return structure            *
ERRSTRUCTURE  errcode;/* Error Code Structure for RCVMSG      */     
int PORT = 443;                                                      
  pthread_t  sig_thread;                                             
                                                                     
void  INZSR(void);                                                   
int   SSL_INZSR(void);                                               
int   SSL_CREATE_AND_HANDSHAKE(int* sockfd);                         
int   SSL_READ(char* buff, int len);                                 
int   SSL_WRITE(char* buff, int len);                             
void  SSL_CLOSE(void);                                            
char* SSL_error(int rc);                                          
void  err_log(char* msg, int opt, char* function, int line);      
void  closesocket(int sockfd);                                    
                                                                  
void main(void){                                                  
   int sockfd, len, rc, newfd, on = 1;                            
  struct sockaddr_in iaddr;                                       
   char buff[48];                                                 
    char errmsg[128];                                             
                                                                  
   printf("** SSLSVR **n");                                      
   getchar();                                                     
   INZSR();                                                       
   if(SSL_INZSR() == FALSE){/*SSL_INZSR*/                         
     exit(0);                                                     
   }/*SSL_INZSR*/                                                 
  /*-------------------------------------------------------*/     
  /*         socket : Get a socket descriptor              */     
  /*-------------------------------------------------------*/     
  if((sockfd = socket(AF_INET, SOCK_STREAM, 0 )) < 0){                 
     perror("SOCKET"); getchar(); exit(0);                             
  }                                                                    
  if((rc = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,                
         (char*)&on,  sizeof(on))) < 0){                               
     perror("SOCKOPT"); getchar(); exit(0);                            
  }                                                                    
  /*-------------------------------------------------------*/          
  /*         bind : bind address to the socket             */          
  /*-------------------------------------------------------*/          
  memset(&iaddr, 0x00, sizeof(struct sockaddr_in));                    
  iaddr.sin_family = AF_INET;                                          
  iaddr.sin_port  = htons(PORT); /* PORT no set*/                      
  iaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* any address OK */      
  if(bind(sockfd, (struct sockaddr *)&iaddr, sizeof(iaddr)) < 0){      
     perror("BIND"); getchar(); exit(0);                               
  }                                                                    
          /*---------------------------------------------------------*/
          /*   listen : wait for new client's call                   */
          /*            Up to 5 clients can be queued                */
          /*---------------------------------------------------------*/
          rc = listen(sockfd, 10);                                      
          if(rc < 0){/* LISTEN */                                       
     perror("LISTEN"); getchar(); exit(0);                              
          }/* LISTEN */                                                 
          /*---------------------------------------------------------*/ 
          /*   accept : accept new client's request                  */ 
          /*            from parent, should block                    */ 
          /*---------------------------------------------------------*/ 
          newfd = accept(sockfd, (struct sockaddr *)NULL, NULL);        
          if(newfd < 0){/*failed*/                                      
             perror("ACCEPT"); getchar(); exit(0);                      
           }/*failed*/                                                  
          /*---------------------------------------------------------*/ 
          /*   SSL_Create : enable SSL support                       */ 
          /*---------------------------------------------------------*/ 
          if(SSL_CREATE_AND_HANDSHAKE(&newfd) == FALSE){                
             perror("SSL_CREATE");                                      
             close(sockfd); close(newfd);                               
             getchar();                                                 
             exit(0);                                                   
          }                                                             
          /*---------------------------------------------------------*/ 
          /*   SSL_READ   : receive message from the client          */ 
          /*---------------------------------------------------------*/ 
          len = 48;                                                     
          memset(buff, 0, sizeof(buff));                                
          if(SSL_READ(buff, len) == FALSE){                             
             perror("SSL_READ"); getchar(); exit(0);                    
          }                                                             
          /*---------------------------------------------------------*/ 
          /*   SSL_WRITE  : send the message to the client           */ 
          /*---------------------------------------------------------*/ 
          memset(buff, 0, sizeof(buff));                                
          strcpy(buff, "* SSL SERVER *");                               
          if(SSL_WRITE(buff, len) == FALSE){                            
             perror("SSL_WRITE"); getchar(); exit(0);                   
          }                                                             
          SSL_CLOSE();                                                  
    close(sockfd);                                                      
    close(newfd);                                                       
    printf("**************************************・n");                 
    printf("*   SSLSVR   SUCCESSFULLY COMPLETE ! *・n");                 
    printf("**************************************・n");           
    getchar();                                                    
    return;                                                       
                                                                  
}                                                                 
/****************/                                                
void  INZSR(void)                                                 
/****************/                                                
 {                                                                
                                                                  
    errcode.BYTESPRO = errcode.BYTESAVL = 0;                      
 }                                                                
/********************/                                            
int   SSL_INZSR(void)                                             
/********************/                                            
 {                                                                
    int rc = 0;                                                   
    char errmsg[128];                                             
                                                                  
    memset((char*)&sslinit, 0, sizeof(sslinit));                  
    sslinit.applicationID = "MY_SERVER_APP";                      
    sslinit.applicationIDLen = 13;                                  
    sslinit.localCertificate = NULL;                                
    sslinit.localCertificateLen = 0;                                
    sslinit.cipherSuiteList = NULL;                                 
    sslinit.cipherSuiteListLen = 0;                                 
                                                                    
    malloc_ptr = (void*)malloc(malloc_size);                        
    sslinit.localCertificate = (unsigned char*)malloc_ptr;          
    sslinit.localCertificateLen = malloc_size;                      
                                                                    
    rc = SSL_Init_Application(&sslinit);                            
    if(rc != 0){/* init App err */                                  
      memset(errmsg, 0, sizeof(errmsg));                            
      sprintf(errmsg,                                               
             "SSL_INZSR:SSL_Init_App ERROR = %s・n", SSL_error(rc))  
      err_log(errmsg, FALSE, "SSL_INZSR", __LINE__);                
      return FALSE;                                                 
    }/* init App err */                                             
                                                                    
    return TRUE;                                                    
}                                                                   
/******************************************/                            
int   SSL_CREATE_AND_HANDSHAKE(int* sockfd)                             
/******************************************/                            
 {                                                                      
    int rc;                                                             
    char errmsg[128];                                                   
                                                                        
    sslh = SSL_Create(*sockfd, SSL_ENCRYPT);                            
    if(sslh == NULL){/* failed in create */                             
       memset(errmsg, 0, sizeof(errmsg));                               
       sprintf(errmsg, "SSL_Create failed = %s・n", SSL_error(rc));      
       err_log(errmsg, FALSE, "SSL_CRETAE_AND_HANDSHAKE", __LINE__);    
       return FALSE;                                                    
    }/* failed in create */                                             
                                                                        
    /* set parameter for handshake */                                   
    sslh->protocol = 0;                                                 
    sslh->timeout  = 0;                                                 
    sslh->cipherSuiteList = &cipher;                                    
    sslh->cipherSuiteListLen = 1;                                       
                                                                        
    /* initiate the SSL handshake */                                  
    rc = SSL_Handshake(sslh,                                          
                   SSL_HANDSHAKE_AS_SERVER);                          
    if(rc != 0){/* handshake failed */                                
       memset(errmsg, 0, sizeof(errmsg));                             
       sprintf(errmsg, "handshake failed = %s・n", SSL_error(rc));     
       err_log(errmsg, FALSE, "SSL_CREATE_AND_HANDSHAKE", __LINE__);  
       SSL_Destroy(sslh);                                             
       closesocket(*sockfd);                                          
       return FALSE;                                                  
    }/* handshake failed */                                           
                                                                      
    return TRUE;                                                      
}                                                                     
/**********************************/                                  
int   SSL_READ(char* buff, int len)                                   
/**********************************/                                  
 {                                                                    
    int rc;                                                           
    char errmsg[128];                                                 
                                                                      
    rc = SSL_Read(sslh, buff, len);                                    
    if(rc < 0){/* failed in read */                                    
       memset(errmsg, 0, sizeof(errmsg));                              
       sprintf(errmsg, "failed in SSL_Read = %s・n", SSL_error(rc));    
       err_log(errmsg, FALSE, "SSL_READ", __LINE__);                   
       SSL_Destroy(sslh);                                              
       return FALSE;                                                   
    }/* failed in read */                                              
    return rc;                                                         
}                                                                      
/***********************************/                                  
int   SSL_WRITE(char* buff, int len)                                   
/***********************************/                                  
 {                                                                     
    int rc;                                                            
    char errmsg[128];                                                  
                                                                       
    rc = SSL_Write(sslh, buff, len);                                   
    if(rc < 0){/* failed in write */                                   
       memset(errmsg, 0, sizeof(errmsg));                              
       sprintf(errmsg, "failed in SSL_Write = %s・n", SSL_error(rc));   
       err_log(errmsg, FALSE, "SSL_WRITE", __LINE__);       
       SSL_Destroy(sslh);                                   
       return FALSE;                                        
    }/* failed in write */                                  
    return rc;                                              
}                                                           
/********************/                                      
void  SSL_CLOSE(void)                                       
/********************/                                      
 {                                                          
     SSL_Destroy(sslh);                                     
}                                                           
/**********************/                                    
char* SSL_error(int rc)                                     
/**********************/                                    
{                                                           
                                                            
   memset(ssl_msg, 0, sizeof(ssl_msg));                     
   printf("rc = %d・n", rc);                                 
   switch(rc){/*switch*/                                    
   case  SSL_ERROR_NO_CIPHERS:                              
            strcpy(ssl_msg,                                             
            " 適切でない CIPHER が指定されました。 ");                  
            break;                                                      
   case  SSL_ERROR_CERT_EXPIRED:                                        
            strcpy(ssl_msg,                                             
            "the validity time period of certificate is expired.");     
            break;                                                      
   case  SSL_ERROR_KEYPASSWORD_EXPIRED:                                 
            strcpy(ssl_msg,                                             
            "the specified key ring pasword has expied.");              
            break;                                                      
   case  SSL_ERROR_NO_KEYRING:                                          
            strcpy(ssl_msg,                                             
            "No key ring file was found.");                             
            break;                                                      
   case  SSL_ERROR_NOT_REGISTERED:                                      
            strcpy(ssl_msg,                                             
            "The applicatin identifier is not registored width certifi・ 
cate registry facility.");                                              
            break;                                                      
   case  SSL_ERROR_NOT_TRUSTED_ROOT:                                    
            strcpy(ssl_msg,                                             
            "The certificate is not signed by a tructed certificate ・   
authority.");                                                           
            break;                                                      
   case  SSL_ERROR_NO_CERTIFICATE:                                      
            strcpy(ssl_msg,                                             
            "No certificate is available for SSL processing.");         
            break;                                                      
   case  SSL_ERROR_IO:                                                  
            strcpy(ssl_msg,                                             
            "An error occurd in SSL processing; check the error value") 
            break;                                                      
   case  SSL_ERROR_SSL_NOT_AVAILABLE:                                   
            strcpy(ssl_msg,                                             
            "SSL が使用可能ではありません。 ");                         
            break;                                                      
   case  SSL_ERROR_BAD_CERTIFICATE:                                     
            strcpy(ssl_msg,                                             
            "SSL 認証が正しくない。 ");                                 
            break;                                                      
   case  SSL_ERROR_BAD_CERT_SIG:                                        
            strcpy(ssl_msg,                                             
            "SSL 認証のサインが正しくない。 ");                         
            break;                                                      
   case  SSL_ERROR_BAD_CIPHER_SUITE:                                    
          strcpy(ssl_msg, " 指定された CIPHER が正しくありません。 ");  
            break;                                                      
   case  SSL_ERROR_BAD_MAC:                                             
   strcpy(ssl_msg, "A bad message autherntication code was recieved."); 
            break;                                                      
   case  SSL_ERROR_BAD_MALLOC:                                          
     strcpy(ssl_msg, "unable to allocate storage for SSL");             
            break;                                                      
   case  SSL_ERROR_BAD_MESSAGE:                                         
     strcpy(ssl_msg, "SSL received a badlly formated msg");             
            break;                                                      
   case  SSL_ERROR_BAD_PEER:                                            
     strcpy(ssl_msg, "the peer system is not recognized");              
            break;                                                      
   case  SSL_ERROR_BAD_STATE:                                           
     strcpy(ssl_msg, "SSL detected a bad state in SSL session");        
            break;                                                      
   case  SSL_ERROR_CERTIFICATE_REJECTED:                                
     strcpy(ssl_msg,"certificate is not valid or rejected by ext pgm"); 
            break;                                                      
   case  SSL_ERROR_CLOSED:                                              
     strcpy(ssl_msg, "SSL session ended");                              
            break;                                                      
   case  SSL_ERROR_NO_INIT:                                             
     strcpy(ssl_msg,"SSL_Init was not previously called for this job"); 
            break;                                                      
   case  SSL_ERROR_PERMISSION_DENIED:                                   
     strcpy(ssl_msg, "PERMISSIO_DENIED");                               
            break;                                                      
   case  SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE:                        
     strcpy(ssl_msg, "UNSUPPORTED_CERTIFICATE_TYPE");                   
            break;                                                      
   case  SSL_ERROR_UNKNOWN:                                             
            strcpy(ssl_msg,                                             
            "An unknown oe unexpected error occued durering SSL ・       
processing");                                                           
            break;                                                      
   default:                                                             
            sprintf(ssl_msg, "rc = %d", rc);                       
   }/*switch*/                                                     
   return ssl_msg;                                                 
}                                                                  
/********************************************************/         
void  err_log(char* msg, int opt, char* err_at, int line)          
/********************************************************/         
{                                                                  
    fprintf(stderr, "%s・n", msg);                                  
    fprintf(stderr, " ->ERR AT = %s, LINE = %d・n", err_at, line);  
    if(opt == TRUE)                                                
      fprintf(stderr, "%s・n", strerror(errno));                    
}                                                                  
/****************************/                                     
void  closesocket(int sockfd)                                      
/****************************/                                     
{                                                                  
    close(sockfd);                                                 
}
【 解説 】

コンパイルは

CRTBNDC MYOBJLIB/SSLSVR SRCFILE(MYSRCLIB/QCSRC) AUT(*ALL)

キー・ポイントとなるのは

sslinit.applicationID = "MY_SERVER_APP";
sslinit.applicationIDLen = 13;

という 13桁の 「MY_SERVER_APP」 というアプリケーション識別子を宣言している部分である。
このプログラムが SSLの証明書つきで通信を認証するにはプログラムの名前ではなく、
アプリケーション識別子」という ID をディジタル証明書マネージャー(DCM) によって
証明書を割り当てておく必要がある。
このように SSL通信プログラム自体のコーディングは、それほど難しいものではないが、むしろ
ディジタル証明書マネージャー(DCM) の操作に骨が折れる。
また #include <ssl.h> によって SSLのサービス・プログラムである QSOSSLSR
自動的に組み込まれるが、これが SSL を使用可能とする *SRVPGM でありOS400 V4R3M0 から
提供されている。

IBM は OS400 から GSKIT という SSL のための新しい別の API を公示したが、
機能としての *SRVPGM は、同じ QSOSSLSR に組み込まれている。
IBM は GSKIT を推奨しているようだが 上記の例にある SSL API のほうが
やさしく使用することができる。
なお、上記の例では SSL_error というエラーメッセージ処理関数を用意しているが
OS400 V5R1M0 からは QSOSSLSR の中に SSL_Error という API が用意されている。