Implement openssl multithreaded SSL server and client using pthread library

  • 2020-04-02 02:08:29
  • OfStack

The server code is as follows:


#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#ifndef    _WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#else
#include <winsock2.h>
#include <windows.h>
#endif
#include "pthread.h"
#include <openssl/rsa.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define CERTF "certs/sslservercert.pem"
#define KEYF  "certs/sslserverkey.pem"
#define    CAFILE  "certs/cacert.pem"
pthread_mutex_t    mlock=PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t *lock_cs;
static long *lock_count;
#define CHK_NULL(x) if ((x)==NULL) { printf("nulln"); }
#define CHK_ERR(err,s) if ((err)==-1) { printf(" -1 n"); }
#define CHK_SSL(err) if ((err)==-1) {  printf(" -1 n");}
#define    CAFILE  "certs/cacert.pem"

int  verify_callback_server(int ok, X509_STORE_CTX *ctx)
{
              printf("verify_callback_server n");
        return ok;
}

int    SSL_CTX_use_PrivateKey_file_pass(SSL_CTX *ctx,char *filename,char *pass)
{
       EVP_PKEY     *pkey=NULL;
       BIO               *key=NULL;

       key=BIO_new(BIO_s_file());
       BIO_read_filename(key,filename);
       pkey=PEM_read_bio_PrivateKey(key,NULL,NULL,pass);
       if(pkey==NULL)
       {
              printf("PEM_read_bio_PrivateKey err");
              return -1;
       }
       if (SSL_CTX_use_PrivateKey(ctx,pkey) <= 0)
       {
              printf("SSL_CTX_use_PrivateKey errn");
              return -1;
       }
       BIO_free(key);
       return 1;
}

static int s_server_verify=SSL_VERIFY_NONE;
void * thread_main(void *arg)
{  
       SOCKET s,AcceptSocket;
       WORD wVersionRequested;
       WSADATA wsaData;
       struct sockaddr_in  service;
       int    err;
      size_t             client_len;                                                                                           SSL_CTX             *ctx;
      SSL        *ssl;
      X509             *client_cert;
      char        *str;
      char    buf[1024];
      SSL_METHOD     *meth;

       ssl=(SSL *)arg;
       s=SSL_get_fd(ssl);
       err = SSL_accept (ssl);
      if(err<0)
       {
              printf("ssl accerrn");
              return ;
       }
      printf ("SSL connection using %sn", SSL_get_cipher (ssl));
      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);
                   CHK_NULL(str);
                   printf ("t subject: %sn", str);
                   OPENSSL_free (str);
                     str = X509_NAME_oneline (X509_get_issuer_name  (client_cert), 0, 0);
                   CHK_NULL(str);
                   printf ("t issuer: %sn", str);
                   OPENSSL_free (str);
                     X509_free (client_cert);
      }
      else
                  printf ("Client does not have certificate.n");
       memset(buf,0,1024);
       err = SSL_read (ssl, buf, sizeof(buf) - 1);
       if(err<0)
       {
              printf("ssl read errn");
              closesocket(s);
              return;
       }
       printf("get : %sn",buf);
#if 0
      buf[err] = '0';
      err = SSL_write (ssl, "I hear you.", strlen("I hear you."));  CHK_SSL(err);
#endif
      SSL_free (ssl);
       closesocket(s);
}

pthread_t pthreads_thread_id(void)
{
       pthread_t ret;

       ret=pthread_self();
       return(ret);
}

void pthreads_locking_callback(int mode, int type, char *file,
            int line)
{
       if (mode & CRYPTO_LOCK)
              {
              pthread_mutex_lock(&(lock_cs[type]));
              lock_count[type]++;
              }
       else
              {
              pthread_mutex_unlock(&(lock_cs[type]));
              }
}

