/*********************************************************************** * * 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 * Todd Throne * ***********************************************************************/ //===[ Include files ]===================================================== #include "internal.h" #include //===[ 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 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; 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); // 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 { 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 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; WrapperAuthCacheEntry *pWrapperEntry = NULL; AuthCacheEntry *pEntry = NULL; char *pKey; DbgTrace(1, "-FindAuthTokenEntryInCache- Start\n", 0); cacheKeyStrLen = strlen(pCacheKey); groupOrHostNameStrLen = strlen(pGroupOrHostName); // 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); 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); } //++======================================================================= //++======================================================================= //++=======================================================================