/*********************************************************************** * * 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 * ***********************************************************************/ //===[ 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 LIST_ENTRY g_authCacheListHead; // Non-host specific key name char g_allHosts[] = "AllHosts"; //++======================================================================= AuthCacheEntry* CreateAuthCacheEntry( IN char *pCacheKeyName, IN 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); } 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; } //++======================================================================= void FreeAuthCacheEntry( IN AuthCacheEntry *pEntry) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { DbgTrace(1, "-FreeAuthCacheEntry- Start\n", 0); // 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); 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* FindEntryInAuthCache( IN char *pCacheKeyName, IN 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 free it. RemoveEntryList(&pWrkEntry->listEntry); FreeAuthCacheEntry(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); } } 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\n", 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; } // 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); } else { // Not able to successfully add the entry to the cache, // free the entry. FreeAuthCacheEntry(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; } //++======================================================================= //++======================================================================= //++=======================================================================