/***********************************************************************
 * 
 *  Copyright (C) 2006 Novell, Inc. All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; version 2.1
 *  of the License.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, Novell, Inc.
 * 
 *  To contact Novell about this file by physical or electronic mail, 
 *  you may find current contact information at www.novell.com.
 * 
 *  Author: Juan Carlos Luciani <jluciani@novell.com>
 *
 ***********************************************************************/


//===[ Include files ]=====================================================

#include "internal.h"

//===[ Type definitions ]==================================================

//
// Registry Key/Value defines used in the AuthCache
//
#define CASA_AUTH_CACHE_REG_KEY     "CASA_Auth_Cache"
#define CREATION_TIME_REG_VALUE     "CreationTime"
#define EXPIRATION_TIME_REG_VALUE   "ExpirationTime"
#define DOES_NOT_EXPIRE_REG_VALUE   "DoesNotExpire"
#define STATUS_REG_VALUE            "Status"
#define TOKEN_REG_VALUE             "Token"


//===[ Function prototypes ]===============================================

//===[ Global variables ]==================================================

// In memory auth cache list head
static
LIST_ENTRY  g_authCacheListHead;

// Non-host specific key name
static
char  g_allHosts[] = "AllHosts";

static
int   g_cacheEntryCount = 0;

//++=======================================================================
AuthCacheEntry*
CreateAuthCacheEntry(
   IN    const char *pCacheKeyName,
   IN    const char *pHostName)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   AuthCacheEntry *pEntry;

   DbgTrace(1, "-CreateAuthCacheEntry- Start\n", 0);

   // Use allHosts if a host name was not specified
   if (pHostName == NULL)
   {
      pHostName = g_allHosts;
   }

   // Allocate space for the entry
   pEntry = (AuthCacheEntry*) malloc(sizeof(*pEntry));
   if (pEntry)
   {
      // Zero the entry
      memset(pEntry, 0, sizeof(*pEntry));

      // Now allocate buffers to maintain copies of the CacheKeyName and host names
      pEntry->pCacheKeyName = (char*) malloc(strlen(pCacheKeyName) + 1);
      if (pEntry->pCacheKeyName)
      {
         pEntry->pHostName = (char*) malloc(strlen(pHostName) + 1);
         if (pEntry->pHostName)
         {
            // Save the names within the entry
            strcpy(pEntry->pCacheKeyName, pCacheKeyName);
            strcpy(pEntry->pHostName, pHostName);

            // Initialize the entries refCount
            pEntry->refCount = 1;

            // Increment the cache entry count
            g_cacheEntryCount ++;
         }
         else
         {
            DbgTrace(0, "-CreateAuthCacheEntry- Unable to allocate buffer for host name\n", 0);

            // Free allocated resources
            free(pEntry->pCacheKeyName);
            free(pEntry);

            // Adjust return parameter
            pEntry = NULL;
         }
      }
      else
      {
         DbgTrace(0, "-CreateAuthCacheEntry- Unable to allocate buffer for the CacheKeyName\n", 0);

         // Free allocated resources
         free(pEntry);

         // Adjust return parameter
         pEntry = NULL;
      }
   }
   else
   {
      DbgTrace(0, "-CreateAuthCacheEntry- Unable to allocate buffer for auth cache entry\n", 0);
   }

   DbgTrace(1, "-CreateAuthCacheEntry- End, pEntry = %08X\n", pEntry);

   return pEntry;
}


//++=======================================================================
static
void
FreeAuthCacheEntry(
   IN    AuthCacheEntry *pEntry)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   DbgTrace(1, "-FreeAuthCacheEntry- Start, pEntry = %08X\n", pEntry);

   // Free resources associated with the entry
   if (pEntry->pToken)
      free(pEntry->pToken);

   if (pEntry->pHostName)
      free(pEntry->pHostName);

   if (pEntry->pCacheKeyName)
      free(pEntry->pCacheKeyName);

   // Free the entry
   free(pEntry);

   // Decrement the cache entry count
   g_cacheEntryCount --;

   DbgTrace(1, "-FreeAuthCacheEntry- End\n", 0);
}


