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"
51 typedef unsigned char uint8_t;
52 typedef unsigned short uint16_t;
53 typedef unsigned int uint32_t;
54 typedef 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
62 int 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
CAgentLoadBalancer(KMSClientProfile * const i_pProfile)77 CAgentLoadBalancer::CAgentLoadBalancer (KMSClientProfile * const i_pProfile)
78 : m_pProfile (i_pProfile),
79 m_iTransactionStartTimeInMilliseconds (0),
80 m_bFIPS (false),
81 m_iKWKEntryNum (0),
82 m_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
~CAgentLoadBalancer()101 CAgentLoadBalancer::~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
GetHTTPSURL(int i_iIndex,int i_iPort)114 char *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
GetHTTPURL(int i_iIndex,int i_iPort)130 char *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
Balance()146 int 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
BalanceByDataUnitID(const unsigned char * const i_pDataUnitID,int i_iDataUnitIDMaxLen)320 int 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
BalanceByDataUnitKeyID(const unsigned char * const i_pDataUnitKeyID,int i_iDataUnitKeyIDMaxLen)384 int 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
FindIndexByNetworkAddress(char * i_wsApplianceNetworkAddress)448 int 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
GetApplianceNetworkAddress(int i_iIndex)470 char* 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
FailOverLimit(void)480 bool 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
FailOver(int i_iFailedApplianceIndex,struct soap * i_pstSoap)489 int 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
UpdateResponseStatus(int i_iIndex)635 void 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
GetKMAID(int i_iIndex)671 Long64 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
GetKWK(Long64 i_lKMAID)682 CAgentLoadBalancer::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
CreateKWK(Long64 i_lKMAID,struct soap * const i_pstSoap,const char * const i_sURL,bool * const o_pbClientAESKeyWrapSetupError)702 CAgentLoadBalancer::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
DeleteKWKEntry(Long64 i_lKMAID)811 void 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
AESKeyWrapSupported(int i_iIndex)827 bool 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
GetKWKID(int i_Index,Long64 i_lKMAID,struct soap * const i_pstSoap,UTF8_KEYID o_pKWKID,bool * const o_pbClientAESKeyWrapSetupError)837 int 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
GetKWKWrappingKey(struct soap * const i_pstSoap,const char * const i_sURL,CPublicKey * const o_opPublicKEK)890 int 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
RegisterKWK(int i_iWrappedKWKSize,const unsigned char * const i_acWrappedKWK,struct soap * const i_pstSoap,const char * const i_sURL,UTF8_KEYID o_acUTF8KeyID)962 int 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
AESKeyUnwrap(int * const io_pIndex,const WRAPPED_KEY i_pAESWrappedKey,KEY o_pPlainTextKey)1047 bool 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 *--------------------------------------------------------------------------*/
KMSClient_SortClusterArray(KMSClientProfile * const i_pProfile)1097 void 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