view usr/src/lib/libkmsagent/common/KMSClientProfile.cpp @ 12720:3db6e0082404

PSARC 2010/195 PKCS11 KMS Provider 6944296 Solaris needs a PKCS#11 provider to allow access to KMS keystore functionality
author Wyllys Ingersoll <Wyllys.Ingersoll@Sun.COM>
date Mon, 28 Jun 2010 16:04:11 -0700
parents
children
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 */

#include <stdio.h>
#include <errno.h>

#if !defined(UNIX) && !defined(METAWARE)
#include "KMSAgent_direct.h"
#endif

#include <string.h>

#include "KMSClientProfile.h"

#include "KMSAgent.h"
#include "KMS_CAStub.h"
#include "KMS_CertificateStub.h"
#include "KMS_DiscoveryStub.h"
#include "KMSClientProfileImpl.h"
#include "KMSAuditLogger.h"
#include "KMSAgentSoapUtilities.h"
#include "KMSAgentStringUtilities.h"


#include "KMSAgentPKICommon.h" // must be before agentstorage

#include "stdsoap2.h"          
#include "KMSAgentStorage.h"   // uses KMSClientProfile


#include "KMSAgentWebServiceNamespaces.h"
#include "k_setupssl.h"
#include "KMSAgentChallenge.h"
#include "KMSAgentCryptoUtilities.h"
#include "ApplianceParameters.h"
#include "AutoMutex.h"

#include "KMSAgentLoadBalancer.h"
#include "KMSAgentDataUnitCache.h"

#include "ClientSoapFaultCodes.h"
#ifdef METAWARE
#include "debug.h"
#include "sizet.h"
typedef unsigned char		uint8_t;
typedef unsigned short		uint16_t;
typedef unsigned int		uint32_t;
typedef unsigned long long	uint64_t;
#include "literals.h"
#endif
#include "KMSAgentAESKeyWrap.h"

#if defined(METAWARE) && defined(DEBUG)
#include "debug.h"
#endif
#include "KMSAuditLogger.h"
#include "KMSClientProfileImpl.h"

#ifdef METAWARE
extern "C" void
tnMsg( const char   *format,
       ... );
#endif

bool g_bUseFileLog = false;
char g_wsWorkingDirectory[KMS_MAX_PATH_LENGTH+1] = "./";


static bool InitializeLogging( 
   const utf8cstr  i_wsWorkingDirectory,
   int i_bUseFileLog )
{
   FATAL_ASSERT( !i_bUseFileLog || i_wsWorkingDirectory );
   
   bool bFileLogSuccess = true;
   
   g_bUseFileLog = ( i_bUseFileLog != 0 );
   
   // InitializeFileLogging must always be called, 
   // because the file is always used by FATALs.
   
   bFileLogSuccess = InitializeFileLogging( i_wsWorkingDirectory ) ? true:false;
   
   return bFileLogSuccess;
}

static void FinalizeLogging()
{
   // FinalizeFileLogging must always be called, 
   // because the file is always used by FATALs.
   FinalizeFileLogging();
   
   return;
}




/*---------------------------------------------------------------------------
 * Function: KMSClient_InitializeLibrary
 *
 *--------------------------------------------------------------------------*/

bool KMSClient_InitializeLibrary(
   const utf8cstr  i_wsWorkingDirectory,
   int i_bUseFileLog)
{
   bool bSuccess;
   
#if defined(DEBUG) && defined(METAWARE)
   log_printf("KMSClient_InitializeLibrary : ENTERING");
#endif

   // setup SSL
   bSuccess = K_SetupSSL() == 1;
   if(!bSuccess)
   {
      return false;
   }

#if defined(DEBUG) && defined(METAWARE)
   log_printf("KMSClient_InitializeLibrary : set current directory");
#endif

   // if i_wsWorkingDirectory is null, caller means current directory
   if ( i_wsWorkingDirectory != NULL )
   {
#if defined(DEBUG) && defined(METAWARE)
      log_printf("KMSClient_InitializeLibrary : check working directory");
#endif

      // string is there but is empty or junk
      if (strlen(i_wsWorkingDirectory) <= 0)
      {
         strcpy(i_wsWorkingDirectory, ".");
      }

      if ( strlen(i_wsWorkingDirectory) >= KMS_MAX_PATH_LENGTH )
      {
         return false;
      }

#if defined(DEBUG) && defined(METAWARE)
      log_printf("KMSClient_InitializeLibrary : set global working directory");
#endif
      
      // set global working directory to input
      strncpy(g_wsWorkingDirectory, 
              i_wsWorkingDirectory,
              KMS_MAX_PATH_LENGTH); 
      g_wsWorkingDirectory[KMS_MAX_PATH_LENGTH] = 0;
   }
   else   
   {
      strcpy(g_wsWorkingDirectory, ".");
   }

#if defined(DEBUG) && defined(METAWARE)
   log_printf("KMSClient_InitializeLibrary : Initialize logging");
#endif

   // initialize file logging
   bSuccess = InitializeLogging( g_wsWorkingDirectory,
                                 i_bUseFileLog);
    
   return bSuccess;
}


/*---------------------------------------------------------------------------
 * Function: KMSClient_FinalizeLibrary 
 *--------------------------------------------------------------------------*/
bool KMSClient_FinalizeLibrary()
{
#if defined(DEBUG) && defined(METAWARE)
   log_printf("KMSClient_FinalizeLibrary : ENTERING");
#endif
   
   K_CleanupSSL();
   
   FinalizeLogging();
   
   return true; /* always */
}


int LogError_lastErrno;


/** 
 * Construct a message for the KMSAuditLogger and store the message
 *  in the profile as the last error message.  
 */
void LogError_function(KMSClientProfile *i_pProfile,
                       int i_iErrno,
                       const char* i_sOperation,
                       const char* i_sEntityID,
                       const char* i_sNetworkAddress,
                       const char* i_sMessage )
{
   FATAL_ASSERT( i_pProfile && i_sOperation );

   // save for caller's use - this shouldn't be a global, but I don't
   // want this as an item in the profile as I don't want it persisted
   LogError_lastErrno = i_iErrno;

   // log the message to a data file (and internal logs)
#ifndef METAWARE
   if ( g_bUseFileLog )
#endif
   {
      Log_function(i_iErrno, 
                   i_sOperation, 
                   i_sEntityID, 
                   i_sNetworkAddress, 
                   i_sMessage);
   }

#ifdef METAWARE
   /* print this to the T10000/9840 VOP */
   /* NOTE the \n is important to VOP - leave it in */
   tnMsg("`msg`KMS2.0:msg#=%i,op=%s\r\n",
         i_iErrno,
         i_sOperation);
   
   tnMsg("`msg`msg=%s,eid=%s,addr=%s\r\n", 
         i_sMessage,
         i_sEntityID, 
         i_sNetworkAddress);
   
#endif

   // copy the error message into the profile (for later reference)
   strncpy(i_pProfile->m_wsErrorString, 
           i_sOperation,
           KMS_MAX_ERROR_STRING);

   // make sure to NUL out the end
   i_pProfile->m_wsErrorString[KMS_MAX_ERROR_STRING] = 0;

   if ( i_sEntityID )
   {
      strncat(i_pProfile->m_wsErrorString, 
              i_sEntityID,
              KMS_MAX_ERROR_STRING);
   }

   if ( i_sNetworkAddress )
   {
      strncat(i_pProfile->m_wsErrorString, 
              ",Address=",
              KMS_MAX_ERROR_STRING);
      strncat(i_pProfile->m_wsErrorString, 
              i_sNetworkAddress,
              KMS_MAX_ERROR_STRING);
   }

   if ( i_sMessage )
   {
      strncat(i_pProfile->m_wsErrorString, 
              ",Msg=",
              KMS_MAX_ERROR_STRING);
      strncat(i_pProfile->m_wsErrorString, 
              i_sMessage,
              KMS_MAX_ERROR_STRING);
   }

   // make sure to NUL out the end
   i_pProfile->m_wsErrorString[KMS_MAX_ERROR_STRING] = 0;
   
}

// see KMSClientProfileImpl.h
bool SSL_InvalidCertificate (const char * const i_sErrorString)
{
    if (
        // OpenSSL generates this msg
        strstr(i_sErrorString, "sslv3 alert certificate unknown"))
    {
        return true;
    }
    return false;

}

// see KMSClientProfileImpl.h
bool ServerError (const char * i_sErrorString, int i_iErrno )
{
    // The Client Soap Fault Code returned by the KMA
    // may be at the start of i_sErrorString or immediately
    // follwing "SoapFaultString=" depending on the caller's
    // string

    int iErrorCode;
    
    const char* sFaultstringStart  = strstr(i_sErrorString, "SoapFaultString=" );
    if ( sFaultstringStart )
    {
        iErrorCode = GET_FAULT_CODE( sFaultstringStart + strlen("SoapFaultString=") );
    }
    else
    {
        // This may be zero if there is no error code at the start of the string.
        iErrorCode = GET_FAULT_CODE( i_sErrorString );
    }

    // the following is commented out so the former check can be observed.  This check is no longer
    // made since invalid certificate failures may be due to a KMA that is behind on
    // replication updates hence failover would succeed.
//    if (
//            // OpenSSL generates this msg
//            SSL_InvalidCertificate(i_sErrorString))
//    {
//        return false;
//    }
            
    if (
       // when the KMA is locked
       iErrorCode == CLIENT_ERROR_AGENT_APPLIANCE_LOCKED

       // KMS 2.2 change when the KMA is locked
       || iErrorCode == CLIENT_ERROR_MANAGER_APPLIANCE_LOCKED

       // KMS 2.2 change for core security internal error
       || iErrorCode == CLIENT_ERROR_MANAGER_INTERNAL

       // if the KMA's pre-gen'd key pool is depleted
       || iErrorCode == CLIENT_ERROR_AGENT_NO_READY_KEYS

       // if the KMA's HSM is broke and the KMA is in FIPS mode
       || iErrorCode == CLIENT_ERROR_SERVER_HSM_REQUIRED_BUT_MISSING
        
       // when the server is too slow
       || NULL != strstr( i_sErrorString, "Timeout" )
       || NULL != strstr( i_sErrorString, "Operation interrupted or timed out" )
       
       // The Appliance is powered down, or is not reachable
       || NULL != strstr( i_sErrorString, "Connection refused" )

       || NULL != strstr( i_sErrorString, "Unknown error" )

       // SOAP EOF
       || NULL != strstr( i_sErrorString, "End of file or no input:" )

       // Appliance server software is not running (while Appliance machine is OK)
       || NULL != strstr( i_sErrorString, "connect failed in tcp_connect()" )

       // If the server has an internal error but still responds
       || NULL != strstr( i_sErrorString, "Server Error" )

       // OpenSSL protocol errors (Note: the SSL_ERROR_SSL may be due
       // to invalid client-side values, but for now it's used as a
       // catch-all; a side-effect is that any actual invalid client-side
       // value will cause one audit log entry to be created on each
       // Appliance in the cluster).
       || NULL != strstr( i_sErrorString, 
                       "Error observed by underlying BIO: No error" )
       || NULL != strstr( i_sErrorString, 
                          "EOF was observed that violates the protocol" )
       || NULL != strstr( i_sErrorString, 
                          "SSL_ERROR_SSL" ) )
    {
        return true;
    }

#ifndef WIN32
	// check for errno values that imply connection problems to the server
    switch (i_iErrno)
    {
        case ECONNABORTED : return true; // Connection aborted.
        case ECONNREFUSED : return true; // Connection refused.
        case ECONNRESET :   return true; // Connection reset.
        case EHOSTUNREACH : return true; // Host is unreachable.
        case ENETDOWN :     return true; // Network is down.
        case ENETRESET :    return true; // Connection aborted by network.
        case ENETUNREACH :  return true; // Network unreachable.
        case ENOPROTOOPT :  return true; // Protocol not available.
#ifndef METAWARE
        case ETIME :        return true; // Stream ioctl() timeout.
#endif
        case ETIMEDOUT :    return true; // Connection timed out.
    }
#endif    
    // at this point we conclude its a client side issue
    return false;
}

/*---------------------------------------------------------------------------
 * Function: KMSClient_GetLastErrorMessage
 *
 *--------------------------------------------------------------------------*/

// extern "C"
utf8char * KMSClient_GetLastErrorMessage(KMSClientProfile *i_pProfile)
{
   FATAL_ASSERT(i_pProfile);
   
   CAutoMutex oAutoMutex( 0 );
   if ( i_pProfile->m_pLock )
   {
      oAutoMutex.Lock( (K_MUTEX_HANDLE)i_pProfile->m_pLock );
   }
   
   return i_pProfile->m_wsErrorString;
}