//++=======================================================================
void
ReleaseAuthCacheEntry(
   IN    AuthCacheEntry *pEntry)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   DbgTrace(1, "-ReleaseAuthCacheEntry- Start, pEntry = %08X\n", pEntry);

   // Reduce the entries reference count and free it if it goes to zero
   pEntry->refCount --;
   if (pEntry->refCount == 0)
      FreeAuthCacheEntry(pEntry);

   DbgTrace(1, "-ReleaseAuthCacheEntry- End\n", 0);
}


//++=======================================================================
void
IncAuthCacheEntryRefCount(
   IN    AuthCacheEntry *pEntry)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   DbgTrace(1, "-IncAuthCacheEntryRefCount- Start, pEntry = %08X\n", pEntry);

   // Increase the entries reference count
   pEntry->refCount ++;

   DbgTrace(1, "-IncAuthCacheEntryRefCount- End\n", 0);
}


//++=======================================================================
static
BOOL
CacheEntryLifetimeExpired(
   IN    DWORD creationTime,
   IN    DWORD expirationTime)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   DWORD currentTime = GetTickCount();
   BOOL  expired = FALSE;

   DbgTrace(2, "-CacheEntryLifetimeExpired- Start\n", 0);

   // Check if the clock has wrapped
   if (currentTime >= creationTime)
   {
      // The clock has not wrapped, check if the
      // expiration time has wrapped.
      if (expirationTime > creationTime)
      {
         // The expiration time also has not wrapped,
         // do a straight compare against the current
         // time.
         if (currentTime >= expirationTime)
         {
            // It has expired
            expired = TRUE;
         }
      }
   }
   else
   {
      // The clock has wrapped, check if the expiration
      // time also wrapped.
      if (expirationTime > creationTime)
      {
         // The expiration time did not wrap, therefore
         // it has been exceeded since the clock wrapped.
         expired = TRUE;
      }
      else
      {
         // The expiration time also wrapped, do a straight
         // compare against the current time.
         if (currentTime >= expirationTime)
         {
            // It has expired
            expired = TRUE;
         }
      }
   }

   DbgTrace(2, "-CacheEntryLifetimeExpired- End, result = %08X\n", expired);

   return expired;
}


