syncserv.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. /* --- BEGIN COPYRIGHT BLOCK ---
  2. * Copyright (C) 2005 Red Hat, Inc.
  3. * All rights reserved.
  4. * --- END COPYRIGHT BLOCK --- */
  5. // Created: 2-8-2005
  6. // Author(s): Scott Bridges
  7. #include "syncserv.h"
  8. #include "prerror.h"
  9. static char* certdbh;
  10. // ****************************************************************
  11. // passwdcb
  12. // ****************************************************************
  13. char* passwdcb(PK11SlotInfo* info, PRBool retry, void* arg)
  14. {
  15. char* result = NULL;
  16. unsigned long resultLen = 0;
  17. DWORD type;
  18. HKEY regKey;
  19. if (!retry)
  20. {
  21. RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\PasswordSync", &regKey);
  22. RegQueryValueEx(regKey, "Install Path", NULL, &type, NULL, &resultLen);
  23. result = (char*)malloc(resultLen);
  24. RegQueryValueEx(regKey, "Cert Token", NULL, &type, (unsigned char*)result, &resultLen);
  25. RegCloseKey(regKey);
  26. }
  27. return result;
  28. }
  29. // ****************************************************************
  30. // PassSyncService::PassSyncService
  31. // ****************************************************************
  32. PassSyncService::PassSyncService(const TCHAR *serviceName) : CNTService(serviceName)
  33. {
  34. char sysPath[SYNCSERV_BUF_SIZE];
  35. char tempRegBuff[SYNCSERV_BUF_SIZE];
  36. HKEY regKey;
  37. DWORD type;
  38. unsigned long size;
  39. passhookEventHandle = CreateEvent(NULL, FALSE, FALSE, PASSHAND_EVENT_NAME);
  40. mainLdapConnection = NULL;
  41. results = NULL;
  42. currentResult = NULL;
  43. lastLdapError = LDAP_SUCCESS;
  44. certdbh = NULL;
  45. RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\PasswordSync", &regKey);
  46. size = SYNCSERV_BUF_SIZE;
  47. if(RegQueryValueEx(regKey, "Log Level", NULL, &type, (unsigned char*)tempRegBuff, &size) == ERROR_SUCCESS)
  48. {
  49. logLevel = (unsigned long)atoi(tempRegBuff);
  50. }
  51. else
  52. {
  53. logLevel = 0;
  54. }
  55. size = SYNCSERV_BUF_SIZE;
  56. if(RegQueryValueEx(regKey, "Time To Live", NULL, &type, (unsigned char*)tempRegBuff, &size) == ERROR_SUCCESS)
  57. {
  58. maxBackoffTime = (unsigned long)atoi(tempRegBuff);
  59. }
  60. else
  61. {
  62. maxBackoffTime = pow(2, 12) * SYNCSERV_BASE_BACKOFF_LEN;
  63. }
  64. size = SYNCSERV_BUF_SIZE;
  65. RegQueryValueEx(regKey, "Install Path", NULL, &type, (unsigned char*)installPath, &size);
  66. size = SYNCSERV_BUF_SIZE;
  67. RegQueryValueEx(regKey, "Host Name", NULL, &type, (unsigned char*)ldapHostName, &size);
  68. size = SYNCSERV_BUF_SIZE;
  69. RegQueryValueEx(regKey, "Port Number", NULL, &type, (unsigned char*)ldapHostPort, &size);
  70. size = SYNCSERV_BUF_SIZE;
  71. RegQueryValueEx(regKey, "User Name", NULL, &type, (unsigned char*)ldapAuthUsername, &size);
  72. size = SYNCSERV_BUF_SIZE;
  73. RegQueryValueEx(regKey, "Password", NULL, &type, (unsigned char*)ldapAuthPassword, &size);
  74. size = SYNCSERV_BUF_SIZE;
  75. RegQueryValueEx(regKey, "Search Base", NULL, &type, (unsigned char*)ldapSearchBase, &size);
  76. size = SYNCSERV_BUF_SIZE;
  77. RegQueryValueEx(regKey, "User Name Field", NULL, &type, (unsigned char*)ldapUsernameField, &size);
  78. size = SYNCSERV_BUF_SIZE;
  79. RegQueryValueEx(regKey, "Password Field", NULL, &type, (unsigned char*)ldapPasswordField, &size);
  80. RegCloseKey(regKey);
  81. ExpandEnvironmentStrings("%SystemRoot%", sysPath, SYNCSERV_BUF_SIZE);
  82. _snprintf(certPath, SYNCSERV_BUF_SIZE, "%s", installPath);
  83. _snprintf(logPath, SYNCSERV_BUF_SIZE, "%spasssync.log", installPath);
  84. _snprintf(dataFilename, SYNCSERV_BUF_SIZE, "%s\\system32\\passhook.dat", sysPath);
  85. if(logLevel > 0)
  86. {
  87. outLog.open(logPath, ios::out | ios::app);
  88. }
  89. if(outLog.is_open())
  90. {
  91. timeStamp(&outLog);
  92. outLog << "begin log" << endl;
  93. }
  94. PK11_SetPasswordFunc(passwdcb);
  95. isRunning = false;
  96. }
  97. // ****************************************************************
  98. // PassSyncService::~PassSyncService
  99. // ****************************************************************
  100. PassSyncService::~PassSyncService()
  101. {
  102. if(outLog.is_open())
  103. {
  104. timeStamp(&outLog);
  105. outLog << "end log" << endl;
  106. }
  107. outLog.close();
  108. }
  109. // ****************************************************************
  110. //
  111. // ****************************************************************
  112. void PassSyncService::OnStop()
  113. {
  114. isRunning = false;
  115. SetEvent(passhookEventHandle);
  116. }
  117. // ****************************************************************
  118. //
  119. // ****************************************************************
  120. void PassSyncService::OnShutdown()
  121. {
  122. isRunning = false;
  123. SetEvent(passhookEventHandle);
  124. }
  125. // ****************************************************************
  126. // PassSyncService::Run
  127. // ****************************************************************
  128. void PassSyncService::Run()
  129. {
  130. isRunning = true;
  131. SyncPasswords();
  132. while(isRunning)
  133. {
  134. if(passInfoList.empty())
  135. {
  136. WaitForSingleObject(passhookEventHandle, INFINITE);
  137. }
  138. else
  139. {
  140. WaitForSingleObject(passhookEventHandle, BackoffTime(GetMinBackoff()));
  141. }
  142. SyncPasswords();
  143. UpdateBackoff();
  144. ResetEvent(passhookEventHandle);
  145. }
  146. if(saveSet(&passInfoList, dataFilename) == 0)
  147. {
  148. if(outLog.is_open())
  149. {
  150. timeStamp(&outLog);
  151. outLog << passInfoList.size() << " entries saved to file" << endl;
  152. }
  153. }
  154. else
  155. {
  156. if(outLog.is_open())
  157. {
  158. timeStamp(&outLog);
  159. outLog << "failed to save entries to file" << endl;
  160. }
  161. }
  162. CloseHandle(passhookEventHandle);
  163. }
  164. // ****************************************************************
  165. // PassSyncService::SyncPasswords
  166. // ****************************************************************
  167. int PassSyncService::SyncPasswords()
  168. {
  169. int result = 0;
  170. PASS_INFO_LIST emptyPassInfoList;
  171. PASS_INFO_LIST_ITERATOR currentPassInfo;
  172. PASS_INFO_LIST_ITERATOR tempPassInfo;
  173. char* dn;
  174. int tempSize = passInfoList.size();
  175. if(Connect(&mainLdapConnection, ldapAuthUsername, ldapAuthPassword) < 0)
  176. {
  177. // log connection failure.
  178. if(outLog.is_open())
  179. {
  180. timeStamp(&outLog);
  181. outLog << "can not connect to ldap server in SyncPasswords" << endl;
  182. }
  183. goto exit;
  184. }
  185. if(loadSet(&passInfoList, dataFilename) == 0)
  186. {
  187. if(outLog.is_open())
  188. {
  189. timeStamp(&outLog);
  190. outLog << passInfoList.size() - tempSize << " new entries loaded from file" << endl;
  191. }
  192. saveSet(&emptyPassInfoList, dataFilename);
  193. }
  194. else
  195. {
  196. if(outLog.is_open())
  197. {
  198. timeStamp(&outLog);
  199. outLog << "failed to load entries from file" << endl;
  200. }
  201. }
  202. currentPassInfo = passInfoList.begin();
  203. while(currentPassInfo != passInfoList.end())
  204. {
  205. if(QueryUsername(currentPassInfo->username) == 0)
  206. {
  207. while((dn = GetDN()) != NULL)
  208. {
  209. if(FutureOccurrence(currentPassInfo))
  210. {
  211. if(outLog.is_open())
  212. {
  213. timeStamp(&outLog);
  214. outLog << "newer modifies exist: " << currentPassInfo->username << endl;
  215. }
  216. }
  217. else if(MultipleResults() && !SYNCSERV_ALLOW_MULTI_MOD)
  218. {
  219. if(outLog.is_open())
  220. {
  221. timeStamp(&outLog);
  222. outLog << "multiple results not allowed: " << currentPassInfo->username << endl;
  223. }
  224. }
  225. else if(CanBind(dn, currentPassInfo->password))
  226. {
  227. if(outLog.is_open())
  228. {
  229. timeStamp(&outLog);
  230. outLog << "password match, no modify preformed: " << currentPassInfo->username << endl;
  231. }
  232. }
  233. else if(ModifyPassword(dn, currentPassInfo->password) != 0)
  234. {
  235. // log modify failure.
  236. if(outLog.is_open())
  237. {
  238. timeStamp(&outLog);
  239. outLog << "modify password for " << currentPassInfo->username << " failed in SyncPasswords" << endl;
  240. }
  241. }
  242. else
  243. {
  244. if(outLog.is_open())
  245. {
  246. timeStamp(&outLog);
  247. outLog << "password for " << currentPassInfo->username << " modified" << endl;
  248. outLog << "\t" << dn << endl;
  249. }
  250. }
  251. tempPassInfo = currentPassInfo;
  252. currentPassInfo++;
  253. passInfoList.erase(tempPassInfo);
  254. }
  255. }
  256. else
  257. {
  258. currentPassInfo++;
  259. }
  260. }
  261. exit:
  262. if(mainLdapConnection != NULL)
  263. {
  264. Disconnect(&mainLdapConnection);
  265. }
  266. return result;
  267. }
  268. // ****************************************************************
  269. // PassSyncService::Connect
  270. // ****************************************************************
  271. int PassSyncService::Connect(LDAP** connection, char* dn, char* auth)
  272. {
  273. int result = 0;
  274. if(ldapssl_client_init(certPath, &certdbh) != 0)
  275. {
  276. result = PR_GetError();
  277. if(outLog.is_open())
  278. {
  279. //timeStamp(&outLog);
  280. //outLog << "ldapssl_client_init failed in Connect" << endl;
  281. //outLog << "\t" << result << ": " << ldap_err2string(result) << endl;
  282. }
  283. result = GetLastError();
  284. result = -1;
  285. goto exit;
  286. }
  287. *connection = ldapssl_init(ldapHostName, atoi(ldapHostPort), 1);
  288. if(*connection == NULL)
  289. {
  290. if(outLog.is_open())
  291. {
  292. //timeStamp(&outLog);
  293. //outLog << "ldapssl_init failed in Connect" << endl;
  294. }
  295. result = -1;
  296. goto exit;
  297. }
  298. lastLdapError = ldap_simple_bind_s(*connection, dn, auth);
  299. if(lastLdapError != LDAP_SUCCESS)
  300. {
  301. // log reason for bind failure.
  302. if(outLog.is_open())
  303. {
  304. //timeStamp(&outLog);
  305. //outLog << "ldap error in Connect" << endl;
  306. //outLog << "\t" << lastLdapError << ": " << ldap_err2string(lastLdapError) << endl;
  307. }
  308. result = -1;
  309. goto exit;
  310. }
  311. exit:
  312. return result;
  313. }
  314. // ****************************************************************
  315. // PassSyncService::Disconnect
  316. // ****************************************************************
  317. int PassSyncService::Disconnect(LDAP** connection)
  318. {
  319. ldap_unbind(*connection);
  320. *connection = NULL;
  321. return 0;
  322. }
  323. // ****************************************************************
  324. // PassSyncService::QueryUsername
  325. // ****************************************************************
  326. int PassSyncService::QueryUsername(char* username)
  327. {
  328. int result = 0;
  329. char searchFilter[SYNCSERV_BUF_SIZE];
  330. results = NULL;
  331. _snprintf(searchFilter, SYNCSERV_BUF_SIZE, "(%s=%s)", ldapUsernameField, username);
  332. lastLdapError = ldap_search_ext_s(mainLdapConnection, ldapSearchBase, LDAP_SCOPE_ONELEVEL, searchFilter, NULL, 0, NULL, NULL, NULL, -1, &results);
  333. if(lastLdapError != LDAP_SUCCESS)
  334. {
  335. // log reason for search failure.
  336. if(outLog.is_open())
  337. {
  338. timeStamp(&outLog);
  339. outLog << "ldap error in QueryUsername" << endl;
  340. outLog << "\t" << lastLdapError << ": " << ldap_err2string(lastLdapError) << endl;
  341. }
  342. result = -1;
  343. goto exit;
  344. }
  345. if(ldap_first_entry(mainLdapConnection, results) == NULL)
  346. {
  347. if(outLog.is_open())
  348. {
  349. timeStamp(&outLog);
  350. outLog << "there are no entries that match: " << username << endl;
  351. }
  352. result = -1;
  353. goto exit;
  354. }
  355. exit:
  356. return result;
  357. }
  358. // ****************************************************************
  359. // PassSyncService::GetDN
  360. // ****************************************************************
  361. char* PassSyncService::GetDN()
  362. {
  363. char* result = NULL;
  364. if(currentResult == NULL)
  365. {
  366. currentResult = ldap_first_entry(mainLdapConnection, results);
  367. }
  368. else
  369. {
  370. currentResult = ldap_next_entry(mainLdapConnection, currentResult);
  371. }
  372. result = ldap_get_dn(mainLdapConnection, currentResult);
  373. return result;
  374. }
  375. // ****************************************************************
  376. // PassSyncService::ModifyPassword
  377. // ****************************************************************
  378. int PassSyncService::ModifyPassword(char* dn, char* password)
  379. {
  380. int result = 0;
  381. LDAPMod passMod;
  382. LDAPMod* mods[2] = {&passMod, NULL};
  383. char* modValues[2] = {password, NULL};
  384. passMod.mod_type = ldapPasswordField;
  385. passMod.mod_op = LDAP_MOD_REPLACE;
  386. passMod.mod_values = modValues;
  387. lastLdapError = ldap_modify_ext_s(mainLdapConnection, dn, mods, NULL, NULL);
  388. if(lastLdapError != LDAP_SUCCESS)
  389. {
  390. // log reason for modify failure.
  391. if(outLog.is_open())
  392. {
  393. timeStamp(&outLog);
  394. outLog << "ldap error in ModifyPassword" << endl;
  395. outLog << "\t" << lastLdapError << ": " << ldap_err2string(lastLdapError) << endl;
  396. }
  397. result = -1;
  398. }
  399. return result;
  400. }
  401. // ****************************************************************
  402. // PassSyncService::FutureOccurrence
  403. // ****************************************************************
  404. bool PassSyncService::FutureOccurrence(PASS_INFO_LIST_ITERATOR startingPassInfo)
  405. {
  406. bool result = false;
  407. PASS_INFO_LIST_ITERATOR currentPassInfo;
  408. if((startingPassInfo != NULL) && (startingPassInfo != passInfoList.end()))
  409. {
  410. currentPassInfo = startingPassInfo;
  411. currentPassInfo++;
  412. while((currentPassInfo != passInfoList.end()) && (!result))
  413. {
  414. if(strcmp(currentPassInfo->username, startingPassInfo->username) == 0)
  415. {
  416. result = true;
  417. }
  418. currentPassInfo++;
  419. }
  420. }
  421. return result;
  422. }
  423. // ****************************************************************
  424. // PassSyncService::MultipleResults
  425. // ****************************************************************
  426. bool PassSyncService::MultipleResults()
  427. {
  428. bool result = false;
  429. if(ldap_next_entry(mainLdapConnection, ldap_first_entry(mainLdapConnection, results)) != NULL)
  430. {
  431. result = true;
  432. }
  433. return result;
  434. }
  435. // ****************************************************************
  436. // PassSyncService::CanBind
  437. // ****************************************************************
  438. bool PassSyncService::CanBind(char* dn, char* password)
  439. {
  440. bool result;
  441. LDAP* tempConnection = NULL;
  442. if(Connect(&tempConnection, dn, password) == 0)
  443. {
  444. result = true;
  445. }
  446. else
  447. {
  448. result = false;
  449. }
  450. if(tempConnection != NULL)
  451. {
  452. Disconnect(&tempConnection);
  453. }
  454. return result;
  455. }
  456. // ****************************************************************
  457. // PassSyncService::BackoffTime
  458. // ****************************************************************
  459. unsigned long PassSyncService::BackoffTime(int backoff)
  460. {
  461. unsigned long backoffTime = 0;
  462. if(backoff > 0)
  463. {
  464. backoffTime = pow(2, backoff) * SYNCSERV_BASE_BACKOFF_LEN;
  465. }
  466. return backoffTime;
  467. }
  468. // ****************************************************************
  469. // PassSyncService::UpdateBackoff
  470. // ****************************************************************
  471. void PassSyncService::UpdateBackoff()
  472. {
  473. PASS_INFO_LIST_ITERATOR currentPassInfo;
  474. PASS_INFO_LIST_ITERATOR tempPassInfo;
  475. time_t currentTime;
  476. time(&currentTime);
  477. currentPassInfo = passInfoList.begin();
  478. while(currentPassInfo != passInfoList.end())
  479. {
  480. if((currentPassInfo->atTime + (BackoffTime(currentPassInfo->backoffCount) / 1000)) <= currentTime)
  481. {
  482. currentPassInfo->backoffCount++;
  483. }
  484. if((currentTime - currentPassInfo->atTime) > (maxBackoffTime / 1000))
  485. {
  486. if(outLog.is_open())
  487. {
  488. timeStamp(&outLog);
  489. outLog << "abandoning password change for " << currentPassInfo->username << ", backoff expired" << endl;
  490. }
  491. tempPassInfo = currentPassInfo;
  492. currentPassInfo++;
  493. passInfoList.erase(tempPassInfo);
  494. }
  495. else
  496. {
  497. currentPassInfo++;
  498. }
  499. }
  500. }
  501. // ****************************************************************
  502. // PassSyncService::GetMinBackoff
  503. // ****************************************************************
  504. int PassSyncService::GetMinBackoff()
  505. {
  506. PASS_INFO_LIST_ITERATOR currentPassInfo;
  507. unsigned long minBackoff = INFINITE;
  508. for(currentPassInfo = passInfoList.begin(); currentPassInfo != passInfoList.end(); currentPassInfo++)
  509. {
  510. if(currentPassInfo->backoffCount < minBackoff)
  511. {
  512. minBackoff = currentPassInfo->backoffCount;
  513. }
  514. }
  515. return minBackoff;
  516. }