/*---------------------------------------------------------------------------
 * Function: KMSClient_RetrieveEntityCertificate
 * Get the Root CA Certificate and store it into the profile
 *--------------------------------------------------------------------------*/
static bool KMSClient_RetrieveEntityCertificate(
   KMSClientProfile* i_pProfile,
   utf8cstr  i_wsEntityID,
   utf8cstr  i_wsPassphrase,
   char* const o_sHexHashedPassphrase )
{
   FATAL_ASSERT( i_pProfile && i_wsEntityID && i_wsPassphrase );

#if defined(DEBUG) && defined(METAWARE)
    log_printf("KMSClient_RetrieveEntityCertificate : entered");
#endif
   
   CAutoMutex oAutoMutex( (K_MUTEX_HANDLE)i_pProfile->m_pLock );
   char sSoapFaultMsg[g_iMAX_SOAP_FAULT_MESSAGE_LENGTH];
   char sKmaAddress[g_iMAX_PEER_NETWORK_ADDRESS_LENGTH];
   
   strcpy(o_sHexHashedPassphrase, "");
   
   bool bSuccess = true;
   bool bTryFailOver = false;
   
   struct soap *pstCASoap;
   pstCASoap = (struct soap *) malloc( sizeof(struct soap) );
   if(pstCASoap == NULL)
   {
#if defined(DEBUG) && defined(METAWARE)
      log_printf("Malloc %x pstCASoap returned null\n", sizeof(struct soap));
#endif
      LogError(i_pProfile,
               LoadProfile_AUDIT_CLIENT_GET_ROOT_CA_CERTIFICATE_SOAP_ERROR,
               NULL,
               NULL,
               "malloc failure for pstCASoap" );
      return false;
   }

   // initialize the SOAP connection that will get the RootCA 
   soap_init2( pstCASoap, (SOAP_XML_STRICT | SOAP_C_UTFSTRING), (SOAP_XML_STRICT | SOAP_C_UTFSTRING) );

#ifdef METAWARE
   K_SetupCallbacks ( pstCASoap );
#endif

   CCertificate* pRootCACertificate = 0;
   CCertificate* pEntityCertificate = 0;
   CPrivateKey*  pEntityPrivateKey = 0;

   soap_set_namespaces( pstCASoap, KMS_CA_namespaces );
   
   pstCASoap->connect_timeout = i_pProfile->m_iTransactionTimeout;
   pstCASoap->send_timeout    = i_pProfile->m_iTransactionTimeout;
   pstCASoap->recv_timeout    = i_pProfile->m_iTransactionTimeout;
   
   struct soap *pstCertificateSoap;

   pstCertificateSoap = (struct soap *) malloc( sizeof(struct soap) );

   if(pstCertificateSoap == NULL)
   {
#if defined(METAWARE)
      log_printf("Malloc %x pstCertificateSoap returned null\n", 
                 sizeof(struct soap));
#endif
      soap_free( pstCASoap );
      free(pstCASoap);
      return false;
   }

   // initialize the SOAP connection that will get the Certificate
   soap_init2( pstCertificateSoap, (SOAP_XML_STRICT | SOAP_C_UTFSTRING), (SOAP_XML_STRICT | SOAP_C_UTFSTRING) );
    
#ifdef METAWARE
   K_SetupCallbacks ( pstCertificateSoap );
#endif

   soap_set_namespaces( pstCertificateSoap, KMS_Certificate_namespaces );
   
   pstCertificateSoap->connect_timeout = i_pProfile->m_iTransactionTimeout;
   pstCertificateSoap->send_timeout = i_pProfile->m_iTransactionTimeout;
   pstCertificateSoap->recv_timeout = i_pProfile->m_iTransactionTimeout;
   
   CAgentLoadBalancer oLoadBalancer(i_pProfile);
   int iIndex = oLoadBalancer.Balance();

#if defined(DEBUG) && defined(METAWARE)
   log_printf("KMSClient_RetrieveEntityCertificate : call KMS_CA__RetrieveRootCACertificate");
#endif

   // get the server's URL that will provide SOAP services
   do
   {
      bSuccess = true;
      bTryFailOver = false;
      bool bFailedOnRetrieveRootCA = false;
      const char* sURL = 0;
      
      if ( bSuccess )
      {
         sURL = oLoadBalancer.GetHTTPURL(iIndex, 
                                         i_pProfile->m_iPortForCAService);
         
         if ( !sURL )
         {
            bSuccess = false;
         }
      }
      
      if ( bSuccess )
      {
         strncpy(i_pProfile->m_sURL, sURL, KMS_MAX_URL);
         i_pProfile->m_sURL[KMS_MAX_URL] = 0;
      }      
      

      // SOAP CALL -  retrieve Root CA Certificate from the Server
      struct KMS_CA::
         KMS_CA__RetrieveRootCACertificateResponse stRootCACertificateResponse;
      
      if ( bSuccess )
      {
#if defined(DEBUG) && defined(METAWARE)
         log_printf("KMSClient_RetrieveCertificate : call KMS_CA__RetrieveRootCACertificate again");
#endif
         bSuccess = 
            KMS_CA::soap_call_KMS_CA__RetrieveRootCACertificate(
               pstCASoap, 
               i_pProfile->m_sURL,
               NULL,
               i_wsEntityID,
               stRootCACertificateResponse ) == SOAP_OK;

         if ( !bSuccess )
         {            
            GetSoapFault(sSoapFaultMsg, (struct soap*)pstCASoap);      
            GetPeerNetworkAddress(sKmaAddress, pstCASoap);
            LogError(i_pProfile,
                     LoadProfile_AUDIT_CLIENT_GET_ROOT_CA_CERTIFICATE_SOAP_ERROR,
                     NULL,
                     sKmaAddress,
                     sSoapFaultMsg );

            bTryFailOver = ServerError(GET_SOAP_FAULTSTRING(pstCASoap), pstCASoap->errnum);
            bFailedOnRetrieveRootCA = true;
         }
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 1\n");
      }
#endif


      // Validate the SOAP response
      if ( bSuccess )
      {
         if ( stRootCACertificateResponse.RootCACertificate.__size < 1 ||
              stRootCACertificateResponse.RootCACertificate.__ptr == NULL ||
              stRootCACertificateResponse.AuthenticationHashIterationCount < 
              MIN_AUTHENTICATION_ITERATION_COUNT ||
              stRootCACertificateResponse.AuthenticationHashIterationCount > 
                  MAX_AUTHENTICATION_ITERATION_COUNT ||
              stRootCACertificateResponse.ClientAuthenticationChallenge.__size != 
                  AUTHENTICATION_CHALLENGE_LENGTH ||
              stRootCACertificateResponse.ClientAuthenticationChallenge.__ptr == NULL )
         {
            bSuccess = false;

            GetPeerNetworkAddress(sKmaAddress, pstCASoap);
            LogError(i_pProfile,
                     AUDIT_CLIENT_GET_ROOT_CA_CERTIFICATE_INVALID_RESPONSE_FORMAT,
                     NULL,
                     sKmaAddress,
                     NULL);
         }
         else
         {
            GetPeerNetworkAddress(sKmaAddress, pstCASoap);
            Log(AUDIT_CLIENT_GET_ROOT_CA_CERTIFICATE_SUCCESS,
                 NULL,
                 sKmaAddress,
                 NULL);
         }

      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 2\n");
      }
#endif

      // build our RootCACertificate object
      if ( bSuccess )
      {
         pRootCACertificate = new CCertificate;

         // make sure the new was successful
         bSuccess = ( pRootCACertificate != 0 );
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 3\n");
      }
#endif

      if ( bSuccess )
      {
         // OVERLOADED Load method - 3 parameters means 
         // recall from BUFFER
         bSuccess =
            pRootCACertificate->Load(
               stRootCACertificateResponse.RootCACertificate.__ptr,  // to here
               stRootCACertificateResponse.RootCACertificate.__size, // size
               PKI_FORMAT );                                         // ignored

         if( !bSuccess )
         {          
            GetPeerNetworkAddress(sKmaAddress, pstCASoap);
            LogError(i_pProfile,
                     AUDIT_CLIENT_GET_ROOT_CA_CERTIFICATE_INVALID_CA_CERTIFICATE_FORMAT,
                     NULL,
                     sKmaAddress,
                     NULL);
         }

      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 4\n");
      }
#endif

      
      if ( bSuccess )
      {
         // save the built CACertificate object to a FILE (i_pProfile gets the
         // persistent handle to that file) 
         bSuccess = StoreCACertificate( i_pProfile, pRootCACertificate );
         
         if ( !bSuccess )
         {
            LogError(i_pProfile,AUDIT_CLIENT_GET_CERTIFICATE_SAVE_CA_CERTIFICATE_FAILED,
                     NULL,
                     NULL,
                     NULL);
         }           
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 5\n");
      }
#endif
	
      //-------------------------------
      // Initialize SSL - use SERVER AUTH
      //-------------------------------
      if ( bSuccess )
      {
         // SERVER_AUTHENTICATION needs just the pstCertificateSoap
         bSuccess =
            K_soap_ssl_client_context( 
               i_pProfile,                            // in ->m_wsProfileName,->m_sHexHashedPassphrase
               pstCertificateSoap,                    // in - soap structure
               SOAP_SSL_REQUIRE_SERVER_AUTHENTICATION // in - flags
               ) == SOAP_OK;
         
         if ( !bSuccess )
         {
            GetSoapFault(sSoapFaultMsg, (struct soap*)pstCertificateSoap);
            GetPeerNetworkAddress(sKmaAddress, pstCertificateSoap);
            LogError(i_pProfile,AUDIT_CLIENT_GET_CERTIFICATE_SOAP_ERROR,
                     NULL,
                     sKmaAddress,
                     sSoapFaultMsg );
         }
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 6\n");
      }
#endif

      // hash the passphrase passed in
      char sHexAuthenticationSecret[2*HASH_LENGTH+1];

      if ( bSuccess )
      {
         bSuccess = ComputeFixedEntityHashedPassphraseAndAuthenticationSecret(
            i_wsPassphrase,
            o_sHexHashedPassphrase,
            stRootCACertificateResponse.AuthenticationHashIterationCount,
            sHexAuthenticationSecret );

         if ( !bSuccess )
         {
            LogError(i_pProfile,AUDIT_CLIENT_COMPUTE_FIXED_FAILED,
                     NULL,
                     NULL,
                     NULL);
         }
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 7\n");
      }
#endif
      
      // copy received Root CA into buffer for input 
      // into challenge-response computation
      unsigned char aRootCACertificate[MAX_CERT_SIZE];
      int iRootCACertificateLength;

      if ( bSuccess )
      {
         // OVERLOADED save method - save iRootCACertificateLength to aRootCACertificate
         // buffer 
         bSuccess = pRootCACertificate->Save( 
            aRootCACertificate,    
            MAX_CERT_SIZE, 
            &iRootCACertificateLength,   
            PKI_FORMAT );

         if ( !bSuccess )
         {
            LogError(i_pProfile,AUDIT_CLIENT_SAVE_ROOTCA_FAILED,
                     NULL,
                     NULL,
                     NULL);
         }
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 8\n");
      }
#endif

      // respond to server's challenge
      unsigned char aAuthenticationSecret[AUTHENTICATION_SECRET_LENGTH];
      unsigned char 
         aClientAuthenticationChallengeResponse[AUTHENTICATION_RESPONSE_LENGTH];
      
      if ( bSuccess )
      {
         FATAL_ASSERT( AUTHENTICATION_SECRET_LENGTH == 
                       ConvertUTF8HexStringToBinary( 
                          sHexAuthenticationSecret, NULL ) );

         ConvertUTF8HexStringToBinary( 
            sHexAuthenticationSecret, aAuthenticationSecret );

         // client authentication response
         bSuccess = ComputeChallengeResponse(
            aAuthenticationSecret,
            AUTHENTICATION_SECRET_LENGTH,
            aRootCACertificate,
            iRootCACertificateLength,
            stRootCACertificateResponse.ClientAuthenticationChallenge.__ptr,
            AUTHENTICATION_CHALLENGE_LENGTH,
            aClientAuthenticationChallengeResponse,
            AUTHENTICATION_RESPONSE_LENGTH );
         
         if ( !bSuccess )
         {
            LogError(i_pProfile,AUDIT_CLIENT_COMPUTE_CHALLENGE_RESPONSE_FAILED,
                     NULL,
                     NULL,
                     NULL);
         }
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 9\n");
      }
#endif

      struct KMS_Certificate::xsd__hexBinary stClientAuthenticationResponse;

      if ( bSuccess )
      {
         stClientAuthenticationResponse.__size = 
            AUTHENTICATION_RESPONSE_LENGTH;
         stClientAuthenticationResponse.__ptr = 
            (unsigned char*)soap_malloc( 
               pstCertificateSoap, AUTHENTICATION_RESPONSE_LENGTH );

         if ( stClientAuthenticationResponse.__ptr != NULL )
         {
            memcpy( stClientAuthenticationResponse.__ptr, 
                    aClientAuthenticationChallengeResponse, 
                    AUTHENTICATION_RESPONSE_LENGTH );
         }
         else
         {
            bSuccess = false;
         }
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 10\n");
      }
#endif

      // generate challenge nonce
      struct KMS_Certificate::xsd__hexBinary stServerAuthenticationChallenge;

      if ( bSuccess )
      {
         stServerAuthenticationChallenge.__size = 
            AUTHENTICATION_CHALLENGE_LENGTH;
         stServerAuthenticationChallenge.__ptr = 
            (unsigned char*)soap_malloc( pstCertificateSoap, 
                                         AUTHENTICATION_CHALLENGE_LENGTH );
            
         bSuccess = ( stServerAuthenticationChallenge.__ptr != NULL );
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 11\n");
      }
#endif      

      if ( bSuccess )
      {
         bSuccess = GetPseudorandomBytes( 
            AUTHENTICATION_CHALLENGE_LENGTH, 
            stServerAuthenticationChallenge.__ptr );
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 12\n");
      }
#endif      

      if ( bSuccess )
      {
         sURL = oLoadBalancer.GetHTTPSURL(iIndex, 
                                          i_pProfile->
                                          m_iPortForCertificateService);

         if ( !sURL )
         {
            bSuccess = false;
         }
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 13\n");
      }
#endif      

      // Verify that the same URL is used for Root CA Certificate
      // retrieval as for Entity Certificate retrieval
        
      if ( bSuccess )
      {            
         char sTempCAURL[KMS_MAX_URL + 1];
         strncpy( sTempCAURL, i_pProfile->m_sURL, KMS_MAX_URL );
         sTempCAURL[KMS_MAX_URL] = 0;

         char * sRetrieveRootCACertificateURL = strtok( sTempCAURL, ":" );
        
         sRetrieveRootCACertificateURL = strtok(NULL, ":");

         char sTempAgentURL[KMS_MAX_URL + 1];
         strncpy( sTempAgentURL, sURL, KMS_MAX_URL );
         sTempAgentURL[KMS_MAX_URL] = 0;
         char * sRetrieveAgentCertificateURL = strtok( sTempAgentURL, ":" );
         sRetrieveAgentCertificateURL = strtok(NULL, ":");

         FATAL_ASSERT( strcmp( sRetrieveRootCACertificateURL, 
                               sRetrieveAgentCertificateURL ) == 0 );

         strncpy(i_pProfile->m_sURL, sURL, KMS_MAX_URL);
         i_pProfile->m_sURL[KMS_MAX_URL] = 0;
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 14\n");
      }
#endif

      KMS_Certificate::KMS_Certificate__RetrieveEntityCertificateResponse 
         stRetrieveEntityCertificateResponse;

      // SOAP - retrieve ENTITY Certificate, passing the challenge response,
      // a challenge to the server and get back the server's response
      if ( bSuccess )
      {
         bSuccess =
            KMS_Certificate::soap_call_KMS_Certificate__RetrieveEntityCertificate(
               pstCertificateSoap,
               sURL,
               NULL,
               (utf8cstr )i_wsEntityID,
               stClientAuthenticationResponse,
               stServerAuthenticationChallenge,
               stRetrieveEntityCertificateResponse ) == SOAP_OK;

         if( !bSuccess )
         {                
            GetSoapFault(sSoapFaultMsg, (struct soap*)pstCertificateSoap);
            GetPeerNetworkAddress(sKmaAddress, pstCertificateSoap);
            LogError(i_pProfile,AUDIT_CLIENT_GET_CERTIFICATE_SOAP_ERROR,
                     NULL,
                     sKmaAddress,
                     sSoapFaultMsg );
 
            bTryFailOver = ServerError(GET_SOAP_FAULTSTRING(pstCertificateSoap),
                                        pstCertificateSoap->errnum);
         }
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 15\n");
      }
#endif      

      // Validate the response structure
      if ( bSuccess )
      {
         if ( stRetrieveEntityCertificateResponse.
              ServerAuthenticationResponse.__ptr == NULL 

              || stRetrieveEntityCertificateResponse.
              ServerAuthenticationResponse.__size != 
              AUTHENTICATION_RESPONSE_LENGTH

              || stRetrieveEntityCertificateResponse.Certificate.__size < 1

              || stRetrieveEntityCertificateResponse.Certificate.__ptr == 0

              || stRetrieveEntityCertificateResponse.
              WrappedPrivateKeyMaterial.__size < 1

              || stRetrieveEntityCertificateResponse.
              WrappedPrivateKeyMaterial.__ptr == 0 )
         {
            bSuccess = false;

            GetPeerNetworkAddress(sKmaAddress, pstCertificateSoap);
            LogError(i_pProfile,AUDIT_CLIENT_GET_CERTIFICATE_INVALID_RESPONSE_FORMAT,
                     NULL,
                     sKmaAddress,
                     NULL );
         }
         else
         {
            GetPeerNetworkAddress(sKmaAddress, pstCertificateSoap);
            Log(AUDIT_CLIENT_GET_CERTIFICATE_SUCCESS,
                 NULL,
                 sKmaAddress,
                 NULL );
         }
     }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 16\n");
      }
#endif      

      // if valid, calculate the correct challenge-response
      unsigned char 
         aServerAuthenticationChallengeResponse[AUTHENTICATION_RESPONSE_LENGTH];

      if ( bSuccess )
      {
         bSuccess = ComputeChallengeResponse(
            aAuthenticationSecret,
            AUTHENTICATION_SECRET_LENGTH,
            aRootCACertificate,
            iRootCACertificateLength,
            stServerAuthenticationChallenge.__ptr,
            AUTHENTICATION_CHALLENGE_LENGTH,
            aServerAuthenticationChallengeResponse,
            AUTHENTICATION_RESPONSE_LENGTH );
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 17\n");
      }
#endif      

      // if successful, check if the server provided the correct challenge-response
      if ( bSuccess )
      {
         if ( 0 != memcmp(
            aServerAuthenticationChallengeResponse,
            stRetrieveEntityCertificateResponse.ServerAuthenticationResponse.__ptr,
            AUTHENTICATION_RESPONSE_LENGTH )  )
         {
            bSuccess = false;

            GetPeerNetworkAddress(sKmaAddress, pstCertificateSoap);
            LogError(i_pProfile,AUDIT_CLIENT_GET_CERTIFICATE_INVALID_CHALLENGE_RESPONSE,
                     NULL,
                     sKmaAddress,
                     NULL );
         }
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 18\n");
      }
#endif      


      if ( bSuccess )
      {
         pEntityCertificate = new CCertificate;
         // if certificate was obtained
         bSuccess = ( pEntityCertificate != 0 );
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 19\n");
      }
#endif      

      if ( bSuccess )
      {
         // Load(recall) the signed certificate using OVERLOADED load method
         // 3 parameters means load from a buffer
         bSuccess = pEntityCertificate->Load(
            stRetrieveEntityCertificateResponse.Certificate.__ptr,  // load into
            stRetrieveEntityCertificateResponse.Certificate.__size, 
            PKI_FORMAT );

         if ( !bSuccess )
         {
            GetPeerNetworkAddress(sKmaAddress, pstCertificateSoap);
            LogError(i_pProfile,AUDIT_CLIENT_GET_CERTIFICATE_INVALID_CERTIFICATE_FORMAT,
                     NULL,
                     sKmaAddress,
                     NULL );
         }
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 20\n");
      }
#endif      

      if ( bSuccess )
      {
         pEntityPrivateKey = new CPrivateKey;
         bSuccess = ( pEntityPrivateKey != 0 );
      }
#if defined(DEBUG) && defined(METAWARE)
      else
      {
         log_printf("!bSuccess 21\n");
      }
#endif      


      if ( bSuccess )
      {
         // Load the Private Key using OVERLOADED Load method - 3 parameters
         // means load from a buffer

         // TODO: change this when certificate service supports requesting unwrapped private keys
         bSuccess = pEntityPrivateKey->Load(
            stRetrieveEntityCertificateResponse.WrappedPrivateKeyMaterial.__ptr, // load into
            stRetrieveEntityCertificateResponse.WrappedPrivateKeyMaterial.__size, 
            NULL, 
            PKI_FORMAT );

         if (!bSuccess )
         {

            GetPeerNetworkAddress(sKmaAddress, pstCertificateSoap);
            LogError(i_pProfile,AUDIT_CLIENT_GET_CERTIFICATE_INVALID_KEY_FORMAT,
                     NULL,
                     sKmaAddress,
                     NULL );
         }
      }

      if ( bSuccess )
      {
            strncpy(i_pProfile->m_wsEntityID,
                i_wsEntityID,
                KMS_MAX_ENTITY_ID );
            i_pProfile->m_wsEntityID[KMS_MAX_ENTITY_ID] = 0;

            // store PKI certificates and unwrapped private key  
            bSuccess = StorePKIcerts( i_pProfile, 
                            pRootCACertificate, 
                            pEntityCertificate, 
                            pEntityPrivateKey,
#ifdef KMSUSERPKCS12
			    i_wsPassphrase
#else
                            NULL
#endif
			    );
#ifdef KMSUSERPKCS12
		if (bSuccess) {
			/*
			 * Write out the cert and key individually so GetPKIcerts
			 * can use them.
			 */
			bSuccess = StoreTempAgentPKI(i_pProfile,
			    pEntityCertificate, pEntityPrivateKey);
		}

#endif
	}

      if ( !bSuccess )
      {
         if (pRootCACertificate)
         {
             delete pRootCACertificate;
         }
         if (pEntityCertificate)
         {
             delete pEntityCertificate;
         }
         if (pEntityPrivateKey)
         {
             delete pEntityPrivateKey;
         }

         i_pProfile->m_iEnrolled = FALSE;

         if ( bTryFailOver )
         {
            iIndex = oLoadBalancer.FailOver(iIndex, bFailedOnRetrieveRootCA ? pstCASoap : pstCertificateSoap);
         }
      }
   } 
   while ( bTryFailOver && (iIndex >= 0) && !bSuccess );

   // certs are now persisted so free up space
   if ( bSuccess )
   {
        delete pRootCACertificate;
        delete pEntityCertificate;
        delete pEntityPrivateKey;
   }

   // Clean up SOAP resources for pstCASoap
   soap_destroy( pstCASoap );
   soap_end( pstCASoap );
   soap_done( pstCASoap );

   // Clean up SOAP resources for pstCertificateSoap
   soap_destroy( pstCertificateSoap );
   soap_end( pstCertificateSoap );
   soap_done( pstCertificateSoap );

   free(pstCASoap);
   free(pstCertificateSoap);

   return bSuccess;
}