//++=======================================================================
AuthCacheEntry*
FindEntryInAuthCache(
   IN    const char *pCacheKeyName,
   IN    const char *pHostName)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   AuthCacheEntry *pEntry = NULL;
   LIST_ENTRY     *pListEntry;

   DbgTrace(1, "-FindEntryInAuthCache- Start\n", 0);

   // Examine the cache, if entry found then check if it has expired
   // in which case we would want to remove it from the cache.

   // Use allHosts if a host name was not specified
   if (pHostName == NULL)
   {
      pHostName = g_allHosts;
   }

   // First look through the entries in our in-memory cache
   pListEntry = g_authCacheListHead.Flink;
   while (pListEntry != &g_authCacheListHead)
   {
      AuthCacheEntry *pWrkEntry;

      // get a pointer to the entry
      pWrkEntry = CONTAINING_RECORD(pListEntry, AuthCacheEntry, listEntry);

      // Check if this is an entry for the appropriate host
      if (strcmp(pHostName, pWrkEntry->pHostName) == 0)
      {
         // This is an entry for the appropriate host, now check if it is
         // also for the appropriate CacheKeyName
         if (strcmp(pCacheKeyName, pWrkEntry->pCacheKeyName) == 0)
         {
            // This entry is for the appropriate CacheKeyName, check if it
            // has not expired.
            if (pWrkEntry->doesNotExpire == FALSE
                && CacheEntryLifetimeExpired(pWrkEntry->creationTime, pWrkEntry->expirationTime))
            {
               // The lifetime of the entry has expired, remove it from the in-memory cache
               // and release it.
               RemoveEntryList(&pWrkEntry->listEntry);
               ReleaseAuthCacheEntry(pWrkEntry);
            }
            else
            {
               // This cache entry is still good, use it.
               pEntry = pWrkEntry;
            }

            // No need to keep looking in the in-memory cache
            break;

         }
      }

      // Advance to the next entry
      pListEntry = pListEntry->Flink;
   }

   // Look in the persistent cache if an entry was not found in the in-memory cache
   if (pEntry == NULL)
   {
      LONG  status;
      HKEY  hCASARegKey;

      // Open CASA Auth Cache Key
      status = RegOpenKeyExA(HKEY_CURRENT_USER,
                             CASA_AUTH_CACHE_REG_KEY,
                             0,
                             KEY_ALL_ACCESS,
                             &hCASARegKey);
      if (status == ERROR_SUCCESS)
      {
         HKEY  hHostRegKey;

         // CASA Auth Cache key opened, now try to open
         // key for the host.
         status = RegOpenKeyExA(hCASARegKey,
                                pHostName,
                                0,
                                KEY_ALL_ACCESS,
                                &hHostRegKey);
         if (status == ERROR_SUCCESS)
         {
            HKEY  hCacheKeyNameRegKey;

            // Host key opened, now try to open key
            // for the CacheKeyName.
            status = RegOpenKeyExA(hHostRegKey,
                                   pCacheKeyName,
                                   0,
                                   KEY_ALL_ACCESS,
                                   &hCacheKeyNameRegKey);
            if (status == ERROR_SUCCESS)
            {
               DWORD creationTime;
               DWORD expirationTime;
               BOOL  doesNotExpire;
               BOOL  deleteCacheKeyNameKey = TRUE;
               DWORD variableSz;

               // Key for the CacheKeyName key opened, now determine whether
               // or not its lifetime has expired.
               // 
               // Read the creation time value
               variableSz = sizeof(creationTime);
               status = RegQueryValueExA(hCacheKeyNameRegKey,
                                         CREATION_TIME_REG_VALUE,
                                         NULL,
                                         NULL,
                                         (LPBYTE) &creationTime,
                                         &variableSz);
               if (status == ERROR_SUCCESS)
               {
                  // Read the expiration time
                  variableSz = sizeof(expirationTime);
                  status = RegQueryValueExA(hCacheKeyNameRegKey,
                                            EXPIRATION_TIME_REG_VALUE,
                                            NULL,
                                            NULL,
                                            (LPBYTE) &expirationTime,
                                            &variableSz);
                  if (status == ERROR_SUCCESS)
                  {
                     // Read the does not expire
                     variableSz = sizeof(doesNotExpire);
                     status = RegQueryValueExA(hCacheKeyNameRegKey,
                                               EXPIRATION_TIME_REG_VALUE,
                                               NULL,
                                               NULL,
                                               (LPBYTE) &doesNotExpire,
                                               &variableSz);
                     if (status == ERROR_SUCCESS)
                     {
                        // Check if the extry lifetime has been exceeded
                        if (doesNotExpire == TRUE
                            || CacheEntryLifetimeExpired(creationTime, expirationTime) == FALSE)
                        {
                           // Create a AuthCacheEntry
                           pEntry = CreateAuthCacheEntry(pCacheKeyName, pHostName);
                           if (pEntry)
                           {
                              BOOL  entryInitialized = FALSE;

                              // Start setting up the AuthCacheEntry
                              pEntry->creationTime = creationTime;
                              pEntry->expirationTime = expirationTime;
                              pEntry->doesNotExpire = doesNotExpire;

                              // Read the status
                              variableSz = sizeof(pEntry->status);
                              status = RegQueryValueExA(hCacheKeyNameRegKey,
                                                        STATUS_REG_VALUE,
                                                        NULL,
                                                        NULL,
                                                        (LPBYTE) &pEntry->status,
                                                        &variableSz);
                              if (status == ERROR_SUCCESS)
                              {
                                 // Check if there is also an auth token associated with
                                 // this entry.
                                 if (pEntry->status == CASA_STATUS_SUCCESS)
                                 {
                                    DWORD tokenSz = 0;

                                    // There should be an auth token associated with this CacheKeyName,
                                    // first determine what size buffer to allocate for it.
                                    status = RegQueryValueExA(hCacheKeyNameRegKey,
                                                              TOKEN_REG_VALUE,
                                                              NULL,
                                                              NULL,
                                                              (LPBYTE) pEntry->pToken,
                                                              &tokenSz);
                                    if (status == ERROR_SUCCESS
                                       || status == ERROR_MORE_DATA)
                                    {
                                       // Allocate buffer to hold the auth token
                                       pEntry->pToken = (char*) malloc(tokenSz);
                                       if (pEntry->pToken)
                                       {
                                          // Now read token into the allocated buffer
                                          status = RegQueryValueExA(hCacheKeyNameRegKey,
                                                                    TOKEN_REG_VALUE,
                                                                    NULL,
                                                                    NULL,
                                                                    (LPBYTE) pEntry->pToken,
                                                                    &tokenSz);
                                          if (status == ERROR_SUCCESS)
                                          {
                                             // The cache entry has been properly initialized,
                                             // add it to the in-memory cache.
                                             entryInitialized = TRUE;
                                             deleteCacheKeyNameKey = FALSE;
                                             InsertHeadList(&g_authCacheListHead, &pEntry->listEntry);
                                          }
                                          else
                                          {
                                             DbgTrace(0, "-FindEntryInAuthCache- Error reading token, status = %d\n", status);
                                          }
                                       }
                                       else
                                       {
                                          DbgTrace(0, "-FindEntryInAuthCache- Unable to allocate buffer for token\n", 0);
                                       }
                                    }
                                    else
                                    {
                                       DbgTrace(0, "-FindEntryInAuthCache- Error reading token2, status = %d\n", status);
                                    }
                                 }
                                 else
                                 {
                                    // There is no auth token associated with this entry
                                    //
                                    // The cache entry has been properly initialized,
                                    // add it to the in-memory cache.
                                    entryInitialized = TRUE;
                                    deleteCacheKeyNameKey = FALSE;
                                    InsertHeadList(&g_authCacheListHead, &pEntry->listEntry);
                                 }
                              }
                              else
                              {
                                 DbgTrace(0, "-FindEntryInAuthCache- Error reading status, status = %d\n", status);
                              }

                              // Free the auth cache entry if it was not successfully initialized
                              if (entryInitialized == FALSE)
                              {
                                 FreeAuthCacheEntry(pEntry);
                                 pEntry = NULL;
                              }
                           }
                           else
                           {
                              DbgTrace(0, "-FindEntryInAuthCache- Error creating auth cache entry\n", 0);
                           }
                        }
                     }
                     else
                     {
                        DbgTrace(0, "-FindEntryInAuthCache- Error reading does not expire, status = %d\n", status);
                     }
                  }
                  else
                  {
                     DbgTrace(0, "-FindEntryInAuthCache- Error reading expiration time, status = %d\n", status);
                  }
               }
               else
               {
                  DbgTrace(0, "-FindEntryInAuthCache- Error reading creation time, status = %d\n", status);
               }

               // Close CacheKeyName key
               RegCloseKey(hCacheKeyNameRegKey);

               // Delete the CacheKeyName key if necessary
               if (deleteCacheKeyNameKey)
               {
                  RegDeleteKey(hHostRegKey, pCacheKeyName);
               }
            }

            // Close host key
            RegCloseKey(hHostRegKey);
         }

         // Close CASA_Auth_Cache key
         RegCloseKey(hCASARegKey);
      }
      else
      {
         DbgTrace(0, "-FindEntryInAuthCache- Error opening CASA Auth Cache key, status = %d\n", status);
      }
   }

   // Increment the reference count of entry being returned
   if (pEntry)
   {
      // Increment entries reference count since we are returning it to the caller
      IncAuthCacheEntryRefCount(pEntry);
   }

   DbgTrace(1, "-FindEntryInAuthCache- End, pEntry = %08X\n", pEntry);

   return pEntry;
}


