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