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 UmnSrvReadAllocatedConfig( PUMN_SRV_API_CONFIG pConfig ) { DWORD dwError = 0; LWREG_CONFIG_ITEM ConfigDescription[] = { { "CheckInterval", TRUE, LwRegTypeDword, 0, -1, NULL, &pConfig->CheckInterval, NULL }, { "SkipNoLogin", TRUE, LwRegTypeDword, 0, -1, NULL, &pConfig->SkipNoLogin, NULL }, }; UMN_LOG_INFO("Read user monitor configuration settings"); dwError = LwRegProcessConfig( "Services\\" SERVICE_NAME "\\Parameters", "Policy\\Services\\" SERVICE_NAME "\\Parameters", ConfigDescription, sizeof(ConfigDescription)/sizeof(ConfigDescription[0])); BAIL_ON_UMN_ERROR(dwError); error: return dwError; }
DWORD UmnSrvFindDeletedUsers( PLW_EVENTLOG_CONNECTION pEventlog, HANDLE hReg, PCSTR pUserKeyName, HKEY hUsers, long long Now ) { DWORD dwError = 0; DWORD subKeyCount = 0; DWORD maxSubKeyLen = 0; DWORD subKeyLen = 0; DWORD i = 0; PSTR pKeyName = NULL; DWORD lastUpdated = 0; DWORD lastUpdatedLen = 0; USER_MONITOR_PASSWD old = { 0 }; dwError = RegQueryInfoKeyA( hReg, hUsers, NULL, NULL, NULL, &subKeyCount, &maxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL); BAIL_ON_UMN_ERROR(dwError); dwError = LwAllocateMemory( maxSubKeyLen + 1, (PVOID *)&pKeyName); for (i = 0; i < subKeyCount; i++) { if (gbPollerThreadShouldExit) { dwError = ERROR_CANCELLED; BAIL_ON_UMN_ERROR(dwError); } subKeyLen = maxSubKeyLen; dwError = RegEnumKeyExA( hReg, hUsers, i, pKeyName, &subKeyLen, NULL, NULL, NULL, NULL); BAIL_ON_UMN_ERROR(dwError); pKeyName[subKeyLen] = 0; lastUpdatedLen = sizeof(lastUpdated); dwError = RegGetValueA( hReg, hUsers, pKeyName, "LastUpdated", 0, NULL, (PBYTE)&lastUpdated, &lastUpdatedLen); if (dwError == LWREG_ERROR_NO_SUCH_KEY_OR_VALUE) { UMN_LOG_WARNING("User %s not completely written. The user monitor service may have previously terminated ungracefully.", LW_SAFE_LOG_STRING(pKeyName)); lastUpdated = 0; dwError = 0; } else { BAIL_ON_UMN_ERROR(dwError); } if (lastUpdated < Now) { UmnSrvFreeUserContents(&old); dwError = UmnSrvReadUser( pUserKeyName, pKeyName, &old); BAIL_ON_UMN_ERROR(dwError); UMN_LOG_INFO("User '%s' deleted", old.pw_name); dwError = RegDeleteKeyA( hReg, hUsers, pKeyName); BAIL_ON_UMN_ERROR(dwError); if (!strcmp(pUserKeyName, "Users")) { dwError = UmnSrvWriteUserEvent( pEventlog, old.LastUpdated, &old, Now, NULL); BAIL_ON_UMN_ERROR(dwError); } else { dwError = UmnSrvWriteADUserEvent( pEventlog, old.LastUpdated, &old, Now, NULL); BAIL_ON_UMN_ERROR(dwError); } // Make sure we don't skip the next key since this one was deleted i--; subKeyCount--; } } cleanup: UmnSrvFreeUserContents(&old); LW_SAFE_FREE_STRING(pKeyName); return dwError; error: goto cleanup; }
static DWORD UmnSrvUpdateUser( PLW_EVENTLOG_CONNECTION pEventlog, HANDLE hReg, HKEY hUsers, long long PreviousRun, long long Now, struct passwd *pUser ) { DWORD dwError = 0; HKEY hKey = NULL; USER_MONITOR_PASSWD old = { 0 }; DWORD dwNow = Now; PSTR pEncodedUser = NULL; dwError = LwURLEncodeString( pUser->pw_name, &pEncodedUser); BAIL_ON_UMN_ERROR(dwError); dwError = RegOpenKeyExA( hReg, hUsers, pEncodedUser, 0, KEY_ALL_ACCESS, &hKey); if (dwError == LWREG_ERROR_NO_SUCH_KEY_OR_VALUE) { UMN_LOG_INFO("Adding user '%s' (uid %d)", pUser->pw_name, pUser->pw_uid); dwError = RegCreateKeyExA( hReg, hUsers, pEncodedUser, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, NULL); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvWriteUserValues( hReg, hKey, pUser); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvWriteUserEvent( pEventlog, PreviousRun, NULL, Now, pUser); BAIL_ON_UMN_ERROR(dwError); } else { BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvReadUser( "Users", pEncodedUser, &old); BAIL_ON_UMN_ERROR(dwError); if (strcmp(pUser->pw_name, old.pw_name) || strcmp(pUser->pw_passwd, old.pw_passwd) || pUser->pw_uid != old.pw_uid || pUser->pw_gid != old.pw_gid || strcmp(pUser->pw_gecos, old.pw_gecos) || strcmp(pUser->pw_dir, old.pw_dir) || strcmp(pUser->pw_shell, old.pw_shell)) { UMN_LOG_INFO("User '%s' (uid %d) changed", pUser->pw_name, pUser->pw_uid); dwError = UmnSrvWriteUserValues( hReg, hKey, pUser); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvWriteUserEvent( pEventlog, PreviousRun, &old, Now, pUser); BAIL_ON_UMN_ERROR(dwError); } } dwError = RegSetValueExA( hReg, hKey, "LastUpdated", 0, REG_DWORD, (PBYTE)&dwNow, sizeof(dwNow)); BAIL_ON_UMN_ERROR(dwError); cleanup: LW_SAFE_FREE_STRING(pEncodedUser); UmnSrvFreeUserContents(&old); if (hKey) { RegCloseKey( hReg, hKey); } return dwError; error: goto cleanup; }
static DWORD UmnSrvUpdateADUser( PLW_EVENTLOG_CONNECTION pEventlog, HANDLE hReg, HKEY hUsers, long long PreviousRun, long long Now, PLSA_SECURITY_OBJECT pUser ) { DWORD dwError = 0; HKEY hKey = NULL; USER_MONITOR_PASSWD old = { 0 }; DWORD dwNow = Now; PSTR pEncodedUser = NULL; dwError = LwURLEncodeString( pUser->userInfo.pszUnixName, &pEncodedUser); BAIL_ON_UMN_ERROR(dwError); dwError = RegOpenKeyExA( hReg, hUsers, pEncodedUser, 0, KEY_ALL_ACCESS, &hKey); if (dwError == LWREG_ERROR_NO_SUCH_KEY_OR_VALUE) { UMN_LOG_INFO("Adding user '%s' (uid %d)", pUser->userInfo.pszUnixName, pUser->userInfo.uid); dwError = RegCreateKeyExA( hReg, hUsers, pEncodedUser, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, NULL); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvWriteADUserValues( hReg, hKey, pUser); if (dwError == ERROR_NO_UNICODE_TRANSLATION) { UMN_LOG_ERROR("Ignoring user with URL encoding %s because one of their fields has no UCS-2 representation", pEncodedUser); // Delete the key so it does not show up with blank values next // time. dwError = RegCloseKey( hReg, hKey); BAIL_ON_UMN_ERROR(dwError); hKey = NULL; dwError = RegDeleteKeyA( hReg, hUsers, pEncodedUser); BAIL_ON_UMN_ERROR(dwError); dwError = ERROR_NO_UNICODE_TRANSLATION; BAIL_ON_UMN_ERROR(dwError); } BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvWriteADUserEvent( pEventlog, PreviousRun, NULL, Now, pUser); BAIL_ON_UMN_ERROR(dwError); } else { BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvReadUser( "AD Users", pEncodedUser, &old); BAIL_ON_UMN_ERROR(dwError); if (strcmp((pUser->userInfo.pszPasswd ? pUser->userInfo.pszPasswd : "x"), old.pw_passwd) || pUser->userInfo.uid != old.pw_uid || pUser->userInfo.gid != old.pw_gid || !UmnSrvStringsEqual(pUser->userInfo.pszGecos, old.pw_gecos) || !UmnSrvStringsEqual(pUser->userInfo.pszHomedir, old.pw_dir) || !UmnSrvStringsEqual(pUser->userInfo.pszShell, old.pw_shell) || !UmnSrvStringsEqual(pUser->userInfo.pszDisplayName, old.pDisplayName)) { UMN_LOG_INFO("User '%s' (uid %d) changed", pUser->userInfo.pszUnixName, pUser->userInfo.uid); dwError = UmnSrvWriteADUserValues( hReg, hKey, pUser); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvWriteADUserEvent( pEventlog, PreviousRun, &old, Now, pUser); BAIL_ON_UMN_ERROR(dwError); } } dwError = RegSetValueExA( hReg, hKey, "LastUpdated", 0, REG_DWORD, (PBYTE)&dwNow, sizeof(dwNow)); BAIL_ON_UMN_ERROR(dwError); cleanup: LW_SAFE_FREE_STRING(pEncodedUser); UmnSrvFreeUserContents(&old); if (hKey) { RegCloseKey( hReg, hKey); } return dwError; error: goto cleanup; }
PVOID UmnSrvPollerThreadRoutine( IN PVOID pUnused ) { DWORD dwError = 0; struct timeval now; struct timespec periodStart, periodUsed, nextWake, pushWait = {0}; DWORD dwPeriodSecs = 0; char regErrMsg[256] = {0}; char fqdn[1024] = {0}; BOOLEAN bMutexLocked = FALSE; UMN_LOG_INFO("User poller thread started"); dwError = pthread_mutex_lock(&gSignalPollerMutex); BAIL_ON_UMN_ERROR(dwError); bMutexLocked = TRUE; dwError = UmnSrvNow(&periodStart, &now); BAIL_ON_UMN_ERROR(dwError); while (!gbPollerThreadShouldExit) { dwError = UmnSrvGetCheckInterval(&dwPeriodSecs); BAIL_ON_UMN_ERROR(dwError); pushWait.tv_sec = dwPeriodSecs; UmnSrvTimespecAdd( &nextWake, &periodStart, &pushWait); UMN_LOG_INFO("User poller sleeping for %f seconds", pushWait.tv_sec + pushWait.tv_nsec / (double)NANOSECS_PER_SECOND); while (!gbPollerThreadShouldExit && !gbPollerRefresh) { BOOLEAN bWaitElapsed = FALSE; dwError = pthread_cond_timedwait( &gSignalPoller, &gSignalPollerMutex, &nextWake); if (dwError == EINTR) { UMN_LOG_DEBUG("User poller cond wait interrupted; continuing."); continue; } if (dwError == ETIMEDOUT) { UMN_LOG_DEBUG("User poller cond wait completed."); dwError = 0; break; } if (dwError != 0) { UMN_LOG_ERROR("Timed wait error: error %s (%d).", ErrnoToName(dwError), dwError); BAIL_ON_UMN_ERROR(dwError); } dwError = UmnSrvTimespecElapsed(&nextWake, &bWaitElapsed); if (dwError == 0 && bWaitElapsed == TRUE) { break; } } gbPollerRefresh = FALSE; if (!gbPollerThreadShouldExit) { dwError = UmnSrvNow(&periodStart, &now); BAIL_ON_UMN_ERROR(dwError); // obtain the fully qualified domain name and use it throughout this iteration UmnEvtFreeEventComputerName(); UmnEvtGetFQDN(fqdn, sizeof(fqdn)); UmnEvtSetEventComputerName(fqdn); dwError = UmnSrvUpdateAccountInfo(now.tv_sec); if (dwError == ERROR_CANCELLED) { UMN_LOG_INFO("User poller cancelled iteration"); dwError = 0; break; } // simply log other errors and attempt to continue if (dwError != LW_ERROR_SUCCESS) { if (LwRegIsRegistrySpecificError(dwError)) { LwRegGetErrorString(dwError, regErrMsg, sizeof(regErrMsg) - 1); UMN_LOG_ERROR("Failed updating account info, registry error = %d %s. Continuing.", dwError, regErrMsg); } else { UMN_LOG_ERROR("Failed updating account info, error = %d symbol = %s %s. Continuing.", dwError, LwWin32ExtErrorToName(dwError), LwWin32ExtErrorToDescription(dwError)); } dwError = 0; } // periodUsed = now - periodStart dwError = UmnSrvNow(&periodUsed, &now); BAIL_ON_UMN_ERROR(dwError); UmnSrvTimespecSubtract( &periodUsed, &periodUsed, &periodStart); UMN_LOG_DEBUG("Account activity update took %f seconds", periodUsed.tv_sec + periodUsed.tv_nsec / (double)NANOSECS_PER_SECOND); } } UMN_LOG_INFO("User poller thread stopped"); cleanup: if (bMutexLocked) { pthread_mutex_unlock(&gSignalPollerMutex); } if (dwError != 0) { UMN_LOG_ERROR( "User monitor polling thread exiting with code %d", dwError); kill(getpid(), SIGTERM); } return NULL; error: goto cleanup; }
DWORD UmnSrvUpdateADGroupMember( PLW_EVENTLOG_CONNECTION pEventlog, HANDLE hReg, HKEY hGroups, long long PreviousRun, long long Now, PLSA_SECURITY_OBJECT pGroup, PCSTR pMember ) { DWORD dwError = 0; HKEY hKey = NULL; HKEY hMembers = NULL; DWORD dwNow = Now; PSTR pEncodedMember = NULL; PSTR pKeyName = NULL; PSTR pEncodedGroup = NULL; PSTR pMembersKeyName = NULL; dwError = LwURLEncodeString( pMember, &pEncodedMember); BAIL_ON_UMN_ERROR(dwError); dwError = LwURLEncodeString( pGroup->groupInfo.pszUnixName, &pEncodedGroup); BAIL_ON_UMN_ERROR(dwError); dwError = LwAllocateStringPrintf( &pKeyName, "%s\\Members\\%s", pEncodedGroup, pEncodedMember); BAIL_ON_UMN_ERROR(dwError); dwError = RegOpenKeyExA( hReg, hGroups, pKeyName, 0, KEY_ALL_ACCESS, &hKey); if (dwError == LWREG_ERROR_NO_SUCH_KEY_OR_VALUE) { UMN_LOG_INFO("Adding user member '%s' to group '%s' (gid %d)", pMember, pGroup->groupInfo.pszUnixName, pGroup->groupInfo.gid); dwError = LwAllocateStringPrintf( &pMembersKeyName, "%s\\Members", pEncodedGroup); BAIL_ON_UMN_ERROR(dwError); dwError = RegOpenKeyExA( hReg, hGroups, pMembersKeyName, 0, KEY_ALL_ACCESS, &hMembers); if (dwError == LWREG_ERROR_NO_SUCH_KEY_OR_VALUE) { // Previous run left registry in inconsistent state dwError = RegCreateKeyExA( hReg, hGroups, pMembersKeyName, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hMembers, NULL); BAIL_ON_UMN_ERROR(dwError); } BAIL_ON_UMN_ERROR(dwError); dwError = RegCreateKeyExA( hReg, hMembers, pEncodedMember, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, NULL); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvWriteGroupMemberEvent( pEventlog, Now, "AD Groups", PreviousRun, TRUE, //Add member FALSE, //Not gid change pMember, pGroup->groupInfo.gid, pGroup->groupInfo.pszUnixName); BAIL_ON_UMN_ERROR(dwError); } dwError = RegSetValueExA( hReg, hKey, "LastUpdated", 0, REG_DWORD, (PBYTE)&dwNow, sizeof(dwNow)); BAIL_ON_UMN_ERROR(dwError); cleanup: LW_SAFE_FREE_STRING(pEncodedGroup); LW_SAFE_FREE_STRING(pKeyName); LW_SAFE_FREE_STRING(pMembersKeyName); LW_SAFE_FREE_STRING(pEncodedMember); if (hKey) { RegCloseKey( hReg, hKey); } if (hMembers) { RegCloseKey( hReg, hMembers); } return dwError; error: goto cleanup; }
DWORD UmnSrvUpdateADGroup( PLW_EVENTLOG_CONNECTION pEventlog, HANDLE hReg, HKEY hGroups, long long PreviousRun, long long Now, PLSA_SECURITY_OBJECT pGroup ) { DWORD dwError = 0; HKEY hKey = NULL; HKEY hMembers = NULL; USER_MONITOR_GROUP old = { 0 }; DWORD dwNow = Now; old.gr_gid = -1; PSTR pEncodedGroup = NULL; dwError = LwURLEncodeString( pGroup->groupInfo.pszUnixName, &pEncodedGroup); BAIL_ON_UMN_ERROR(dwError); dwError = RegOpenKeyExA( hReg, hGroups, pEncodedGroup, 0, KEY_ALL_ACCESS, &hKey); if (dwError == LWREG_ERROR_NO_SUCH_KEY_OR_VALUE) { dwError = 0; } else { BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvReadGroup( "AD Groups", pEncodedGroup, &old); BAIL_ON_UMN_ERROR(dwError); } // Check if the key does not exist yet, or it was not fully populated. if (old.LastUpdated == 0) { UMN_LOG_INFO("Adding group '%s' (gid %d)", pGroup->groupInfo.pszUnixName, pGroup->groupInfo.gid); dwError = RegCreateKeyExA( hReg, hGroups, pEncodedGroup, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, NULL); if (dwError == LWREG_ERROR_KEYNAME_EXIST) { // The key exists, but the values were not fully populated on a // previous run because the user monitor crashed or was killed. Use // the existing key and let the values get overwritten. dwError = 0; } BAIL_ON_UMN_ERROR(dwError); dwError = RegCreateKeyExA( hReg, hKey, "Members", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hMembers, NULL); if (dwError == LWREG_ERROR_KEYNAME_EXIST) { // The key exists, but the values were not fully populated on a // previous run because the user monitor crashed or was killed. Use // the existing key and let the values get overwritten. dwError = 0; } BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvWriteADGroupValues( hReg, hKey, pGroup); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvWriteADGroupEvent( pEventlog, PreviousRun, NULL, Now, pGroup); BAIL_ON_UMN_ERROR(dwError); } else if (strcmp(pGroup->groupInfo.pszUnixName, old.gr_name)) { // The group's name changed. This is too drastic of a change for a // change event. File a deletion and addition event. dwError = UmnSrvWriteADGroupEvent( pEventlog, PreviousRun, &old, Now, NULL); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvWriteADGroupEvent( pEventlog, PreviousRun, NULL, Now, pGroup); BAIL_ON_UMN_ERROR(dwError); } else if (strcmp((pGroup->groupInfo.pszPasswd ? pGroup->groupInfo.pszPasswd : "x"), old.gr_passwd) || pGroup->groupInfo.gid != old.gr_gid) { UMN_LOG_INFO("Group '%s' (gid %d) changed", pGroup->groupInfo.pszUnixName, pGroup->groupInfo.gid); dwError = UmnSrvWriteADGroupValues( hReg, hKey, pGroup); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvWriteADGroupEvent( pEventlog, PreviousRun, &old, Now, pGroup); BAIL_ON_UMN_ERROR(dwError); if (pGroup->groupInfo.gid != old.gr_gid) { // Send out membership deletion events for all members. They // will get readded through normal processing with the new gid dwError = RegOpenKeyExA( hReg, hKey, "Members", 0, KEY_ALL_ACCESS, &hMembers); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvFindDeletedGroupMembers( pEventlog, hReg, "AD Groups", hMembers, Now, TRUE, old.gr_gid, old.gr_name); BAIL_ON_UMN_ERROR(dwError); } } dwError = RegSetValueExA( hReg, hKey, "LastUpdated", 0, REG_DWORD, (PBYTE)&dwNow, sizeof(dwNow)); BAIL_ON_UMN_ERROR(dwError); cleanup: LW_SAFE_FREE_STRING(pEncodedGroup); UmnSrvFreeGroupContents(&old); if (hKey) { RegCloseKey( hReg, hKey); } if (hMembers) { RegCloseKey( hReg, hMembers); } return dwError; error: goto cleanup; }