/***********************************************************************
 * 
 *  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.
 * 
 *  Authors: Juan Carlos Luciani <jluciani@novell.com>
 *           Todd Throne <tthrone@novell.com>
 *
 ***********************************************************************/


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

#include "internal.h"
#include <micasa.h>

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

//
// Auth Cache Entry Wrapper definition 
//
typedef struct _WrapperAuthCacheEntry
{
   int               size;
   AuthCacheEntry    entry;
    
} WrapperAuthCacheEntry, *PWrapperAuthCacheEntry;

// Undocumented CASA Flags
#define CASA_SECRET_PERSIST_FLAG 0x10000000
#define CASA_SECRET_DO_NOT_PERSIST_FLAG 0x20000000


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

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

static
bool     g_authCacheInitialized = false;
HANDLE   g_hCASAContext;


//++=======================================================================
AuthCacheEntry*
CreateAuthTokenCacheEntry(
   IN    const char *pCacheKey,
   IN    const char *pGroupOrHostName,
   IN    const ATSHostEntry *pATSHost,
   IN    CasaStatus status,
   IN    char *pToken,
   IN    int entryLifetime, // seconds (0 == Lives forever)
   IN    void *pCredStoreScope
   )
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int32_t                 miCasaStatus;
   SSCS_KEYCHAIN_ID_T      sessionKeyChain = {26, "SSCS_SESSION_KEY_CHAIN_ID"};
   SSCS_SECRET_ID_T        sharedId = {27, "CASA_AUTHENTICATION_TOKENS"};
   uint32_t                entrySize, keySize;
   size_t                  tokenSize, wrapperEntrySize, cacheKeyStrLen, groupOrHostNameStrLen, hostAndPortStrLen;
   WrapperAuthCacheEntry   *pWrapperEntry = NULL;
   AuthCacheEntry          *pEntry = NULL;
   char                    *pKey;

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

   if (status == CASA_STATUS_SUCCESS)
   {
      tokenSize = (uint32_t) strlen(pToken);
   }
   else
   {
      tokenSize = 0;
   }

   wrapperEntrySize = tokenSize + sizeof(WrapperAuthCacheEntry);

   // Verify that entrySize will not overflow
   if ((tokenSize + sizeof(AuthCacheEntry)) <= UINT32_MAX)
   {
      entrySize = tokenSize + sizeof(AuthCacheEntry);

      // Allocate space for the entry wrapper
      // 
      // The WrapperAuthCacheEntry structure contains room for the tokens NULL terminator
      pWrapperEntry = (WrapperAuthCacheEntry*) malloc(wrapperEntrySize);
      if (pWrapperEntry)
      {
         // Save the entry size
         pWrapperEntry->size = wrapperEntrySize;

         // Set the AuthCacheEntry pointer
         pEntry = &pWrapperEntry->entry;

         // Set the status
         pEntry->status = status;

         if (pEntry->status == CASA_STATUS_SUCCESS)
         {
            memcpy(&pEntry->token[0], pToken, tokenSize);
         }

         pEntry->token[tokenSize] = '\0';

         // 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;
         }

         cacheKeyStrLen = strlen(pCacheKey);
         groupOrHostNameStrLen = strlen(pGroupOrHostName);

         // Build the cache entry key based on the status
         if (status == CASA_STATUS_SUCCESS)
         {
            // Successful cache entries have a key of the form
            // cachekey@group_or_host_name.
            //
            // Verify that keySize will not overflow
            if ((cacheKeyStrLen + groupOrHostNameStrLen + 2) <= UINT32_MAX)
            {
               keySize = (uint32_t) (cacheKeyStrLen + groupOrHostNameStrLen + 2);

               pKey = malloc(keySize);
               if (pKey)
               {
                  strncpy(pKey, pCacheKey, keySize);
                  strncat(pKey, "@", keySize);
                  strncat(pKey, pGroupOrHostName, keySize);

                  miCasaStatus = miCASAWriteBinaryKey(g_hCASAContext,
                                                      CASA_SECRET_DO_NOT_PERSIST_FLAG,
                                                      &sessionKeyChain,
                                                      &sharedId,
                                                      (SS_UTF8_T*) pKey,
                                                      keySize,
                                                      (uint8_t *) pEntry,
                                                      &entrySize,
                                                      NULL,
                                                      (SSCS_EXT_T*) pCredStoreScope);
                  if (miCasaStatus != NSSCS_SUCCESS)
                  {
                     DbgTrace(0, "-CreateAuthTokenCacheEntry- miCASAWriteBinaryKey failure, status = %0X\n", miCasaStatus);
                  }

                  free(pKey);
               }
               else
               {
                  DbgTrace(0, "-CreateAuthTokenCacheEntry- Memory allocation failure\n", 0);
               }
            }
            else
            {
               DbgTrace(0, "-CreateAuthTokenCacheEntry- keySize overflow prevented\n", 0);
            }
         }
         else
         {
            // Unsuccessful cache entries have a key of the form
            // cachekey@group_or_host_name@ATSHostAddress.
            //
            // Verify that keySize will not overflow
            hostAndPortStrLen = strlen(pATSHost->pNameAndPort);
            if ((cacheKeyStrLen + groupOrHostNameStrLen + hostAndPortStrLen + 3) <= UINT32_MAX)
            {
               keySize = (uint32_t) (cacheKeyStrLen + groupOrHostNameStrLen + hostAndPortStrLen + 3);

               pKey = malloc(keySize);
               if (pKey)
               {
                  strncpy(pKey, pCacheKey, keySize);
                  strncat(pKey, "@", keySize);
                  strncat(pKey, pGroupOrHostName, keySize);
                  strncat(pKey, "@", keySize);
                  strncat(pKey, pATSHost->pNameAndPort, keySize);

                  miCasaStatus = miCASAWriteBinaryKey(g_hCASAContext,
                                                      CASA_SECRET_DO_NOT_PERSIST_FLAG,
                                                      &sessionKeyChain,
                                                      &sharedId,
                                                      (SS_UTF8_T*) pKey,
                                                      keySize,
                                                      (uint8_t *) pEntry,
                                                      &entrySize,
                                                      NULL,
                                                      (SSCS_EXT_T*) pCredStoreScope);
                  if (miCasaStatus != NSSCS_SUCCESS)
                  {
                     DbgTrace(0, "-CreateAuthTokenCacheEntry- miCASAWriteBinaryKey failure, status = %0X\n", miCasaStatus);
                  }

                  free(pKey);
               }
               else
               {
                  DbgTrace(0, "-CreateAuthTokenCacheEntry- Memory allocation failure\n", 0);
               }
            }
            else
            {
               DbgTrace(0, "-CreateAuthTokenCacheEntry- keySize overflow prevented\n", 0);
            }
         }
      }
      else
      {
         DbgTrace(0, "-CreateAuthTokenCacheEntry- Memory allocation failure\n", 0);
      }
   }
   else
   {
      DbgTrace(0, "-CreateAuthTokenCacheEntry- entrySize overflow prevented\n", 0);
   }

   DbgTrace(1, "-CreateAuthTokenCacheEntry- End, pEntry = 0x%X\n", pEntry);

   return pEntry;
}


