//----------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 2000.
//
//  File:       isakmp-r.c
//
//  Contents:   ISAKMP management for registry.
//
//
//  History:    KrishnaG.
//              AbhisheV.
//
//----------------------------------------------------------------------------

#include "precomp.h"

extern LPWSTR ISAKMPDNAttributes[];


DWORD
RegEnumISAKMPData(
    HKEY hRegistryKey,
    LPWSTR pszIpsecRootContainer,
    PIPSEC_ISAKMP_DATA ** pppIpsecISAKMPData,
    PDWORD pdwNumISAKMPObjects
    )
{
    DWORD dwError = 0;
    PIPSEC_ISAKMP_OBJECT * ppIpsecISAKMPObjects = NULL;
    PIPSEC_ISAKMP_DATA pIpsecISAKMPData = NULL;
    PIPSEC_ISAKMP_DATA * ppIpsecISAKMPData = NULL;
    DWORD dwNumISAKMPObjects = 0;
    DWORD i = 0;
    DWORD j = 0;


    dwError = RegEnumISAKMPObjects(
                    hRegistryKey,
                    pszIpsecRootContainer,
                    &ppIpsecISAKMPObjects,
                    &dwNumISAKMPObjects
                    );
    BAIL_ON_WIN32_ERROR(dwError);

    if (dwNumISAKMPObjects) {
        ppIpsecISAKMPData = (PIPSEC_ISAKMP_DATA *) AllocPolMem(
                        dwNumISAKMPObjects*sizeof(PIPSEC_ISAKMP_DATA));
        if (!ppIpsecISAKMPData) {
            dwError = ERROR_OUTOFMEMORY;
            BAIL_ON_WIN32_ERROR(dwError);
        }
    }

    for (i = 0; i < dwNumISAKMPObjects; i++) {

        dwError = RegUnmarshallISAKMPData(
                        *(ppIpsecISAKMPObjects + i),
                        &pIpsecISAKMPData
                        );
        if (!dwError) {
            *(ppIpsecISAKMPData + j) = pIpsecISAKMPData;
            j++;
        }
    }

    if (j == 0) {
        if (ppIpsecISAKMPData) {
            FreePolMem(ppIpsecISAKMPData);
            ppIpsecISAKMPData = NULL;
        }
    }

    *pppIpsecISAKMPData = ppIpsecISAKMPData;
    *pdwNumISAKMPObjects = j;

    dwError = ERROR_SUCCESS;

cleanup:

    if (ppIpsecISAKMPObjects) {
        FreeIpsecISAKMPObjects(
                ppIpsecISAKMPObjects,
                dwNumISAKMPObjects
                );
    }

    return(dwError);


error:

    if (ppIpsecISAKMPData) {
        FreeMulIpsecISAKMPData(
                ppIpsecISAKMPData,
                i
                );
    }

    *pppIpsecISAKMPData = NULL;
    *pdwNumISAKMPObjects = 0;

    goto cleanup;
}