/*--------------------------------------------------------------------------
 * LoadClusterInformation
 *  calls GetCluster - that's it.
 *    If there is no cluster file, this function will return true, 
 *    but o_bClusterInformationFound will be false.
 *-------------------------------------------------------------------------*/
static bool LoadClusterInformation( KMSClientProfile* i_pProfile, 
                                    int& o_bClusterInformationFound )
{
    FATAL_ASSERT( i_pProfile );

    o_bClusterInformationFound = false;

    CAutoMutex oAutoMutex( (K_MUTEX_HANDLE)i_pProfile->m_pLock );    

    return GetCluster( i_pProfile, o_bClusterInformationFound ) ;
    
}


/*--------------------------------------------------------------------------
 * EnrollAgent
 *  calls functions to perform enrollment and save PKI info to persistent storage 
 *  stores configuration in persistent storage
 *-------------------------------------------------------------------------*/

static bool EnrollAgent( KMSClientProfile * io_pProfile,
                         utf8cstr           i_wsEntityID,
                         utf8cstr           i_wsPassphrase )
{
    FATAL_ASSERT( io_pProfile && i_wsEntityID && i_wsPassphrase );

    bool bSuccess = true;

    // see KMSAgentCryptoUtilities for HASH_LENGTH, aka KMS_MAX_HASH_SIZE
    char sHexHashedPassphrase[2*KMS_MAX_HASH_SIZE+1];

    if ( bSuccess )
    {
        // performs enrollment and saves PKI info to persistent storage 
        bSuccess = KMSClient_RetrieveEntityCertificate(
                                    io_pProfile,
                                    i_wsEntityID,
                                    i_wsPassphrase,
                                    sHexHashedPassphrase );

        // KMSClient_RetrieveCertificate logs errors
    }

    if (bSuccess)
    {
        strncpy(io_pProfile->m_sHexHashedPassphrase, 
            sHexHashedPassphrase,
            2*KMS_MAX_HASH_SIZE );
        io_pProfile->m_sHexHashedPassphrase[2*KMS_MAX_HASH_SIZE] = 0;
        
        // persist the profile now updated with the hashed passphrase
        bSuccess = StoreConfig( io_pProfile ); 

        if (!bSuccess)
        {
              Log(AUDIT_CLIENT_LOAD_PROFILE,
                  i_wsEntityID,
                  NULL,
                  "store config failed following enrollment" );
        }
    }

    return bSuccess;
}

/*---------------------------------------------------------------------------
 * Function: KMSClient_LoadProfile
 *
 *--------------------------------------------------------------------------*/
bool KMSClient_LoadProfile(
                KMSClientProfile *io_pProfile,
                utf8char *i_wsProfileName,
                utf8char *i_wsEntityID,
                utf8char *i_wsPassphrase,
                utf8char *i_wsApplianceAddress,
                int      i_iTransactionTimeout,
                int      i_iFailOverLimit,
                int      i_iClusterDiscoveryFrequency,
                int       i_eKMSmode)
{
    FATAL_ASSERT(io_pProfile);
    FATAL_ASSERT(i_wsProfileName);

    bool bSuccess = true;

    char sSoapFaultMsg[g_iMAX_SOAP_FAULT_MESSAGE_LENGTH];
    char sKmaAddress[g_iMAX_PEER_NETWORK_ADDRESS_LENGTH];

#if defined(DEBUG) && defined(METAWARE)
    log_printf("KMSClient_LoadProfile : entered");
#endif
    
    memset( io_pProfile, 0, sizeof(KMSClientProfile) );

    // create lock

    if (bSuccess)
    {
        bSuccess = 
           ( K_CreateMutex((K_MUTEX_HANDLE *)&io_pProfile->m_pLock) == 
             K_SYS_OK );
    }

    // initialize profile with parameters

    strncpy(io_pProfile->m_wsProfileName,
            i_wsProfileName,
            KMS_MAX_ENTITY_ID); 
    io_pProfile->m_wsProfileName[KMS_MAX_ENTITY_ID] = 0;

    io_pProfile->m_iPortForCAService = 
       DEFAULT_CA_SERVICE_PORT_NUMBER;
    io_pProfile->m_iPortForCertificateService = 
       DEFAULT_CERTIFICATE_SERVICE_PORT_NUMBER;
    io_pProfile->m_iPortForDiscoveryService = 
       DEFAULT_DISCOVERY_SERVICE_PORT_NUMBER;
    io_pProfile->m_iPortForAgentService = 
       DEFAULT_AGENT_SERVICE_PORT_NUMBER;
    strncpy(io_pProfile->m_wsApplianceAddress,
            i_wsApplianceAddress,
            KMS_MAX_NETWORK_ADDRESS); 
    io_pProfile->m_wsApplianceAddress[KMS_MAX_NETWORK_ADDRESS] = 0;
    io_pProfile->m_iClusterDiscoveryFrequency = i_iClusterDiscoveryFrequency;
    io_pProfile->m_iTransactionTimeout = i_iTransactionTimeout;
    io_pProfile->m_iFailoverLimit = i_iFailOverLimit;
    io_pProfile->m_eKMSmode = i_eKMSmode;

    // if the file isn't found, create a new one
    bool bProfileExists = ProfileExists( g_wsWorkingDirectory,  /* pass in default */
                                         io_pProfile->m_wsProfileName );

#ifdef KMSUSERPKCS12
	/*
	 * Fix logic for determining if this request is for enrollment.
	 * Look to see if the server cert and clientkey.p12 file exist.
	 * We always expect a password for Solaris which is used to
	 * validate that the user has access to the clientkey data by
	 * attempting to use it to open the PKCS12 file.
	 */
	 bool bEnrolling = !ClientKeyP12Exists(io_pProfile->m_wsProfileName);
#else
    bool bEnrolling = i_wsEntityID && i_wsPassphrase;
#endif

    if ( bSuccess && !bEnrolling && !bProfileExists )
    {
       // when not enrolling a profile must exist
       bSuccess = false;
       Log(AUDIT_CLIENT_LOAD_PROFILE,
           i_wsProfileName,
           NULL,
           "Enrollment attempted but profile could not be found" );
    }

    // if the file isn't found, create a new one
    if ( bSuccess && !bProfileExists )
    {
       strncpy(io_pProfile->m_wsEntityID,
               i_wsEntityID,
               KMS_MAX_ENTITY_ID );
       io_pProfile->m_wsEntityID[KMS_MAX_ENTITY_ID] = 0;
       bSuccess = CreateProfile( io_pProfile, 
                                 g_wsWorkingDirectory, 
                                 io_pProfile->m_wsProfileName );
    }
    
    // load profile.cfg file
    if ( bSuccess )
    {
        bSuccess = GetConfig( io_pProfile );

    }

    // if profile settings changed then update the profile storage
    if ( bSuccess && 
         ( strncmp(io_pProfile->m_wsApplianceAddress, 
                   i_wsApplianceAddress, KMS_MAX_NETWORK_ADDRESS ) != 0 ||
           io_pProfile->m_iClusterDiscoveryFrequency != i_iClusterDiscoveryFrequency ||
           io_pProfile->m_iTransactionTimeout != i_iTransactionTimeout ||
           io_pProfile->m_iFailoverLimit != i_iFailOverLimit
         ))
    {
        strncpy(io_pProfile->m_wsApplianceAddress,
                i_wsApplianceAddress,
                KMS_MAX_NETWORK_ADDRESS); 
        io_pProfile->m_wsApplianceAddress[KMS_MAX_NETWORK_ADDRESS] = 0;
        io_pProfile->m_iClusterDiscoveryFrequency = i_iClusterDiscoveryFrequency;
        io_pProfile->m_iTransactionTimeout = i_iTransactionTimeout;
        io_pProfile->m_iFailoverLimit = i_iFailOverLimit;

        bSuccess = StoreConfig( io_pProfile );
    }

    // get PKI info from prior enrollment
    if ( bSuccess && !bEnrolling )
    {
#ifdef KMSUSERPKCS12
	/*
	 * Decrypt the PKCS12 file with the client cert and key using
	 * the given password.  If it fails, then return an auth failure
	 * status.  If success, write the client cert and key to the client file
	 * so it can be used later by the SOAP SSL functions.
	 */
	CCertificate* pEntityCertificate = new CCertificate;;
	CPrivateKey*  pEntityPrivateKey = new CPrivateKey;
	bSuccess = GetPKCS12CertAndKey(io_pProfile,
	    i_wsPassphrase,
	    pEntityCertificate,
	    pEntityPrivateKey);
	if (!bSuccess) {
		Log(AUDIT_CLIENT_LOAD_PROFILE,
			i_wsProfileName,
			NULL,
			"Enrollment Certificate and Private Key "\
			"were not loaded from PKCS12" );
	} else {
		/*
		 * Write out the cert and key individually so GetPKIcerts
		 * can use them.
		 */
		 bSuccess = StoreTempAgentPKI(io_pProfile,
		    pEntityCertificate, pEntityPrivateKey);
		 if (!bSuccess) {
			Log(AUDIT_CLIENT_LOAD_PROFILE,
				i_wsProfileName,
				NULL,
				"Enrollment Certificate and Private Key "\
				"were not stored to file." );
		 }
	}
	delete pEntityCertificate;
	delete pEntityPrivateKey;

#endif
	if (bSuccess)
        	bSuccess = GetPKIcerts( io_pProfile );
    }

    // if not enrolling then previously enrolled PKI info should now be initialized
    if ( bSuccess && !bEnrolling && 
        (!io_pProfile->m_sHexHashedPassphrase || 
        !io_pProfile->m_iEnrolled  ))
    {
        bSuccess = false;
        Log(AUDIT_CLIENT_LOAD_PROFILE,
          i_wsProfileName,
          NULL,
          "Enrollment Certificates and Private Key were not loaded from profile" );
    }

    io_pProfile->m_bIsClusterDiscoveryCalled = false;

    // allocate main soap struct
    struct soap* pstSoap = 0;

    if ( bSuccess )
    {
        pstSoap = (struct soap*)malloc( sizeof(struct soap) );

        io_pProfile->m_pvSoap = pstSoap;

        bSuccess = ( pstSoap != NULL );

        if ( bSuccess )
        {
            soap_init2( pstSoap, 
                    (SOAP_XML_STRICT | SOAP_C_UTFSTRING ),
                    (SOAP_XML_STRICT | SOAP_C_UTFSTRING) );
            
#ifdef METAWARE
            K_SetupCallbacks ( pstSoap );
#endif

            soap_set_namespaces( pstSoap, KMS_Agent_namespaces );

            pstSoap->connect_timeout = io_pProfile->m_iTransactionTimeout;
            pstSoap->send_timeout = io_pProfile->m_iTransactionTimeout;
            pstSoap->recv_timeout = io_pProfile->m_iTransactionTimeout;
        }
        else
        {
#if defined(DEBUG) && defined(METAWARE)
           log_printf("Malloc %x pstSoap returned null\n", 
                      sizeof(struct soap));
#endif
           
        }      
    }

    // delete the existing cluster config if the input IP address 
    // does not match one already known to the cluster config

    // Note that KMSClientProfile may be too large to fit on the stack, so we're
    // going to put it on the heap.

    KMSClientProfile* pstTempProfile = 0;
    bool bFound = false;
    int i;

    if ( bSuccess )
    {
        pstTempProfile = (KMSClientProfile*)malloc( sizeof(KMSClientProfile) );
        bSuccess = (pstTempProfile != 0);
#if defined(METAWARE)
        if (!bSuccess) 
           log_printf("Malloc %x pstTempProfile returned null\n", 
                      sizeof(KMSClientProfile));
#endif

    }

    int bClusterInformationFound = false;

    if ( bSuccess )
    {
        memcpy( pstTempProfile, io_pProfile, sizeof(KMSClientProfile) );

        bSuccess = LoadClusterInformation( pstTempProfile, bClusterInformationFound );
    }

    // got cluster info from persistent storage
    if ( bSuccess && bClusterInformationFound )
    {
       // see if address is a member of the remembered cluster or is a 
       // new kma, meaning this KMA joins the cluster as the 
       // discovery KMA.
        for ( i = 0; i < pstTempProfile->m_iClusterNum; i++ )
        {
            bFound = (strncmp( pstTempProfile->m_aCluster[i].m_wsApplianceNetworkAddress, 
                              io_pProfile->m_wsApplianceAddress,
                              KMS_MAX_NETWORK_ADDRESS) == 0);

            if ( bFound )
            {
                break;
            }
#if defined(DEBUG) && defined(METAWARE)
            else
               log_printf ("KMSClient_LoadProfile : Appliance Address doesn't match");
#endif
        }
        
        if ( !bFound ) 
        {
#if defined(DEBUG) && defined(METAWARE)
           log_printf ("KMSClient_LoadProfile : delete cluster");
#endif
           DeleteCluster( pstTempProfile );
           char msg[256];
           K_snprintf(msg, 256,
               "KMSClientProfile.LoadProfile(): deleting previous cluster config, %s not found\n",
                io_pProfile->m_wsApplianceAddress);
           Log(AUDIT_CLIENT_LOAD_PROFILE,
              i_wsProfileName,
              NULL,
              msg );
           DeleteCluster( pstTempProfile );
        }
        else
        {
            // since address is a member of the persisted cluster copy the persisted cluster info to the profile 
            io_pProfile->m_iClusterNum = pstTempProfile->m_iClusterNum;
            memcpy(io_pProfile->m_aCluster,
                   pstTempProfile->m_aCluster,
                    sizeof(KMSClusterEntry)*io_pProfile->m_iClusterNum);
        }
    }
#if defined(DEBUG) && defined(METAWARE)
    else
       log_printf ("KMSClient_LoadProfile : no persisted cluster information");
#endif

    if ( pstTempProfile )
    {
#if defined(DEBUG) && defined(METAWARE)
       log_printf ("KMSClient_LoadProfile : free the temporary profile");
#endif
        free( pstTempProfile );
        pstTempProfile = 0;
    }

    if ( bSuccess && !io_pProfile->m_iEnrolled )
    {
#if defined(DEBUG) && defined(METAWARE)
       log_printf ("KMSClient_LoadProfile : call EnrollAgent");
#endif
        // enroll the agent
        bSuccess = EnrollAgent( io_pProfile,
                                i_wsEntityID,
                                i_wsPassphrase );
    }
#if defined(DEBUG) && defined(METAWARE)
    else if (io_pProfile->m_iEnrolled)
       log_printf ("KMSClient_LoadProfile : Already Enrolled");
#endif


 
    if (bSuccess)
    {
       // Initialize SSL - use CLIENT AUTH
       // CLIENT_AUTHENTICATION needs the pstSoap, and expects 
       // the profile io_pProfile to be full (have the other certificates 
       // and keypair)

        if ( bSuccess )
        {
            bSuccess = 
                K_soap_ssl_client_context( 
                   io_pProfile,                            // in/out
                   pstSoap,                                // out
                   SOAP_SSL_REQUIRE_CLIENT_AUTHENTICATION  // in - flags
                    ) == SOAP_OK;

            if ( !bSuccess )
            {
#if defined(DEBUG) && defined(METAWARE)
                if (!bSuccess)
                  log_printf ("KMSClient_LoadProfile : K_soap_ssl_client_context failed");
#endif
                GetSoapFault(sSoapFaultMsg, (struct soap*)pstSoap);      
                GetPeerNetworkAddress(sKmaAddress, pstSoap);

                LogError(io_pProfile,
                    AUDIT_CLIENT_LOAD_PROFILE_SOAP_ERROR,
                    NULL,
                    sKmaAddress,
                    sSoapFaultMsg );
            }
        }
        
        // discover the cluster

        if ( bSuccess && 
            io_pProfile->m_iClusterDiscoveryFrequency > 0 )
         {
              bSuccess = ( KMSClient_GetClusterInformation(
                                            io_pProfile,
                                            io_pProfile->m_wsEntitySiteID, 
                                            sizeof(io_pProfile->m_wsEntitySiteID),
                                            &(io_pProfile->m_iClusterNum),
                                            io_pProfile->m_aCluster,
                                            KMS_MAX_CLUSTER_NUM) != 0 );
              // KMSClient_GetClusterInformation logs errors
              
              if (bSuccess && i_eKMSmode == FIPS_MODE)
              {
                    bSuccess = !KMSClient_NoFIPSCompatibleKMAs(io_pProfile);
                    if (!bSuccess)
                    {
                        LogError(io_pProfile,
                            AUDIT_CLIENT_AGENT_LOAD_PROFILE_NO_FIPS_COMPATIBLE_KMAS_AVAILABLE,
                            NULL,
                            NULL,
                            NULL );                        
                    }
              }
         }
#if defined(DEBUG) && defined(METAWARE)
        if (!bSuccess)
           log_printf ("KMSClient_LoadProfile : getClusterInformation failed");
#endif

#ifdef KMSUSERPKCS12
	/*
	 * Once the SSL context is established, delete the
	 * private key file.
	 */
	 (void) CleanupPrivateKeyFile(io_pProfile);
#endif
    }
#if defined(DEBUG) && defined(METAWARE)
    else if (!bSuccess)
       log_printf ("KMSClient_LoadProfile : EnrollAgent failed");
#endif

    CAgentLoadBalancer *pAgentLoadBalancer = new CAgentLoadBalancer(io_pProfile);
    if(pAgentLoadBalancer == NULL)
    {
        bSuccess = false;
    }

#if defined(DEBUG) && defined(METAWARE)
    if (!bSuccess)
       log_printf ("KMSClient_LoadProfile : new CAgentLoadBalancer failed");
#endif

    io_pProfile->m_pAgentLoadBalancer = pAgentLoadBalancer;

    // create a data unit server affinity cache for Agents

    if ( bSuccess )
    {
        io_pProfile->m_pDataUnitCache = new CDataUnitCache();

        bSuccess = ( io_pProfile->m_pDataUnitCache != NULL );
    }

    if ( bSuccess )
    {
#if defined(DEBUG) && defined(METAWARE)
       log_printf ("KMSClient_LoadProfile : set version to KMS_AGENT_VERSION = %x", 
                   KMS_AGENT_VERSION);
       log_printf ("KMSClient_LoadProfile : profile is: %x\n", io_pProfile);
#endif
       // this is checked later by ProfileLoaded and is taken 
       // to indicate that the profile was correctly loaded
	   io_pProfile->m_iVersion = KMS_AGENT_VERSION;
    }

    if( !bSuccess )
    {
        K_DestroyMutex((K_MUTEX_HANDLE)io_pProfile->m_pLock);
        io_pProfile->m_pLock = 0;

        if ( io_pProfile->m_pvSoap )
        {
            soap_destroy( (struct soap*)io_pProfile->m_pvSoap );
            soap_end( (struct soap*)io_pProfile->m_pvSoap );
            soap_done( (struct soap*)io_pProfile->m_pvSoap );

            free( (struct soap*)io_pProfile->m_pvSoap );
            io_pProfile->m_pvSoap = 0;

            if( io_pProfile->m_pAgentLoadBalancer != NULL)
            {
                delete(reinterpret_cast <CAgentLoadBalancer *>(io_pProfile->m_pAgentLoadBalancer));
            }

            if( io_pProfile->m_pDataUnitCache != NULL)
            {
                delete(reinterpret_cast <CDataUnitCache *>(io_pProfile->m_pDataUnitCache));
            }

        }
#if defined(DEBUG) && defined(METAWARE)
        log_printf ("KMSClient_LoadProfile : failed - returning");
#endif
    }

    return bSuccess;
}

/**
 *  compare cluster entries having equivalent KMA names (aka Appliance alias) and 
 *  return true if equal.  Note:  KMANetworkAddress comparison is handled separately
 *  due to IPv4/IPv6
 */
static bool EqualClusterEntry( 
                       struct KMS_Discovery::KMS_Discovery_ClusterMember const *i_pLeft, 
                       KMSClusterEntry                                   const *i_pRight)
{
    bool bEnabled = i_pRight->m_iEnabled ? true : false;
    if ( i_pLeft->Enabled != bEnabled )
    {
        return false;
    }
    if ( i_pLeft->KMAID != i_pRight->m_lApplianceID )
    {
        return false;
    }
    if ( strncmp(i_pLeft->KMASiteID, 
            i_pRight->m_wsApplianceSiteID,
            KMS_MAX_ENTITY_SITE_ID) != 0 )
    {
        return false;
    }
    //    Note: we now minimize persistence of cluster changes by not saving 
    //      whenever m_iResponding changes

    return true;
}
/**
 *  @return true if the current address matches the provided IPv6Address
 *  when the i_bUseIPv6 arg is true, otherwise compare the current address
 *  with the IPv4Address.  If i_bUseIPv6 then i_pCurrentAddress must be
 *  enclosed in brackets, i.e. as in RFC 2396.
 */
static bool EqualKMANetworkAddress (
                                    bool i_bUseIPv6,
                                    const char * const i_pIPv6Address,
                                    const char * const i_pIPv4Address,
                                    const char * const i_pCurrentAddress
                                    )
{
    bool bEqualAddress = true;
    
    if ( i_pCurrentAddress == NULL )
    {
        return false;
    }
    
    if (i_bUseIPv6)
    {
        if ( i_pIPv6Address == NULL )
        {
            return false;
        }
        char sIPv6Address[KMS_MAX_NETWORK_ADDRESS] = "[";
        
        strcat(sIPv6Address, i_pIPv6Address);
        
        char * pLoc = strchr(sIPv6Address, '/');
                
        if ( pLoc != NULL )
        {
            // remove prefix from address
            *pLoc = '\0';
        }
        strcat(sIPv6Address, "]");
        bEqualAddress = strncmp(sIPv6Address, i_pCurrentAddress, KMS_MAX_NETWORK_ADDRESS) == 0;
    }
    else
    {
        if ( i_pIPv4Address == NULL )
        {
            return false;
        }
        bEqualAddress = strncmp(i_pIPv4Address, i_pCurrentAddress, KMS_MAX_NETWORK_ADDRESS) == 0;
    }
    
    return bEqualAddress;
}

/**
 *  compares the profile's current cluster state with the filtered discover
 *  cluster response and returns true if the repsonse
 *  differs from i_pProfile->m_aCluster.  A cluster has changed if the state of any
 *  cluster node has changed or if the set of cluster nodes has changed.
 *  The order of nodes is immaterial.
 */
static bool ClusterConfigChanged (
                                  KMSClientProfile const *i_pProfile,
                                  char * const i_sResponseEntitySiteID,
                                  struct KMS_Discovery::KMS_Discovery__ArrayOfClusterMembers const *i_pFilteredCluster)
{
    int i, j;

    FATAL_ASSERT(i_pProfile);
    FATAL_ASSERT(i_pFilteredCluster);

    // cardinality check
    if (i_pProfile->m_iClusterNum !=
        i_pFilteredCluster->__size)
    {
        return true;
    }

    // check if the agent's site ID changed
    if (strncmp(i_pProfile->m_wsEntitySiteID,
        i_sResponseEntitySiteID, KMS_MAX_ENTITY_SITE_ID) != 0)
    {
        return true;
    }

    // for all KMAs in filtered response check if they exist unchanged in the profile
    for (i = 0; i < i_pFilteredCluster->__size; i++)
    {
        bool bFound = false;
        for (j = 0; j < i_pProfile->m_iClusterNum; j++)
        {
            if (strncmp(i_pFilteredCluster->__ptr[i].KMAName,
                    i_pProfile->m_aCluster[j].m_wsApplianceAlias,
                    KMS_MAX_ENTITY_ID) == 0)
            {
                bFound = true;
                if (
                !EqualKMANetworkAddress(
                    strchr(i_pProfile->m_wsApplianceAddress, ':') ? true : false,
                    i_pFilteredCluster->__ptr[i].KMANetworkAddressIPv6,
                    i_pFilteredCluster->__ptr[i].KMANetworkAddress,
                    i_pProfile->m_aCluster[j].m_wsApplianceNetworkAddress) ||
                !EqualClusterEntry((i_pFilteredCluster->__ptr + i),
                    &i_pProfile->m_aCluster[j]))
                
                {
                    return true;
                }
            }
        }
        if ( !bFound )
        {
            return true;
        }
    }
    return false;
}

/**
 *  returns true if the string is a valid IPv6 address syntactically
 */
static bool ValidIPv6KMAaddress( const char * const i_pIPAddress )
{
    FATAL_ASSERT( i_pIPAddress );
    
    if ( strlen(i_pIPAddress) <= 0 )
    {
        return false;
    }
    
    // simple check
    if ( strchr( i_pIPAddress, ':'))
    {
        return true;
    }
    
    return false;
}
/**
 *
 */
static void FreeFilteredCluster (
                                  struct KMS_Discovery::KMS_Discovery__ArrayOfClusterMembers * const io_stFilteredCluster,
                                  int iLimit )
{
    int j = 0;
    for (; j < iLimit; j++ )
    {
        free( io_stFilteredCluster->__ptr[j].KMAName );
        free( io_stFilteredCluster->__ptr[j].KMASiteID );
        free( io_stFilteredCluster->__ptr[j].KMAHostName );
        free( io_stFilteredCluster->__ptr[j].KMANetworkAddress );
        free( io_stFilteredCluster->__ptr[j].KMAVersion );
        free( io_stFilteredCluster->__ptr[j].KMAHostNameIPv6 );
        free( io_stFilteredCluster->__ptr[j].KMANetworkAddressIPv6 );
    }

    free( io_stFilteredCluster->__ptr );
}

/**
 *  filters the discover cluster response to be less than or equal to KMS_MAX_CLUSTER_NUM KMAs.  The heuristic used to filter
 *  the response is the same as used by CAgentLoadBalancer::KMSClient_SortClusterArray(), FIPS compatibility, then within site,
 *  then responding and enabled KMAs.
 *  @param i_stResponse pointer to gsoap discover cluster service response
 *  @param io_stFilteredCluster pointer to gsoap discover cluster array to be populated with the filtered list of KMAs
 *  @return true on success and io_stFilteredCluster->__size less than or equal to KMS_MAX_CLUSTER_NUM,
 *  otherwise io_stFilteredCluster is undefined. io_stFilteredCluster->__ptr is populated with the array of elements
 *  malloc'd.
 */
static bool FilterCluster (struct KMS_Discovery::KMS_Discovery__DiscoverClusterResponse * const i_stResponse,
                           bool i_bFIPS,
                           struct KMS_Discovery::KMS_Discovery__ArrayOfClusterMembers * const io_stFilteredCluster)
{
    /*
     *  do something like KMSAgentLoadBalancer:SortClusterArray() to the stResponse array
     *  return 1st KMS_MAX_CLUSTER_NUM entries and free the rest.
    */

    FATAL_ASSERT(i_stResponse);
    FATAL_ASSERT(io_stFilteredCluster);

    io_stFilteredCluster->__size = i_stResponse->ArrayOfClusterMembers.__size;
    io_stFilteredCluster->__ptr = reinterpret_cast < struct KMS_Discovery::KMS_Discovery_ClusterMember * >
            ( calloc( io_stFilteredCluster->__size,
                      sizeof (struct KMS_Discovery::KMS_Discovery_ClusterMember ) ) );

    if (io_stFilteredCluster->__ptr == NULL)
    {
        Log(AUDIT_CLIENT_FILTER_CLUSTER_FAILED,
                NULL,
                NULL,
                "calloc failed");
        return false;
    }

    if (io_stFilteredCluster->__size <= 0)
    {
        Log(AUDIT_CLIENT_FILTER_CLUSTER_FAILED,
                NULL,
                NULL,
                "returned cluster size is not positive");
        return false;
    }

    // copy response cluster members
    for (int i = 0; i < io_stFilteredCluster->__size; i++)
    {
        bool bSuccess = true;

        size_t iKMANameSize = 0, iKMASiteIDSize = 0, iKMAHostNameSize = 0,
                iKMANetworkAddressSize = 0, iKMAVersionSize = 0, iKMAHostNameIPv6Size = 0,
                iKMANetworkAddressIPv6Size = 0;
        
        // allocate storage for the various struct member's arrays
        iKMANameSize = strlen(i_stResponse->ArrayOfClusterMembers.__ptr[i].KMAName)+1;
        io_stFilteredCluster->__ptr[i].KMAName = reinterpret_cast <char *> (malloc(iKMANameSize));

        iKMASiteIDSize = strlen(i_stResponse->ArrayOfClusterMembers.__ptr[i].KMASiteID)+1;
        io_stFilteredCluster->__ptr[i].KMASiteID = reinterpret_cast <char *> (malloc(iKMASiteIDSize));

        iKMAHostNameSize = strlen(i_stResponse->ArrayOfClusterMembers.__ptr[i].KMAHostName)+1;
        io_stFilteredCluster->__ptr[i].KMAHostName = reinterpret_cast <char *> (malloc(iKMAHostNameSize));

        iKMANetworkAddressSize = strlen(i_stResponse->ArrayOfClusterMembers.__ptr[i].KMANetworkAddress)+1;
        io_stFilteredCluster->__ptr[i].KMANetworkAddress = reinterpret_cast <char *> (malloc(iKMANetworkAddressSize));

        // KMAVersion is an optional field derived from an xml attribute in the soap interface that will not be present in 2.0 KMAs
        if (i_stResponse->ArrayOfClusterMembers.__ptr[i].KMAVersion)
        {
            iKMAVersionSize = strlen(i_stResponse->ArrayOfClusterMembers.__ptr[i].KMAVersion)+1;
            io_stFilteredCluster->__ptr[i].KMAVersion = reinterpret_cast <char *> (malloc(iKMAVersionSize));
            if (io_stFilteredCluster->__ptr[i].KMAVersion == NULL)
            {
                bSuccess = false;
            }
        }
        else
        {
            io_stFilteredCluster->__ptr[i].KMAVersion = NULL;
        }

        // KMAHostNameIPv6 is an optional field derived from an xml attribute in the soap interface that will not be present in 2.0 KMAs
        if (i_stResponse->ArrayOfClusterMembers.__ptr[i].KMAHostNameIPv6)
        {
            iKMAHostNameIPv6Size = strlen(i_stResponse->ArrayOfClusterMembers.__ptr[i].KMAHostNameIPv6)+1;
            io_stFilteredCluster->__ptr[i].KMAHostNameIPv6 = reinterpret_cast <char *> (malloc(iKMAHostNameIPv6Size));
            if ( io_stFilteredCluster->__ptr[i].KMAHostNameIPv6 == NULL )
            {
                bSuccess = false;
            }
        }
        else
        {
            io_stFilteredCluster->__ptr[i].KMAHostNameIPv6 = NULL;
        }

        // KMANetworkAddressIPv6 is an optional field derived from an xml attribute in the soap interface that will not be present in 2.0 KMAs
        if (i_stResponse->ArrayOfClusterMembers.__ptr[i].KMANetworkAddressIPv6)
        {
            iKMANetworkAddressIPv6Size = strlen(i_stResponse->ArrayOfClusterMembers.__ptr[i].KMANetworkAddressIPv6)+1;
            io_stFilteredCluster->__ptr[i].KMANetworkAddressIPv6 = reinterpret_cast <char *> (malloc(iKMANetworkAddressIPv6Size));
            if ( io_stFilteredCluster->__ptr[i].KMANetworkAddressIPv6 == NULL )
            {
                bSuccess = false;
            }
            }
        else
        {
            io_stFilteredCluster->__ptr[i].KMANetworkAddressIPv6 = NULL;
        }

        if ( io_stFilteredCluster->__ptr[i].KMAName == NULL ||
             io_stFilteredCluster->__ptr[i].KMASiteID == NULL ||
             io_stFilteredCluster->__ptr[i].KMAHostName == NULL ||
             io_stFilteredCluster->__ptr[i].KMANetworkAddress == NULL ||
             !bSuccess )
        {
            // cleanup and return
            FreeFilteredCluster( io_stFilteredCluster, i+1 );
            Log( AUDIT_CLIENT_FILTER_CLUSTER_FAILED,
                    NULL,
                    NULL,
                    "malloc failed" );
            return false;
        }

        strncpy(io_stFilteredCluster->__ptr[i].KMAName,
                i_stResponse->ArrayOfClusterMembers.__ptr[i].KMAName,
                iKMANameSize);
        io_stFilteredCluster->__ptr[i].KMAName[iKMANameSize-1] = '\0';

        strncpy(io_stFilteredCluster->__ptr[i].KMASiteID,
                i_stResponse->ArrayOfClusterMembers.__ptr[i].KMASiteID,
                iKMASiteIDSize);
        io_stFilteredCluster->__ptr[i].KMASiteID[iKMASiteIDSize-1] = '\0';

        strncpy(io_stFilteredCluster->__ptr[i].KMAHostName,
                i_stResponse->ArrayOfClusterMembers.__ptr[i].KMAHostName,
                iKMAHostNameSize);
        io_stFilteredCluster->__ptr[i].KMAHostName[iKMAHostNameSize-1] = '\0';

        strncpy(io_stFilteredCluster->__ptr[i].KMANetworkAddress,
                i_stResponse->ArrayOfClusterMembers.__ptr[i].KMANetworkAddress,
                iKMANetworkAddressSize);
        io_stFilteredCluster->__ptr[i].KMANetworkAddress[iKMANetworkAddressSize-1] = '\0';

        if ( io_stFilteredCluster->__ptr[i].KMAVersion )
        {
            strncpy( io_stFilteredCluster->__ptr[i].KMAVersion,
                    i_stResponse->ArrayOfClusterMembers.__ptr[i].KMAVersion,
                    iKMAVersionSize );
            io_stFilteredCluster->__ptr[i].KMAVersion[iKMAVersionSize-1] = '\0';
        }

        if (io_stFilteredCluster->__ptr[i].KMAHostNameIPv6)
        {
            strncpy(io_stFilteredCluster->__ptr[i].KMAHostNameIPv6,
                    i_stResponse->ArrayOfClusterMembers.__ptr[i].KMAHostNameIPv6,
                    iKMAHostNameIPv6Size);
            io_stFilteredCluster->__ptr[i].KMAHostNameIPv6[iKMAHostNameIPv6Size-1] = '\0';
        }

        if ( io_stFilteredCluster->__ptr[i].KMANetworkAddressIPv6 )
        {
            strncpy( io_stFilteredCluster->__ptr[i].KMANetworkAddressIPv6,
                    i_stResponse->ArrayOfClusterMembers.__ptr[i].KMANetworkAddressIPv6,
                    iKMANetworkAddressIPv6Size );
            io_stFilteredCluster->__ptr[i].KMANetworkAddressIPv6[iKMANetworkAddressIPv6Size-1] = '\0';
        }

        io_stFilteredCluster->__ptr[i].KMAID = i_stResponse->ArrayOfClusterMembers.__ptr[i].KMAID;
        io_stFilteredCluster->__ptr[i].Enabled = i_stResponse->ArrayOfClusterMembers.__ptr[i].Enabled;
        io_stFilteredCluster->__ptr[i].KMS_Discovery__Locked = i_stResponse->ArrayOfClusterMembers.__ptr[i].KMS_Discovery__Locked;
        
        // set load to zero, KMA with version <= Build600 don't initialize
        // the load field from the service network
        if ( ( io_stFilteredCluster->__ptr[i].KMAVersion &&
             strcmp( io_stFilteredCluster->__ptr[i].KMAVersion, "Build600" ) <= 0 ) ||
             io_stFilteredCluster->__ptr[i].KMAVersion == NULL )
        {
            io_stFilteredCluster->__ptr[i].Load = 0;
        }
        else
        {
            io_stFilteredCluster->__ptr[i].Load = i_stResponse->ArrayOfClusterMembers.__ptr[i].Load;
        }

        io_stFilteredCluster->__ptr[i].Responding = i_stResponse->ArrayOfClusterMembers.__ptr[i].Responding;

        if (!bSuccess)
        {
            FreeFilteredCluster( io_stFilteredCluster, i );
            Log(AUDIT_CLIENT_FILTER_CLUSTER_FAILED,
                    NULL,
                    NULL,
                    "cluster member copy failed");
            return false;
        }
    }

    // is filtering necessary?
    if (io_stFilteredCluster->__size <= KMS_MAX_CLUSTER_NUM)
    {
        // no filtering required
        return true;
    }
    else
    {
        char sMesg[100];
        K_snprintf(sMesg, sizeof (sMesg), "DiscoverCluster returned %d KMAs, filtering to %d ...", io_stFilteredCluster->__size, KMS_MAX_CLUSTER_NUM);
        Log(AUDIT_CLIENT_FILTER_CLUSTER,
                    NULL,
                    NULL,
                    sMesg);

    }

    // adjust loads according to availability, site and FIPS compatibility
    {
        int i = 0;
        for (; i < io_stFilteredCluster->__size; i++)
        {
            if (io_stFilteredCluster->__ptr[i].Enabled == false
                || io_stFilteredCluster->__ptr[i].Responding == false
                || io_stFilteredCluster->__ptr[i].KMS_Discovery__Locked == true)
            {
                io_stFilteredCluster->__ptr[i].Load += 0x40;
            }

            if (strcmp(io_stFilteredCluster->__ptr[i].KMASiteID,
                i_stResponse->EntitySiteID) != 0)
            {
                io_stFilteredCluster->__ptr[i].Load += 0x20;

            }

            if ( i_bFIPS &&
                    !FIPScompatibleKMA(io_stFilteredCluster->__ptr[i].KMAVersion))
            {
                io_stFilteredCluster->__ptr[i].Load += 0x80;
            }
        }
    }

    // sort ascending by load

    // gnome sort: the simplest sort algoritm
    {
        int i = 0;
        while (i < io_stFilteredCluster->__size)
        {
            if (i == 0 || io_stFilteredCluster->__ptr[i - 1].Load <= io_stFilteredCluster->__ptr[i].Load)
            {
                i++;
            }
            else
            {
                struct KMS_Discovery::KMS_Discovery_ClusterMember tmp = io_stFilteredCluster->__ptr[i];
                io_stFilteredCluster->__ptr[i] = io_stFilteredCluster->__ptr[i - 1];
                io_stFilteredCluster->__ptr[--i] = tmp;
            }
        }
    }

    // now filter the list, freeing memory allocated for copied elements that are not being retained
    {
        int i=KMS_MAX_CLUSTER_NUM;
        for (; i < io_stFilteredCluster->__size; i++)
        {
            free(io_stFilteredCluster->__ptr[i].KMAName);
            free(io_stFilteredCluster->__ptr[i].KMASiteID);
            free(io_stFilteredCluster->__ptr[i].KMAHostName);
            free(io_stFilteredCluster->__ptr[i].KMANetworkAddress);
            free(io_stFilteredCluster->__ptr[i].KMAVersion);
            free(io_stFilteredCluster->__ptr[i].KMAHostNameIPv6);
            free(io_stFilteredCluster->__ptr[i].KMANetworkAddressIPv6);
        }
    }

    io_stFilteredCluster->__size = KMS_MAX_CLUSTER_NUM;
    
    Log(AUDIT_CLIENT_FILTER_CLUSTER,
                NULL,
                NULL,
                "success");
    
    return true;
};

/*---------------------------------------------------------------------------
 * Function: KMSClient_GetClusterInformation
 *
 *--------------------------------------------------------------------------*/
bool KMSClient_GetClusterInformation(
        KMSClientProfile *i_pProfile, 
        utf8char *o_wsEntitySiteID,
        int i_iEntitySiteIDSize,
        int *o_pApplianceNum,
        KMSClusterEntry *o_pClusterEntryArray,
        int i_iClusterEntryArraySize)
{
   FATAL_ASSERT(i_pProfile);
   FATAL_ASSERT( o_wsEntitySiteID );
   FATAL_ASSERT( o_pApplianceNum );
   FATAL_ASSERT( o_pClusterEntryArray );
   FATAL_ASSERT( i_iEntitySiteIDSize <= KMS_MAX_ENTITY_ID+1 );

   CAutoMutex oAutoMutex( (K_MUTEX_HANDLE)i_pProfile->m_pLock );

   bool bSuccess = true;
   char sSoapFaultMsg[g_iMAX_SOAP_FAULT_MESSAGE_LENGTH];
   char sKmaAddress[g_iMAX_PEER_NETWORK_ADDRESS_LENGTH];

   char sURL[KMS_MAX_URL+1];

   // set URL from the initial appliance address
   utf8cstr sApplianceAddress = i_pProfile->m_wsApplianceAddress;

#if defined(DEBUG) && defined(METAWARE)
    log_printf("KMSClient_GetClusterInformation : entered");
#endif

   K_snprintf(sURL,
           KMS_MAX_URL,
           "https://%s:%d",
           sApplianceAddress,
           i_pProfile->m_iPortForDiscoveryService);
   strncpy(i_pProfile->m_sURL, sURL, KMS_MAX_URL);
   i_pProfile->m_sURL[KMS_MAX_URL] = 0;
           
   // allocate and initialize a new soap env for the cluster discovery call
   struct soap *pstSoap = (struct soap*)i_pProfile->m_pvDiscoverySoap;

   if ( !i_pProfile->m_iEnrolled )
   {
        bSuccess = false;
   }
   
   if ( bSuccess )
   {
	   // allocate discovery soap runtime
	   if (pstSoap == NULL )
	   {
   	   	   pstSoap = soap_new();
		   i_pProfile->m_pvDiscoverySoap = pstSoap;
           /* soap_copy results in a segfault in sk_free() within libcrytpo.so
           pstSoap = soap_copy( (soap*)i_pProfile->m_pvSoap );
           */
           if (pstSoap == NULL)
           {
               bSuccess = false;
           }
           else
           {
               pstSoap->connect_timeout = i_pProfile->m_iTransactionTimeout;
               pstSoap->send_timeout = i_pProfile->m_iTransactionTimeout;
               pstSoap->recv_timeout = i_pProfile->m_iTransactionTimeout;

               soap_set_imode( pstSoap, (SOAP_XML_STRICT | SOAP_C_UTFSTRING) );      
               soap_set_omode( pstSoap, (SOAP_XML_STRICT | SOAP_C_UTFSTRING) );      

               soap_set_namespaces( pstSoap, KMS_Discovery_namespaces );
               bSuccess = K_soap_ssl_client_context( 
                               i_pProfile,
                               pstSoap,                   
                               SOAP_SSL_REQUIRE_CLIENT_AUTHENTICATION 
                                ) == SOAP_OK;
               if ( !bSuccess )
               {
                    Log(AUDIT_CLIENT_GetClusterInformation, 
                       NULL, 
                       NULL, 
                       "K_soap_ssl_client_context failed");
                    soap_destroy(pstSoap);
                    soap_end(pstSoap);
                    soap_done(pstSoap);
               }
           }
	   }
   }

   // Discovery
   struct KMS_Discovery::KMS_Discovery__DiscoverClusterResponse stResponse;

#if defined(DEBUG) && defined(METAWARE)
    log_printf("KMSClient_GetClusterInformation : call KMS_Discovery_DiscoverCluster");
#endif
    
    // SOAP - discover cluster
   if ( bSuccess )
   {
#ifdef DEBUG
      int iStartTickCount = K_GetTickCount();
      int iEndTickCount;
      char sDiscoverTimeMsg[100];
#endif
      bSuccess = 
         KMS_Discovery::soap_call_KMS_Discovery__DiscoverCluster(
            pstSoap, 
            sURL,
            NULL,
            NULL,
            stResponse ) == SOAP_OK;
#ifdef DEBUG
      iEndTickCount = K_GetTickCount();
      sprintf(sDiscoverTimeMsg, "DiscoverCluster soapcall elapsed time=%u ms",
              iEndTickCount-iStartTickCount);
      Log(AUDIT_CLIENT_GetClusterInformation, 
           NULL, 
           sApplianceAddress, 
           sDiscoverTimeMsg);
#endif

      if ( !bSuccess )
      {
         GetSoapFault(sSoapFaultMsg, (struct soap*)pstSoap);      
         GetPeerNetworkAddress(sKmaAddress, pstSoap);
         LogError(i_pProfile,AUDIT_CLIENT_GET_CLUSTER_INFORMATION_SOAP_ERROR,
                  NULL,
                  sKmaAddress,
                  sSoapFaultMsg );

         if ( !ServerError( sSoapFaultMsg, pstSoap->errnum ) )
         {
                // do not failover if error is client related
                soap_destroy( pstSoap );
                soap_end( pstSoap );
                soap_free( pstSoap );
                return false;
         }
      }

      // If we did not succeed to Discover from the initial appliance, 
      // try to discover from other appliances that we know about that are enabled.
      // Disabled Appliances are not attempted because they may have a stale view
      // of the cluster. In particular, they themselves are not aware that they
      // are disabled.

      if ( !bSuccess && i_pProfile->m_iClusterNum > 0 )
      {
         // Copy the profile's cluster array so that we don't have to lock the 
         // profile around a SOAP call

         int j = 0;
         int iClusterNum = 0;
         KMSClusterEntry* aCluster =
            (KMSClusterEntry*)malloc(sizeof(KMSClusterEntry) * KMS_MAX_CLUSTER_NUM);

         bSuccess = ( aCluster != 0 );
#if defined(DEBUG) && defined(METAWARE)
        if (!bSuccess) 
           log_printf("Malloc %x aCluster returned null\n", 
                      sizeof(KMSClusterEntry) * KMS_MAX_CLUSTER_NUM);
#endif

         if ( bSuccess )
         {
            iClusterNum = i_pProfile->m_iClusterNum;
            memcpy( aCluster, i_pProfile->m_aCluster, 
                    sizeof(KMSClusterEntry) * iClusterNum );

            // initialize to false since all KMAs could be disabled
            bSuccess = false;
            for ( j = 0; j < iClusterNum; j++ )
            {
               if ( aCluster[j].m_iEnabled == FALSE )
               {
                  continue;
               }

               sApplianceAddress = aCluster[j].m_wsApplianceNetworkAddress;
               K_snprintf(sURL,
                       KMS_MAX_URL,
                       "https://%s:%d", 
                       sApplianceAddress,
                       i_pProfile->m_iPortForDiscoveryService);

               Log(AUDIT_CLIENT_GetClusterInformation, 
                   NULL, 
                   sApplianceAddress, 
                   "Failing over and trying this appliance");

               // SOAP - discover cluster
               bSuccess = 
                  KMS_Discovery::soap_call_KMS_Discovery__DiscoverCluster(
                     pstSoap, 
                     sURL,
                     NULL,
                     NULL,
                     stResponse ) == SOAP_OK;

               if ( !bSuccess )
               {                        
                  GetSoapFault(sSoapFaultMsg, (struct soap*)pstSoap);
                  GetPeerNetworkAddress(sKmaAddress, pstSoap);
                  LogError(i_pProfile,AUDIT_CLIENT_GET_CLUSTER_INFORMATION_SOAP_ERROR,
                           NULL,
                           sKmaAddress,
                           sSoapFaultMsg );
               }
               else
               {
                  // The discover succeeded
                  break;
               }
            }
         }

         if ( aCluster != 0 )
         {
            free(aCluster);
         }

         if ( bSuccess )
         {
            // Set the Profile's initial appliance to the Appliance
            // that we just succeeded to Discover from. KMSClient_SelectAppliance()
            // persists the updated config
            KMSClient_SelectAppliance( i_pProfile, 
                                       i_pProfile->m_aCluster[j].m_wsApplianceNetworkAddress );
         }
      }
   }

   if ( bSuccess )
   {
      if (((int)strlen(stResponse.EntitySiteID) > i_iEntitySiteIDSize - 1)) 
      {
         bSuccess = false;
         LogError(i_pProfile,AUDIT_CLIENT_GET_CLUSTER_INFORMATION,
                  NULL,
                  NULL,
                  "returned site id size too large" );
      }
   }

   // copy returned cluster information into i_pProfile->m_aCluster after
   // filtering the cluster members to a list with size <= KMS_MAX_CLUSTER_NUM
   if ( bSuccess )
   {
      KMS_Discovery::KMS_Discovery__ArrayOfClusterMembers aFilteredCluster;
      
      bSuccess = FilterCluster(&stResponse, i_pProfile->m_eKMSmode == FIPS_MODE, &aFilteredCluster);
      if (!bSuccess )
      {
          LogError(i_pProfile, AUDIT_CLIENT_GET_CLUSTER_INFORMATION,
                  NULL,
                  NULL,
                  "cluster response filtering failed" );
      }

      if(bSuccess)
      {
         int i;
         bool bPersistClusterConfig = ClusterConfigChanged(i_pProfile,
                    stResponse.EntitySiteID,
                    &aFilteredCluster);
                      
         strncpy(o_wsEntitySiteID,stResponse.EntitySiteID, i_iEntitySiteIDSize-1 );
         o_wsEntitySiteID[i_iEntitySiteIDSize-1] = '\0';

         strncpy(i_pProfile->m_wsEntitySiteID, stResponse.EntitySiteID, i_iEntitySiteIDSize-1 );
         i_pProfile->m_wsEntitySiteID[i_iEntitySiteIDSize-1] = '\0';

         // fill the aCluster array in the i_pProfile
         i_pProfile->m_iClusterNum = aFilteredCluster.__size;
         for (i = 0;  i < i_pProfile->m_iClusterNum; i++)
         {
            i_pProfile->m_aCluster[i].m_lApplianceID = 
               (aFilteredCluster.__ptr+i)->KMAID;
            i_pProfile->m_aCluster[i].m_iEnabled = 
               (aFilteredCluster.__ptr+i)->Enabled;
            i_pProfile->m_aCluster[i].m_iResponding = 
               (aFilteredCluster.__ptr+i)->Responding;

            i_pProfile->m_aCluster[i].m_lLoad = (aFilteredCluster.__ptr+i)->Load;
            strncpy(i_pProfile->m_aCluster[i].m_wsApplianceAlias, 
                   (aFilteredCluster.__ptr+i)->KMAName,
                   KMS_MAX_ENTITY_ID);
            i_pProfile->m_aCluster[i].m_wsApplianceAlias[KMS_MAX_ENTITY_ID] = '\0';
            // if the m_wsApplianceAddress is IPv6 then we'll store
            // KMA IPv6 addresses if they have one
            if ( strchr( i_pProfile->m_wsApplianceAddress, ':') )
            {
                // KMAs prior to 2.1, or 2.1 KMAs at rep schema < 10
                // will not have IPv6 attributes in the soap response
                if ( (aFilteredCluster.__ptr+i)->KMANetworkAddressIPv6 &&
                      ValidIPv6KMAaddress((aFilteredCluster.__ptr+i)->KMANetworkAddressIPv6))
                {
                    strcpy(i_pProfile->m_aCluster[i].m_wsApplianceNetworkAddress, "[");
                    char * pLoc = strchr((aFilteredCluster.__ptr+i)->KMANetworkAddressIPv6,
                            '/');
                    if ( pLoc != NULL )
                    {
                        // remove prefix from address
                        *pLoc = '\0';
                        strcat(i_pProfile->m_aCluster[i].m_wsApplianceNetworkAddress,
                               (aFilteredCluster.__ptr+i)->KMANetworkAddressIPv6 );
                    }
                    else
                    {
                        strcat(i_pProfile->m_aCluster[i].m_wsApplianceNetworkAddress,
                                (aFilteredCluster.__ptr + i)->KMANetworkAddressIPv6);
                    }
                    strcat(i_pProfile->m_aCluster[i].m_wsApplianceNetworkAddress, "]");
                }
                else
                {
                    // use the IPv4 address
                    strncpy(i_pProfile->m_aCluster[i].m_wsApplianceNetworkAddress, 
                           (aFilteredCluster.__ptr+i)->KMANetworkAddress,
                           KMS_MAX_NETWORK_ADDRESS);                    
                }
            }
            else
            {
                strncpy(i_pProfile->m_aCluster[i].m_wsApplianceNetworkAddress, 
                       (aFilteredCluster.__ptr+i)->KMANetworkAddress,
                       KMS_MAX_NETWORK_ADDRESS);
            }
            i_pProfile->m_aCluster[i].m_wsApplianceNetworkAddress[KMS_MAX_NETWORK_ADDRESS] = '\0';
            strncpy(i_pProfile->m_aCluster[i].m_wsApplianceSiteID, 
                   (aFilteredCluster.__ptr+i)->KMASiteID,
                   KMS_MAX_ENTITY_SITE_ID);
            i_pProfile->m_aCluster[i].m_wsApplianceSiteID[KMS_MAX_ENTITY_SITE_ID] = '\0';

            if ((aFilteredCluster.__ptr + i)->KMAVersion)
            {
                strncpy(i_pProfile->m_aCluster[i].m_sKMAVersion,
                        (aFilteredCluster.__ptr + i)->KMAVersion,
                        KMS_MAX_VERSION_LENGTH);
                i_pProfile->m_aCluster[i].m_sKMAVersion[KMS_MAX_VERSION_LENGTH] = '\0';
            }
            else
            {
                i_pProfile->m_aCluster[i].m_sKMAVersion[0] = '\0';
            }

            if ((aFilteredCluster.__ptr + i)->KMS_Discovery__Locked)
            {
                i_pProfile->m_aCluster[i].m_iKMALocked = TRUE;
            }
            else
            {
                i_pProfile->m_aCluster[i].m_iKMALocked = FALSE;
            }
         }

         // now release malloc'd storage from filtering the cluster response
         FreeFilteredCluster( &aFilteredCluster, aFilteredCluster.__size );

         // fill the array specified by the caller
         *o_pApplianceNum = i_pProfile->m_iClusterNum;
         for (i = 0;  i < i_pProfile->m_iClusterNum; i++)
         {
            o_pClusterEntryArray[i].m_lApplianceID = i_pProfile->m_aCluster[i].m_lApplianceID;
            o_pClusterEntryArray[i].m_iEnabled = i_pProfile->m_aCluster[i].m_iEnabled;
            o_pClusterEntryArray[i].m_iResponding = i_pProfile->m_aCluster[i].m_iResponding;
            o_pClusterEntryArray[i].m_lLoad = i_pProfile->m_aCluster[i].m_lLoad;
            strncpy(o_pClusterEntryArray[i].m_wsApplianceAlias, 
                   i_pProfile->m_aCluster[i].m_wsApplianceAlias,
                   KMS_MAX_ENTITY_ID);
            o_pClusterEntryArray[i].m_wsApplianceAlias[KMS_MAX_ENTITY_ID] = '\0';
            strncpy(o_pClusterEntryArray[i].m_wsApplianceNetworkAddress, 
                   i_pProfile->m_aCluster[i].m_wsApplianceNetworkAddress,
                   KMS_MAX_NETWORK_ADDRESS);
            o_pClusterEntryArray[i].m_wsApplianceNetworkAddress[KMS_MAX_NETWORK_ADDRESS] = '\0';
            strncpy(o_pClusterEntryArray[i].m_wsApplianceSiteID, 
                   i_pProfile->m_aCluster[i].m_wsApplianceSiteID,
                   KMS_MAX_ENTITY_SITE_ID);
            o_pClusterEntryArray[i].m_wsApplianceSiteID[KMS_MAX_ENTITY_SITE_ID] = '\0';
            strncpy(o_pClusterEntryArray[i].m_sKMAVersion, i_pProfile->m_aCluster[i].m_sKMAVersion,
                    KMS_MAX_VERSION_LENGTH);
            o_pClusterEntryArray[i].m_sKMAVersion[KMS_MAX_VERSION_LENGTH] = '\0';
         }

         i_pProfile->m_iLastClusterDiscoveryTime = K_GetTickCount() / 1000;
         i_pProfile->m_bIsClusterDiscoveryCalled = true;

         if ( bPersistClusterConfig )
         {
             bSuccess = StoreCluster(i_pProfile);
             if (!bSuccess)
             {
                 Log(AUDIT_CLIENT_GetClusterInformation, 
                     NULL, 
                     NULL, 
                     "Could not store cluster");
             }
         }
      }
   }

   // cleanup 
   if (pstSoap)
   {
      soap_destroy(pstSoap);
      soap_end(pstSoap);
      if (!bSuccess)
      {
          soap_free(pstSoap);
      }
      else
      {
        // we want to persist discovery soap runtime to avoid ssl handshakes so soap_free() is not called
      }
   }
         
   // if we're enrolled but cannot get cluster information from an appliance, then we'll try to load
   // it from the profile
   if ( !bSuccess && i_pProfile->m_iEnrolled )
   {
      int bClusterInformationFound = false;

      bSuccess = LoadClusterInformation( i_pProfile, bClusterInformationFound );

      if ( bSuccess && bClusterInformationFound )
      {
         Log(AUDIT_CLIENT_GetClusterInformation, 
                 NULL, 
                 NULL, 
                 "Using persisted cluster information");

         strncpy(o_wsEntitySiteID, i_pProfile->m_wsEntitySiteID, i_iEntitySiteIDSize-1);
         o_wsEntitySiteID[i_iEntitySiteIDSize-1] = '\0';

         // fill the array specified by the caller
         *o_pApplianceNum = i_pProfile->m_iClusterNum;
         for (int i = 0;  i < i_pProfile->m_iClusterNum; i++)
         {
            o_pClusterEntryArray[i].m_lApplianceID = i_pProfile->m_aCluster[i].m_lApplianceID;
            o_pClusterEntryArray[i].m_iEnabled = i_pProfile->m_aCluster[i].m_iEnabled;
            o_pClusterEntryArray[i].m_iResponding = TRUE; // since cluster info comes from a file, set it to TRUE

            o_pClusterEntryArray[i].m_lLoad = i_pProfile->m_aCluster[i].m_lLoad;
            strncpy(o_pClusterEntryArray[i].m_wsApplianceAlias, 
                   i_pProfile->m_aCluster[i].m_wsApplianceAlias,
                   KMS_MAX_ENTITY_ID);
            o_pClusterEntryArray[i].m_wsApplianceAlias[KMS_MAX_ENTITY_ID] = '\0';
            strncpy(o_pClusterEntryArray[i].m_wsApplianceNetworkAddress, 
                   i_pProfile->m_aCluster[i].m_wsApplianceNetworkAddress,
                   KMS_MAX_NETWORK_ADDRESS);
            o_pClusterEntryArray[i].m_wsApplianceNetworkAddress[KMS_MAX_NETWORK_ADDRESS] = '\0';
            strncpy(o_pClusterEntryArray[i].m_wsApplianceSiteID, 
                   i_pProfile->m_aCluster[i].m_wsApplianceSiteID,
                   KMS_MAX_ENTITY_SITE_ID);
            o_pClusterEntryArray[i].m_wsApplianceSiteID[KMS_MAX_ENTITY_SITE_ID] = '\0';
            strncpy(o_pClusterEntryArray[i].m_sKMAVersion,
                    i_pProfile->m_aCluster[i].m_sKMAVersion, 
                    KMS_MAX_VERSION_LENGTH);
            o_pClusterEntryArray[i].m_sKMAVersion[KMS_MAX_VERSION_LENGTH] = '\0';
         }

         i_pProfile->m_iLastClusterDiscoveryTime = K_GetTickCount() / 1000;
      }
      else if ( bSuccess && !bClusterInformationFound )
      {
         // if we're here, then we need to return an error
         bSuccess = false;
      }
   }

   return bSuccess;
}