//++=======================================================================
AuthCacheEntry*
CreateSessionTokenCacheEntry(
   IN    const char *pCacheKey,
   IN    CasaStatus status,
   IN    char *pToken,
   IN    int entryLifetime, // seconds (0 == Lives forever)
   IN    void *pCredStoreScope
   )
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int32_t                 miCasaStatus;
   SSCS_KEYCHAIN_ID_T      sessionKeyChain = {26, "SSCS_SESSION_KEY_CHAIN_ID"};
   SSCS_SECRET_ID_T        sharedId = {20, "CASA_SESSION_TOKENS"};
   uint32_t                 entrySize;
   size_t                  tokenSize, wrapperEntrySize, cacheKeyStrLen;
   WrapperAuthCacheEntry   *pWrapperEntry = NULL;
   AuthCacheEntry          *pEntry = NULL;

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

   if (status == CASA_STATUS_SUCCESS)
   {
      tokenSize = (uint32_t)strlen(pToken);
   }
   else
   {
      tokenSize = 0;
   }

   wrapperEntrySize = tokenSize + sizeof(WrapperAuthCacheEntry);

   // Verify that entrySize will not overflow
   if ((tokenSize + sizeof(AuthCacheEntry)) <= UINT32_MAX)
   {
      entrySize = tokenSize + sizeof(AuthCacheEntry);

      // Allocate space for the entry wrapper
      // 
      // The WrapperAuthCacheEntry structure contains room for the tokens NULL terminator
      pWrapperEntry = (WrapperAuthCacheEntry*) malloc(wrapperEntrySize);
      if (pWrapperEntry)
      {
         // Save the entry size
         pWrapperEntry->size = wrapperEntrySize;

         // Set the AuthCacheEntry pointer
         pEntry = &pWrapperEntry->entry;

         // Set the status
         pEntry->status = status;

         if (pEntry->status == CASA_STATUS_SUCCESS)
         {
            memcpy(&pEntry->token[0], pToken, tokenSize);
         }

         pEntry->token[tokenSize] = '\0';

         // 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;
         }

         cacheKeyStrLen = strlen(pCacheKey) + 1;

         // Verify that the cacheKeyStrLen can be casted to a uint32_t
         if (cacheKeyStrLen <= UINT32_MAX)
         {
            miCasaStatus = miCASAWriteBinaryKey(g_hCASAContext,
                                                CASA_SECRET_DO_NOT_PERSIST_FLAG,
                                                &sessionKeyChain,
                                                &sharedId,
                                                (SS_UTF8_T*) pCacheKey,
                                                (uint32_t) cacheKeyStrLen,
                                                (uint8_t *) pEntry,
                                                &entrySize,
                                                NULL,
                                                (SSCS_EXT_T*) pCredStoreScope);
            if (miCasaStatus != NSSCS_SUCCESS)
            {
               DbgTrace(0, "-CreateSessionTokenCacheEntry- miCASAWriteBinaryKey failure, status = %0X\n", miCasaStatus);
            }
         }
         else
         {
            DbgTrace(0, "-CreateSessionTokenCacheEntry- cacheKeyStrLen overflow prevented\n", 0);
         }
      }
      else
      {
         DbgTrace(0, "-CreateSessionTokenCacheEntry- Memory allocation failure\n", 0);
      }
   }
   else
   {
      DbgTrace(0, "-CreateSessionTokenCacheEntry- entrySize overflow prevented\n", 0);
   }

   DbgTrace(1, "-CreateSessionTokenCacheEntry- End, pEntry = 0x%X\n", pEntry);

   return pEntry;
}


