DWORD UmnSrvReadConfig( PUMN_SRV_API_CONFIG *ppConfig ) { DWORD dwError = 0; PUMN_SRV_API_CONFIG pConfig = NULL; dwError = UmnSrvInitConfig(&pConfig); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvReadAllocatedConfig(pConfig); BAIL_ON_UMN_ERROR(dwError); UMN_LOG_VERBOSE("CheckInterval = %d seconds\n", pConfig->CheckInterval); UMN_LOG_VERBOSE("SkipNoLogin = %d\n", pConfig->SkipNoLogin); *ppConfig = pConfig; cleanup: return dwError; error: UmnSrvFreeConfig(pConfig); *ppConfig = NULL; goto cleanup; }
NTSTATUS SvcmStop( PLW_SVCM_INSTANCE pInstance ) { UMN_LOG_VERBOSE("Shutting down threads"); UmnSrvStopPollerThread(); UmnSrvFreeConfig(gpAPIConfig); LW_ASSERT(pthread_rwlock_destroy(&gUmnConfigLock) == 0); UMN_LOG_INFO("Usermonitor Service exiting..."); return 0; }
NTSTATUS SvcmRefresh( PLW_SVCM_INSTANCE pInstance ) { DWORD dwError = 0; UMN_LOG_VERBOSE("Refreshing configuration"); dwError = UmnSrvRefreshConfiguration(); if (dwError) { UMN_LOG_WARNING("Failed refreshing configuration: error %d", dwError); } else { UMN_LOG_INFO("Succeeded refreshing configuration"); } return dwError; }
DWORD UmnSrvUpdateUsers( HANDLE hLsass, PLW_EVENTLOG_CONNECTION pEventlog, HANDLE hReg, HKEY hParameters, long long PreviousRun, long long Now ) { DWORD uid = 0; DWORD dwError = 0; struct passwd *pUser = NULL; LSA_QUERY_LIST list = { 0 }; PLSA_SECURITY_OBJECT* ppObjects = NULL; HKEY hUsers = NULL; BOOLEAN skipNoLogin = FALSE; dwError = UmnSrvGetSkipNoLogin( &skipNoLogin); BAIL_ON_UMN_ERROR(dwError); list.pdwIds = &uid; dwError = RegOpenKeyExA( hReg, hParameters, "Users", 0, KEY_ALL_ACCESS, &hUsers); BAIL_ON_UMN_ERROR(dwError); while ((pUser = getpwent()) != NULL) { if (skipNoLogin && pUser->pw_shell && (!strcmp(pUser->pw_shell, "/sbin/nologin") || !strcmp(pUser->pw_shell, "/bin/nologin") || !strcmp(pUser->pw_shell, "/usr/sbin/nologin") || !strcmp(pUser->pw_shell, "/usr/bin/false") || !strcmp(pUser->pw_shell, "/bin/false"))) { UMN_LOG_VERBOSE("Skipping enumerated user '%s' (uid %d) because their shell prevents them from logging in.", LW_SAFE_LOG_STRING(pUser->pw_name), uid); continue; } uid = pUser->pw_uid; dwError = LsaFindObjects( hLsass, NULL, 0, LSA_OBJECT_TYPE_USER, LSA_QUERY_TYPE_BY_UNIX_ID, 1, list, &ppObjects); if (dwError == ERROR_SUCCESS && ppObjects && ppObjects[0] && ppObjects[0]->enabled && ppObjects[0]->userInfo.pszUnixName && pUser->pw_name && !strcmp(ppObjects[0]->userInfo.pszUnixName, pUser->pw_name)) { UMN_LOG_VERBOSE("Skipping enumerated user '%s' (uid %d) because they came from lsass", LW_SAFE_LOG_STRING(pUser->pw_name), uid); } else { dwError = UmnSrvUpdateUser( pEventlog, hReg, hUsers, PreviousRun, Now, pUser); BAIL_ON_UMN_ERROR(dwError); } if (ppObjects) { LsaFreeSecurityObjectList(1, ppObjects); ppObjects = NULL; } } dwError = UmnSrvFindDeletedUsers( pEventlog, hReg, "Users", hUsers, Now); BAIL_ON_UMN_ERROR(dwError); cleanup: if (ppObjects) { LsaFreeSecurityObjectList(1, ppObjects); } if (hUsers) { RegCloseKey(hReg, hUsers); } return dwError; error: goto cleanup; }
DWORD UmnSrvReadUser( PCSTR pParentKey, PCSTR pName, PUSER_MONITOR_PASSWD pResult ) { DWORD dwError = 0; PSTR pUserPath = NULL; LWREG_CONFIG_ITEM userLayout[] = { { "pw_name", FALSE, LwRegTypeString, 0, -1, NULL, &pResult->pw_name, NULL }, { "pw_passwd", FALSE, LwRegTypeString, 0, -1, NULL, &pResult->pw_passwd, NULL }, { "pw_uid", FALSE, LwRegTypeDword, 0, -1, NULL, &pResult->pw_uid, NULL }, { "pw_gid", FALSE, LwRegTypeDword, 0, -1, NULL, &pResult->pw_gid, NULL }, { "pw_gecos", FALSE, LwRegTypeString, 0, -1, NULL, &pResult->pw_gecos, NULL }, { "pw_dir", FALSE, LwRegTypeString, 0, -1, NULL, &pResult->pw_dir, NULL }, { "pw_shell", FALSE, LwRegTypeString, 0, -1, NULL, &pResult->pw_shell, NULL }, { "pDisplayName", FALSE, LwRegTypeString, 0, -1, NULL, &pResult->pDisplayName, NULL }, { "LastUpdated", FALSE, LwRegTypeDword, 0, -1, NULL, &pResult->LastUpdated, NULL }, }; UMN_LOG_VERBOSE("Reading previous values for user '%s'", pName); dwError = LwAllocateStringPrintf( &pUserPath, "Services\\" SERVICE_NAME "\\Parameters\\%s\\%s", pParentKey, pName); BAIL_ON_UMN_ERROR(dwError); dwError = LwRegProcessConfig( pUserPath, NULL, userLayout, sizeof(userLayout)/sizeof(userLayout[0])); BAIL_ON_UMN_ERROR(dwError); cleanup: LW_SAFE_FREE_STRING(pUserPath); return dwError; error: goto cleanup; }
static DWORD UmnSrvUpdateADAccountsByHash( HANDLE hLsass, PLW_EVENTLOG_CONNECTION pEventlog, HANDLE hReg, HKEY hParameters, PLW_HASH_TABLE pUsers, long long PreviousRun, long long Now ) { DWORD dwError = 0; HKEY hUsers = NULL; HKEY hGroups = NULL; LW_HASH_ITERATOR usersIterator = { 0 }; LW_HASH_ENTRY* pEntry = NULL; DWORD groupSidCount = 0; PSTR* ppGroupSids = NULL; DWORD lookupGroupSidCount = 0; DWORD lookupGroupSidCapacity = 0; // Only free the first level of this array, do not free the strings it // points to. PSTR* ppLookupGroupSids = NULL; LSA_QUERY_LIST list = { 0 }; PLSA_SECURITY_OBJECT *ppLookedupGroups = NULL; PLW_HASH_TABLE pGroups = NULL; PLW_HASH_TABLE pNameToUser = NULL; PLW_HASH_TABLE pNameToGroup = NULL; DWORD i = 0; // Do not free PLSA_SECURITY_OBJECT pGroup = NULL; // Do not free PLSA_SECURITY_OBJECT pExisting = NULL; PSTR pNewName = NULL; dwError = LwHashCreate( 100, LwHashStringCompare, LwHashStringHash, UmnSrvHashFreeObjectValue, NULL, &pGroups); BAIL_ON_UMN_ERROR(dwError); dwError = LwHashCreate( 100, LwHashStringCompare, LwHashStringHash, NULL, NULL, &pNameToGroup); BAIL_ON_UMN_ERROR(dwError); dwError = LwHashCreate( pUsers->sCount * 2, LwHashStringCompare, LwHashStringHash, NULL, NULL, &pNameToUser); BAIL_ON_UMN_ERROR(dwError); dwError = RegOpenKeyExA( hReg, hParameters, "AD Users", 0, KEY_ALL_ACCESS, &hUsers); BAIL_ON_UMN_ERROR(dwError); dwError = RegOpenKeyExA( hReg, hParameters, "AD Groups", 0, KEY_ALL_ACCESS, &hGroups); BAIL_ON_UMN_ERROR(dwError); dwError = LwHashGetIterator( pUsers, &usersIterator); BAIL_ON_UMN_ERROR(dwError); while((pEntry = LwHashNext(&usersIterator)) != NULL) { PLSA_SECURITY_OBJECT pUser = (PLSA_SECURITY_OBJECT)pEntry->pValue; if (gbPollerThreadShouldExit) { dwError = ERROR_CANCELLED; BAIL_ON_UMN_ERROR(dwError); } dwError = LwHashGetValue( pNameToUser, pUser->userInfo.pszUnixName, (PVOID*)&pExisting); if (dwError != ERROR_NOT_FOUND) { BAIL_ON_UMN_ERROR(dwError); dwError = LwAllocateStringPrintf( &pNewName, "%s\\%s", pUser->pszNetbiosDomainName, pUser->pszSamAccountName); BAIL_ON_UMN_ERROR(dwError); UMN_LOG_ERROR("Found conflict on user name '%hhs'. Sid %hhs will now be reported as name '%s' instead because its alias conflicts with sid %hhs.", pUser->userInfo.pszUnixName, pUser->pszObjectSid, pNewName, pExisting->pszObjectSid); BAIL_ON_UMN_ERROR(dwError); LW_SAFE_FREE_STRING(pUser->userInfo.pszUnixName); pUser->userInfo.pszUnixName = pNewName; pNewName = NULL; } dwError = LwHashSetValue( pNameToUser, pUser->userInfo.pszUnixName, pUser); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvUpdateADUser( pEventlog, hReg, hUsers, PreviousRun, Now, pUser); if (dwError == ERROR_NO_UNICODE_TRANSLATION) { // Error message already logged dwError = 0; continue; } BAIL_ON_UMN_ERROR(dwError); if (ppGroupSids) { LsaFreeSidList( groupSidCount, ppGroupSids); } dwError = LsaQueryMemberOf( hLsass, NULL, 0, 1, &pUser->pszObjectSid, &groupSidCount, &ppGroupSids); BAIL_ON_UMN_ERROR(dwError); if (groupSidCount > lookupGroupSidCapacity) { LW_SAFE_FREE_MEMORY(ppLookupGroupSids); dwError = LwAllocateMemory( groupSidCount * sizeof(ppLookupGroupSids[0]), (PVOID*)&ppLookupGroupSids); BAIL_ON_UMN_ERROR(dwError); lookupGroupSidCapacity = groupSidCount; } lookupGroupSidCount = 0; for (i = 0; i < groupSidCount; i++) { dwError = LwHashGetValue( pGroups, ppGroupSids[i], (PVOID*)&pGroup); if (dwError == ERROR_NOT_FOUND) { ppLookupGroupSids[lookupGroupSidCount++] = ppGroupSids[i]; } else { BAIL_ON_UMN_ERROR(dwError); UMN_LOG_VERBOSE("Found AD user %s is a member of processed group %s", pUser->userInfo.pszUnixName, pGroup->groupInfo.pszUnixName); if (!pGroup->enabled) { UMN_LOG_VERBOSE("Skipping unenabled group %s", pGroup->groupInfo.pszUnixName); } else { dwError = UmnSrvUpdateADGroupMember( pEventlog, hReg, hGroups, PreviousRun, Now, pGroup, pUser->userInfo.pszUnixName); BAIL_ON_UMN_ERROR(dwError); } } } if (lookupGroupSidCount) { list.ppszStrings = (PCSTR *)ppLookupGroupSids; dwError = LsaFindObjects( hLsass, NULL, 0, LSA_OBJECT_TYPE_GROUP, LSA_QUERY_TYPE_BY_SID, lookupGroupSidCount, list, &ppLookedupGroups); BAIL_ON_UMN_ERROR(dwError); for (i = 0; i < lookupGroupSidCount; i++) { if (gbPollerThreadShouldExit) { dwError = ERROR_CANCELLED; BAIL_ON_UMN_ERROR(dwError); } pGroup = ppLookedupGroups[i]; if (!pGroup) { UMN_LOG_ERROR("Unable to find group sid %s that user %s is a member of", ppLookupGroupSids[i], pUser->userInfo.pszUnixName); continue; } UMN_LOG_VERBOSE("Found AD user %s is a member of unprocessed group %s", pUser->userInfo.pszUnixName, pGroup->groupInfo.pszUnixName); dwError = LwHashGetValue( pNameToGroup, pGroup->groupInfo.pszUnixName, (PVOID*)&pExisting); if (dwError != ERROR_NOT_FOUND) { BAIL_ON_UMN_ERROR(dwError); dwError = LwAllocateStringPrintf( &pNewName, "%s\\%s", pGroup->pszNetbiosDomainName, pGroup->pszSamAccountName); BAIL_ON_UMN_ERROR(dwError); UMN_LOG_ERROR("Found conflict on group name '%hhs'. Sid %hhs will now be reported as name '%s' instead because its alias conflicts with sid %hhs.", pGroup->groupInfo.pszUnixName, pGroup->pszObjectSid, pNewName, pExisting->pszObjectSid); BAIL_ON_UMN_ERROR(dwError); LW_SAFE_FREE_STRING(pGroup->groupInfo.pszUnixName); pGroup->groupInfo.pszUnixName = pNewName; pNewName = NULL; } dwError = LwHashSetValue( pNameToGroup, pGroup->groupInfo.pszUnixName, pGroup); BAIL_ON_UMN_ERROR(dwError); if (!pGroup->enabled) { UMN_LOG_VERBOSE("Skipping unenabled group %s", pGroup->groupInfo.pszUnixName); } else { dwError = UmnSrvUpdateADGroup( pEventlog, hReg, hGroups, PreviousRun, Now, pGroup); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvUpdateADGroupMember( pEventlog, hReg, hGroups, PreviousRun, Now, pGroup, pUser->userInfo.pszUnixName); BAIL_ON_UMN_ERROR(dwError); } dwError = LwHashSetValue( pGroups, pGroup->pszObjectSid, pGroup); BAIL_ON_UMN_ERROR(dwError); ppLookedupGroups[i] = NULL; } LsaFreeSecurityObjectList( lookupGroupSidCount, ppLookedupGroups); ppLookedupGroups = NULL; } } dwError = UmnSrvFindDeletedUsers( pEventlog, hReg, "AD Users", hUsers, Now); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvFindDeletedGroups( pEventlog, hReg, "AD Groups", hGroups, Now); BAIL_ON_UMN_ERROR(dwError); cleanup: LW_SAFE_FREE_STRING(pNewName); LW_SAFE_FREE_MEMORY(ppLookupGroupSids); if (ppGroupSids) { LsaFreeSidList( groupSidCount, ppGroupSids); } if (ppLookedupGroups) { LsaFreeSecurityObjectList( lookupGroupSidCount, ppLookedupGroups); } if (hUsers) { RegCloseKey(hReg, hUsers); } if (hGroups) { RegCloseKey(hReg, hGroups); } LwHashSafeFree(&pGroups); LwHashSafeFree(&pNameToUser); LwHashSafeFree(&pNameToGroup); return dwError; error: goto cleanup; }
static DWORD UmnSrvAddUsersFromMembership( HANDLE hLsass, LW_HASH_TABLE *pUsers, PCSTR pLookup ) { DWORD dwError = 0; LSA_QUERY_LIST list; PLSA_SECURITY_OBJECT *ppObjects = NULL; PLSA_SECURITY_OBJECT *ppMembers = NULL; DWORD memberCount = 0; DWORD i = 0; list.ppszStrings = &pLookup; dwError = LsaFindObjects( hLsass, NULL, 0, LSA_OBJECT_TYPE_UNDEFINED, LSA_QUERY_TYPE_BY_NT4, 1, list, &ppObjects); switch(dwError) { // The string was not a valid NT4 name case LW_ERROR_INVALID_PARAMETER: // The user/group does not exist case LW_ERROR_NO_SUCH_OBJECT: goto cleanup; default: BAIL_ON_UMN_ERROR(dwError); } if (ppObjects[0] && ppObjects[0]->type == LSA_OBJECT_TYPE_USER) { if (ppObjects[0]->enabled && !LwHashExists(pUsers, ppObjects[0]->pszObjectSid)) { dwError = LwHashSetValue( pUsers, ppObjects[0]->pszObjectSid, ppObjects[0]); BAIL_ON_UMN_ERROR(dwError); ppObjects[0] = NULL; } } else if (ppObjects[0] && ppObjects[0]->type == LSA_OBJECT_TYPE_GROUP) { dwError = LsaQueryExpandedGroupMembers( hLsass, NULL, 0, LSA_OBJECT_TYPE_USER, ppObjects[0]->pszObjectSid, &memberCount, &ppMembers); BAIL_ON_UMN_ERROR(dwError); for (i = 0; i < memberCount; i++) { if (ppMembers[i]->enabled && !LwHashExists(pUsers, ppMembers[i]->pszObjectSid)) { UMN_LOG_VERBOSE("Found AD user %s that can login because of group %s", ppMembers[i]->userInfo.pszUnixName, pLookup); dwError = LwHashSetValue( pUsers, ppMembers[i]->pszObjectSid, ppMembers[i]); BAIL_ON_UMN_ERROR(dwError); ppMembers[i] = NULL; } } } cleanup: if (ppObjects) { LsaFreeSecurityObjectList( 1, ppObjects); } if (ppMembers) { LsaFreeSecurityObjectList( memberCount, ppMembers); } return dwError; error: goto cleanup; }