DWORD
RegEnumISAKMPObjects(
    HKEY hRegistryKey,
    LPWSTR pszIpsecRootContainer,
    PIPSEC_ISAKMP_OBJECT ** pppIpsecISAKMPObjects,
    PDWORD pdwNumISAKMPObjects
    )
{

    DWORD dwError = 0;
    DWORD i = 0;
    DWORD dwCount = 0;
    PIPSEC_ISAKMP_OBJECT pIpsecISAKMPObject =  NULL;
    PIPSEC_ISAKMP_OBJECT * ppIpsecISAKMPObjects = NULL;

    DWORD dwNumISAKMPObjectsReturned = 0;
    DWORD dwIndex = 0;
    WCHAR szISAKMPName[MAX_PATH];
    DWORD dwSize = 0;
    DWORD dwReserved = 0;

    *pppIpsecISAKMPObjects = NULL;
    *pdwNumISAKMPObjects = 0;

    while (1) {

        dwSize = MAX_PATH;
        dwReserved = 0;
        szISAKMPName[0] = L'\0';

        dwError = RegEnumKeyExW(
                        hRegistryKey,
                        dwIndex,
                        szISAKMPName,
                        &dwSize,
                        NULL,
                        NULL,
                        0,
                        0
                        );
        if (dwError == ERROR_NO_MORE_ITEMS) {
            break;
        }
        BAIL_ON_WIN32_ERROR(dwError);

        if (!wcsstr(szISAKMPName, L"ipsecISAKMPPolicy")) {
            dwIndex++;
            continue;
        }

        pIpsecISAKMPObject = NULL;

        dwError =UnMarshallRegistryISAKMPObject(
                    hRegistryKey,
                    pszIpsecRootContainer,
                    szISAKMPName,
                    REG_RELATIVE_NAME,
                    &pIpsecISAKMPObject
                    );
        if (dwError == ERROR_SUCCESS) {

            dwError = ReallocatePolMem(
                          (LPVOID *) &ppIpsecISAKMPObjects,
                          sizeof(PIPSEC_ISAKMP_OBJECT)*(dwNumISAKMPObjectsReturned),
                          sizeof(PIPSEC_ISAKMP_OBJECT)*(dwNumISAKMPObjectsReturned + 1)
                          );
            BAIL_ON_WIN32_ERROR(dwError);

            *(ppIpsecISAKMPObjects + dwNumISAKMPObjectsReturned) = pIpsecISAKMPObject;

            dwNumISAKMPObjectsReturned++;

        }

        dwIndex++;

    }

    *pppIpsecISAKMPObjects = ppIpsecISAKMPObjects;
    *pdwNumISAKMPObjects = dwNumISAKMPObjectsReturned;

    dwError = ERROR_SUCCESS;

    return(dwError);

error:

    if (ppIpsecISAKMPObjects) {
        FreeIpsecISAKMPObjects(
            ppIpsecISAKMPObjects,
            dwNumISAKMPObjectsReturned
            );
    }

    if (pIpsecISAKMPObject) {
        FreeIpsecISAKMPObject(
            pIpsecISAKMPObject
            );
    }

    *pppIpsecISAKMPObjects = NULL;
    *pdwNumISAKMPObjects = 0;


    return(dwError);
}


DWORD
RegSetISAKMPData(
    HKEY hRegistryKey,
    LPWSTR pszIpsecRootContainer,
    LPWSTR pszLocationName,
    PIPSEC_ISAKMP_DATA pIpsecISAKMPData
    )
{
    DWORD dwError = 0;
    PIPSEC_ISAKMP_OBJECT pIpsecISAKMPObject = NULL;

    dwError = RegMarshallISAKMPObject(
                    pIpsecISAKMPData,
                    &pIpsecISAKMPObject
                    );
    BAIL_ON_WIN32_ERROR(dwError);

    dwError = RegSetISAKMPObject(
                    hRegistryKey,
                    pszIpsecRootContainer,
                    pIpsecISAKMPObject
                    );
    BAIL_ON_WIN32_ERROR(dwError);

    dwError = RegBackPropIncChangesForISAKMPToPolicy(
                  hRegistryKey,
                  pszIpsecRootContainer,
                  pszLocationName,
                  pIpsecISAKMPObject
                  );
    BAIL_ON_WIN32_ERROR(dwError);

error:

    if (pIpsecISAKMPObject) {
        FreeIpsecISAKMPObject(pIpsecISAKMPObject);
    }

    return(dwError);
}


DWORD
RegSetISAKMPObject(
    HKEY hRegistryKey,
    LPWSTR pszIpsecRootContainer,
    PIPSEC_ISAKMP_OBJECT pIpsecISAKMPObject
    )
{
    DWORD dwError = 0;

    dwError = PersistISAKMPObject(
                    hRegistryKey,
                    pIpsecISAKMPObject
                    );
    BAIL_ON_WIN32_ERROR(dwError);

error:

    return(dwError);
}


DWORD
RegCreateISAKMPData(
    HKEY hRegistryKey,
    LPWSTR pszIpsecRootContainer,
    PIPSEC_ISAKMP_DATA pIpsecISAKMPData
    )
{
    DWORD dwError = 0;
    PIPSEC_ISAKMP_OBJECT pIpsecISAKMPObject = NULL;

    dwError = RegMarshallISAKMPObject(
                        pIpsecISAKMPData,
                        &pIpsecISAKMPObject
                        );
    BAIL_ON_WIN32_ERROR(dwError);

    dwError = RegCreateISAKMPObject(
                    hRegistryKey,
                    pszIpsecRootContainer,
                    pIpsecISAKMPObject
                    );
    BAIL_ON_WIN32_ERROR(dwError);

error:

    if (pIpsecISAKMPObject) {
        FreeIpsecISAKMPObject(
                pIpsecISAKMPObject
                );
    }

    return(dwError);
}

DWORD
RegCreateISAKMPObject(
    HKEY hRegistryKey,
    LPWSTR pszIpsecRootContainer,
    PIPSEC_ISAKMP_OBJECT pIpsecISAKMPObject
    )
{

    DWORD dwError = 0;

    dwError = PersistISAKMPObject(
                    hRegistryKey,
                    pIpsecISAKMPObject
                    );
    BAIL_ON_WIN32_ERROR(dwError);

error:

    return(dwError);
}


DWORD
RegDeleteISAKMPData(
    HKEY hRegistryKey,
    LPWSTR pszIpsecRootContainer,
    GUID ISAKMPIdentifier
    )
{
    DWORD dwError = ERROR_SUCCESS;
    WCHAR szGuid[MAX_PATH];
    WCHAR szDistinguishedName[MAX_PATH];
    LPWSTR pszStringUuid = NULL;

    szGuid[0] = L'\0';
    szDistinguishedName[0] = L'\0';

    dwError = UuidToString(
                  &ISAKMPIdentifier,
                  &pszStringUuid
                  );
    BAIL_ON_WIN32_ERROR(dwError);
    wcscpy(szGuid, L"{");
    wcscat(szGuid, pszStringUuid);
    wcscat(szGuid, L"}");

    wcscpy(szDistinguishedName,L"ipsecISAKMPPolicy");
    wcscat(szDistinguishedName, szGuid);

    dwError = RegDeleteKeyW(
                  hRegistryKey,
                  szDistinguishedName
                  );
    BAIL_ON_WIN32_ERROR(dwError);

error:

    if (pszStringUuid) {
        RpcStringFree(&pszStringUuid);
    }

    return(dwError);
}


DWORD
RegUnmarshallISAKMPData(
    PIPSEC_ISAKMP_OBJECT pIpsecISAKMPObject,
    PIPSEC_ISAKMP_DATA * ppIpsecISAKMPData
    )
{
    DWORD dwError = 0;

    dwError = UnmarshallISAKMPObject(
                    pIpsecISAKMPObject,
                    ppIpsecISAKMPData
                    );


    return(dwError);
}