//++=======================================================================
void
FreeAuthCacheEntry(
   IN    AuthCacheEntry *pEntry
   )
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   WrapperAuthCacheEntry   *pWrapperEntry = CONTAINING_RECORD(pEntry, WrapperAuthCacheEntry, entry);

   DbgTrace(1, "-FreeAuthCacheEntry- Start, pEntry = 0x%X\n", pEntry);

   // Free the entry after clearing the memory holding it since it
   // may contain security sensitive data.
   memset(pWrapperEntry, 0, pWrapperEntry->size);
   free(pWrapperEntry);

   DbgTrace(1, "-FreeAuthCacheEntry- 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*
FindSessionTokenEntryInCache(
   IN    const char *pCacheKey,
   IN    void *pCredStoreScope
   )
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int32_t                 miCasaStatus;
   SSCS_KEYCHAIN_ID_T      sessionKeyChain = {26, "SSCS_SESSION_KEY_CHAIN_ID"};
   SSCS_SECRET_ID_T        sharedId = {20, "CASA_SESSION_TOKENS"};
   uint32_t                valueLength, bytesRequired;
   size_t                  wrapperEntrySize, cacheKeyStrLen;
   WrapperAuthCacheEntry   *pWrapperEntry = NULL;
   AuthCacheEntry          *pEntry = NULL;

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

   valueLength = 0;
   bytesRequired = 0;

   cacheKeyStrLen = strlen(pCacheKey) + 1;

   // Verify that the cacheKeyStrLen can be casted to a uint32_t
   if (cacheKeyStrLen <= UINT32_MAX)
   {
      miCasaStatus = miCASAReadBinaryKey(g_hCASAContext,
                                         0,
                                         &sessionKeyChain,
                                         &sharedId,
                                         (SS_UTF8_T*) pCacheKey,
                                         cacheKeyStrLen,
                                         NULL,
                                         &valueLength,
                                         (SSCS_PASSWORD_T*) NULL,
                                         &bytesRequired,
                                         (SSCS_EXT_T*) pCredStoreScope);

      if (miCasaStatus == NSSCS_E_ENUM_BUFF_TOO_SHORT
          && bytesRequired != 0)
      {
         wrapperEntrySize = bytesRequired + sizeof(WrapperAuthCacheEntry) - sizeof(AuthCacheEntry);
         pWrapperEntry = (WrapperAuthCacheEntry*) malloc(wrapperEntrySize);
         if (pWrapperEntry)
         {
            pWrapperEntry->size = wrapperEntrySize;
            pEntry = &pWrapperEntry->entry;
            valueLength = bytesRequired;
            bytesRequired = 0;

            miCasaStatus = miCASAReadBinaryKey(g_hCASAContext,
                                               0,
                                               &sessionKeyChain,
                                               &sharedId,
                                               (SS_UTF8_T*) pCacheKey,
                                               cacheKeyStrLen,
                                               (uint8_t *) pEntry,
                                               &valueLength,
                                               (SSCS_PASSWORD_T*) NULL,
                                               &bytesRequired,
                                               (SSCS_EXT_T*) pCredStoreScope);
            if (miCasaStatus == NSSCS_SUCCESS)
            {
               if (pEntry->doesNotExpire == false
                   && CacheEntryLifetimeExpired(pEntry->creationTime, pEntry->expirationTime))
               {
                  // Remove the entry from the cache
                  miCasaStatus = miCASARemoveKey(g_hCASAContext,
                                                 0,
                                                 &sessionKeyChain,
                                                 &sharedId,
                                                 (SS_UTF8_T*) pCacheKey,
                                                 cacheKeyStrLen,
                                                 (SSCS_PASSWORD_T*) NULL,
                                                 (SSCS_EXT_T*) pCredStoreScope);
                  if (miCasaStatus != NSSCS_SUCCESS)
                  {
                     DbgTrace(0, "-FindSessionTokenEntryInCache- miCASARemoveKey error = %0X\n", miCasaStatus);
                  }

                  FreeAuthCacheEntry(pEntry);
                  pEntry = NULL;
               }
            }
            else
            {
               DbgTrace(0, "-FindSessionTokenEntryInCache- miCASAReadBinaryKey error = %0X\n", miCasaStatus);
               FreeAuthCacheEntry(pEntry);
               pEntry = NULL;
            }
         }
      }
   }
   else
   {
      DbgTrace(0, "-FindSessionTokenEntryInCache- cacheKeyStrLen overflow prevented\n", 0);
   }
              
   DbgTrace(1, "-FindSessionTokenEntryInCache- End, pEntry = 0x%X\n", pEntry);

   return pEntry;
}