int main ()
{
       int                  err;                 
       int                  i;
       SOCKET        s,AcceptSocket;
       WORD           wVersionRequested;
       WSADATA            wsaData;
       struct sockaddr_in  service;
       pthread_tpid;
      size_t             client_len;
      SSL_CTX             *ctx;
      SSL               *ssl;
      X509             *client_cert;
       char        *str;
      char    buf[1024];
      SSL_METHOD     *meth;

      SSL_load_error_strings();
      SSLeay_add_ssl_algorithms();
      meth = SSLv3_server_method();
      ctx = SSL_CTX_new (meth);
      if (!ctx)
      {
                  ERR_print_errors_fp(stderr);
                  exit(2);
      }
       if ((!SSL_CTX_load_verify_locations(ctx,CAFILE,NULL)) ||
                (!SSL_CTX_set_default_verify_paths(ctx)))
    {
              printf("errn");
              exit(1);
    }
      if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0)
      {
           ERR_print_errors_fp(stderr);
           exit(3);
      }
      if (SSL_CTX_use_PrivateKey_file_pass(ctx, KEYF, "123456") <= 0)
      {
                  ERR_print_errors_fp(stderr);
                  exit(4);
      }
       if (!SSL_CTX_check_private_key(ctx))
       {
                  fprintf(stderr,"Private key does not match the certificate public keyn");
                  exit(5);
      }
       s_server_verify=SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT|
                                SSL_VERIFY_CLIENT_ONCE;
       SSL_CTX_set_verify(ctx,s_server_verify,verify_callback_server);
       SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file(CAFILE));
       wVersionRequested = MAKEWORD( 2, 2 );
       err = WSAStartup( wVersionRequested, &wsaData );
       if ( err != 0 )
       {
              printf("errn");      
              return -1;
       }
       s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
       if(s<0) return -1;
       service.sin_family = AF_INET;
       service.sin_addr.s_addr = inet_addr("127.0.0.1");
       service.sin_port = htons(1111);
       if (bind( s, (SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR)
       {
              printf("bind() failed.n");
              closesocket(s);
              return -1;
       }
    if (listen( s, 1 ) == SOCKET_ERROR)
              printf("Error listening on socket.n");

       printf("recv .....n");
       lock_cs=OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
       lock_count=OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
       for (i=0; i<CRYPTO_num_locks(); i++)
       {
              lock_count[i]=0;
              pthread_mutex_init(&(lock_cs[i]),NULL);
       }
       CRYPTO_set_id_callback((unsigned long (*)())pthreads_thread_id);
       CRYPTO_set_locking_callback((void (*)())pthreads_locking_callback);
       while(1)
       {
              struct timeval tv;
              fd_set fdset;
              tv.tv_sec = 1;
              tv.tv_usec = 0;
              FD_ZERO(&fdset);
              FD_SET(s, &fdset);
           select(s+1, &fdset, NULL, NULL, (struct timeval *)&tv);
           if(FD_ISSET(s, &fdset))
              {
                     AcceptSocket=accept(s, NULL,NULL);
                     ssl = SSL_new (ctx);      
                    CHK_NULL(ssl);
                     err=SSL_set_fd (ssl, AcceptSocket);
                     if(err>0)
                     {
                            err=pthread_create(&pid,NULL,&thread_main,(void *)ssl);
                            pthread_detach(pid);
                     }
                     else
                            continue;
              }
       }
      SSL_CTX_free (ctx);
      return 0;
}

The client code is as follows:


#include <stdio.h>
#include <memory.h>
#include <errno.h>
#ifndef    _WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#else
#include <windows.h>
#endif
#include "pthread.h"
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define    MAX_T 1000
#define    CLIENTCERT       "certs/sslclientcert.pem"
#define    CLIENTKEY  "certs/sslclientkey.pem"
#define    CAFILE         "certs/cacert.pem"
static pthread_mutex_t *lock_cs;
static long *lock_count;

pthread_t pthreads_thread_id(void)
{
       pthread_t ret;

       ret=pthread_self();
       return(ret);
}

void pthreads_locking_callback(int mode, int type, char *file,
            int line)
{
       if (mode & CRYPTO_LOCK)
              {
              pthread_mutex_lock(&(lock_cs[type]));
              lock_count[type]++;
              }
       else
              {
              pthread_mutex_unlock(&(lock_cs[type]));
              }
}

int    verify_callback(int ok, X509_STORE_CTX *ctx)
{
       printf("verify_callbackn");
       return ok;
}

int    SSL_CTX_use_PrivateKey_file_pass(SSL_CTX *ctx,char *filename,char *pass)
{
       EVP_PKEY     *pkey=NULL;
       BIO               *key=NULL;

       key=BIO_new(BIO_s_file());
       BIO_read_filename(key,filename);
       pkey=PEM_read_bio_PrivateKey(key,NULL,NULL,pass);
       if(pkey==NULL)
       {
              printf("PEM_read_bio_PrivateKey err");
              return -1;
       }
       if (SSL_CTX_use_PrivateKey(ctx,pkey) <= 0)
       {
              printf("SSL_CTX_use_PrivateKey errn");
              return -1;
       }
       BIO_free(key);
       return 1;
}

void*thread_main(void *arg)
{
       int          err,buflen,read;
      int          sd;
       SSL_CTX             *ctx=(SSL_CTX *)arg;
       struct            sockaddr_in dest_sin;
       SOCKET        sock;
       PHOSTENT   phe;
       WORD           wVersionRequested;
       WSADATA            wsaData;
      SSL               *ssl;
      X509             *server_cert;
      char     *str;
      char        buf [1024];
      SSL_METHOD     *meth;
       FILE              *fp;

       wVersionRequested = MAKEWORD( 2, 2 );
       err = WSAStartup( wVersionRequested, &wsaData );
       if ( err != 0 )
       {
              printf("WSAStartup errn");      
              return -1;
       }
       sock = socket(AF_INET, SOCK_STREAM, 0);
       dest_sin.sin_family = AF_INET;
       dest_sin.sin_addr.s_addr = inet_addr( "127.0.0.1" );
       dest_sin.sin_port = htons( 1111 );

again:
       err=connect( sock,(PSOCKADDR) &dest_sin, sizeof( dest_sin));
       if(err<0)
       {
              Sleep(1);
              goto again;
       }
    ssl = SSL_new (ctx);                        
       if(ssl==NULL)
       {
              printf("ss new errn");
              return ;
       }
       SSL_set_fd(ssl,sock);
      err = SSL_connect (ssl);                    
      if(err<0)
       {
              printf("SSL_connect errn");
              return;
       }
      printf ("SSL connection using %sn", SSL_get_cipher (ssl));
      server_cert = SSL_get_peer_certificate (ssl);      
      printf ("Server certificate:n");
      str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);
      printf ("t subject: %sn", str);
      OPENSSL_free (str);
      str = X509_NAME_oneline (X509_get_issuer_name  (server_cert),0,0);
      printf ("t issuer: %sn", str);
      OPENSSL_free (str);  
      X509_free (server_cert);
       err = SSL_write (ssl, "Hello World!", strlen("Hello World!"));
       if(err<0)
       {
              printf("ssl write errn");
              return ;
       }
#if 0
       memset(buf,0,ONE_BUF_SIZE);
      err = SSL_read (ssl, buf, sizeof(buf) - 1);                  
       if(err<0)
       {
              printf("ssl read errn");
              return ;
       }
      buf[err] = '0';
      printf ("Got %d chars:'%s'n", err, buf);
#endif
      SSL_shutdown (ssl);  /* send SSL/TLS close_notify */
      SSL_free (ssl);
       closesocket(sock);
}

