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 |