//++=======================================================================
AuthCacheEntry*
FindAuthTokenEntryInCache(
   IN    const char *pCacheKey,
   IN    const char *pGroupOrHostName,
   IN    const ATSHostEntry *pATSHost,
   IN    void *pCredStoreScope
   )
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int32_t                 miCasaStatus;
   SSCS_KEYCHAIN_ID_T      sessionKeyChain = {26, "SSCS_SESSION_KEY_CHAIN_ID"};
   SSCS_SECRET_ID_T        sharedId = {27, "CASA_AUTHENTICATION_TOKENS"};
   uint32_t                valueLength, bytesRequired, keySize;
   size_t                  wrapperEntrySize, cacheKeyStrLen, groupOrHostNameStrLen, hostAndPortStrLen;
   WrapperAuthCacheEntry   *pWrapperEntry = NULL;
   AuthCacheEntry          *pEntry = NULL;
   char                    *pKey;


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

   cacheKeyStrLen = strlen(pCacheKey);
   groupOrHostNameStrLen = strlen(pGroupOrHostName);
   hostAndPortStrLen = strlen(pATSHost->pNameAndPort);

   // Verify that the worst case keySize will not overflow
   if ((cacheKeyStrLen + groupOrHostNameStrLen + hostAndPortStrLen + 3) <= UINT32_MAX)
   {
      // Allocate space for the worst case key
      keySize = (uint32_t) (cacheKeyStrLen + groupOrHostNameStrLen + hostAndPortStrLen + 3);

      pKey = malloc(keySize);
      if (pKey)
      {
         // First try to read entry using key for successful cache entries
         strncpy(pKey, pCacheKey, keySize);
         strncat(pKey, "@", keySize);
         strncat(pKey, pGroupOrHostName, keySize);

         valueLength = 0;
         bytesRequired = 0;

         miCasaStatus = miCASAReadBinaryKey(g_hCASAContext,
                                            0,
                                            &sessionKeyChain,
                                            &sharedId,
                                            (SS_UTF8_T*) pKey,
                                            strlen(pKey) + 1,
                                            NULL,
                                            &valueLength,
                                            (SSCS_PASSWORD_T*) NULL,
                                            &bytesRequired,
                                            (SSCS_EXT_T*) pCredStoreScope);
         if (miCasaStatus == NSSCS_E_ENUM_BUFF_TOO_SHORT
             && bytesRequired != 0)
         {
            wrapperEntrySize = bytesRequired + sizeof(WrapperAuthCacheEntry) - sizeof(AuthCacheEntry);
            pWrapperEntry = (WrapperAuthCacheEntry*) malloc(wrapperEntrySize);
            if (pWrapperEntry)
            {
               pWrapperEntry->size = wrapperEntrySize;
               pEntry = &pWrapperEntry->entry;
               valueLength = bytesRequired;
               bytesRequired = 0;

               miCasaStatus = miCASAReadBinaryKey(g_hCASAContext,
                                                  0,
                                                  &sessionKeyChain,
                                                  &sharedId,
                                                  (SS_UTF8_T*) pKey,
                                                  strlen(pKey) + 1,
                                                  (uint8_t *) pEntry,
                                                  &valueLength,
                                                  (SSCS_PASSWORD_T*) NULL,
                                                  &bytesRequired,
                                                  (SSCS_EXT_T*) pCredStoreScope);
               if (miCasaStatus == NSSCS_SUCCESS)
               {
                  if (pEntry->doesNotExpire == false
                      && CacheEntryLifetimeExpired(pEntry->creationTime, pEntry->expirationTime))
                  {
                     // Remove the entry from the cache
                     miCasaStatus = miCASARemoveKey(g_hCASAContext,
                                                    0,
                                                    &sessionKeyChain,
                                                    &sharedId,
                                                    (SS_UTF8_T*) pKey,
                                                     keySize,
                                                    (SSCS_PASSWORD_T*) NULL,
                                                    (SSCS_EXT_T*) pCredStoreScope);
                     if (miCasaStatus != NSSCS_SUCCESS)
                     {
                        DbgTrace(0, "-FindAuthTokenEntryInCache- miCASARemoveKey error = %0X\n", miCasaStatus);
                     }

                     FreeAuthCacheEntry(pEntry);
                     pEntry = NULL;
                  }
               }
               else
               {
                  DbgTrace(0, "-FindAuthTokenEntryInCache- miCASAReadBinaryKey error = %0X\n", miCasaStatus);
                  FreeAuthCacheEntry(pEntry);
                  pEntry = NULL;
               }
            }
         }
         else
         {
            // We failed to obtain a cache entry using key for successful cache entry, try for using key for
            // unsuccessful cache entry.
            strncat(pKey, "@", keySize);
            strncat(pKey, pATSHost->pNameAndPort, keySize);

            valueLength = 0;
            bytesRequired = 0;

            miCasaStatus = miCASAReadBinaryKey(g_hCASAContext,
                                               0,
                                               &sessionKeyChain,
                                               &sharedId,
                                               (SS_UTF8_T*) pKey,
                                               keySize,
                                               NULL,
                                               &valueLength,
                                               (SSCS_PASSWORD_T*) NULL,
                                               &bytesRequired,
                                               (SSCS_EXT_T*) pCredStoreScope);
            if (miCasaStatus == NSSCS_E_ENUM_BUFF_TOO_SHORT
                && bytesRequired != 0)
            {
               wrapperEntrySize = bytesRequired + sizeof(WrapperAuthCacheEntry) - sizeof(AuthCacheEntry);
               pWrapperEntry = (WrapperAuthCacheEntry*) malloc(wrapperEntrySize);
               if (pWrapperEntry)
               {
                  pWrapperEntry->size = wrapperEntrySize;
                  pEntry = &pWrapperEntry->entry;
                  valueLength = bytesRequired;
                  bytesRequired = 0;

                  miCasaStatus = miCASAReadBinaryKey(g_hCASAContext,
                                                     0,
                                                     &sessionKeyChain,
                                                     &sharedId,
                                                     (SS_UTF8_T*) pKey,
                                                     keySize,
                                                     (uint8_t *) pEntry,
                                                     &valueLength,
                                                     (SSCS_PASSWORD_T*) NULL,
                                                     &bytesRequired,
                                                     (SSCS_EXT_T*) pCredStoreScope);
                  if (miCasaStatus == NSSCS_SUCCESS)
                  {
                     if (pEntry->doesNotExpire == false
                         && CacheEntryLifetimeExpired(pEntry->creationTime, pEntry->expirationTime))
                     {
                        // Remove the entry from the cache
                        miCasaStatus = miCASARemoveKey(g_hCASAContext,
                                                       0,
                                                       &sessionKeyChain,
                                                       &sharedId,
                                                       (SS_UTF8_T*) pKey,
                                                        keySize,
                                                       (SSCS_PASSWORD_T*) NULL,
                                                       (SSCS_EXT_T*) pCredStoreScope);
                        if (miCasaStatus != NSSCS_SUCCESS)
                        {
                           DbgTrace(0, "-FindAuthTokenEntryInCache- miCASARemoveKey error = %0X\n", miCasaStatus);
                        }

                        FreeAuthCacheEntry(pEntry);
                        pEntry = NULL;
                     }
                  }
                  else
                  {
                     DbgTrace(0, "-FindAuthTokenEntryInCache- miCASAReadBinaryKey error = %0X\n", miCasaStatus);
                     FreeAuthCacheEntry(pEntry);
                     pEntry = NULL;
                  }
               }
            }
         }

         free(pKey);
      }
   }
   else
   {
      DbgTrace(0, "-FindAuthTokenEntryInCache- keySize overflow prevented\n", 0);
   }

   DbgTrace(1, "-FindAuthTokenEntryInCache- End, pEntry = 0x%X\n", pEntry);

   return pEntry;
}


