Hi,
  I have 2 clients applications to send https request to 2 different web
server. My first client run on Linux Red Hat 7 and send https request to
IIS 4.0 web server on an NT 4.0 Server and my second client run on NT
4.0 Server and send https request to Apache web server on a Linux Red
Hat 7 machine. These 2 clients use the same C++ class using OpenSSL to
send http request on an SSL connection.

My first client application (client on Linux Red Hat 7 connecting to IIS
web server) work perfectly.

My second client application (client on NT 4 connection to Apache) have
some problem with the handshaking.
First, my client establish the connection with Apache when I call
BIO_write to send my https request. At this point, the handshaking seems
to work correctly (client and server certificates are exenged wihtout
any error).
But when I call BIO_read to read the response from the server, a
re-negotiation is performed and the BIO_read function fail and
BIO_should_retry return 0 after 1 or 2 retry (this appen only if Apache
need a client certificate).

My Apache setting:

Alias /php/ "/var/www/php/"

<Directory "/var/www/php/">
    AllowOverride None
    Order allow,deny
    Allow from All
    SSLVerifyClient require
    SSLVerifyDepth 1
    SSLRequireSSL
    SSLRequire (     %{SSL_CLIENT_S_DN_O} eq "TravelNet Technologies
Inc."  \
                 and %{SSL_CLIENT_S_DN_OU} eq "Data Valet" \
                 and %{SSL_CLIENT_S_DN_CN} eq "10.255.255.253")
    SSLOptions +StrictRequire +OptRenegotiate
</Directory>


My code:

#ifdef _WINDEF_
 #include <winsock2.h>
#else
 #include <string.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
#endif

#include "DVMacro.h"
#include "DVCertPasswd.h"
#include "SSLSupport.h"

#define CERT_ORGANISATION  "TravelNet Technologies Inc."
#define CERT_ORGANISATION_UNIT "Data Valet"
#define CERT_CITY    "Montreal"
#define CERT_STATE    "Quebec"
#define CERT_COUNTRY   "CA"

#define CA_CERT_ORGANISATION  "TravelNet Technologies Inc."
#define CA_CERT_ORGANISATION_UNIT "Data Valet"
#define CA_CERT_COMMON_NAME   "TravelNet"
#define CA_CERT_CITY    "Montreal"
#define CA_CERT_STATE    "Quebec"
#define CA_CERT_COUNTRY    "CA"

static char * g_sCertCommonName = NULL;

static int VerifyCallback(int preverify_ok, X509_STORE_CTX * ctx);
static int VerifyCertData( char * buf, bool bCA);
static int PasswordCallback(char *buf, int size, int rwflag, void
*password);

//////////////////////////////////////////////////////////////////////////////////////////////////

// This function is used to verify the content of the server certificate
or the CA certificate.
// This function is called by VerifyCallback().
// Return value: 0 if the data is not valide.
//               1 if the data is valide.
//
// Note: buf supposed to have this format:
// for CA
certificate:
/Email is not verify.
//   /C=CA/ST=Quebec/O=TravelNet Technologies Inc./OU=Data
[EMAIL PROTECTED]
// for server
certificate:                                                 /CN must be
Data Valet server IP address.
//   /C=CA/ST=Quebec/L=Montreal/O=TravelNet Technologies Inc./OU=Data //
[EMAIL PROTECTED]
//////////////////////////////////////////////////////////////////////////////////////////////////

static int VerifyCertData( char * buf, bool bCA)
{
 int iResult = 1;
 char * sField = NULL;
 char * sTemp = NULL;

#ifdef _WINDEF_

 if (buf)
  sTemp = strtok(buf, "/=");
 while (sTemp && iResult)
 {
  sField = strtok(NULL, "/=");
  if (sField)

#else

 if (buf)
 {
  if (buf[0] == '/')
   buf++;
  else
   iResult = 0;
 }
 while (buf && iResult)
 {
  sField = strsep(&buf, "/");
  if (sField)
  {
   sTemp = strsep(&sField, "=");
   if (sTemp)

#endif

   {
    if (strcmp(sTemp, "C") == 0)
    {
     if (bCA)
     {
      if (strcmp(sField, CA_CERT_COUNTRY) != 0)
       iResult = 0;
     }
     else
     {
      if (strcmp(sField, CERT_COUNTRY) != 0)
       iResult = 0;
     }
    }
    else if (strcmp(sTemp, "ST") == 0)
    {
     if (bCA)
     {
      if (strcmp(sField, CA_CERT_STATE) != 0)
       iResult = 0;
     }
     else
     {
      if (strcmp(sField, CERT_STATE) != 0)
       iResult = 0;
     }
    }
    else if (strcmp(sTemp, "L") == 0)
    {
     if (bCA)
     {
      if (strcmp(sField, CA_CERT_CITY) != 0)
       iResult = 0;
     }
     else
     {
      if (strcmp(sField, CERT_CITY) != 0)
       iResult = 0;
     }
    }
    else if (strcmp(sTemp, "O") == 0)
    {
     if (bCA)
     {
      if (strcmp(sField, CA_CERT_ORGANISATION) != 0)
       iResult = 0;
     }
     else
     {
      if (strcmp(sField, CERT_ORGANISATION) != 0)
       iResult = 0;
     }
    }
    else if (strcmp(sTemp, "OU") == 0)
    {
     if (bCA)
     {
      if (strcmp(sField, CA_CERT_ORGANISATION_UNIT) != 0)
       iResult = 0;
     }
     else
     {
      if (strcmp(sField, CERT_ORGANISATION_UNIT) != 0)
       iResult = 0;
     }
    }
    else if (strcmp(sTemp, "CN") == 0)
    {
     if (bCA)
     {
      if (strcmp(sField, CA_CERT_COMMON_NAME) != 0)
       iResult = 0;
     }
     else
     {
      if (strcmp(sField, g_sCertCommonName) != 0)
       iResult = 0;
     }
    }
    else if (strcmp(sTemp, "Email") == 0)
     iResult = 1; // Ignore this field.
    else
     iResult = 0; // invalid field.
   }
   else
    iResult = 0; // invalid field.
#ifdef _WINDEF_

  sTemp = strtok(NULL, "/=");

#else
  }
#endif
 }
 return iResult;
}

//////////////////////////////////////////////////////////////////////////////////////////////////

// VerifyCallback is called by OpenSSL when SSL_connect() is called in
OpenSSLConnection().
// This function is used to verify the content of the server certificate
and their CA certificate.
// Return value: 0 if the certificate is not valide.
//               1 if the certificate is valide.
//////////////////////////////////////////////////////////////////////////////////////////////////

static int VerifyCallback(int preverify_ok, X509_STORE_CTX * ctx)
{
 int     iResult = 0;
 char    buf[256];
 X509   *err_cert;
 int     err, depth;
 SSL    *ssl;

 if (preverify_ok == 1)
 {
  err_cert = X509_STORE_CTX_get_current_cert(ctx);
  err = X509_STORE_CTX_get_error(ctx);
  depth = X509_STORE_CTX_get_error_depth(ctx);

  if (depth > 2)
  {
   preverify_ok = 0;
   err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
   X509_STORE_CTX_set_error(ctx, err);
  }
  else
  {
      ssl = (SSL *)X509_STORE_CTX_get_ex_data(ctx,
SSL_get_ex_data_X509_STORE_CTX_idx());

   memset(buf, 0, 256);
   X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);

   if (depth == 0)
   {
    // Verify server certificate.
    if (VerifyCertData(buf, false))
    {
     memset(buf, 0, 256);
     X509_NAME_oneline(X509_get_issuer_name(err_cert), buf, 256);
     // Verify issuer => supposed to be the CA.
     iResult = VerifyCertData(buf, true);
    }
    if (iResult == 0)
    {
     err = X509_V_ERR_CERT_REJECTED;
     X509_STORE_CTX_set_error(ctx, err);
    }
   }
   else
   {
    // Verify CA certificate.
    if (VerifyCertData(buf, true))
    {
     memset(buf, 0, 256);
     X509_NAME_oneline(X509_get_issuer_name(err_cert), buf, 256);
     // Verify issuer => supposed to be the CA.
     iResult = VerifyCertData(buf, true);
    }
    if (iResult == 0)
    {
     err = X509_V_ERR_INVALID_CA;
     X509_STORE_CTX_set_error(ctx, err);
    }
   }
  }
 }
 return iResult;
}

static int PasswordCallback(char *buf, int size, int rwflag, void
*password)
{
 strncpy(buf, (char *)(password), size);
 buf[size - 1] = '\0';
 return(strlen(buf));
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////

// CSSLSupport class
/////////////////////////////////////////////////////////////////////////////////////////////////////////

CSSLSupport::CSSLSupport()
{
 m_pBIO = NULL;
 m_pSSLContext = NULL;
 m_pSSLConnection = NULL;
 m_bInitialized = false;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////

CSSLSupport::~CSSLSupport()
{
 if (m_pBIO)
  Disconnect();
 if (m_pSSLContext)
 {
  SSL_CTX_free(m_pSSLContext);
  m_pSSLContext = NULL;
 }
 if (g_sCertCommonName)
  DELETE_NULL_TAB(g_sCertCommonName);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////

int CSSLSupport::Init(const char *sCACertsPath,
       const char *sCACertificate,
       const char *sClientCertificate,
       const char *sClientPrivateKey,
       const char *sValidCN)
{
 int iResult = DV_ERROR;

 if (sCACertsPath && sCACertificate && sValidCN)
 {
  SSL_library_init();
  CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
  SSLeay_add_ssl_algorithms();
  m_pSSLContext = SSL_CTX_new( SSLv23_client_method() );
  if (m_pSSLContext)
  {
   char sCACert[512];
   strcpy(sCACert, sCACertsPath);
   strcat(sCACert, sCACertificate);
   if(SSL_CTX_load_verify_locations(m_pSSLContext, sCACert,
sCACertsPath))
   {
    SSL_CTX_set_verify(m_pSSLContext, SSL_VERIFY_PEER, VerifyCallback);
    SSL_CTX_set_default_passwd_cb(m_pSSLContext, PasswordCallback);
    SSL_CTX_set_default_passwd_cb_userdata(m_pSSLContext, (void
*)CLIENT_CERTIFICATE_PASSWORD);

    if(SSL_CTX_use_certificate_file(m_pSSLContext, sClientCertificate,
SSL_FILETYPE_PEM)==1)
    {
     if(SSL_CTX_use_PrivateKey_file(m_pSSLContext, sClientPrivateKey,
SSL_FILETYPE_PEM)==1)
     {
      if (g_sCertCommonName)
       DELETE_NULL_TAB(g_sCertCommonName);
      g_sCertCommonName = new char[strlen(sValidCN)+1];
      if (g_sCertCommonName)
      {
       strcpy(g_sCertCommonName, sValidCN);
       iResult = DV_SUCCESS;
       m_bInitialized = true;
      }
     }
    }
   }
  }
 }
 return iResult;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////

// I'm not sure how to use BIO in this function.
////////////////////////////////
int CSSLSupport::Connect(char *sHostName, int iPort)
{
 int iResult = DV_ERROR;

 if (sHostName && m_bInitialized)
 {
  m_pSSLConnection = SSL_new( m_pSSLContext );
  if (m_pSSLConnection)
  {
   BIO * pSSL_BIO = BIO_new(BIO_f_ssl());
   if (pSSL_BIO)
   {
    char *sConnectStr = new char[strlen(sHostName) + 7];

    if (sConnectStr)
    {
     sprintf(sConnectStr, "%s:%d", sHostName, iPort);
     SSL_set_connect_state(m_pSSLConnection);
     SSL_set_cipher_list(m_pSSLConnection, SSL_TXT_ALL);

     BIO_set_ssl(pSSL_BIO, m_pSSLConnection, BIO_NOCLOSE);
     BIO_set_ssl_mode(pSSL_BIO, 1); // Set to client mode.

     m_pBIO = BIO_new_connect(sConnectStr);
     if (m_pBIO)
     {
      BIO_set_nbio(m_pBIO, 1);
      m_pBIO = BIO_push(pSSL_BIO, m_pBIO);

//      if (BIO_do_connect(m_pBIO) == 1)    // Alawys fail and I don't
know why ???? => remove it.
//      {                     // The connection is perfomed
automatically on the first call of BIO_write.
//       if(SSL_get_verify_result(m_pSSLConnection) == X509_V_OK)
//       {
      if (m_pBIO)
        iResult = DV_SUCCESS;
//       }
//      }
     }
     DELETE_NULL_TAB(sConnectStr);
    }
   }
  }
  if (DV_FAILED(iResult))
   Disconnect();
 }
 return iResult;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////

void CSSLSupport::Disconnect()
{
 if (m_pBIO)
 {
  BIO_free_all(m_pBIO);
  m_pBIO = NULL;
 }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////

int CSSLSupport::Send(const char *sHTTPRequest)
{
 int iResult = DV_SUCCESS;
 int iResLen = strlen(sHTTPRequest);
 int iLen;
 int iOffset = 0;

 if (m_bInitialized)
 {
  while (DV_SUCCEEDED(iResult) && iResLen > 0)
  {
   iLen = BIO_write(m_pBIO, &sHTTPRequest[iOffset], iResLen);
   if (iLen <= 0)
   {
    if (BIO_should_retry(m_pBIO))
    {
     struct timeval tv;
     fd_set wfds;
     int iSockId = SSL_get_fd(m_pSSLConnection);

     FD_ZERO(&wfds);
     FD_SET(iSockId, &wfds);

     tv.tv_sec = 5;
     tv.tv_usec = 0;

     select(iSockId + 1, NULL, &wfds, NULL, &tv);
    }
    else if (iLen < 0)
     iResult = DV_ERROR;
   }
   else
   {
    iOffset += iLen;
    iResLen -= iLen;
   }
  }
 }
 else
  iResult = DV_ERROR;
 return iResult;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////

int CSSLSupport::Receive(char *&sHTTPResponse)
{
 int iResult = DV_SUCCESS;
 int iBufSize = 1024;
 int iResLen = iBufSize;

 if (m_bInitialized)
 {
  if (sHTTPResponse)
   DELETE_NULL_TAB(sHTTPResponse);
  sHTTPResponse = new char[iResLen+1];
  if (sHTTPResponse)
  {
   char *sBuffer = sHTTPResponse;
   char *sNewHTTPResponse;
   int iReadResult = -1;

   memset(sHTTPResponse, 0, iResLen+1);

   while (DV_SUCCEEDED(iResult) && iReadResult != 0)
   {
    iReadResult = BIO_read(m_pBIO, sBuffer, iResLen);
    if (iReadResult <= 0)
    {
     if (BIO_should_retry(m_pBIO))
     {
      struct timeval tv;
      fd_set rfds;
      int iSockId = SSL_get_fd(m_pSSLConnection);

      FD_ZERO(&rfds);
      FD_SET(iSockId, &rfds);

      tv.tv_sec = 5;
      tv.tv_usec = 0;

      select(iSockId + 1, &rfds, NULL, NULL, &tv);
     }
     else if (iReadResult < 0)
      iResult = DV_ERROR;
    }
    else if (iReadResult > 0)
    {
     if (iReadResult < iResLen)
     {
      iResLen -= iReadResult;
      sBuffer += iReadResult;
     }
     else
     { // The buffer sHTTPResponse is full and maybe more data must be
read
      // => save the data and try to read more data.
      sNewHTTPResponse = (char *)realloc(sHTTPResponse,
sizeof(sHTTPResponse) + iBufSize);
      if (sNewHTTPResponse)
      {
       iResLen = iBufSize;
       sHTTPResponse = sNewHTTPResponse;
       sBuffer = sHTTPResponse + strlen(sHTTPResponse);
       memset(sBuffer, 0, iBufSize);
      }
      else
       iResult = DV_ERROR;
     }
    }
   }
  }
  else
   iResult = DV_ERROR;
 }
 else
  iResult = DV_ERROR;
 return iResult;
}


My main program:

...
char * buffer = NULL;
SSLSupport cSSL;

cSSL.Init("c:\\winnt\\cert\\", "MyCA.cer", "c:\\winnt\\cert\\clnt.cer",
"c:\\winnt\\cert\\clnt.key", "10.255.255.253");
cSSL.Connect("10.255.255.253", 443);
cSSL.Send("GET /php/test.html HTTP/1.0\r\nHost:
10.255.255.253\r\n\r\n");
cSSL.Receive(buffer);
...


NOTE:
1) When I use IE with the same client certificate, I can access to my
page on my Apache web server. => My Apache configuration and my
certificates (clnt.cer, MyCA.cer and my Apache server certificate)
should be correct.

2) Also, if I configure my Apache like this:

<Directory "/var/www/php/">
    AllowOverride None
    Order allow,deny
    Allow from All
    SSLRequireSSL
</Directory>

my client program work but my client certificate is not used.

3) I have tried to run my client program with the same client and CA
certificate on some others NT Server and it work on only one PC!

What is wrong???? I think it's maybe my Connect() function because I'm
not sure how to use BIO.



______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    [EMAIL PROTECTED]
Automated List Manager                           [EMAIL PROTECTED]

Reply via email to