DWORD
RegMarshallISAKMPObject(
    PIPSEC_ISAKMP_DATA pIpsecISAKMPData,
    PIPSEC_ISAKMP_OBJECT * ppIpsecISAKMPObject
    )
{
    DWORD dwError = 0;
    PIPSEC_ISAKMP_OBJECT pIpsecISAKMPObject = NULL;
    WCHAR szGuid[MAX_PATH];
    WCHAR szDistinguishedName[MAX_PATH];
    LPBYTE pBuffer = NULL;
    DWORD dwBufferLen = 0;
    LPWSTR pszStringUuid = NULL;
    time_t PresentTime;

    szGuid[0] = L'\0';
    szDistinguishedName[0] = L'\0';
    pIpsecISAKMPObject = (PIPSEC_ISAKMP_OBJECT)AllocPolMem(
                                                    sizeof(IPSEC_ISAKMP_OBJECT)
                                                    );
    if (!pIpsecISAKMPObject) {
        dwError = ERROR_OUTOFMEMORY;
        BAIL_ON_WIN32_ERROR(dwError);
    }

    dwError = UuidToString(
                    &pIpsecISAKMPData->ISAKMPIdentifier,
                    &pszStringUuid
                    );
    BAIL_ON_WIN32_ERROR(dwError);
    wcscpy(szGuid, L"{");
    wcscat(szGuid, pszStringUuid);
    wcscat(szGuid, L"}");

    //
    // Fill in the distinguishedName
    //

    wcscpy(szDistinguishedName,L"ipsecISAKMPPolicy");
    wcscat(szDistinguishedName, szGuid);
    pIpsecISAKMPObject->pszDistinguishedName = AllocPolStr(
                                                    szDistinguishedName
                                                    );
    if (!pIpsecISAKMPObject->pszDistinguishedName) {
        dwError = ERROR_OUTOFMEMORY;
        BAIL_ON_WIN32_ERROR(dwError);
    }


    //
    // Fill in the ipsecName.
    // ISAKMPData doesn't have a name.
    //

    pIpsecISAKMPObject->pszIpsecName = NULL;

    /*
    if (pIpsecISAKMPData->pszIpsecName &&
        *pIpsecISAKMPData->pszIpsecName) {
        pIpsecISAKMPObject->pszIpsecName = AllocPolStr(
                                           pIpsecISAKMPData->pszIpsecName
                                           );
        if (!pIpsecISAKMPObject->pszIpsecName) {
            dwError = ERROR_OUTOFMEMORY;
            BAIL_ON_WIN32_ERROR(dwError);
        }
    }
    */

    //
    // Fill in the ipsecID
    //

    pIpsecISAKMPObject->pszIpsecID = AllocPolStr(
                                         szGuid
                                         );
    if (!pIpsecISAKMPObject->pszIpsecID) {
        dwError = ERROR_OUTOFMEMORY;
        BAIL_ON_WIN32_ERROR(dwError);
    }

    //
    // Fill in the ipsecDataType
    //

    pIpsecISAKMPObject->dwIpsecDataType = 0x100;


    //
    // Marshall the pIpsecDataBuffer and the Length
    //

    dwError = MarshallISAKMPBuffer(
                    pIpsecISAKMPData,
                    &pBuffer,
                    &dwBufferLen
                    );
    BAIL_ON_WIN32_ERROR(dwError);

    pIpsecISAKMPObject->pIpsecData  = pBuffer;

    pIpsecISAKMPObject->dwIpsecDataLen = dwBufferLen;

    time(&PresentTime);

    pIpsecISAKMPObject->dwWhenChanged = (DWORD) PresentTime;

    *ppIpsecISAKMPObject = pIpsecISAKMPObject;

cleanup:

    if (pszStringUuid) {
        RpcStringFree(
            &pszStringUuid
            );
    }

    return(dwError);

error:

    if (pIpsecISAKMPObject) {
        FreeIpsecISAKMPObject(
            pIpsecISAKMPObject
            );
    }

    *ppIpsecISAKMPObject = NULL;
    goto cleanup;
}