//++=======================================================================
void
AddEntryToAuthCache(
   IN    AuthCacheEntry *pEntry,
   IN    int entryLifetime) // seconds (0 == Lives forever)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   LONG  status;
   HKEY  hCASARegKey;

   DbgTrace(1, "-AddEntryToAuthCache- Start, pEntry = %08X\n", pEntry);

   // Set the time when the entry was added to the cache
   pEntry->creationTime = GetTickCount();

   // First determine the time when the entry is due to expire
   if (entryLifetime != 0)
   {
      pEntry->expirationTime = pEntry->creationTime + (entryLifetime * 1000);
      pEntry->doesNotExpire = FALSE;
   }
   else
   {
      // The entry does not expire
      pEntry->expirationTime = 0;
      pEntry->doesNotExpire = TRUE;
   }

   // Save the entry in our persistent cache (registry)
   //
   // Open CASA Auth Cache key
   status = RegOpenKeyExA(HKEY_CURRENT_USER,
                          CASA_AUTH_CACHE_REG_KEY,
                          0,
                          KEY_ALL_ACCESS,
                          &hCASARegKey);
   if (status == ERROR_SUCCESS)
   {
      HKEY  hHostRegKey;

      // CASA_Auth_Cache key created or opened, now open or create key for the host.
      status = RegCreateKeyExA(hCASARegKey,
                               pEntry->pHostName,
                               0,
                               NULL,
                               REG_OPTION_VOLATILE,
                               KEY_ALL_ACCESS,
                               NULL,
                               &hHostRegKey,
                               NULL);
      if (status == ERROR_SUCCESS)
      {
         HKEY  hCacheKeyNameRegKey;

         // Host key created or opened, now create key for the CacheKeyName.
         status = RegCreateKeyExA(hHostRegKey,
                                  pEntry->pCacheKeyName,
                                  0,
                                  NULL,
                                  REG_OPTION_VOLATILE,
                                  KEY_ALL_ACCESS,
                                  NULL,
                                  &hCacheKeyNameRegKey,
                                  NULL);
         if (status == ERROR_SUCCESS)
         {
            // Write entry values
            status = RegSetValueExA(hCacheKeyNameRegKey,
                                    CREATION_TIME_REG_VALUE,
                                    0,
                                    REG_DWORD,
                                    (LPBYTE) &pEntry->creationTime,
                                    sizeof(pEntry->creationTime));
            if (status == ERROR_SUCCESS)
            {
               status = RegSetValueExA(hCacheKeyNameRegKey,
                                       EXPIRATION_TIME_REG_VALUE,
                                       0,
                                       REG_DWORD,
                                       (LPBYTE) &pEntry->expirationTime,
                                       sizeof(pEntry->expirationTime));
               if (status == ERROR_SUCCESS)
               {
                  status = RegSetValueExA(hCacheKeyNameRegKey,
                                          DOES_NOT_EXPIRE_REG_VALUE,
                                          0,
                                          REG_DWORD,
                                          (LPBYTE) &pEntry->doesNotExpire,
                                          sizeof(pEntry->doesNotExpire));
                  if (status == ERROR_SUCCESS)
                  {
                     status = RegSetValueExA(hCacheKeyNameRegKey,
                                             STATUS_REG_VALUE,
                                             0,
                                             REG_DWORD,
                                             (LPBYTE) &pEntry->status,
                                             sizeof(pEntry->status));
                     if (status == ERROR_SUCCESS)
                     {
                        // Check if there is also an auth token associated with this entry
                        // this entry.
                        if (pEntry->status == CASA_STATUS_SUCCESS)
                        {
                           status = RegSetValueExA(hCacheKeyNameRegKey,
                                                   TOKEN_REG_VALUE,
                                                   0,
                                                   REG_SZ,
                                                   (LPBYTE) pEntry->pToken,
                                                   (DWORD) strlen(pEntry->pToken) + 1);
                           if (status != ERROR_SUCCESS)
                           {
                              DbgTrace(0, "-AddEntryToAuthCache- Error setting token, status = %d\n", status);
                           }
                        }
                     }
                     else
                     {
                        DbgTrace(0, "-AddEntryToAuthCache- Error setting status, status = %d\n", status);
                     }
                  }
                  else
                  {
                     DbgTrace(0, "-AddEntryToAuthCache- Error setting does not expire, status = %d\n", status);
                  }
               }
               else
               {
                  DbgTrace(0, "-AddEntryToAuthCache- Error setting expiration time, status = %d\n", status);
               }
            }
            else
            {
               DbgTrace(0, "-AddEntryToAuthCache- Error setting creation time, status = %d\n", status);
            }

            // Close CacheKeyName key
            RegCloseKey(hCacheKeyNameRegKey);

            // Delete the CacheKeyName key if not successful
            if (status != ERROR_SUCCESS)
            {
               RegDeleteKey(hHostRegKey, pEntry->pCacheKeyName);
            }
         }
         else
         {
            DbgTrace(0, "-AddEntryToAuthCache- Error creating key for CacheKeyName, status = %d\n", status);
         }

         // Close host key
         RegCloseKey(hHostRegKey);
      }
      else
      {
         DbgTrace(0, "-AddEntryToAuthCache- Error creating key for host, status = %d\n", status);
      }

      // Close CASA_Auth_Cache key
      RegCloseKey(hCASARegKey);
   }
   else
   {
      DbgTrace(0, "-AddEntryToAuthCache- Error opening CASA Auth Cache Key, status = %d\n", status);
   }

   // Either place the cache entry in our in-memory cache or
   // free it depending on the status of the operations performed.
   if (status == ERROR_SUCCESS)
   {
      // The entry was added to the cache, save it in
      // our in-memory cache.
      InsertHeadList(&g_authCacheListHead, &pEntry->listEntry);

      // Increment its reference count since we are keeping a reference
      IncAuthCacheEntryRefCount(pEntry);
   }

   DbgTrace(1, "-AddEntryToAuthCache- End\n", 0);
}


