syncserv.cpp 17 KB

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