#include <precomp.h>
#include "tracing.h"
#include "utils.h"
#include "intflist.h"
#include "hash.h"
#include "storage.h"
#include "rpcsrv.h"
#include "wzcsvc.h"

//-----------------------------------------------------------
// Loads per interface configuration parameters to the persistent
// storage.
// Parameters:
//   hkRoot
//     [in] Opened registry key to the "...WZCSVC\Parameters" location
//   pIntf
//     [in] Interface context to load from the registry
// Returned value:
//     Win32 error code 
DWORD
StoLoadIntfConfig(
    HKEY          hkRoot,
    PINTF_CONTEXT pIntfContext)
{
    DWORD           dwErr = ERROR_SUCCESS;
    HKEY            hkIntf = NULL;
    LPWSTR          pKeyName = NULL;
    UINT            nLength;
    UINT            nType;
    DWORD           dwData;
    DWORD           dwVersion;
    UINT            nEntries;
    RAW_DATA        rdBuffer = {0, NULL};
    DWORD           dwGuidLen = 0;

    DbgPrint((TRC_TRACK|TRC_STORAGE,"[StoLoadIntfConfig(%S)", 
             (pIntfContext->wszGuid == NULL)? L"Global" : pIntfContext->wszGuid));

    if (pIntfContext->wszGuid != NULL)
        dwGuidLen = wcslen(pIntfContext->wszGuid) + 1;

    if (hkRoot == NULL)
    {
        // if no root has been provided allocate space for the absolute path to WZC params,
        // the relative path to the interfaces location, the guid plus 2 '\' and one null terminator
        pKeyName = MemCAlloc((
                       wcslen(WZCREGK_ABS_PARAMS) + 
                       dwGuidLen + 
                       wcslen(WZCREGK_REL_INTF) + 
                       2)*sizeof(WCHAR));
        if (pKeyName == 0)
        {
            dwErr = GetLastError();
            goto exit;
        }
        if (dwGuidLen != 0)
            wsprintf(pKeyName,L"%s\\%s\\%s", WZCREGK_ABS_PARAMS, WZCREGK_REL_INTF, pIntfContext->wszGuid);
        else
            wsprintf(pKeyName,L"%s\\%s", WZCREGK_ABS_PARAMS, WZCREGK_REL_INTF);

        hkRoot = HKEY_LOCAL_MACHINE;
    }
    else
    {
        // if a root has been provided, allocate space just for the "Interfaces\{guid}"
        // add 2 wchars: one for the '\' after 'Interfaces' and one for the null terminator
        pKeyName = MemCAlloc((wcslen(WZCREGK_REL_INTF) + dwGuidLen + 1)*sizeof(WCHAR));
        if (pKeyName == NULL)
        {
            dwErr = GetLastError();
            goto exit;
        }
        // create the local key name
        if (dwGuidLen != 0)
            wsprintf(pKeyName,L"%s\\%s", WZCREGK_REL_INTF, pIntfContext->wszGuid);
        else
            wsprintf(pKeyName,L"%s", WZCREGK_REL_INTF);
    }

    // open the interface's key first
    dwErr = RegOpenKeyEx(
                hkRoot,
                pKeyName,
                0,
                KEY_READ,
                &hkIntf);

    // break out if not successful
    if (dwErr != ERROR_SUCCESS)
    {
        // if the key is not there, no harm, go on with the defaults
        if (dwErr == ERROR_FILE_NOT_FOUND)
            dwErr = ERROR_SUCCESS;
        goto exit;
    }

    // get first the whole number of values in this key and the size of the largest data
    dwErr = RegQueryInfoKey(
                hkIntf,               // handle to key
                NULL,                 // class buffer
                NULL,                 // size of class buffer
                NULL,                 // reserved
                NULL,                 // number of subkeys
                NULL,                 // longest subkey name
                NULL,                 // longest class string
                &nEntries,            // number of value entries
                NULL,                 // longest value name
                &rdBuffer.dwDataLen,  // longest value data
                NULL,                 // descriptor length
                NULL);                // last write time
    // this call should better be not failing
    if (dwErr != ERROR_SUCCESS)
        goto exit;
    // if there are no keys at all, exit now.
    if (rdBuffer.dwDataLen == 0)
        goto exit;

    // prepare the receiving buffer for the size of the largest data
    // this will be used when reading in the active settings and each
    // of the static configurations
    rdBuffer.pData = MemCAlloc(rdBuffer.dwDataLen);
    if (rdBuffer.pData == NULL)
    {
        dwErr = GetLastError();
        goto exit;
    }

    // load the registry layout version information
    // don't worry about the return code. In case of any error,
    // we'll assume we deal with the latest registry layout
    nLength = sizeof(DWORD);
    dwVersion = REG_LAYOUT_VERSION;
    dwErr = RegQueryValueEx(
                hkIntf,
                WZCREGV_VERSION,
                0,
                &nType,
                (LPBYTE)&dwVersion,
                &nLength);

    // load the interface's control flags
    nLength = sizeof(DWORD);
    dwData = 0;
    dwErr = RegQueryValueEx(
                hkIntf,
                WZCREGV_CTLFLAGS,
                0,
                &nType,
                (LPBYTE)&dwData,
                &nLength);
    // if this entry is not there, no harm, rely on defaults
    // break out only in case of any other error
    if (dwErr != ERROR_SUCCESS && dwErr != ERROR_FILE_NOT_FOUND)
        goto exit;

    // copy the control flags to the INTF_CONTEXT only in the case the registry entry
    // has the REG_DWORD type and the right length
    if (dwErr == ERROR_SUCCESS &&
        nType == REG_DWORD &&
        nLength == sizeof(REG_DWORD))
    {
        pIntfContext->dwCtlFlags = dwData & INTFCTL_PUBLIC_MASK;
    }

    // load the last active settings
    //
    // NOTE: loading the whole set of parameters from below (excluding here the static list)
    // could be useless since these params should come directly from querying the driver. However
    // we load them here in the attemt to restore a previously saved state - at some point this
    // information could be useful in the configuration selection logic.
    //
    ZeroMemory(&pIntfContext->wzcCurrent, sizeof(WZC_WLAN_CONFIG));
    pIntfContext->wzcCurrent.Length = sizeof(WZC_WLAN_CONFIG);
    dwErr = StoLoadWZCConfig(
                hkIntf,
                NULL,   // not passing a GUID here means don't mess with 802.1X setting!
                dwVersion,
                WZCREGV_INTFSETTINGS,
                &pIntfContext->wzcCurrent,
                &rdBuffer);
    // if this entry is not there, no harm, rely on defaults
    // break out in case of any other error
    if (dwErr != ERROR_SUCCESS && dwErr != ERROR_FILE_NOT_FOUND)
        goto exit;

    // load the static configurations for this interface
    dwErr = StoLoadStaticConfigs(
                hkIntf,
                nEntries,
                pIntfContext,
                dwVersion,
                &rdBuffer);
    DbgAssert((dwErr == ERROR_SUCCESS, "Failed to load the static configurations"));

exit:
    if (hkIntf != NULL)
        RegCloseKey(hkIntf);

    MemFree(pKeyName);
    MemFree(rdBuffer.pData);

    DbgPrint((TRC_TRACK|TRC_STORAGE,"StoLoadIntfConfig]=%d", dwErr));
    return dwErr;
}