//++=======================================================================
void
RemoveSessionTokenEntryInCache(
   IN    const char *pCacheKey,
   IN    void *pCredStoreScope
   )
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int32_t                 miCasaStatus;
   SSCS_KEYCHAIN_ID_T      sessionKeyChain = {26, "SSCS_SESSION_KEY_CHAIN_ID"};
   SSCS_SECRET_ID_T        sharedId = {20, "CASA_SESSION_TOKENS"};

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

   // Remove the entry from the cache
   miCasaStatus = miCASARemoveKey(g_hCASAContext,
                                  0,
                                  &sessionKeyChain,
                                  &sharedId,
                                  (SS_UTF8_T*) pCacheKey,
                                  (uint32_t) strlen(pCacheKey) + 1,
                                  (SSCS_PASSWORD_T*) NULL,
                                  (SSCS_EXT_T*) pCredStoreScope);
   if (miCasaStatus != NSSCS_SUCCESS)
   {
      DbgTrace(0, "-RemoveSessionTokenEntryInCache- miCASARemoveKey error = %0X\n", miCasaStatus);
   }
              
   DbgTrace(1, "-RemoveSessionTokenEntryInCache- End\n", 0);
}


//++=======================================================================
void
DeleteAuthTokenEntriesInCache(
   IN    void *pCredStoreScope
)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int32_t                 miCasaStatus;
   SSCS_SECRET_ID_T        sharedId = {27, "CASA_AUTHENTICATION_TOKENS"};

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

   // Remove all of the auth tokens from the cache
   miCasaStatus = miCASARemoveCredential(0,
                                         &sharedId,
                                         (SSCS_SECRET_ID_T*) NULL,
                                         (SSCS_EXT_T*) pCredStoreScope);
   if (miCasaStatus != NSSCS_SUCCESS)
   {
      DbgTrace(0, "-DeleteAuthTokenEntriesInCache- miCASADeleteCredential error = %0X\n", miCasaStatus);
   }

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