DWORD
MarshallISAKMPBuffer(
    PIPSEC_ISAKMP_DATA pIpsecISAKMPData,
    LPBYTE * ppBuffer,
    DWORD * pdwBufferLen
    )
{
    LPBYTE pCurrentPos = NULL;
    LPBYTE pBuffer = NULL;
    DWORD dwSize = 0;
    DWORD dwError = 0;
    DWORD i = 0;
    DWORD dwNumISAKMPSecurityMethods = 0;
    PCRYPTO_BUNDLE pSecurityMethods = NULL;
    PCRYPTO_BUNDLE pSecurityMethod = NULL;
    ISAKMP_POLICY * pISAKMPPolicy = NULL;
    DWORD dwEffectiveSize = 0;

    // {80DC20B8-2EC8-11d1-A89E-00A0248D3021}
    static const GUID GUID_IPSEC_ISAKMP_POLICY_BLOB =
    { 0x80dc20b8, 0x2ec8, 0x11d1, { 0xa8, 0x9e, 0x0, 0xa0, 0x24, 0x8d, 0x30, 0x21 } };


    dwNumISAKMPSecurityMethods =pIpsecISAKMPData->dwNumISAKMPSecurityMethods;
    pISAKMPPolicy = &(pIpsecISAKMPData->ISAKMPPolicy);
    pSecurityMethods = pIpsecISAKMPData->pSecurityMethods;

    dwSize += sizeof(GUID);

    dwSize += sizeof(DWORD);

    dwSize += sizeof(ISAKMP_POLICY);

    dwSize += sizeof(DWORD);

    dwSize += sizeof(CRYPTO_BUNDLE)*dwNumISAKMPSecurityMethods;
    dwSize++;

    pBuffer = (LPBYTE)AllocPolMem(dwSize);
    if (!pBuffer) {
        dwError = ERROR_OUTOFMEMORY;
        BAIL_ON_WIN32_ERROR(dwError);
    }

    pCurrentPos = pBuffer;

    memcpy(pCurrentPos, &GUID_IPSEC_ISAKMP_POLICY_BLOB, sizeof(GUID));
    pCurrentPos += sizeof(GUID);

    dwEffectiveSize = dwSize - sizeof(GUID) - sizeof(DWORD) - 1;

    memcpy(pCurrentPos, &dwEffectiveSize, sizeof(DWORD));
    pCurrentPos += sizeof(DWORD);

    memcpy(pCurrentPos, pISAKMPPolicy, sizeof(ISAKMP_POLICY));
    pCurrentPos += sizeof(ISAKMP_POLICY);

    memcpy(pCurrentPos, &dwNumISAKMPSecurityMethods, sizeof(DWORD));
    pCurrentPos += sizeof(DWORD);

    for (i = 0; i < dwNumISAKMPSecurityMethods; i++) {

        pSecurityMethod = pSecurityMethods + i;
        memcpy(pCurrentPos, pSecurityMethod, sizeof(CRYPTO_BUNDLE));
        pCurrentPos += sizeof(CRYPTO_BUNDLE);

    }

    *ppBuffer = pBuffer;
    *pdwBufferLen = dwSize;

    return(dwError);

error:

    if (pBuffer) {
        FreePolMem(pBuffer);
    }

    *ppBuffer = NULL;
    *pdwBufferLen = 0;
    return(dwError);
}


DWORD
RegGetISAKMPData(
    HKEY hRegistryKey,
    LPWSTR pszIpsecRootContainer,
    GUID ISAKMPGUID,
    PIPSEC_ISAKMP_DATA * ppIpsecISAKMPData
    )
{
    DWORD dwError = 0;
    PIPSEC_ISAKMP_OBJECT pIpsecISAKMPObject = NULL;
    PIPSEC_ISAKMP_DATA pIpsecISAKMPData = NULL;
    WCHAR szIpsecISAKMPName[MAX_PATH];
    LPWSTR pszISAKMPName = NULL;

    szIpsecISAKMPName[0] = L'\0';
    wcscpy(szIpsecISAKMPName, L"ipsecISAKMPPolicy");

    dwError = UuidToString(&ISAKMPGUID, &pszISAKMPName);
    BAIL_ON_WIN32_ERROR(dwError);

    wcscat(szIpsecISAKMPName, L"{");
    wcscat(szIpsecISAKMPName, pszISAKMPName);
    wcscat(szIpsecISAKMPName, L"}");


    dwError =UnMarshallRegistryISAKMPObject(
                hRegistryKey,
                pszIpsecRootContainer,
                szIpsecISAKMPName,
                REG_RELATIVE_NAME,
                &pIpsecISAKMPObject
                );
    BAIL_ON_WIN32_ERROR(dwError);

    dwError = RegUnmarshallISAKMPData(
                    pIpsecISAKMPObject,
                    &pIpsecISAKMPData
                    );
    BAIL_ON_WIN32_ERROR(dwError);

error:

    if (pIpsecISAKMPObject) {
        FreeIpsecISAKMPObject(
                pIpsecISAKMPObject
                );
    }

    if (pszISAKMPName) {
        RpcStringFree(&pszISAKMPName);
    }

    *ppIpsecISAKMPData = pIpsecISAKMPData;

    return(dwError);
}