//-----------------------------------------------------------
// Loads the list of the static configurations from the registry
// Parameters:
//   hkRoot
//     [in] Opened registry key to the "...WZCSVC\Parameters\Interfaces\{guid}" location
//   nEntries
//     [in] Number of registry entries in the above reg key
//   pIntf
//     [in] Interface context to load the static list into
//   dwRegLayoutVer
//     [in] the version of the registry layout
//   prdBuffer
//     [in] assumed large enough for getting any static config
// Returned value:
//     Win32 error code 
DWORD
StoLoadStaticConfigs(
    HKEY          hkIntf,
    UINT          nEntries,
    PINTF_CONTEXT pIntfContext,
    DWORD         dwRegLayoutVer,
    PRAW_DATA     prdBuffer)
{
    DWORD               dwErr = ERROR_SUCCESS;
    UINT                nPrefrd, nIdx;
    WCHAR               wszStConfigName[sizeof(WZCREGV_STSETTINGS)/sizeof(WCHAR)];
    LPWSTR              pwszStConfigNum;
    PWZC_WLAN_CONFIG    pwzcPArray = NULL;
    UINT                nStructSize = (dwRegLayoutVer == REG_LAYOUT_VERSION) ? 
                            sizeof(WZC_WLAN_CONFIG) : 
                            FIELD_OFFSET(WZC_WLAN_CONFIG, rdUserData);

    DbgPrint((TRC_TRACK|TRC_STORAGE,"[StoLoadStaticConfigs"));

    // we need to scan all the entries named "Static#0001, Static#0002, etc". We could assume 
    // they are numbered sequentially, but if we want to be smart we can't rely on this assumption.
    // There could be a user intervention there (i.e deleting by hand some of the configs directly
    // from the registry hence breaking the sequence).
    // So, what we'll do is:
    // 1. allocate a buffer large enough to hold so many static configs.
    // 2. iterate through all the values - if a value is Static#** and has the right length, type,
    //    and value, copy it in the buffer and keep a count of them.
    // 3. copy the exact number of static configs to the INTF_CONTEXT allocating as much memory
    //    as needed.

    // get the estimated memory for all the static entries.
    pwzcPArray = (PWZC_WLAN_CONFIG) MemCAlloc(nEntries * sizeof(WZC_WLAN_CONFIG));
    if (pwzcPArray == NULL)
    {
        dwErr = GetLastError();
        goto exit;
    }

    // build the prefix for the static configuration's name
    wcscpy(wszStConfigName, WZCREGV_STSETTINGS);
    pwszStConfigNum = wcschr(wszStConfigName, REG_STSET_DELIM);

    // iterate through the whole set of entries in this key
    for (nIdx = 0, nPrefrd = 0;
         nIdx < nEntries && nPrefrd < nEntries;
         nIdx++)
    {
        // complete the configuration's name
        wsprintf(pwszStConfigNum, L"%04x", nIdx);

        dwErr = StoLoadWZCConfig(
                    hkIntf,
                    pIntfContext->wszGuid,
                    dwRegLayoutVer,
                    wszStConfigName,
                    &(pwzcPArray[nPrefrd]),
                    prdBuffer);

        if (dwErr == ERROR_SUCCESS)
            nPrefrd++;
    }

    DbgPrint((TRC_STORAGE,"Uploading %d static configurations", nPrefrd));

    // no matter what error we might have had till now, we can safely reset it.
    dwErr = ERROR_SUCCESS;

    // here, pwzcPArray has nPrefrd static configurations, in the correct order
    if (pIntfContext->pwzcPList != NULL)
        MemFree(pIntfContext->pwzcPList);

    // if there is anything to upload into the INTF_CONTEXT, do it now
    if (nPrefrd > 0)
    {
        pIntfContext->pwzcPList = (PWZC_802_11_CONFIG_LIST)
                                   MemCAlloc(sizeof(WZC_802_11_CONFIG_LIST) + (nPrefrd-1)*sizeof(WZC_WLAN_CONFIG));

        if (pIntfContext->pwzcPList == NULL)
        {
            dwErr = GetLastError();
            goto exit;
        }

        pIntfContext->pwzcPList->NumberOfItems = nPrefrd;
        pIntfContext->pwzcPList->Index = nPrefrd;
        memcpy(&(pIntfContext->pwzcPList->Config), pwzcPArray, nPrefrd*sizeof(WZC_WLAN_CONFIG));
    }

exit:
    if (pwzcPArray != NULL)
        MemFree(pwzcPArray);

    DbgPrint((TRC_TRACK|TRC_STORAGE,"StoLoadStaticConfigs]=%d", dwErr));
    return dwErr;
}

