KMSAgentLoadBalancer.cpp revision 12720:3db6e0082404
1/* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22/* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26/** 27 * \file KMSAgentLoadBalancer.cpp 28 */ 29 30#ifdef WIN32 31#define _WIN32_WINNT 0x0400 32#include <windows.h> 33#include <process.h> 34#endif 35 36#include <stdlib.h> 37 38#include "KMS_AgentH.h" 39#include "KMSClientProfile.h" 40#include "KMSAgentSoapUtilities.h" 41#include "KMSAgentStringUtilities.h" 42#include "KMSClientProfileImpl.h" 43#include "KMSAgent.h" 44#include "KMSAuditLogger.h" 45#include "ApplianceParameters.h" 46#include "KMSAgentCryptoUtilities.h" 47 48#ifdef METAWARE 49#include "debug.h" 50#include "sizet.h" 51typedef unsigned char uint8_t; 52typedef unsigned short uint16_t; 53typedef unsigned int uint32_t; 54typedef unsigned long long uint64_t; 55#endif 56#include "KMSAgentAESKeyWrap.h" 57 58#ifdef METAWARE 59#include "stdsoap2.h" /* makes fewer platform assumptions 60 than the standard stdsoap2.h */ 61 62int time (char *); 63#include "literals.h" 64#else 65#include "stdsoap2.h" 66#endif 67 68#include "AutoMutex.h" 69 70// real declaration of soap * 71#include "KMSAgentDataUnitCache.h" 72 73#include "ClientSoapFaultCodes.h" 74#include "KMSAgentPKICommon.h" 75#include "KMSAgentLoadBalancer.h" // needs to be after stdsoap2.h to use the 76 77CAgentLoadBalancer::CAgentLoadBalancer (KMSClientProfile * const i_pProfile) 78: m_pProfile (i_pProfile), 79m_iTransactionStartTimeInMilliseconds (0), 80m_bFIPS (false), 81m_iKWKEntryNum (0), 82m_iLastAttemptedWhenNoneResponding (0) 83{ 84 CAutoMutex oAutoMutex((K_MUTEX_HANDLE) m_pProfile->m_pLock); 85 86 // initialize the aCluster, let it contain the default appliance 87 m_iClusterNum = 1; 88 memset(&(m_aCluster[0]), 0, sizeof (KMSClusterEntry)); 89 strncpy(m_aCluster[0].m_wsApplianceNetworkAddress, 90 i_pProfile->m_wsApplianceAddress, 91 sizeof(m_aCluster[0].m_wsApplianceNetworkAddress)); 92 m_aCluster[0].m_wsApplianceNetworkAddress[sizeof(m_aCluster[0].m_wsApplianceNetworkAddress)-1] = '\0'; 93 94 // This may not be known because the initial 95 // appliance's Alias is not yet entered. 96 strcpy(m_aCluster[0].m_wsApplianceAlias, ""); 97 strcpy(m_sURL, ""); 98 memset(m_aKWKEntries, 0, KMS_MAX_CLUSTER_NUM * sizeof(struct KWKEntry *)); 99} 100 101CAgentLoadBalancer::~CAgentLoadBalancer () 102{ 103 // free up KWK entries 104 for( int i=0; i < m_iKWKEntryNum && i < KMS_MAX_CLUSTER_NUM; i++) 105 { 106 if (m_aKWKEntries[i] != NULL) 107 { 108 delete m_aKWKEntries[i]; 109 } 110 } 111 return; 112} 113 114char *CAgentLoadBalancer::GetHTTPSURL (int i_iIndex, int i_iPort) 115{ 116 if (i_iIndex < 0 || i_iIndex >= m_iClusterNum) 117 { 118 strcpy(m_sURL, ""); 119 } 120 else 121 { 122 K_snprintf(m_sURL, KMS_MAX_URL, "https://%s:%d", 123 m_aCluster[i_iIndex].m_wsApplianceNetworkAddress, 124 i_iPort); 125 } 126 127 return m_sURL; 128} 129 130char *CAgentLoadBalancer::GetHTTPURL (int i_iIndex, int i_iPort) 131{ 132 if (i_iIndex < 0 || i_iIndex >= m_iClusterNum) 133 { 134 strcpy(m_sURL, ""); 135 } 136 else 137 { 138 K_snprintf(m_sURL, KMS_MAX_URL, "http://%s:%d", 139 m_aCluster[i_iIndex].m_wsApplianceNetworkAddress, 140 i_iPort); 141 } 142 143 return m_sURL; 144} 145 146int CAgentLoadBalancer::Balance () 147{ 148 CAutoMutex oAutoMutex((K_MUTEX_HANDLE) m_pProfile->m_pLock); 149 150 int i; 151 unsigned int iSelected = 0; 152 unsigned int iSelected2 = 0; 153 154 // clear the failover attempts 155 m_pProfile->m_iFailoverAttempts = 0; 156 157 // This assumes Balance()/BalanceBy...() are called at the top of 158 // each Agent Library transaction 159 // m_iTransactionStartTimeInMilliseconds is used to determine if 160 // enough time remains 161 // (vs. KMSClientProfile::m_iTransactionTimeout) to retry a 162 // request if there was a Server Busy error. 163 164 m_iTransactionStartTimeInMilliseconds = K_GetTickCount(); 165 166 // if not enabling load balancing, return the default appliance & if 167 // its FIPS compatible when running in FIPS_MODE 168 169 if (m_pProfile->m_iClusterDiscoveryFrequency == 0) 170 { 171 if (m_bFIPS && !FIPScompatibleKMA(m_aCluster[0].m_sKMAVersion)) 172 { 173 return NO_FIPS_KMA_AVAILABLE; 174 } 175 return 0; 176 } 177 178 int iCurrentTime = K_GetTickCount() / 1000; 179 180 // if it is the first time or time to get cluster information 181 if ((!m_pProfile->m_bIsClusterDiscoveryCalled) || 182 ((iCurrentTime - m_pProfile->m_iLastClusterDiscoveryTime) > 183 m_pProfile->m_iClusterDiscoveryFrequency)) 184 { 185 if (!KMSClient_GetClusterInformation(m_pProfile, 186 m_pProfile->m_wsEntitySiteID, 187 sizeof (m_pProfile->m_wsEntitySiteID), 188 &(m_pProfile->m_iClusterNum), 189 m_pProfile->m_aCluster, 190 KMS_MAX_CLUSTER_NUM)) 191 { 192 // if failed due to some error, return default one 193 // KMSClient_GetClusterInformation logs 194 195 return 0; 196 } 197 198 m_pProfile->m_bIsClusterDiscoveryCalled = true; 199 200 // Reset the transaction start time to not include the time spent 201 // calling KMSClient_GetClusterInformation. 202 203 m_iTransactionStartTimeInMilliseconds = K_GetTickCount(); 204 205 // reset this index since cluster size may have changed 206 m_iLastAttemptedWhenNoneResponding = 0; 207 208 // TODO: Adjust timeouts to guarentee a response to the Agent 209 // Library called in m_iTransactionTimeout seconds? This means 210 // not adjusting m_iTransactionStartTimeInMilliseconds, but also 211 // reducing socket timeouts for subsequent calls. 212 } 213 214 // sort the cluster array by Load 215 216 KMSClient_SortClusterArray(m_pProfile); 217 218 // copy all Appliances to this object 219 220 for (i = 0; i < m_pProfile->m_iClusterNum; i++) 221 { 222 m_aCluster[i] = m_pProfile->m_aCluster[i]; 223 } 224 225 m_iClusterNum = m_pProfile->m_iClusterNum; 226 227 int iCandidateAppliances = 0; 228 229 // the initial set of candidates for load balancing are all enabled, 230 // responding and unlocked KMAs (assumes they are at the top of the sort 231 // order) & FIPS compatible if we're in that mode 232 233 for (i = 0; i < m_iClusterNum; i++) 234 { 235 if ((m_aCluster[i].m_iResponding == TRUE) && 236 (m_aCluster[i].m_iEnabled == TRUE ) && 237 (m_aCluster[i].m_iKMALocked == FALSE)) 238 { 239 iCandidateAppliances++; 240 } 241 } 242 243 // check if there are any enabled and responding Appliances in the 244 // same site as this Agent, and if so make those the candidates 245 // (assumes they are at the top of the sort order) 246 247 int iCandidateAppliancesInSameSite = 0; 248 249 if (strlen(m_pProfile->m_wsEntitySiteID) > 0) 250 { 251 for (i = 0; i < iCandidateAppliances; i++) 252 { 253 if (strncmp(m_aCluster[i].m_wsApplianceSiteID, 254 m_pProfile->m_wsEntitySiteID, 255 sizeof(m_aCluster[i].m_wsApplianceSiteID)) == 0) 256 { 257 iCandidateAppliancesInSameSite++; 258 } 259 } 260 } 261 262 // reduce the candidate set to just KMAs within the site 263 if (iCandidateAppliancesInSameSite > 0) 264 { 265 iCandidateAppliances = iCandidateAppliancesInSameSite; 266 } 267 268 // constrain the candidate set to just FIPS compatible KMAs 269 if (m_bFIPS) 270 { 271 int iCandidateFIPSKMAs = 0; 272 273 for (i = 0; i < iCandidateAppliances; i++) 274 { 275 if ( FIPScompatibleKMA(m_aCluster[i].m_sKMAVersion )) 276 { 277 iCandidateFIPSKMAs++; 278 } 279 } 280 281 // select only from FIPS capable KMAs 282 iCandidateAppliances = iCandidateFIPSKMAs; 283 } 284 285 // if there are no candidate Appliances, use the default Appliance unless 286 // we're in FIPS mode 287 288 if (!m_bFIPS && iCandidateAppliances <= 1) 289 { 290 return 0; 291 } 292 293 // FIPS mode 294 else if (iCandidateAppliances <= 0) 295 { 296 return NO_FIPS_KMA_AVAILABLE; 297 } 298 else if (iCandidateAppliances == 1) 299 { 300 return 0; 301 } 302 303 // randomly select two candidate Appliances and select the one 304 // with the smaller load 305 306 // choose one random number between 0 -- iCandidateAppliances - 1 307 iSelected = rand() % iCandidateAppliances; 308 iSelected2 = (iSelected + 1) % iCandidateAppliances; 309 310 // select the one with the smaller load 311 312 if (m_aCluster[iSelected2].m_lLoad < m_aCluster[iSelected].m_lLoad) 313 { 314 iSelected = iSelected2; 315 } 316 317 return iSelected; 318} 319 320int CAgentLoadBalancer::BalanceByDataUnitID ( 321 const unsigned char * const i_pDataUnitID, 322 int i_iDataUnitIDMaxLen) 323{ 324 FATAL_ASSERT(i_pDataUnitID); 325 326 CAutoMutex oAutoMutex((K_MUTEX_HANDLE) m_pProfile->m_pLock); 327 328 // clear the failover attempts 329 m_pProfile->m_iFailoverAttempts = 0; 330 331 // This assumes Balance(), or BalanceBy...(), 332 // is called at the top of each Agent Library transaction 333 // m_iTransactionStartTimeInMilliseconds is used to determine if enough time remains 334 // (vs. KMSClientProfile::m_iTransactionTimeout) to retry a request if there was 335 // a Server Busy error. 336 337 m_iTransactionStartTimeInMilliseconds = K_GetTickCount(); 338 339 // look in cache 340 341 CDataUnitCache *pDataUnitCache = (CDataUnitCache *) m_pProfile->m_pDataUnitCache; 342 343 // if not enabling load balancing, return the default appliance & if 344 // its FIPS compatible when running in FIPS_MODE 345 346 if (m_pProfile->m_iClusterDiscoveryFrequency == 0) 347 { 348 if (m_bFIPS && !FIPScompatibleKMA(m_aCluster[0].m_sKMAVersion)) 349 { 350 return NO_FIPS_KMA_AVAILABLE; 351 } 352 return 0; 353 } 354 355 // if the Data Unit ID is in the server affinity cache, use that Appliance 356 357 utf8char wsApplianceNetworkAddress[KMS_MAX_NETWORK_ADDRESS]; 358 int iIndex = CLIENT_SIDE_ERROR; 359 360 if (pDataUnitCache->GetApplianceByDataUnitID( 361 i_pDataUnitID, 362 i_iDataUnitIDMaxLen, 363 wsApplianceNetworkAddress, 364 sizeof(wsApplianceNetworkAddress))) 365 { 366 iIndex = FindIndexByNetworkAddress(wsApplianceNetworkAddress); 367 } 368 369 if (iIndex != CLIENT_SIDE_ERROR) 370 { 371 if (m_bFIPS && !FIPScompatibleKMA(m_aCluster[iIndex].m_sKMAVersion)) 372 { 373 // in spite of caching we need to attempt an alternate KMA due 374 // to the FIPS mode setting 375 return Balance(); 376 } 377 return iIndex; 378 } 379 380 // normal balancing 381 return Balance(); 382} 383 384int CAgentLoadBalancer::BalanceByDataUnitKeyID ( 385 const unsigned char * const i_pDataUnitKeyID, 386 int i_iDataUnitKeyIDMaxLen) 387{ 388 FATAL_ASSERT(i_pDataUnitKeyID); 389 390 CAutoMutex oAutoMutex((K_MUTEX_HANDLE) m_pProfile->m_pLock); 391 392 // clear the failover attempts 393 m_pProfile->m_iFailoverAttempts = 0; 394 395 // This assumes Balance()/BalanceBy...() 396 // are called at the top of each Agent Library transaction 397 // m_iTransactionStartTimeInMilliseconds is used to determine if enough time remains 398 // (vs. KMSClientProfile::m_iTransactionTimeout) to retry a request if there was 399 // a Server Busy error. 400 401 m_iTransactionStartTimeInMilliseconds = K_GetTickCount(); 402 403 // look in cache 404 405 CDataUnitCache *pDataUnitCache = (CDataUnitCache *) m_pProfile->m_pDataUnitCache; 406 407 // if not enabling load balancing, return the default appliance & if 408 // its FIPS compatible when running in FIPS_MODE 409 410 if (m_pProfile->m_iClusterDiscoveryFrequency == 0) 411 { 412 if (m_bFIPS && !FIPScompatibleKMA(m_aCluster[0].m_sKMAVersion)) 413 { 414 return NO_FIPS_KMA_AVAILABLE; 415 } 416 return 0; 417 } 418 419 // if the Data Unit Key ID is in the server affinity cache, use that Appliance 420 421 utf8char sApplianceNetworkAddress[KMS_MAX_NETWORK_ADDRESS]; 422 int iIndex = CLIENT_SIDE_ERROR; 423 424 if (pDataUnitCache->GetApplianceByDataUnitKeyID( 425 i_pDataUnitKeyID, 426 i_iDataUnitKeyIDMaxLen, 427 sApplianceNetworkAddress, 428 sizeof(sApplianceNetworkAddress))) 429 { 430 iIndex = FindIndexByNetworkAddress(sApplianceNetworkAddress); 431 } 432 433 if (iIndex != CLIENT_SIDE_ERROR) 434 { 435 if (m_bFIPS && !FIPScompatibleKMA(m_aCluster[iIndex].m_sKMAVersion)) 436 { 437 // in spite of caching we need to attempt an alternate KMA due 438 // to the FIPS mode setting 439 return Balance(); 440 } 441 return iIndex; 442 } 443 444 // normal balancing 445 return Balance(); 446} 447 448int CAgentLoadBalancer::FindIndexByNetworkAddress 449(char * i_wsApplianceNetworkAddress) 450{ 451 FATAL_ASSERT(i_wsApplianceNetworkAddress); 452 453 for (int i = 0; i < m_iClusterNum; i++) 454 { 455 456 if ((strncmp(m_aCluster[i].m_wsApplianceNetworkAddress, 457 i_wsApplianceNetworkAddress, 458 sizeof(m_aCluster[i].m_wsApplianceNetworkAddress)) == 0) && 459 m_aCluster[i].m_iEnabled == TRUE && 460 m_aCluster[i].m_iResponding == TRUE) 461 { 462 return i; 463 } 464 465 } 466 467 return CLIENT_SIDE_ERROR; 468} 469 470char* CAgentLoadBalancer::GetApplianceNetworkAddress (int i_iIndex) 471{ 472 if (i_iIndex < 0 || i_iIndex >= m_iClusterNum) 473 { 474 return (char *)""; 475 } 476 477 return m_aCluster[i_iIndex].m_wsApplianceNetworkAddress; 478} 479 480bool CAgentLoadBalancer::FailOverLimit (void) 481{ 482 if (m_pProfile->m_iFailoverLimit >= 0 && 483 m_pProfile->m_iFailoverAttempts > m_pProfile->m_iFailoverLimit) 484 return true; 485 else 486 return false; 487} 488 489int CAgentLoadBalancer::FailOver (int i_iFailedApplianceIndex, 490 struct soap *i_pstSoap) 491{ 492 FATAL_ASSERT(i_pstSoap); 493 494 CAutoMutex oAutoMutex((K_MUTEX_HANDLE) m_pProfile->m_pLock); 495 496 const char *strError = GET_SOAP_FAULTSTRING(i_pstSoap); 497 int iSoapErrno = i_pstSoap->errnum; 498 int iErrorCode = GET_FAULT_CODE(strError); 499 int i; 500 501 if ( m_bFIPS && 502 KMSClient_NoFIPSCompatibleKMAs(m_pProfile)) 503 { 504 return NO_FIPS_KMA_AVAILABLE; 505 } 506 507 m_pProfile->m_iFailoverAttempts++; 508 509 /* 510 * if KWK is not registered, or mismatched, most likely KMA lost its key due to a service 511 * restart. Call RegisterKWK to re-register the KWK. 512 * If RegisterKWK fails proceed from here with new failover info 513 */ 514 if ( iErrorCode == CLIENT_ERROR_AGENT_KWK_NOT_REGISTERED || 515 iErrorCode == CLIENT_ERROR_AGENT_KWK_ID_MISMATCH ) 516 { 517 LogError(m_pProfile, 518 AGENT_LOADBALANCER_FAILOVER, 519 NULL, 520 m_aCluster[i_iFailedApplianceIndex].m_wsApplianceNetworkAddress, 521 "KWK not registered or ID mismatch - registering"); 522 // delete the KWK entry since the KMA no longer has it 523 DeleteKWKEntry( GetKMAID(i_iFailedApplianceIndex)); 524 525 return i_iFailedApplianceIndex; 526 } 527 528 bool bServerError = false; 529 530 // if the request failed due to a Server Busy error, and if 531 // - transaction timeout has not been exceeded OR 532 // - failover attempts remain 533 // then failover 534 535 if (iErrorCode == CLIENT_ERROR_SERVER_BUSY && 536 (K_GetTickCount() < m_iTransactionStartTimeInMilliseconds + (m_pProfile->m_iTransactionTimeout * 1000) || 537 !CAgentLoadBalancer::FailOverLimit())) 538 { 539 LogError(m_pProfile, 540 AGENT_LOADBALANCER_FAILOVER, 541 NULL, 542 m_aCluster[i_iFailedApplianceIndex].m_wsApplianceNetworkAddress, 543 "Server Busy - failing over"); 544 bServerError = true; 545 } 546 else if (ServerError(strError,iSoapErrno)) 547 { 548 bServerError = true; 549 } 550 else 551 { 552 if (i_iFailedApplianceIndex == AES_KEY_WRAP_SETUP_ERROR) 553 { 554 return AES_KEY_WRAP_SETUP_ERROR; 555 } 556 else 557 { 558 return CLIENT_SIDE_ERROR; // it is a client side problem, don't fail over 559 } 560 } 561 562 // disable the failed Appliance in the profile, and 563 // re-sort the cluster array, so transactions in other threads 564 // will not send requests to the same failed Appliance 565#if defined(METAWARE) 566 log_cond_printf(ECPT_LOG_AGENT, "CAgentLoadBalancer::Failover(): FailoverAttempts=%d\n", 567 m_pProfile->m_iFailoverAttempts); 568#endif 569 for (i = 0; i < m_pProfile->m_iClusterNum; i++) 570 { 571 if (m_pProfile->m_aCluster[i].m_lApplianceID == 572 m_aCluster[i_iFailedApplianceIndex].m_lApplianceID) 573 { 574 m_pProfile->m_aCluster[i].m_iResponding = FALSE; 575 break; 576 } 577 } 578 579 KMSClient_SortClusterArray(m_pProfile); 580 581 // mark the failed Appliance as not responding (unlike the case 582 // above which is conditional on bServerError, this marking is 583 // only local to this transaction; it must be done to ensure that 584 // this transaction does not cycle in its fail-over loop.) 585 586 m_aCluster[i_iFailedApplianceIndex].m_iResponding = FALSE; 587 588 if (!CAgentLoadBalancer::FailOverLimit()) 589 { 590 // now try to fail over to all other Appliances that are 591 // apparently enabled and responding 592 593 for (i = 0; i < m_iClusterNum; i++) 594 { 595 if (m_aCluster[i].m_iEnabled == TRUE && 596 m_aCluster[i].m_iResponding == TRUE && 597 m_aCluster[i].m_iKMALocked == FALSE) 598 { 599 Log(AGENT_LOADBALANCER_FAILOVER, 600 NULL, 601 m_aCluster[i].m_wsApplianceNetworkAddress, 602 "Failing over to this addr"); 603 604 return i; 605 } 606 } 607 608 // now retry KMAs previously reported as not responding 609 610 m_iLastAttemptedWhenNoneResponding++; 611 612 if (m_iLastAttemptedWhenNoneResponding >= m_iClusterNum) 613 { 614 m_iLastAttemptedWhenNoneResponding = m_iLastAttemptedWhenNoneResponding % m_iClusterNum; 615 } 616 617 Log(AGENT_LOADBALANCER_FAILOVER, 618 NULL, 619 m_aCluster[m_iLastAttemptedWhenNoneResponding].m_wsApplianceNetworkAddress, 620 "Failing over to retry this addr"); 621 622 return m_iLastAttemptedWhenNoneResponding; 623 } 624 else 625 { 626 Log(AGENT_LOADBALANCER_FAILOVER, 627 NULL, 628 NULL, 629 "Failover limit reached"); 630 } 631 632 return m_bFIPS ? NO_FIPS_KMA_AVAILABLE : NO_KMA_AVAILABLE; 633} 634 635void CAgentLoadBalancer::UpdateResponseStatus(int i_iIndex) 636{ 637 bool bStatusChanged = false; 638 639 CAutoMutex oAutoMutex((K_MUTEX_HANDLE) m_pProfile->m_pLock); 640 641 // enable the responding Appliance in the profile, and 642 // re-sort the cluster array, so transactions in other threads 643 // will not send requests to the same failed Appliance 644 645 for (int i = 0; i < m_pProfile->m_iClusterNum; i++) 646 { 647 if (m_pProfile->m_aCluster[i].m_lApplianceID == 648 m_aCluster[i_iIndex].m_lApplianceID) 649 { 650 if (m_pProfile->m_aCluster[i].m_iResponding == FALSE) 651 { 652 bStatusChanged = true; 653 } 654 m_pProfile->m_aCluster[i].m_iResponding = TRUE; 655 break; 656 } 657 } 658 659 // only resort if the responding status actually changed 660 if (bStatusChanged) 661 { 662 KMSClient_SortClusterArray(m_pProfile); 663 } 664 665 // mark the Appliance as now responding 666 m_aCluster[i_iIndex].m_iResponding = TRUE; 667 668 return; 669} 670 671Long64 CAgentLoadBalancer::GetKMAID ( 672 int i_iIndex) 673{ 674 if (i_iIndex < 0 || i_iIndex >= m_iClusterNum) 675 { 676 return -1; 677 } 678 679 return m_aCluster[i_iIndex].m_lApplianceID; 680} 681 682CAgentLoadBalancer::KWKEntry *CAgentLoadBalancer::GetKWK ( 683 Long64 i_lKMAID) 684{ 685 if (i_lKMAID == -1) 686 { 687 return NULL; 688 } 689 690 for (int i = 0; i < m_iKWKEntryNum && i < KMS_MAX_CLUSTER_NUM; i++) 691 { 692 if (m_aKWKEntries[i] != NULL && 693 m_aKWKEntries[i]->m_lKMAID == i_lKMAID ) 694 { 695 return m_aKWKEntries[i]; 696 } 697 } 698 699 return NULL; 700} 701 702CAgentLoadBalancer::KWKEntry *CAgentLoadBalancer::CreateKWK ( 703 Long64 i_lKMAID, 704 struct soap * const i_pstSoap, 705 const char * const i_sURL, 706 bool * const o_pbClientAESKeyWrapSetupError) 707{ 708 FATAL_ASSERT(i_pstSoap); 709 FATAL_ASSERT(i_sURL); 710 711 int bSuccess = FALSE; 712 KWKEntry *oKWKEntry = new KWKEntry; 713 714 oKWKEntry->m_lKMAID = i_lKMAID; 715 *o_pbClientAESKeyWrapSetupError = false; 716 717 bSuccess = GetPseudorandomBytes(sizeof (oKWKEntry->m_acKWK), 718 oKWKEntry->m_acKWK); 719 if (!bSuccess) 720 { 721 Log(AUDIT_CLIENT_AGENT_CREATE_KWK_RNG_ERROR, 722 NULL, 723 NULL, 724 "Error from RNG"); 725 *o_pbClientAESKeyWrapSetupError = true; 726 delete(oKWKEntry); 727 return NULL; 728 } 729 730#if defined(DEBUG) 731 char sHexKWK[2*KMS_MAX_KEY_SIZE+1]; 732 ConvertBinaryToUTF8HexString( sHexKWK, oKWKEntry->m_acKWK, sizeof (oKWKEntry->m_acKWK)); 733#if defined(METAWARE) 734 log_printf("CAgentLoadBalancer::CreateKWK(): KWK hex=%s\n", 735 sHexKWK); 736#else 737// printf("CAgentLoadBalancer::CreateKWK(): KWK hex=%s\n", 738// sHexKWK); 739#endif 740#endif 741 742 CPublicKey oPublicKEK; 743 744 bSuccess = GetKWKWrappingKey(i_pstSoap, i_sURL, &oPublicKEK); 745 746 if (!bSuccess) 747 { 748 // GetKWKWrappingKey logs errors 749 750 if (!ServerError(GET_SOAP_FAULTSTRING(i_pstSoap),i_pstSoap->errnum)) 751 { 752 *o_pbClientAESKeyWrapSetupError = true; 753 } 754 delete(oKWKEntry); 755 return NULL; 756 } 757 758 unsigned char acWrappedKWK[MAX_RSA_PUB_KEY_LENGTH]; 759 int iWrappedKWKLength; 760 bSuccess = oPublicKEK.Encrypt(sizeof (oKWKEntry->m_acKWK), 761 oKWKEntry->m_acKWK, (unsigned char *) acWrappedKWK, &iWrappedKWKLength); 762 763 if (!bSuccess) 764 { 765 Log(AUDIT_CLIENT_AGENT_CREATE_KWK_PUBLIC_ENCRYPT_ERROR, 766 NULL, 767 NULL, 768 "Error encrypting KWK with KMA public key"); 769 *o_pbClientAESKeyWrapSetupError = true; 770 delete(oKWKEntry); 771 return NULL; 772 } 773//#if defined(DEBUG) && !defined(METAWARE) 774// char sHexWrappedKWK[2*MAX_RSA_PUB_KEY_LENGTH+1]; 775// ConvertBinaryToUTF8HexString( sHexWrappedKWK, acWrappedKWK, iWrappedKWKLength); 776// printf("CAgentLoadBalancer::CreateKWK(): wrapped KWK hex=%s\n", 777// sHexWrappedKWK); 778//#endif 779 780 // register the new KWK 781 bSuccess = RegisterKWK(iWrappedKWKLength, acWrappedKWK, i_pstSoap, 782 i_sURL, oKWKEntry->m_acKWKID); 783 784 if (!bSuccess) 785 { 786 // RegisterKWK logs errors 787 if (!ServerError(GET_SOAP_FAULTSTRING(i_pstSoap), i_pstSoap->error)) 788 { 789 *o_pbClientAESKeyWrapSetupError = true; 790 } 791 delete(oKWKEntry); 792 return NULL; 793 } 794 795 // save the new KWK entry in an empty slot in the array 796 for (int i=0; i < m_iKWKEntryNum && i < KMS_MAX_CLUSTER_NUM; i++) 797 { 798 if (m_aKWKEntries[i] == NULL) 799 { 800 m_aKWKEntries[i] = oKWKEntry; 801 return oKWKEntry; 802 } 803 } 804 805 // no empty slots so add it to the end 806 m_aKWKEntries[m_iKWKEntryNum++] = oKWKEntry; 807 808 return oKWKEntry; 809} 810 811void CAgentLoadBalancer::DeleteKWKEntry(Long64 i_lKMAID) 812{ 813 for (int i=0; i < m_iKWKEntryNum && i < KMS_MAX_CLUSTER_NUM; i++) 814 { 815 if (m_aKWKEntries[i] && m_aKWKEntries[i]->m_lKMAID == i_lKMAID) 816 { 817 delete(m_aKWKEntries[i]); 818 m_aKWKEntries[i] = NULL; 819 return; 820 } 821 } 822 // should not occur 823 FATAL_ASSERT(0); 824 return; 825} 826 827bool CAgentLoadBalancer::AESKeyWrapSupported (int i_iIndex) 828{ 829 if (i_iIndex < 0 || i_iIndex >= m_iClusterNum) 830 { 831 return false; 832 } 833 return (strcmp(m_aCluster[i_iIndex].m_sKMAVersion, 834 FIPS_COMPATIBLE_KMA_VERSION) >= 0); 835} 836 837int CAgentLoadBalancer::GetKWKID ( 838 int i_Index, 839 Long64 i_lKMAID, 840 struct soap * const i_pstSoap, 841 UTF8_KEYID o_pKWKID, 842 bool * const o_pbClientAESKeyWrapSetupError) 843{ 844 FATAL_ASSERT(i_Index >= 0 && i_Index <= m_iClusterNum); 845 FATAL_ASSERT(i_lKMAID != 0); 846 FATAL_ASSERT(i_pstSoap); 847 FATAL_ASSERT(o_pKWKID); 848 FATAL_ASSERT(o_pbClientAESKeyWrapSetupError); 849 850 *o_pbClientAESKeyWrapSetupError = false; 851 852 // check if the KMA for this cluster index is at a version supporting 853 // AES key wrap 854 if (!AESKeyWrapSupported(i_Index)) 855 { 856 strcpy(o_pKWKID, ""); 857 return TRUE; 858 } 859 860 // AES Key Wrap Mode 861 862 struct KWKEntry* pKWKentry = GetKWK(i_lKMAID); 863 864 if (pKWKentry == NULL) 865 { 866 const char* sURL = GetHTTPSURL( 867 i_Index, 868 m_pProfile->m_iPortForAgentService); 869 870 pKWKentry = CreateKWK(i_lKMAID, i_pstSoap, sURL, o_pbClientAESKeyWrapSetupError); 871 872 if (pKWKentry == NULL) 873 { 874 return FALSE; 875 } 876 } 877 878#if defined(DEBUG) && defined(METAWARE) 879 log_printf("CAgentLoadBalancer::GetKWKID(): KWK IDhex=%s\n", 880 pKWKentry->m_acKWKID, 881 sizeof (UTF8_KEYID)); 882#endif 883 884 strncpy(o_pKWKID, pKWKentry->m_acKWKID, sizeof(UTF8_KEYID)); 885 o_pKWKID[sizeof(UTF8_KEYID)-1] = '\0'; 886 887 return TRUE; 888}; 889 890int CAgentLoadBalancer::GetKWKWrappingKey ( 891 struct soap * const i_pstSoap, 892 const char * const i_sURL, 893 CPublicKey * const o_opPublicKEK) 894{ 895 FATAL_ASSERT(i_pstSoap); 896 FATAL_ASSERT(i_sURL); 897 FATAL_ASSERT(o_opPublicKEK); 898 899 int bSuccess = TRUE; 900 struct KMS_Agent::KMS_Agent__GetAgentKWKPublicKeyResponse oResponse; 901 char sSoapFaultMsg[g_iMAX_SOAP_FAULT_MESSAGE_LENGTH]; 902 char sKmaAddress[g_iMAX_PEER_NETWORK_ADDRESS_LENGTH]; 903 904 bSuccess = KMS_Agent::soap_call_KMS_Agent__GetAgentKWKPublicKey( 905 const_cast<struct soap *> (i_pstSoap), 906 i_sURL, 907 NULL, 908 oResponse) == SOAP_OK; 909 910 if (!bSuccess) 911 { 912 GetSoapFault(sSoapFaultMsg, const_cast<struct soap *> (i_pstSoap)); 913 GetPeerNetworkAddress(sKmaAddress, const_cast<struct soap *> (i_pstSoap)); 914 915 LogError(m_pProfile, 916 AUDIT_CLIENT_GET_KWK_WRAPPING_KEY_SOAP_ERROR, 917 NULL, 918 sKmaAddress, 919 sSoapFaultMsg); 920 921 return FALSE; 922 } 923 924 // Validate the response structure 925 if (bSuccess) 926 { 927 if (oResponse.KWKPublicKey.__ptr == NULL 928 || oResponse.KWKPublicKey.__size < 1) 929 { 930 bSuccess = FALSE; 931 932 GetPeerNetworkAddress(sKmaAddress, const_cast<struct soap *> (i_pstSoap)); 933 934 LogError(m_pProfile, 935 AUDIT_CLIENT_GET_KWK_WRAPPING_KEY_INVALID_KEY_RESPONSE, 936 NULL, 937 sKmaAddress, 938 NULL); 939 } 940 else 941 { 942 bSuccess = o_opPublicKEK->Load(oResponse.KWKPublicKey.__ptr, 943 oResponse.KWKPublicKey.__size, PKI_FORMAT); 944 if (!bSuccess) 945 { 946 GetPeerNetworkAddress(sKmaAddress, const_cast<struct soap *> (i_pstSoap)); 947 948 LogError(m_pProfile, 949 AUDIT_CLIENT_GET_KWK_WRAPPING_KEY_INVALID_RSA_PUB_KEY, 950 NULL, 951 sKmaAddress, 952 NULL); 953 } 954 } 955 } 956 957 // Note: no SOAP cleanup as caller's environment will get destroyed 958 959 return bSuccess; 960}; 961 962int CAgentLoadBalancer::RegisterKWK ( 963 int i_iWrappedKWKSize, 964 const unsigned char * const i_acWrappedKWK, 965 struct soap * const i_pstSoap, 966 const char * const i_sURL, 967 UTF8_KEYID o_acUTF8KeyID) 968{ 969 FATAL_ASSERT(i_iWrappedKWKSize > 0); 970 FATAL_ASSERT(i_acWrappedKWK); 971 FATAL_ASSERT(i_pstSoap); 972 FATAL_ASSERT(i_sURL); 973 FATAL_ASSERT(o_acUTF8KeyID); 974 975 int bSuccess; 976 977 struct KMS_Agent::xsd__hexBinary oKWK; 978 979#if defined(DEBUG) && defined(METAWARE) 980 char sHexWrappedKWK[512]; 981 ConvertBinaryToUTF8HexString( sHexWrappedKWK, i_acWrappedKWK, i_iWrappedKWKSize); 982 log_printf("CAgentLoadBalancer::RegisterKWK(): Wrapped KWK hex=%s, len=%d\n", 983 sHexWrappedKWK, i_iWrappedKWKSize); 984#endif 985 986 if (!PutBinaryIntoSoapBinary(i_pstSoap, 987 i_acWrappedKWK, 988 i_iWrappedKWKSize, 989 oKWK.__ptr, 990 oKWK.__size)) 991 { 992 return FALSE; 993 } 994 995 char sSoapFaultMsg[g_iMAX_SOAP_FAULT_MESSAGE_LENGTH]; 996 char sKmaAddress[g_iMAX_PEER_NETWORK_ADDRESS_LENGTH]; 997 struct KMS_Agent::KMS_Agent__RegisterAgentKWKResponse oResponse; 998 999 bSuccess = KMS_Agent::soap_call_KMS_Agent__RegisterAgentKWK( 1000 const_cast<struct soap *> (i_pstSoap), 1001 i_sURL, NULL, oKWK, oResponse) == SOAP_OK; 1002 1003 if (bSuccess) 1004 { 1005 // verify response 1006 if (oResponse.AgentKWKID && 1007 strlen(oResponse.AgentKWKID) == 2 * KMS_KWK_KEY_ID_SIZE) 1008 { 1009#if defined(DEBUG) && defined(METAWARE) 1010 log_printf("CAgentLoadBalancer::RegisterKWK(): KWK ID hex=%s\n", 1011 oResponse.AgentKWKID, 1012 sizeof (UTF8_KEYID)); 1013#endif 1014 strncpy(o_acUTF8KeyID, oResponse.AgentKWKID, sizeof(UTF8_KEYID)); 1015 o_acUTF8KeyID[sizeof(UTF8_KEYID)-1] = '\0'; 1016 } 1017 else 1018 { 1019 GetPeerNetworkAddress(sKmaAddress, const_cast<struct soap *> (i_pstSoap)); 1020 GetSoapFault(sSoapFaultMsg, const_cast<struct soap *> (i_pstSoap)); 1021 1022 Log(AUDIT_CLIENT_AGENT_REGISTER_KWK_INVALID_KEYID_RESPONSE, 1023 NULL, 1024 sKmaAddress, 1025 sSoapFaultMsg); 1026 bSuccess = FALSE; 1027 } 1028 } 1029 else 1030 { 1031 GetPeerNetworkAddress(sKmaAddress, const_cast<struct soap *> (i_pstSoap)); 1032 GetSoapFault(sSoapFaultMsg, const_cast<struct soap *> (i_pstSoap)); 1033 1034 Log(AUDIT_CLIENT_AGENT_REGISTER_KWK_ERROR, 1035 NULL, 1036 sKmaAddress, 1037 sSoapFaultMsg); 1038 bSuccess = FALSE; 1039 } 1040 1041 // Note: Clean up SOAP must happen in caller, not here 1042 1043 return bSuccess; 1044 1045}; 1046 1047bool CAgentLoadBalancer::AESKeyUnwrap ( 1048 int * const io_pIndex, 1049 const WRAPPED_KEY i_pAESWrappedKey, 1050 KEY o_pPlainTextKey) 1051{ 1052 FATAL_ASSERT(io_pIndex); 1053 FATAL_ASSERT(*io_pIndex >= 0); 1054 FATAL_ASSERT(o_pPlainTextKey); 1055 FATAL_ASSERT(i_pAESWrappedKey); 1056 1057 struct KWKEntry * pKWKEntry = GetKWK(GetKMAID(*io_pIndex)); 1058 1059 if (pKWKEntry == NULL) 1060 { 1061 Log(AGENT_LOADBALANCER_AESKEYUNWRAP_GETKWK_RETURNED_NULL, 1062 NULL, 1063 m_aCluster[*io_pIndex].m_wsApplianceNetworkAddress, 1064 NULL); 1065 *io_pIndex = CAgentLoadBalancer::AES_KEY_UNWRAP_ERROR; 1066 1067 return false; 1068 } 1069 1070#if defined(DEBUG) && defined(METAWARE) 1071 char sHexKWK[2*KMS_MAX_KEY_SIZE+1]; 1072 ConvertBinaryToUTF8HexString( sHexKWK, pKWKEntry->m_acKWK, sizeof (pKWKEntry->m_acKWK)); 1073 log_printf("CAgentLoadBalancer::AESKeyUnwrap(): KWK hex=%s\n", 1074 sHexKWK); 1075#endif 1076 1077 if (aes_key_unwrap(pKWKEntry->m_acKWK, 1078 sizeof (pKWKEntry->m_acKWK), 1079 i_pAESWrappedKey, 1080 o_pPlainTextKey, 4) != 0) 1081 { 1082 Log(AGENT_LOADBALANCER_AESKEYUNWRAP_KEY_UNWRAP_FAILED, 1083 NULL, 1084 m_aCluster[*io_pIndex].m_wsApplianceNetworkAddress, 1085 NULL); 1086 *io_pIndex = CAgentLoadBalancer::AES_KEY_UNWRAP_ERROR; 1087 return false; 1088 } 1089 1090 return true; 1091} 1092 1093/*--------------------------------------------------------------------------- 1094 * Function: KMSClient_SortClusterArray 1095 * 1096 *--------------------------------------------------------------------------*/ 1097void CAgentLoadBalancer::KMSClient_SortClusterArray (KMSClientProfile * const i_pProfile) 1098{ 1099 FATAL_ASSERT(i_pProfile); 1100 1101 CAutoMutex oAutoMutex((K_MUTEX_HANDLE) i_pProfile->m_pLock); 1102 1103 int i; 1104 1105 1106 // adjust loads according to availability, site and FIPS compatibility 1107 for (i = 0; i < i_pProfile->m_iClusterNum; i++) 1108 { 1109 if ((i_pProfile->m_aCluster[i].m_iEnabled == FALSE 1110 || i_pProfile->m_aCluster[i].m_iResponding == FALSE 1111 || i_pProfile->m_aCluster[i].m_iKMALocked)) 1112 { 1113 ((unsigned char*) &(i_pProfile->m_aCluster[i].m_lLoad))[sizeof (int)+1] = 1; 1114 } 1115 else 1116 { 1117 ((unsigned char*) &(i_pProfile->m_aCluster[i].m_lLoad))[sizeof (int)+1] = 0; 1118 } 1119 1120 if (strcmp(i_pProfile->m_aCluster[i].m_wsApplianceSiteID, 1121 i_pProfile->m_wsEntitySiteID) != 0) 1122 { 1123 ((unsigned char*) &(i_pProfile->m_aCluster[i].m_lLoad))[sizeof (int)] = 1; 1124 } 1125 else 1126 { 1127 ((unsigned char*) &(i_pProfile->m_aCluster[i].m_lLoad))[sizeof (int)] = 0; 1128 } 1129 1130 if ( m_bFIPS && 1131 !FIPScompatibleKMA(i_pProfile->m_aCluster[i].m_sKMAVersion)) 1132 { 1133 ((unsigned char*) &(i_pProfile->m_aCluster[i].m_lLoad))[sizeof (int)+2] = 1; 1134 } 1135 else 1136 { 1137 ((unsigned char*) &(i_pProfile->m_aCluster[i].m_lLoad))[sizeof (int)+2] = 0; 1138 } 1139 } 1140 1141 // sort ascending by load 1142 1143 // gnome sort: the simplest sort algoritm 1144 // http://www.cs.vu.nl/~dick/gnomesort.html 1145 1146 //void gnomesort(int n, int ar[]) { 1147 // int i = 0; 1148 // 1149 // while (i < n) { 1150 // if (i == 0 || ar[i-1] <= ar[i]) i++; 1151 // else {int tmp = ar[i]; ar[i] = ar[i-1]; ar[--i] = tmp;} 1152 // } 1153 //} 1154 1155 i = 0; 1156 while (i < i_pProfile->m_iClusterNum) 1157 { 1158 if (i == 0 || i_pProfile->m_aCluster[i - 1].m_lLoad <= i_pProfile->m_aCluster[i].m_lLoad) 1159 { 1160 i++; 1161 } 1162 else 1163 { 1164 KMSClusterEntry tmp = i_pProfile->m_aCluster[i]; 1165 i_pProfile->m_aCluster[i] = i_pProfile->m_aCluster[i - 1]; 1166 i_pProfile->m_aCluster[--i] = tmp; 1167 } 1168 } 1169} 1170