/* --- BEGIN COPYRIGHT BLOCK --- * Copyright (C) 2005 Red Hat, Inc. * All rights reserved. * --- END COPYRIGHT BLOCK --- */ // Created: 2-8-2005 // Author(s): Scott Bridges #include "syncserv.h" #include "prerror.h" static char* certdbh; // **************************************************************** // passwdcb // **************************************************************** char* passwdcb(PK11SlotInfo* info, PRBool retry, void* arg) { char* result = NULL; unsigned long resultLen = 0; DWORD type; HKEY regKey; if (!retry) { RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\PasswordSync", ®Key); RegQueryValueEx(regKey, "Install Path", NULL, &type, NULL, &resultLen); result = (char*)malloc(resultLen); RegQueryValueEx(regKey, "Cert Token", NULL, &type, (unsigned char*)result, &resultLen); RegCloseKey(regKey); } return result; } // **************************************************************** // PassSyncService::PassSyncService // **************************************************************** PassSyncService::PassSyncService(const TCHAR *serviceName) : CNTService(serviceName) { char sysPath[SYNCSERV_BUF_SIZE]; char tempRegBuff[SYNCSERV_BUF_SIZE]; HKEY regKey; DWORD type; unsigned long size; passhookEventHandle = CreateEvent(NULL, FALSE, FALSE, PASSHAND_EVENT_NAME); mainLdapConnection = NULL; results = NULL; currentResult = NULL; lastLdapError = LDAP_SUCCESS; certdbh = NULL; RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\PasswordSync", ®Key); size = SYNCSERV_BUF_SIZE; if(RegQueryValueEx(regKey, "Log Level", NULL, &type, (unsigned char*)tempRegBuff, &size) == ERROR_SUCCESS) { logLevel = (unsigned long)atoi(tempRegBuff); } else { logLevel = 0; } size = SYNCSERV_BUF_SIZE; if(RegQueryValueEx(regKey, "Time To Live", NULL, &type, (unsigned char*)tempRegBuff, &size) == ERROR_SUCCESS) { maxBackoffTime = (unsigned long)atoi(tempRegBuff); } else { maxBackoffTime = pow(2, 12) * SYNCSERV_BASE_BACKOFF_LEN; } size = SYNCSERV_BUF_SIZE; RegQueryValueEx(regKey, "Install Path", NULL, &type, (unsigned char*)installPath, &size); size = SYNCSERV_BUF_SIZE; RegQueryValueEx(regKey, "Host Name", NULL, &type, (unsigned char*)ldapHostName, &size); size = SYNCSERV_BUF_SIZE; RegQueryValueEx(regKey, "Port Number", NULL, &type, (unsigned char*)ldapHostPort, &size); size = SYNCSERV_BUF_SIZE; RegQueryValueEx(regKey, "User Name", NULL, &type, (unsigned char*)ldapAuthUsername, &size); size = SYNCSERV_BUF_SIZE; RegQueryValueEx(regKey, "Password", NULL, &type, (unsigned char*)ldapAuthPassword, &size); size = SYNCSERV_BUF_SIZE; RegQueryValueEx(regKey, "Search Base", NULL, &type, (unsigned char*)ldapSearchBase, &size); size = SYNCSERV_BUF_SIZE; RegQueryValueEx(regKey, "User Name Field", NULL, &type, (unsigned char*)ldapUsernameField, &size); size = SYNCSERV_BUF_SIZE; RegQueryValueEx(regKey, "Password Field", NULL, &type, (unsigned char*)ldapPasswordField, &size); RegCloseKey(regKey); ExpandEnvironmentStrings("%SystemRoot%", sysPath, SYNCSERV_BUF_SIZE); _snprintf(certPath, SYNCSERV_BUF_SIZE, "%s", installPath); _snprintf(logPath, SYNCSERV_BUF_SIZE, "%spasssync.log", installPath); _snprintf(dataFilename, SYNCSERV_BUF_SIZE, "%s\\system32\\passhook.dat", sysPath); if(logLevel > 0) { outLog.open(logPath, ios::out | ios::app); } if(outLog.is_open()) { timeStamp(&outLog); outLog << "begin log" << endl; } PK11_SetPasswordFunc(passwdcb); isRunning = false; } // **************************************************************** // PassSyncService::~PassSyncService // **************************************************************** PassSyncService::~PassSyncService() { if(outLog.is_open()) { timeStamp(&outLog); outLog << "end log" << endl; } outLog.close(); } // **************************************************************** // // **************************************************************** void PassSyncService::OnStop() { isRunning = false; SetEvent(passhookEventHandle); } // **************************************************************** // // **************************************************************** void PassSyncService::OnShutdown() { isRunning = false; SetEvent(passhookEventHandle); } // **************************************************************** // PassSyncService::Run // **************************************************************** void PassSyncService::Run() { isRunning = true; SyncPasswords(); while(isRunning) { if(passInfoList.empty()) { WaitForSingleObject(passhookEventHandle, INFINITE); } else { WaitForSingleObject(passhookEventHandle, BackoffTime(GetMinBackoff())); } SyncPasswords(); UpdateBackoff(); ResetEvent(passhookEventHandle); } if(saveSet(&passInfoList, dataFilename) == 0) { if(outLog.is_open()) { timeStamp(&outLog); outLog << passInfoList.size() << " entries saved to file" << endl; } } else { if(outLog.is_open()) { timeStamp(&outLog); outLog << "failed to save entries to file" << endl; } } CloseHandle(passhookEventHandle); } // **************************************************************** // PassSyncService::SyncPasswords // **************************************************************** int PassSyncService::SyncPasswords() { int result = 0; PASS_INFO_LIST emptyPassInfoList; PASS_INFO_LIST_ITERATOR currentPassInfo; PASS_INFO_LIST_ITERATOR tempPassInfo; char* dn; int tempSize = passInfoList.size(); if(Connect(&mainLdapConnection, ldapAuthUsername, ldapAuthPassword) < 0) { // log connection failure. if(outLog.is_open()) { timeStamp(&outLog); outLog << "can not connect to ldap server in SyncPasswords" << endl; } goto exit; } if(loadSet(&passInfoList, dataFilename) == 0) { if(outLog.is_open()) { timeStamp(&outLog); outLog << passInfoList.size() - tempSize << " new entries loaded from file" << endl; } saveSet(&emptyPassInfoList, dataFilename); } else { if(outLog.is_open()) { timeStamp(&outLog); outLog << "failed to load entries from file" << endl; } } currentPassInfo = passInfoList.begin(); while(currentPassInfo != passInfoList.end()) { if(QueryUsername(currentPassInfo->username) == 0) { while((dn = GetDN()) != NULL) { if(FutureOccurrence(currentPassInfo)) { if(outLog.is_open()) { timeStamp(&outLog); outLog << "newer modifies exist: " << currentPassInfo->username << endl; } } else if(MultipleResults() && !SYNCSERV_ALLOW_MULTI_MOD) { if(outLog.is_open()) { timeStamp(&outLog); outLog << "multiple results not allowed: " << currentPassInfo->username << endl; } } else if(CanBind(dn, currentPassInfo->password)) { if(outLog.is_open()) { timeStamp(&outLog); outLog << "password match, no modify preformed: " << currentPassInfo->username << endl; } } else if(ModifyPassword(dn, currentPassInfo->password) != 0) { // log modify failure. if(outLog.is_open()) { timeStamp(&outLog); outLog << "modify password for " << currentPassInfo->username << " failed in SyncPasswords" << endl; } } else { if(outLog.is_open()) { timeStamp(&outLog); outLog << "password for " << currentPassInfo->username << " modified" << endl; outLog << "\t" << dn << endl; } } tempPassInfo = currentPassInfo; currentPassInfo++; passInfoList.erase(tempPassInfo); } } else { currentPassInfo++; } } exit: if(mainLdapConnection != NULL) { Disconnect(&mainLdapConnection); } return result; } // **************************************************************** // PassSyncService::Connect // **************************************************************** int PassSyncService::Connect(LDAP** connection, char* dn, char* auth) { int result = 0; if(ldapssl_client_init(certPath, &certdbh) != 0) { result = PR_GetError(); if(outLog.is_open()) { //timeStamp(&outLog); //outLog << "ldapssl_client_init failed in Connect" << endl; //outLog << "\t" << result << ": " << ldap_err2string(result) << endl; } result = GetLastError(); result = -1; goto exit; } *connection = ldapssl_init(ldapHostName, atoi(ldapHostPort), 1); if(*connection == NULL) { if(outLog.is_open()) { //timeStamp(&outLog); //outLog << "ldapssl_init failed in Connect" << endl; } result = -1; goto exit; } lastLdapError = ldap_simple_bind_s(*connection, dn, auth); if(lastLdapError != LDAP_SUCCESS) { // log reason for bind failure. if(outLog.is_open()) { //timeStamp(&outLog); //outLog << "ldap error in Connect" << endl; //outLog << "\t" << lastLdapError << ": " << ldap_err2string(lastLdapError) << endl; } result = -1; goto exit; } exit: return result; } // **************************************************************** // PassSyncService::Disconnect // **************************************************************** int PassSyncService::Disconnect(LDAP** connection) { ldap_unbind(*connection); *connection = NULL; return 0; } // **************************************************************** // PassSyncService::QueryUsername // **************************************************************** int PassSyncService::QueryUsername(char* username) { int result = 0; char searchFilter[SYNCSERV_BUF_SIZE]; results = NULL; _snprintf(searchFilter, SYNCSERV_BUF_SIZE, "(%s=%s)", ldapUsernameField, username); lastLdapError = ldap_search_ext_s(mainLdapConnection, ldapSearchBase, LDAP_SCOPE_ONELEVEL, searchFilter, NULL, 0, NULL, NULL, NULL, -1, &results); if(lastLdapError != LDAP_SUCCESS) { // log reason for search failure. if(outLog.is_open()) { timeStamp(&outLog); outLog << "ldap error in QueryUsername" << endl; outLog << "\t" << lastLdapError << ": " << ldap_err2string(lastLdapError) << endl; } result = -1; goto exit; } if(ldap_first_entry(mainLdapConnection, results) == NULL) { if(outLog.is_open()) { timeStamp(&outLog); outLog << "there are no entries that match: " << username << endl; } result = -1; goto exit; } exit: return result; } // **************************************************************** // PassSyncService::GetDN // **************************************************************** char* PassSyncService::GetDN() { char* result = NULL; if(currentResult == NULL) { currentResult = ldap_first_entry(mainLdapConnection, results); } else { currentResult = ldap_next_entry(mainLdapConnection, currentResult); } result = ldap_get_dn(mainLdapConnection, currentResult); return result; } // **************************************************************** // PassSyncService::ModifyPassword // **************************************************************** int PassSyncService::ModifyPassword(char* dn, char* password) { int result = 0; LDAPMod passMod; LDAPMod* mods[2] = {&passMod, NULL}; char* modValues[2] = {password, NULL}; passMod.mod_type = ldapPasswordField; passMod.mod_op = LDAP_MOD_REPLACE; passMod.mod_values = modValues; lastLdapError = ldap_modify_ext_s(mainLdapConnection, dn, mods, NULL, NULL); if(lastLdapError != LDAP_SUCCESS) { // log reason for modify failure. if(outLog.is_open()) { timeStamp(&outLog); outLog << "ldap error in ModifyPassword" << endl; outLog << "\t" << lastLdapError << ": " << ldap_err2string(lastLdapError) << endl; } result = -1; } return result; } // **************************************************************** // PassSyncService::FutureOccurrence // **************************************************************** bool PassSyncService::FutureOccurrence(PASS_INFO_LIST_ITERATOR startingPassInfo) { bool result = false; PASS_INFO_LIST_ITERATOR currentPassInfo; if((startingPassInfo != NULL) && (startingPassInfo != passInfoList.end())) { currentPassInfo = startingPassInfo; currentPassInfo++; while((currentPassInfo != passInfoList.end()) && (!result)) { if(strcmp(currentPassInfo->username, startingPassInfo->username) == 0) { result = true; } currentPassInfo++; } } return result; } // **************************************************************** // PassSyncService::MultipleResults // **************************************************************** bool PassSyncService::MultipleResults() { bool result = false; if(ldap_next_entry(mainLdapConnection, ldap_first_entry(mainLdapConnection, results)) != NULL) { result = true; } return result; } // **************************************************************** // PassSyncService::CanBind // **************************************************************** bool PassSyncService::CanBind(char* dn, char* password) { bool result; LDAP* tempConnection = NULL; if(Connect(&tempConnection, dn, password) == 0) { result = true; } else { result = false; } if(tempConnection != NULL) { Disconnect(&tempConnection); } return result; } // **************************************************************** // PassSyncService::BackoffTime // **************************************************************** unsigned long PassSyncService::BackoffTime(int backoff) { unsigned long backoffTime = 0; if(backoff > 0) { backoffTime = pow(2, backoff) * SYNCSERV_BASE_BACKOFF_LEN; } return backoffTime; } // **************************************************************** // PassSyncService::UpdateBackoff // **************************************************************** void PassSyncService::UpdateBackoff() { PASS_INFO_LIST_ITERATOR currentPassInfo; PASS_INFO_LIST_ITERATOR tempPassInfo; time_t currentTime; time(¤tTime); currentPassInfo = passInfoList.begin(); while(currentPassInfo != passInfoList.end()) { if((currentPassInfo->atTime + (BackoffTime(currentPassInfo->backoffCount) / 1000)) <= currentTime) { currentPassInfo->backoffCount++; } if((currentTime - currentPassInfo->atTime) > (maxBackoffTime / 1000)) { if(outLog.is_open()) { timeStamp(&outLog); outLog << "abandoning password change for " << currentPassInfo->username << ", backoff expired" << endl; } tempPassInfo = currentPassInfo; currentPassInfo++; passInfoList.erase(tempPassInfo); } else { currentPassInfo++; } } } // **************************************************************** // PassSyncService::GetMinBackoff // **************************************************************** int PassSyncService::GetMinBackoff() { PASS_INFO_LIST_ITERATOR currentPassInfo; unsigned long minBackoff = INFINITE; for(currentPassInfo = passInfoList.begin(); currentPassInfo != passInfoList.end(); currentPassInfo++) { if(currentPassInfo->backoffCount < minBackoff) { minBackoff = currentPassInfo->backoffCount; } } return minBackoff; }