int    main ()
{
       int          err,buflen,read;
      int          sd;

       struct            sockaddr_in dest_sin;
       SOCKETsock;
       PHOSTENT phe;
       WORD wVersionRequested;
       WSADATA wsaData;
      SSL_CTX             *ctx;
      SSL        *ssl;
      X509             *server_cert;
      char     *str;
      char        buf [1024];
      SSL_METHOD     *meth;
       int           i;
       pthread_tpid[MAX_T];

      SSLeay_add_ssl_algorithms();
      meth = SSLv3_client_method();
      SSL_load_error_strings();
      ctx = SSL_CTX_new (meth);                      
       if(ctx==NULL)
       {
              printf("ssl ctx new eern");
              return -1;
       }

       if (SSL_CTX_use_certificate_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM) <= 0)
    {
        ERR_print_errors_fp(stderr);
        exit(3);
    }
    if (SSL_CTX_use_PrivateKey_file_pass(ctx, CLIENTKEY, "123456") <= 0)
    {
         ERR_print_errors_fp(stderr);
         exit(4);
     }
       lock_cs=OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
       lock_count=OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
       for (i=0; i<CRYPTO_num_locks(); i++)
       {
              lock_count[i]=0;
              pthread_mutex_init(&(lock_cs[i]),NULL);
       }
       CRYPTO_set_id_callback((unsigned long (*)())pthreads_thread_id);
       CRYPTO_set_locking_callback((void (*)())pthreads_locking_callback);
       for(i=0;i<MAX_T;i++)
       {           
              err=pthread_create(&(pid[i]),NULL,&thread_main,(void *)ctx);
              if(err!=0)
              {
                     printf("pthread_create errn");
                     continue;
              }
       }
       for (i=0; i<MAX_T; i++)
       {
              pthread_join(pid[i],NULL);
       }
      SSL_CTX_free (ctx);
      printf("test okn");
       return 0;
}

The above program runs successfully under Windows, using the open source pthread library under Windows.
Note that if multiple threads use openssl, you need to set up two callback functions


CRYPTO_set_id_callback((unsigned long (*)())pthreads_thread_id);
CRYPTO_set_locking_callback((void (*)())pthreads_locking_callback);


Related articles: