diff options
Diffstat (limited to 'usr/src/lib/libkmsagent/common/KMSClientProfile.cpp')
| -rw-r--r-- | usr/src/lib/libkmsagent/common/KMSClientProfile.cpp | 2878 | 
1 files changed, 2878 insertions, 0 deletions
| diff --git a/usr/src/lib/libkmsagent/common/KMSClientProfile.cpp b/usr/src/lib/libkmsagent/common/KMSClientProfile.cpp new file mode 100644 index 0000000000..cefd75a1b9 --- /dev/null +++ b/usr/src/lib/libkmsagent/common/KMSClientProfile.cpp @@ -0,0 +1,2878 @@ +/* + * 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 | 