//++=======================================================================
void
DeleteSessionTokenEntriesInCache(
   IN    void *pCredStoreScope
)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int32_t                 miCasaStatus;
   SSCS_SECRET_ID_T        sharedId = {20, "CASA_SESSION_TOKENS"};

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

   // Remove all of the auth tokens from the cache
   miCasaStatus = miCASARemoveCredential(0,
                                         &sharedId,
                                         (SSCS_SECRET_ID_T*) NULL,
                                         (SSCS_EXT_T*) pCredStoreScope);
   if (miCasaStatus != NSSCS_SUCCESS)
   {
      DbgTrace(0, "-DeleteSessionTokenEntriesInCache- miCASADeleteCredential error = %0X\n", miCasaStatus);
   }

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


//++=======================================================================
CasaStatus
InitializeAuthCache()
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   CasaStatus           retStatus;
   SSCS_SECRETSTORE_T   ssId;

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

   ssId.version = NSSCS_VERSION_NUMBER;
   strcpy((char *)ssId.ssName, (char *)SSCS_DEFAULT_SECRETSTORE_ID);

   g_hCASAContext = miCASAOpenSecretStoreCache(&ssId,
                                               0,
                                               NULL);
   if (!g_hCASAContext)
   {
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_UNSUCCESSFUL);
   }
   else
   {
      g_authCacheInitialized = true;
      retStatus = CASA_STATUS_SUCCESS;
   }

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

   return retStatus;
}


//++=======================================================================
void
UnInitializeAuthCache(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   DbgTrace(1, "-UnInitializeAuthCache- Start\n", 0);

   // Proceed if initialized
   if (g_authCacheInitialized)
   {
      // Close the secret store cache
      miCASACloseSecretStoreCache(g_hCASAContext,
                                  0,
                                  NULL);

      // Forget about being initialized
      g_hCASAContext = NULL;
      g_authCacheInitialized = false;
   }

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


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