//-----------------------------------------------------------
// Saves all the configuration parameters to the persistant
// storage (registry in our case).
// Uses the global external g_lstIntfHashes.
// Returned value:
//     Win32 error code 
DWORD
StoSaveConfig()
{
    DWORD       dwErr = ERROR_SUCCESS;
    HKEY        hkRoot = NULL;
    PLIST_ENTRY pEntry;

    DbgPrint((TRC_TRACK|TRC_STORAGE,"[StoSaveConfig"));

    // open the root key first
    dwErr = RegCreateKeyExW(
                HKEY_LOCAL_MACHINE,
                WZCREGK_ABS_PARAMS,
                0,
                NULL,
                0,
                KEY_WRITE,
                NULL,
                &hkRoot,
                NULL);

    // failure at this point breaks the function
    if (dwErr != ERROR_SUCCESS)
        goto exit;

    if (g_lstIntfHashes.bValid)
    {
        // lock the hashes since we're iterating through all
        // the interfaces contexts
        EnterCriticalSection(&g_lstIntfHashes.csMutex);

        for (pEntry = g_lstIntfHashes.lstIntfs.Flink;
             pEntry != &g_lstIntfHashes.lstIntfs;
             pEntry = pEntry->Flink)
        {
            PINTF_CONTEXT pIntfContext;

            pIntfContext = CONTAINING_RECORD(pEntry, INTF_CONTEXT, Link);

            // save per interface configuration settings
            dwErr = StoSaveIntfConfig(hkRoot, pIntfContext);
            if (dwErr != ERROR_SUCCESS)
            {
                // some event logging should be added here in the future
                DbgAssert((FALSE, "Couldn't save interface configuration. Ignore and go on!"));
                dwErr = ERROR_SUCCESS;
            }
        }

        LeaveCriticalSection(&g_lstIntfHashes.csMutex);
    }

    if (g_wzcInternalCtxt.bValid)
    {
        //Save users preferences
        EnterCriticalSection(&g_wzcInternalCtxt.csContext);
        dwErr = StoSaveWZCContext(hkRoot, &g_wzcInternalCtxt.wzcContext);
        DbgAssert((dwErr == ERROR_SUCCESS, "Couldn't save service context. Ignore and go on!"));

        // save the global interface template
        dwErr = StoSaveIntfConfig(NULL, g_wzcInternalCtxt.pIntfTemplate);
        DbgAssert((dwErr == ERROR_SUCCESS, "Couldn't save the global interface template. Ignore and go on!"));

        dwErr = ERROR_SUCCESS;
        LeaveCriticalSection(&g_wzcInternalCtxt.csContext);
    }

exit:
    if (hkRoot != NULL)
        RegCloseKey(hkRoot);

    DbgPrint((TRC_TRACK|TRC_STORAGE,"StoSaveConfig]=%d", dwErr));
    return dwErr;
}