bool KMSClient_NoFIPSCompatibleKMAs(const KMSClientProfile * const i_pProfile)
{
    bool bNoFIPScompatibleKMA = true;
    for (int i=0; i < i_pProfile->m_iClusterNum; i++)
    {
        if ( FIPScompatibleKMA(i_pProfile->m_aCluster[i].m_sKMAVersion))
        {
            bNoFIPScompatibleKMA = false;
            break;
        }
    }
    return bNoFIPScompatibleKMA;
}

/*---------------------------------------------------------------------------
 * Function: KMSClient_SelectAppliance
 *
 *--------------------------------------------------------------------------*/
bool KMSClient_SelectAppliance(KMSClientProfile *i_pProfile,
                                utf8char *i_wsApplianceAddress)
{
    FATAL_ASSERT(i_pProfile);
    FATAL_ASSERT(i_wsApplianceAddress);

    CAutoMutex oAutoMutex( (K_MUTEX_HANDLE)i_pProfile->m_pLock );

    bool bSuccess = true;

    if(strlen(i_wsApplianceAddress) >= KMS_MAX_NETWORK_ADDRESS)
    {
        LogError(i_pProfile,AUDIT_CLIENT_SELECT_APPLIANCE,
            NULL,
            NULL,
            "Appliance Address too large" );
        bSuccess = false;        
    }

    if(bSuccess)
    {
        strncpy(i_pProfile->m_wsApplianceAddress, 
            i_wsApplianceAddress,
            KMS_MAX_NETWORK_ADDRESS);
        i_pProfile->m_wsApplianceAddress[KMS_MAX_NETWORK_ADDRESS] = 0;
    }

    bSuccess = StoreConfig( i_pProfile );

    return bSuccess;
}

bool KMSClient_ProfileLoaded( KMSClientProfile *i_pProfile )
{

#if defined(DEBUG) && defined(METAWARE)
   log_printf ("profile: %x", i_pProfile);
   log_printf ("profile: enrolled %x", i_pProfile->m_iEnrolled);
   log_printf ("profile: version  %x", i_pProfile->m_iVersion);
#endif   

    // more extensive tests could be performed but this should suffice
    if ( i_pProfile && 
        i_pProfile->m_iEnrolled &&
		i_pProfile->m_iVersion == KMS_AGENT_VERSION )
    {
        return true;
    }
    else
    {
        return false;
    }
}

/*---------------------------------------------------------------------------
 * Function: KMSClient_DeleteProfile
 *
 *--------------------------------------------------------------------------*/
bool KMSClient_DeleteProfile(utf8char *i_wsProfileName)
{
    FATAL_ASSERT( i_wsProfileName && (strlen(i_wsProfileName) > 0) );
    
    bool bSuccess = true;

    if (ProfileExists(g_wsWorkingDirectory, /* pass in default */
                      i_wsProfileName))
    {
        bSuccess = DeleteStorageProfile(i_wsProfileName);
    }

    return bSuccess;
}

/*---------------------------------------------------------------------------
 * Function: KMSClient_UnloadProfile
 *
 *--------------------------------------------------------------------------*/
bool KMSClient_UnloadProfile(KMSClientProfile *i_pProfile)
{
    if(i_pProfile != NULL && i_pProfile->m_pLock != NULL )
    {
#ifdef KMSUSERPKCS12
	/* Delete the private client key file if it's still around */
	CleanupPrivateKeyFile(i_pProfile);
#endif
        if (i_pProfile->m_pAgentLoadBalancer != NULL)
        {
            delete reinterpret_cast
                <CAgentLoadBalancer *> (i_pProfile->m_pAgentLoadBalancer);
        }
        if (i_pProfile->m_pDataUnitCache != NULL)
        {
            delete reinterpret_cast<CDataUnitCache *> (i_pProfile->m_pDataUnitCache);
        }
        K_DestroyMutex((K_MUTEX_HANDLE)i_pProfile->m_pLock);
        i_pProfile->m_pLock = 0;

        if ( i_pProfile->m_pvSoap )
        {
            soap_destroy( (struct soap*)i_pProfile->m_pvSoap );
            soap_end( (struct soap*)i_pProfile->m_pvSoap );
            soap_done( (struct soap*)i_pProfile->m_pvSoap );

            free( (struct soap*)i_pProfile->m_pvSoap );
            i_pProfile->m_pvSoap = 0;
        }
        
        if ( i_pProfile->m_pvDiscoverySoap)
        {
            soap_destroy( (struct soap*)i_pProfile->m_pvDiscoverySoap );
            soap_end( (struct soap*)i_pProfile->m_pvDiscoverySoap );
            soap_done( (struct soap*)i_pProfile->m_pvDiscoverySoap );

            free( (struct soap*)i_pProfile->m_pvDiscoverySoap );
            i_pProfile->m_pvDiscoverySoap = 0;           
        }
    }

    i_pProfile->m_iEnrolled = FALSE;

    return true; /* always return true, maybe there are cases which return false in the future */
}

bool FIPScompatibleKMA(
        const char * const i_sKMAVersion) {
    return (strcmp(i_sKMAVersion,
            FIPS_COMPATIBLE_KMA_VERSION) >= 0);
}

#ifdef KMSUSERPKCS12
extern "C"
KMS_AGENT_STATUS
KMSAgent_GetProfileStatus(
	char* i_pProfileName,
	KMSAGENT_PROFILE_FLAGS *flags)
{
	/*
	 * Determine how "initialized" the KMS token is by checking for
	 * the profile config file and also the entity key container (pkcs#12).
	 */
	if (ProfileExists(g_wsWorkingDirectory, i_pProfileName)) {
		*flags |= KMSAGENT_PROFILE_EXISTS_FLAG;
		if (ClientKeyP12Exists(i_pProfileName))
			*flags |= KMSAGENT_CLIENTKEY_EXISTS_FLAG;
	}
	return (KMS_AGENT_STATUS_OK);
}
#endif