//++=======================================================================
CasaStatus
InitializeAuthCache(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   CasaStatus                 retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                          CASA_FACILITY_AUTHTOKEN,
                                                          CASA_STATUS_UNSUCCESSFUL);
   PSID                       pEveryoneSID;
   SID_IDENTIFIER_AUTHORITY   SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;

   DbgTrace(1, "-InitializeAuthCache- Start\n", 0);

   // Initialize the cache list head
   InitializeListHead(&g_authCacheListHead);

   // Lets create the CASA Auth Cache registry key in the
   // user's hive and limit access to it.
   //
   // Create a well-known SID for the Everyone group.
   if (AllocateAndInitializeSid(&SIDAuthWorld,
                                1,
                                SECURITY_WORLD_RID,
                                0, 0, 0, 0, 0, 0, 0,
                                &pEveryoneSID))
   {
      EXPLICIT_ACCESS            ea[3] = {0};
      SID_IDENTIFIER_AUTHORITY   SIDAuthNT = SECURITY_NT_AUTHORITY;
      PSID                       pAdminSID;

      // Initialize an EXPLICIT_ACCESS structure for an ACE.
      // The ACE will revoke Everyone access to the key.
      ea[0].grfAccessPermissions = KEY_ALL_ACCESS;
      ea[0].grfAccessMode = REVOKE_ACCESS;
      ea[0].grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
      ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
      ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
      ea[0].Trustee.ptstrName  = (LPTSTR) pEveryoneSID;

      // Create a SID for the BUILTIN\Administrators group.
      if (AllocateAndInitializeSid(&SIDAuthNT,
                                   2,
                                   SECURITY_BUILTIN_DOMAIN_RID,
                                   DOMAIN_ALIAS_RID_ADMINS,
                                   0, 0, 0, 0, 0, 0,
                                   &pAdminSID))
      {
         DWORD    status;
         PACL     pACL;
         HANDLE   hToken;

         // Initialize an EXPLICIT_ACCESS structure for an ACE.
         // The ACE will revoke the Administrators group access to the key.
         ea[1].grfAccessPermissions = KEY_ALL_ACCESS;
         ea[1].grfAccessMode = REVOKE_ACCESS;
         ea[1].grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
         ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
         ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
         ea[1].Trustee.ptstrName  = (LPTSTR) pAdminSID;

         // Create a SID for the interactive user, first get the process token
         if (OpenProcessToken(GetCurrentProcess(),
                              TOKEN_QUERY,
                              &hToken))
         {
            char  tokenInformation[1024];
            DWORD infoLength;

            if (GetTokenInformation(hToken,
                                    TokenUser,
                                    tokenInformation,
                                    sizeof(tokenInformation),
                                    &infoLength))
            {
               TOKEN_USER  *pTokenUser = (TOKEN_USER*) tokenInformation;

               // Initialize an EXPLICIT_ACCESS structure for an ACE.
               // The ACE will grant the interactive user access to the key.
               ea[2].grfAccessPermissions = KEY_ALL_ACCESS;
               ea[2].grfAccessMode = SET_ACCESS;
               ea[2].grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
               ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;
               ea[2].Trustee.TrusteeType = TRUSTEE_IS_USER;
               ea[2].Trustee.ptstrName  = (LPTSTR) pTokenUser->User.Sid;

               // Create a new ACL that contains the new ACEs.
               status = SetEntriesInAcl(3, ea, NULL, &pACL);
               if (status == ERROR_SUCCESS)
               {
                  PSECURITY_DESCRIPTOR pSD;

                  // Allocate space for a security descriptor
                  pSD = (SECURITY_DESCRIPTOR*) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); 
                  if (pSD)
                  {
                     // Initialize a security descriptor
                     if (InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) 
                     {
                        // Add the ACL to the security descriptor
                        if (SetSecurityDescriptorDacl(pSD, 
                                                      TRUE,     // bDaclPresent flag   
                                                      pACL, 
                                                      FALSE))   // not a default DACL 
                        {
                           SECURITY_ATTRIBUTES  sa;
                           HKEY                 hCASARegKey;
                           DWORD                disposition;

                           // Initialize a security attributes structure
                           sa.nLength = sizeof(SECURITY_ATTRIBUTES);
                           sa.lpSecurityDescriptor = pSD;
                           sa.bInheritHandle = FALSE;

                           // Now create the CASA Auth Cache registry key for this user
                           // with the required access control restrictions.
                           status = RegCreateKeyExA(HKEY_CURRENT_USER,
                                                    CASA_AUTH_CACHE_REG_KEY,
                                                    0,
                                                    NULL,
                                                    REG_OPTION_VOLATILE,
                                                    KEY_ALL_ACCESS,
                                                    &sa,
                                                    &hCASARegKey,
                                                    &disposition);
                           if (status == ERROR_SUCCESS)
                           {
                              // Success
                              retStatus = CASA_STATUS_SUCCESS;

                              // Close CASA_Auth_Cache key
                              RegCloseKey(hCASARegKey);
                           }
                           else
                           {
                              DbgTrace(0, "-InitializeAuthCache- Error creating CASA Key, status = %d\n", status);
                           }
                        }
                        else
                        {  
                           DbgTrace(0, "-InitializeAuthCache- SetSecurityDescriptorDacl Error = %d\n", GetLastError());
                        } 
                     }
                     else
                     {  
                        DbgTrace(0, "-InitializeAuthCache- InitializeSecurityDescriptor Error %d\n", GetLastError());
                     } 

                     // Free the space allocated for the security descriptor
                     LocalFree(pSD);
                  }
                  else
                  { 
                     DbgTrace(0, "-InitializeAuthCache- Unable to allocate memory for SD\n", 0);
                  } 

                  // Free the ACL structure
                  LocalFree(pACL);
               }
               else
               {
                  DbgTrace(0, "-InitializeAuthCache- SetEntriesInAcl Error %d\n", status);
               }
            }
            else
            {
               DbgTrace(0, "-InitializeAuthCache- Error obtaining token information, error = %d\n", GetLastError());
            }

            // Release the process token handle
            CloseHandle(hToken);
         }
         else
         {
            DbgTrace(0, "-InitializeAuthCache- Unable to obtain process token, error = %d\n", GetLastError());
         }

         // Free the SID for the administrator
         FreeSid(pAdminSID);
      }
      else
      {
         DbgTrace(0, "-InitializeAuthCache- AllocateAndInitializeSid Error %d\n", GetLastError());
      }

      // Free the SID for everyone
      FreeSid(pEveryoneSID);
   }
   else
   {
      DbgTrace(0, "-InitializeAuthCache- AllocateAndInitializeSid Error = %d\n", GetLastError());
   }

   DbgTrace(1, "-InitializeAuthCache- End, retStatus = %08X\n", retStatus);
   
   return retStatus;
}


//++=======================================================================
//++=======================================================================
//++=======================================================================