//-----------------------------------------------------------
// Saves per interface configuration parameters to the persistant
// storage.
// Parameters:
//   hkRoot
//     [in] Opened registry key to the "...WZCSVC\Parameters" location
//   pIntf
//     [in] Interface context to save to the registry
// Returned value:
//     Win32 error code 
DWORD
StoSaveIntfConfig(
    HKEY          hkRoot,
    PINTF_CONTEXT pIntfContext)
{
    DWORD           dwErr = ERROR_SUCCESS;
    HKEY            hkIntf = NULL;
    LPWSTR          pKeyName = NULL;
    DWORD           dwLayoutVer = REG_LAYOUT_VERSION;
    DWORD           dwCtlFlags;
    RAW_DATA        rdBuffer = {0, NULL};
    DWORD           dwGuidLen = 0;

    DbgPrint((TRC_TRACK|TRC_STORAGE,"[StoSaveIntfConfig(%S)", 
             (pIntfContext->wszGuid == NULL) ? L"Global" : pIntfContext->wszGuid));

    if (pIntfContext == NULL)
    {
        dwErr = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if (pIntfContext->wszGuid != NULL)
        dwGuidLen = wcslen(pIntfContext->wszGuid) + 1;

    if (hkRoot == NULL)
    {
        // if no root has been provided allocate space for the absolute path to WZC params,
        // the relative path to the interfaces location, the guid plus 2 '\' and one null terminator
        pKeyName = MemCAlloc((
                       wcslen(WZCREGK_ABS_PARAMS) + 
                       dwGuidLen + 
                       wcslen(WZCREGK_REL_INTF) + 
                       2)*sizeof(WCHAR));
        if (pKeyName == 0)
        {
            dwErr = GetLastError();
            goto exit;
        }

        if (dwGuidLen != 0)
            wsprintf(pKeyName,L"%s\\%s\\%s", WZCREGK_ABS_PARAMS, WZCREGK_REL_INTF, pIntfContext->wszGuid);
        else
            wsprintf(pKeyName,L"%s\\%s", WZCREGK_ABS_PARAMS, WZCREGK_REL_INTF);

        hkRoot = HKEY_LOCAL_MACHINE;
    }
    else
    {
        // if a root has been provided, allocate space just for the "Interfaces\{guid}"
        // add 2 wchars: one for the '\' after 'Interfaces' and one for the null terminator
        pKeyName = MemCAlloc((dwGuidLen + wcslen(WZCREGK_REL_INTF) + 1)*sizeof(WCHAR));
        if (pKeyName == NULL)
        {
            dwErr = GetLastError();
            goto exit;
        }
        // create the local key name
        if (dwGuidLen != 0)
            wsprintf(pKeyName,L"%s\\%s", WZCREGK_REL_INTF, pIntfContext->wszGuid);
        else
            wsprintf(pKeyName,L"%s", WZCREGK_REL_INTF);
    }

    // open the interface's key first
    dwErr = RegCreateKeyExW(
                hkRoot,
                pKeyName,
                0,
                NULL,
                0,
                KEY_QUERY_VALUE | KEY_WRITE,
                NULL,
                &hkIntf,
                NULL);
    // failure at this point breaks the function
    if (dwErr != ERROR_SUCCESS)
        goto exit;

    // set the registry layout version value
    dwErr = RegSetValueEx(
                hkIntf,
                WZCREGV_VERSION,
                0,
                REG_DWORD,
                (LPBYTE)&dwLayoutVer,
                sizeof(DWORD));
    DbgAssert((dwErr == ERROR_SUCCESS, "Can't write %S=%d to the registry", WZCREGV_VERSION, dwLayoutVer));

    // set the interface's control flags only if they are not volatile
    dwCtlFlags = pIntfContext->dwCtlFlags;
    if (!(dwCtlFlags & INTFCTL_VOLATILE))
    {
        dwCtlFlags &= ~INTFCTL_OIDSSUPP;
        dwErr = RegSetValueEx(
                    hkIntf,
                    WZCREGV_CTLFLAGS,
                    0,
                    REG_DWORD,
                    (LPBYTE)&dwCtlFlags,
                    sizeof(DWORD));
        DbgAssert((dwErr == ERROR_SUCCESS, "Can't write %S=0x%08x to the registry", WZCREGV_CTLFLAGS, pIntfContext->dwCtlFlags));
    }

    // we're done here, write the current WZC configuration to the registry
    dwErr = StoSaveWZCConfig(
                hkIntf,
                WZCREGV_INTFSETTINGS,
                &pIntfContext->wzcCurrent,
                &rdBuffer);
    DbgAssert((dwErr == ERROR_SUCCESS, "Can't save active settings"));

    // update the list of static configurations
    dwErr = StoUpdateStaticConfigs(
                hkIntf,
                pIntfContext,
                &rdBuffer);
    DbgAssert((dwErr == ERROR_SUCCESS, "Can't update the list of static configurations"));


exit:
    if (hkIntf != NULL)
        RegCloseKey(hkIntf);
    MemFree(pKeyName);
    MemFree(rdBuffer.pData);

    DbgPrint((TRC_TRACK|TRC_STORAGE,"StoSaveIntfConfig]=%d", dwErr));
    return dwErr;
}

//-----------------------------------------------------------
// Updates the list of static configurations for the given interface in the 
// persistant storage. The new list is saved, whatever configuration was removed
// is taken out of the persistant storage.
// Parameters:
//   hkRoot
//     [in] Opened registry key to the "...WZCSVC\Parameters\Interfaces\{guid}" location
//   pIntf
//     [in] Interface context to take the static list from
//   prdBuffer
//     [in/out] buffer to be used for preparing the registry blobs
// Returned value:
//     Win32 error code 
DWORD
StoUpdateStaticConfigs(
    HKEY          hkIntf,
    PINTF_CONTEXT pIntfContext,
    PRAW_DATA     prdBuffer)
{
    DWORD   dwErr = ERROR_SUCCESS;
    DWORD   dwLocalErr = ERROR_SUCCESS;
    UINT    nEntries, nIdx;
    WCHAR   wszStConfigName[sizeof(WZCREGV_STSETTINGS)/sizeof(WCHAR)];
    LPWSTR  pwszStConfigNum;

    DbgPrint((TRC_TRACK|TRC_STORAGE,"[StoUpdateStaticConfigs"));

    // get the initial number of values in this registry key
    dwErr = RegQueryInfoKey(
                hkIntf,     // handle to key
                NULL,       // class buffer
                NULL,       // size of class buffer
                NULL,       // reserved
                NULL,       // number of subkeys
                NULL,       // longest subkey name
                NULL,       // longest class string
                &nEntries,  // number of value entries
                NULL,       // longest value name
                NULL,       // longest value data
                NULL,       // descriptor length
                NULL);      // last write time
    if (dwErr != ERROR_SUCCESS)
        goto exit;

    // build the prefix for the static configuration's name
    wcscpy(wszStConfigName, WZCREGV_STSETTINGS);
    pwszStConfigNum = wcschr(wszStConfigName, REG_STSET_DELIM);
    nIdx = 0;

    if (pIntfContext->pwzcPList != NULL)
    {
        UINT i;
        for (i = 0;
             i < pIntfContext->pwzcPList->NumberOfItems && i < REG_STSET_MAX;
             i++)
        {
            if (pIntfContext->pwzcPList->Config[i].dwCtlFlags & WZCCTL_VOLATILE)
                continue;

            // complete the configuration's name
            wsprintf(pwszStConfigNum, L"%04x", nIdx++);
            // save the configuration to the registry
            dwLocalErr = StoSaveWZCConfig(
                            hkIntf,
                            wszStConfigName,
                            &(pIntfContext->pwzcPList->Config[i]),
                            prdBuffer);

            DbgAssert((dwLocalErr == ERROR_SUCCESS,
                       "Failed to save static configuration 0x%x. err=%d",
                       i, dwLocalErr));
            if (dwErr == ERROR_SUCCESS && dwLocalErr != ERROR_SUCCESS)
                dwErr = dwLocalErr;
        }
    }

    // delete now whatever remaining static
    // configurations might still be in the registry
    do
    {
        // complete the configuration's name
        wsprintf(pwszStConfigNum, L"%04x", nIdx);
        // and attempt to delete it - at some point
        // we should get back ERROR_FILE_NOT_FOUND
        dwLocalErr = RegDeleteValue(
                        hkIntf,
                        wszStConfigName);
        nIdx++;
    } while (nIdx < nEntries);

exit:
    DbgPrint((TRC_TRACK|TRC_STORAGE,"StoUpdateStaticConfigs]=%d", dwErr));

    return dwErr;
}

// externalities from 802.1X
DWORD
ElSetInterfaceParams (
        IN  WCHAR           *pwszGUID,
        IN  EAPOL_INTF_PARAMS  *pIntfParams
        );

DWORD
ElGetInterfaceParams (
        IN  WCHAR           *pwszGUID,
        IN  OUT EAPOL_INTF_PARAMS  *pIntfParams
        );


//-----------------------------------------------------------
// Loads from the registry a WZC Configuration, un-protects the WEP key field
// and stores the result in the output param pWzcCfg.
// Parameters:
//   hkCfg
//     [in] Opened registry key to load the WZC configuration from
//   dwRegLayoutVer,
//     [in] registry layout version
//   wszCfgName
//     [in] registry entry name for the WZC configuration
//   pWzcCfg
//     [out] pointer to a WZC_WLAN_CONFIG object that receives the registry data
//   prdBuffer
//     [in] allocated buffer, assumed large enough for getting the registry data!
DWORD
StoLoadWZCConfig(
    HKEY             hkCfg,
    LPWSTR           wszGuid,
    DWORD            dwRegLayoutVer,
    LPWSTR           wszCfgName,
    PWZC_WLAN_CONFIG pWzcCfg,
    PRAW_DATA        prdBuffer)
{
    DWORD dwErr = ERROR_SUCCESS;
    UINT  nType, nLength;

    DbgPrint((TRC_TRACK|TRC_STORAGE,"[StoLoadWZCConfig(\"%S\")", wszCfgName));
    DbgAssert((prdBuffer != NULL, "No buffer provided for loading the registry blob!"));

    // zero out the buffer and get the value from the registry
    ZeroMemory(prdBuffer->pData, prdBuffer->dwDataLen);
    nLength = prdBuffer->dwDataLen;
    dwErr = RegQueryValueEx(
                hkCfg,
                wszCfgName,
                0,
                &nType,
                prdBuffer->pData,
                &nLength);

    if (dwErr == ERROR_SUCCESS)
    {
        switch(dwRegLayoutVer)
	    {
        case REG_LAYOUT_LEGACY_1:
            // first legacy code (WinXP Beta2)
            if (nType == REG_BINARY && nLength == FIELD_OFFSET(WZC_WLAN_CONFIG, rdUserData))
            {
                memcpy(pWzcCfg, prdBuffer->pData, nLength);
                if (pWzcCfg->Length == nLength)
                {
                    pWzcCfg->Length = sizeof(WZC_WLAN_CONFIG);
                    pWzcCfg->AuthenticationMode = NWB_GET_AUTHMODE(pWzcCfg);
                    pWzcCfg->Reserved[0] = pWzcCfg->Reserved[1] = 0;
                }
                else
                    dwErr = ERROR_INVALID_DATA;
            }
            else
                dwErr = ERROR_INVALID_DATA;
            break;
        case REG_LAYOUT_LEGACY_2:
            // second legacy code (WinXP 2473)
            if (nType == REG_BINARY && nLength == sizeof(WZC_WLAN_CONFIG))
            {
                memcpy(pWzcCfg, prdBuffer->pData, nLength);
                if (pWzcCfg->Length != nLength)
                    dwErr = ERROR_INVALID_DATA;
            }
            break;
        case REG_LAYOUT_LEGACY_3:
        case REG_LAYOUT_VERSION:
            // revert the logic: assume failure (ERROR_INVALID_DATA) and
            // explictly set success if the case is
            dwErr = ERROR_INVALID_DATA;

            if (nType == REG_BINARY && nLength > sizeof(WZC_WLAN_CONFIG))
            {
                memcpy(pWzcCfg, prdBuffer->pData, sizeof(WZC_WLAN_CONFIG));
                if (pWzcCfg->Length == sizeof(WZC_WLAN_CONFIG))
                {
                    DATA_BLOB blobIn, blobOut;

                    blobIn.cbData = nLength - sizeof(WZC_WLAN_CONFIG);
                    blobIn.pbData = prdBuffer->pData + sizeof(WZC_WLAN_CONFIG);
                    blobOut.cbData = 0;
                    blobOut.pbData = NULL;
                    if (CryptUnprotectData(
                            &blobIn,
                            NULL,
                            NULL,
                            NULL,
                            NULL,
                            0,
                            &blobOut) &&
                        blobOut.cbData == WZCCTL_MAX_WEPK_MATERIAL)
                    {
                        memcpy(pWzcCfg->KeyMaterial, blobOut.pbData, blobOut.cbData);
                        // now this is success
                        dwErr = ERROR_SUCCESS;
                    }

                    if (blobOut.pbData != NULL)
                    {
                        ZeroMemory(blobOut.pbData, blobOut.cbData);
                        LocalFree(blobOut.pbData);
                    }
                }
            }
            // for now don't read anything - rely on defaults;
            break;
        default:
            dwErr = ERROR_BAD_FORMAT;
        }
    }

    // if everything went up fine and this is an infrastructure network and
    // we're in some legacy registry layout.. make sure to disable 802.1X in
    // the following cases:
    if (dwErr == ERROR_SUCCESS && 
        dwRegLayoutVer <= REG_LAYOUT_LEGACY_3 &&
        pWzcCfg->InfrastructureMode != Ndis802_11IBSS &&
        wszGuid != NULL)
    {
        BOOL                bDisableOneX = FALSE;

        // the Infrastructure network being loaded doesn't require privacy
        bDisableOneX = bDisableOneX || (pWzcCfg->Privacy == 0);
        // it is Infrastructure with privacy, but some explicit key is also provided..
        bDisableOneX = bDisableOneX || (pWzcCfg->dwCtlFlags & WZCCTL_WEPK_PRESENT);
        if (bDisableOneX == TRUE)
        {
            EAPOL_INTF_PARAMS   elIntfParams = {0};
            elIntfParams.dwSizeOfSSID = pWzcCfg->Ssid.SsidLength;
            memcpy(&elIntfParams.bSSID, &pWzcCfg->Ssid.Ssid, pWzcCfg->Ssid.SsidLength);
            dwErr = ElGetInterfaceParams (
                        wszGuid,   // wsz GUID
                        &elIntfParams);

            if (dwErr == ERROR_SUCCESS)
            {
                elIntfParams.dwEapFlags &= ~EAPOL_ENABLED;
                dwErr = ElSetInterfaceParams (
                        wszGuid,   // wsz GUID
                        &elIntfParams);
            }
        }
    }

    // if everything went ok so far it means we have loaded pWzcCfg with
    // data from the registry.
    // Lets check this data is consistent!
    if (dwErr == ERROR_SUCCESS)
    {
        // as the first thing - make sure the configuration's control
        // flags don't show it as "Volatile" - such a configuration shouldn't be
        // in the registry in the first instance. On upgrade, it might happen to
        // have this bit set since once it had a different meaning (the config contains
        // a 40bit WEP key) which is now obsolete.
        pWzcCfg->dwCtlFlags &= ~WZCCTL_VOLATILE;

        // since dwErr is ERROR_SUCCESS, it is guaranteed pWzcCfg
        // points to at least the Length field.
        dwErr = WZCSvcCheckConfig(pWzcCfg, pWzcCfg->Length);
    }

    DbgPrint((TRC_TRACK|TRC_STORAGE,"StoLoadWZCConfig]=%d", dwErr));
    return dwErr;
}
    
//-----------------------------------------------------------
// Takes the input param pWzcCfg, protects the WEP key field and stores the
// resulting BLOB into the registry.
// Parameters:
//   hkCfg
//     [in] Opened registry key to load the WZC configuration from
//   wszCfgName
//     [in] registry entry name for the WZC configuration
//   pWzcCfg
//     [in] WZC_WLAN_CONFIG object that is written to the registry
//   prdBuffer
//     [in/out] allocated buffer, assumed large enough for getting the registry data!
DWORD
StoSaveWZCConfig(
    HKEY             hkCfg,
    LPWSTR           wszCfgName,
    PWZC_WLAN_CONFIG pWzcCfg,
    PRAW_DATA        prdBuffer)
{
    DWORD       dwErr = ERROR_SUCCESS;
    DATA_BLOB   blobIn, blobOut;

    DbgPrint((TRC_TRACK|TRC_STORAGE,"[StoSaveWZCConfig(\"%S\")", wszCfgName));
    DbgAssert((prdBuffer != NULL, "No buffer provided for creating the registry blob!"));

    blobIn.cbData = WZCCTL_MAX_WEPK_MATERIAL;
    blobIn.pbData = &(pWzcCfg->KeyMaterial[0]);
    blobOut.cbData = 0;
    blobOut.pbData = NULL;
    if (!CryptProtectData(
            &blobIn,        // DATA_BLOB *pDataIn,
            L"",            // LPCWSTR szDataDescr,
            NULL,           // DATA_BLOB *pOptionalEntropy,
            NULL,           // PVOID pvReserved,
            NULL,           // CRYPTPROTECT_PROMPTSTRUCT *pPromptStrct,
            0,              // DWORD dwFlags,
            &blobOut))      // DATA_BLOB *pDataOut
        dwErr = GetLastError();

    DbgAssert((dwErr == ERROR_SUCCESS, "CryptProtectData failed with err=%d", dwErr));

    // if crypting the wep key went fine, check if we have enough storage to prepare
    // the blob for the registry. If not, allocate as much as needed.
    if (dwErr == ERROR_SUCCESS && 
        prdBuffer->dwDataLen < sizeof(WZC_WLAN_CONFIG) + blobOut.cbData)
    {
        MemFree(prdBuffer->pData);
        prdBuffer->dwDataLen = 0;
        prdBuffer->pData = NULL;
        prdBuffer->pData = MemCAlloc(sizeof(WZC_WLAN_CONFIG) + blobOut.cbData);
        if (prdBuffer->pData == NULL)
            dwErr = GetLastError();
        else
            prdBuffer->dwDataLen = sizeof(WZC_WLAN_CONFIG) + blobOut.cbData;
    }

    // now we have the buffer, all what remains is to:
    // - copy the WZC_WLAN_CONFIG object into the blob that goes into the registry
    // - clean the "clear" WEP key from that blob
    // - append the encrypted WEP key to the blob going into the registry
    // - write the blob to the registry
    if (dwErr == ERROR_SUCCESS)
    {
        PWZC_WLAN_CONFIG pRegCfg;

        memcpy(prdBuffer->pData, pWzcCfg, sizeof(WZC_WLAN_CONFIG));
        pRegCfg = (PWZC_WLAN_CONFIG)prdBuffer->pData;
        ZeroMemory(pRegCfg->KeyMaterial, WZCCTL_MAX_WEPK_MATERIAL);
        memcpy(prdBuffer->pData+sizeof(WZC_WLAN_CONFIG), blobOut.pbData, blobOut.cbData);
        dwErr = RegSetValueEx(
                    hkCfg,
                    wszCfgName,
                    0,
                    REG_BINARY,
                    prdBuffer->pData,
                    prdBuffer->dwDataLen);
    }

    // cleanup whatever CryptProtectData might have allocated.
    if (blobOut.pbData != NULL)
        LocalFree(blobOut.pbData);

    DbgPrint((TRC_TRACK|TRC_STORAGE,"StoSaveWZCConfig]=%d", dwErr));
    return dwErr;
}

// StoLoadWZCContext:
// Description: Loads a context from the registry
// Parameters: 
// [out] pwzvCtxt: pointer to a WZC_CONTEXT allocated by user, initialised
// with WZCContextInit. On  success, contains values from registry.  
// [in]  hkRoot, a handle to "...WZCSVC\Parameters"
// Returns: win32 error
DWORD StoLoadWZCContext(HKEY hkRoot, PWZC_CONTEXT pwzcCtxt)
{
    BOOL        bCloseKey = FALSE;
    DWORD       dwErr = ERROR_SUCCESS;
    DWORD       dwcbSize = sizeof(WZC_CONTEXT);
    DWORD       dwType = REG_BINARY;
    WZC_CONTEXT wzcTempCtxt;

    DbgPrint((TRC_TRACK|TRC_STORAGE, "[StoLoadWZCContext"));

    if (pwzcCtxt == NULL)
    {
        dwErr = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if (hkRoot == NULL)
    {
        // open the root key first
        dwErr = RegOpenKeyEx(
                    HKEY_LOCAL_MACHINE,
                    WZCREGK_ABS_PARAMS,
                    0,
                    KEY_READ,
                    &hkRoot);
        // if we couldn't find the WZC key, no problem, this is not
        // a failure - we'll just have to rely on the default values
        if (dwErr == ERROR_FILE_NOT_FOUND)
        {
            dwErr = ERROR_SUCCESS;
            goto exit;
        }

        // failure at this point breaks the function
        if (dwErr != ERROR_SUCCESS)
            goto exit;

	    bCloseKey = TRUE;
    }

    dwErr = RegQueryValueEx(
                hkRoot,
                WZCREGV_CONTEXT,
                NULL,
                &dwType,
			    (LPBYTE)&wzcTempCtxt,
                &dwcbSize);
    switch(dwErr)
    {
    case ERROR_FILE_NOT_FOUND:
      /* If there is no registry entry, this is not an error - we rely
       * on the defaults. Translate this case to ERROR_SUCCESS.
       */
        DbgPrint((TRC_STORAGE, "No service context present in the registry!"));
        dwErr = ERROR_SUCCESS;
        break;
    case ERROR_SUCCESS:
        // we got our registry values, copy them in the running memory.
	    memcpy(pwzcCtxt, &wzcTempCtxt, sizeof(WZC_CONTEXT));
        break;
    default:
        // for any other error, it will be bubbled up
        DbgAssert((FALSE,"Error %d loading the service's context.", dwErr));
    }

exit:
    if (TRUE == bCloseKey)
        RegCloseKey(hkRoot);

    DbgPrint((TRC_TRACK|TRC_STORAGE,"StoLoadWZCContext]=%d", dwErr));
    return dwErr;
}

// StoSaveWZCContext:
// Description: Saves a context to the registry. Does not check values. If 
// the registry key dosent exist, it is created.
// Parameters: [in] pwzcCtxt, pointer to a valid WZC_CONTEXT
//             [in]  hkRoot, a handle to "...WZCSVC\Parameters"
// Returns: win32 error
DWORD StoSaveWZCContext(HKEY hkRoot, PWZC_CONTEXT pwzcCtxt)
{
    BOOL  bCloseKey = FALSE;
    DWORD dwErr = ERROR_SUCCESS;

    DbgPrint((TRC_TRACK|TRC_STORAGE, "[StoSaveWZCContext"));

    if (pwzcCtxt == NULL)
    {
        dwErr = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if (NULL == hkRoot)
	{
        // open the root key first
        dwErr = RegOpenKeyEx(
		           HKEY_LOCAL_MACHINE,
		           WZCREGK_ABS_PARAMS,
		           0,
		           KEY_READ|KEY_SET_VALUE,
		           &hkRoot);
        // if we couldn't find the WZC key, no problem, this is not
        // a failure - we'll just have to rely on the default values
        if (dwErr == ERROR_FILE_NOT_FOUND)
        {
            dwErr = ERROR_SUCCESS;
            goto exit;
        }
	  
        // failure at this point breaks the function
        if (dwErr != ERROR_SUCCESS)
            goto exit;
	  
        bCloseKey = TRUE;
    }

    dwErr = RegSetValueEx(
                hkRoot,
                WZCREGV_CONTEXT,
                0,
                REG_BINARY,
                (LPBYTE) pwzcCtxt,
                sizeof(WZC_CONTEXT));

    DbgAssert((dwErr == ERROR_SUCCESS, "Error %d saving the service's context.", dwErr));

 exit:
    if (TRUE == bCloseKey)
      RegCloseKey(hkRoot);

    DbgPrint((TRC_TRACK|TRC_STORAGE,"StoSaveWZCContext]=%d", dwErr));
    return dwErr;
}