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