Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

632 lines
17 KiB

  1. // Users.cpp: implementation of the CUsers class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "stdafx.h"
  5. #include "efsadu.h"
  6. #include "Users.h"
  7. #include <wincrypt.h>
  8. #ifdef _DEBUG
  9. #undef THIS_FILE
  10. static char THIS_FILE[]=__FILE__;
  11. #define new DEBUG_NEW
  12. #endif
  13. //////////////////////////////////////////////////////////////////////
  14. // Construction/Destruction
  15. //////////////////////////////////////////////////////////////////////
  16. CUsers::CUsers()
  17. {
  18. m_UsersRoot = NULL;
  19. m_UserAddedCnt = 0;
  20. m_UserRemovedCnt = 0;
  21. }
  22. //////////////////////////////////////////////////////////////////////
  23. // Walk through the chain to free the memory
  24. //////////////////////////////////////////////////////////////////////
  25. CUsers::~CUsers()
  26. {
  27. Clear();
  28. }
  29. PUSERSONFILE
  30. CUsers::RemoveItemFromHead(void)
  31. {
  32. PUSERSONFILE PItem = m_UsersRoot;
  33. if (m_UsersRoot){
  34. m_UsersRoot = m_UsersRoot->Next;
  35. if ((PItem->Flag & USERADDED) && !(PItem->Flag & USERREMOVED)){
  36. m_UserAddedCnt--;
  37. }
  38. if ((PItem->Flag & USERINFILE) && (PItem->Flag & USERREMOVED)){
  39. m_UserRemovedCnt--;
  40. }
  41. }
  42. return PItem;
  43. }
  44. DWORD
  45. CUsers::Add( CUsers &NewUsers )
  46. {
  47. PUSERSONFILE NewItem;
  48. while (NewItem = NewUsers.RemoveItemFromHead()){
  49. PUSERSONFILE TmpItem = m_UsersRoot;
  50. while ( TmpItem ){
  51. if ((NewItem->UserName && TmpItem->UserName && !_tcsicmp(NewItem->UserName, TmpItem->UserName)) ||
  52. ((NULL == NewItem->UserName) && (TmpItem->UserName == NULL))){
  53. //
  54. // User exist
  55. //
  56. if ( TmpItem->Flag & USERREMOVED ){
  57. if ( TmpItem->Flag & USERADDED ){
  58. ASSERT(!(TmpItem->Flag & USERINFILE));
  59. //
  60. // User added and removed
  61. //
  62. m_UserAddedCnt++;
  63. } else if ( TmpItem->Flag & USERINFILE ){
  64. //
  65. // User added and removed
  66. //
  67. m_UserRemovedCnt--;
  68. }
  69. TmpItem->Flag &= ~USERREMOVED;
  70. }
  71. //
  72. // The caller will count on CUsers to release the memory
  73. //
  74. if (NewItem->UserName){
  75. delete [] NewItem->UserName;
  76. }
  77. if ( NewItem->Context ) {
  78. CertFreeCertificateContext((PCCERT_CONTEXT)NewItem->Context);
  79. }
  80. delete [] NewItem->Cert;
  81. if (NewItem->UserSid){
  82. delete [] NewItem->UserSid;
  83. }
  84. delete NewItem;
  85. NewItem = NULL;
  86. break;
  87. }
  88. TmpItem = TmpItem->Next;
  89. }
  90. if (NewItem ){
  91. //
  92. // New item. Insert into the head.
  93. //
  94. NewItem->Next = m_UsersRoot;
  95. m_UsersRoot = NewItem;
  96. m_UserAddedCnt++;
  97. }
  98. }
  99. return ERROR_SUCCESS;
  100. }
  101. DWORD
  102. CUsers::Add(
  103. LPTSTR UserName,
  104. PVOID UserCert,
  105. PSID UserSid, /* = NULL */
  106. DWORD Flag, /* = USERINFILE */
  107. PVOID Context /* = NULL */
  108. )
  109. //////////////////////////////////////////////////////////////////////
  110. // Routine Description:
  111. // Create an item for a user
  112. // Arguments:
  113. // UserName -- User's name
  114. // UserCert -- User's certificate blob or hash
  115. // UserSid -- User's ID. Can be NULL
  116. // Flag -- Indicate if the item is existing in the file, to be added or removed
  117. // Return Value:
  118. // NO_ERROR if succeed.
  119. // Will throw exception if memory allocation fails. ( From new.)
  120. //
  121. //////////////////////////////////////////////////////////////////////
  122. {
  123. PUSERSONFILE UserItem;
  124. PUSERSONFILE TmpUserItem = m_UsersRoot;
  125. PEFS_CERTIFICATE_BLOB CertBlob;
  126. PEFS_HASH_BLOB CertHashBlob;
  127. DWORD CertSize;
  128. DWORD SidSize;
  129. if ( !UserCert ){
  130. return ERROR_INVALID_PARAMETER;
  131. }
  132. ASSERT ( (( Flag & USERADDED ) || ( Flag & USERINFILE )) &&
  133. ( (Flag & (USERADDED | USERINFILE)) != (USERADDED | USERINFILE)));
  134. //
  135. // If the user already in the memory, no new item is to be created except for unknown user
  136. //
  137. while ( TmpUserItem ){
  138. if ( (UserName && TmpUserItem->UserName && !_tcsicmp(UserName, TmpUserItem->UserName)) ||
  139. ((NULL == UserName) && (TmpUserItem->UserName == NULL))){
  140. //
  141. // User exist
  142. //
  143. if ( TmpUserItem->Flag & USERREMOVED ){
  144. if ( TmpUserItem->Flag & USERADDED ){
  145. ASSERT(!(TmpUserItem->Flag & USERINFILE));
  146. //
  147. // User added and removed
  148. //
  149. m_UserAddedCnt++;
  150. } else if ( TmpUserItem->Flag & USERINFILE ){
  151. //
  152. // User added and removed
  153. //
  154. m_UserRemovedCnt--;
  155. }
  156. TmpUserItem->Flag &= ~USERREMOVED;
  157. }
  158. //
  159. // The caller will count on CUsers to release
  160. // the context if the call returns CRYPT_E_EXISTS. This is just for
  161. // performance reason.
  162. //
  163. /*
  164. if (UserName){
  165. delete [] UserName;
  166. }
  167. */
  168. if ( Context ) {
  169. CertFreeCertificateContext((PCCERT_CONTEXT)Context);
  170. Context = NULL;
  171. }
  172. return CRYPT_E_EXISTS;
  173. }
  174. TmpUserItem = TmpUserItem->Next;
  175. }
  176. try {
  177. UserItem = new USERSONFILE;
  178. if ( NULL == UserItem ){
  179. AfxThrowMemoryException( );
  180. }
  181. UserItem->Next = NULL;
  182. //
  183. // In case exception raised, we can call delete.
  184. // Delete NULL is OK, but random data is not OK.
  185. //
  186. UserItem->UserSid = NULL;
  187. UserItem->Cert = NULL;
  188. UserItem->Context = NULL;
  189. if ( UserSid ){
  190. SidSize = GetLengthSid( UserSid );
  191. if ( SidSize > 0 ){
  192. UserItem->UserSid = new BYTE[SidSize];
  193. if ( NULL == UserItem->UserSid ){
  194. AfxThrowMemoryException( );
  195. }
  196. if ( !CopySid(SidSize, UserItem->UserSid, UserSid)){
  197. delete [] UserItem->UserSid;
  198. delete UserItem;
  199. return GetLastError();
  200. }
  201. } else {
  202. delete UserItem;
  203. return GetLastError();
  204. }
  205. } else {
  206. UserItem->UserSid = NULL;
  207. }
  208. if ( Flag & USERINFILE ){
  209. //
  210. // The info is from the file. Use the hash structure
  211. //
  212. CertHashBlob = ( PEFS_HASH_BLOB ) UserCert;
  213. CertSize = sizeof(EFS_HASH_BLOB) + CertHashBlob->cbData;
  214. UserItem->Cert = new BYTE[CertSize];
  215. if ( NULL == UserItem->Cert ){
  216. AfxThrowMemoryException( );
  217. }
  218. ((PEFS_HASH_BLOB)UserItem->Cert)->cbData = CertHashBlob->cbData;
  219. ((PEFS_HASH_BLOB)UserItem->Cert)->pbData = (PBYTE)(UserItem->Cert) + sizeof(EFS_HASH_BLOB);
  220. memcpy(((PEFS_HASH_BLOB)UserItem->Cert)->pbData,
  221. CertHashBlob->pbData,
  222. CertHashBlob->cbData
  223. );
  224. } else {
  225. //
  226. // The info is from the user picked cert. Use Cert Blob structure
  227. //
  228. CertBlob = ( PEFS_CERTIFICATE_BLOB ) UserCert;
  229. CertSize = sizeof(EFS_CERTIFICATE_BLOB) + CertBlob->cbData;
  230. UserItem->Cert = new BYTE[CertSize];
  231. if ( NULL == UserItem->Cert ){
  232. AfxThrowMemoryException( );
  233. }
  234. ((PEFS_CERTIFICATE_BLOB)UserItem->Cert)->cbData = CertBlob->cbData;
  235. ((PEFS_CERTIFICATE_BLOB)UserItem->Cert)->dwCertEncodingType = CertBlob->dwCertEncodingType;
  236. ((PEFS_CERTIFICATE_BLOB)UserItem->Cert)->pbData = (PBYTE)(UserItem->Cert) + sizeof(EFS_CERTIFICATE_BLOB);
  237. memcpy(((PEFS_CERTIFICATE_BLOB)UserItem->Cert)->pbData,
  238. CertBlob->pbData,
  239. CertBlob->cbData
  240. );
  241. }
  242. UserItem->UserName = UserName;
  243. UserItem->Context = Context;
  244. UserItem->Flag = Flag;
  245. if ( Flag & USERADDED ){
  246. m_UserAddedCnt ++;
  247. }
  248. }
  249. catch (...) {
  250. delete [] UserItem->UserSid;
  251. delete [] UserItem->Cert;
  252. delete UserItem;
  253. AfxThrowMemoryException( );
  254. return ERROR_NOT_ENOUGH_MEMORY;
  255. }
  256. //
  257. // Add to the head
  258. //
  259. if ( NULL != m_UsersRoot ){
  260. UserItem->Next = m_UsersRoot;
  261. }
  262. m_UsersRoot = UserItem;
  263. return NO_ERROR;
  264. }
  265. DWORD
  266. CUsers::Remove(
  267. LPCTSTR UserName
  268. )
  269. //////////////////////////////////////////////////////////////////////
  270. // Routine Description:
  271. // Remove a user from the list. Actually just mark for remove.
  272. // Arguments:
  273. // UserName -- User's name
  274. // Return Value:
  275. // NO_ERROR if succeed.
  276. // ERROR_NOT_FOUND if the user cannot be found.
  277. //
  278. //////////////////////////////////////////////////////////////////////
  279. {
  280. PUSERSONFILE TmpUserItem = m_UsersRoot;
  281. BOOL UserMatched =FALSE;
  282. while ( TmpUserItem ){
  283. if (((NULL==UserName) && ( NULL == TmpUserItem->UserName)) ||
  284. ( UserName && TmpUserItem->UserName && !_tcsicmp(UserName, TmpUserItem->UserName))){
  285. //
  286. // User exist, mark it for remove
  287. //
  288. if ( TmpUserItem->Flag & USERINFILE ){
  289. m_UserRemovedCnt++;
  290. } else if ( TmpUserItem->Flag & USERADDED ) {
  291. m_UserAddedCnt--;
  292. }
  293. TmpUserItem->Flag |= USERREMOVED;
  294. return NO_ERROR;
  295. }
  296. TmpUserItem = TmpUserItem->Next;
  297. }
  298. return ERROR_NOT_FOUND;
  299. }
  300. PVOID
  301. CUsers::StartEnum()
  302. //////////////////////////////////////////////////////////////////////
  303. // Routine Description:
  304. // Prepare for GetNextUser
  305. // Arguments:
  306. //
  307. // Return Value:
  308. // A pointer used for GetNextUser
  309. //
  310. //////////////////////////////////////////////////////////////////////
  311. {
  312. return ((PVOID)m_UsersRoot);
  313. }
  314. PVOID
  315. CUsers::GetNextUser(
  316. PVOID Token,
  317. CString &UserName,
  318. CString &CertHash
  319. )
  320. //////////////////////////////////////////////////////////////////////
  321. // Routine Description:
  322. // Get next user in the list.(Not removed).
  323. // Arguments:
  324. // UserName -- Next User's name
  325. // CertHash -- Certificate Thumbprinter
  326. // Token -- A pointer returned by previous GetNextUser or StartEnum.
  327. // Return Value:
  328. // A pointer for GetNextUser()
  329. //
  330. //////////////////////////////////////////////////////////////////////
  331. {
  332. PUSERSONFILE TmpItem = (PUSERSONFILE) Token;
  333. PVOID RetPointer = NULL;
  334. while ( TmpItem ){
  335. if ( TmpItem->Flag & USERREMOVED ){
  336. TmpItem = TmpItem->Next;
  337. continue;
  338. }
  339. try{
  340. LPWSTR HashString = NULL;
  341. UserName = TmpItem->UserName;
  342. if (TmpItem->Flag & USERINFILE){
  343. PEFS_HASH_BLOB UserHashBlob;
  344. UserHashBlob = (PEFS_HASH_BLOB)TmpItem->Cert;
  345. HashString = new WCHAR[((((UserHashBlob->cbData + 1)/2) * 5) + 1)];
  346. if (HashString) {
  347. ConvertHashToStr(UserHashBlob->pbData, UserHashBlob->cbData, HashString);
  348. }
  349. } else if ( TmpItem->Context ){
  350. DWORD cbHash;
  351. PBYTE pbHash;
  352. if (CertGetCertificateContextProperty(
  353. (PCCERT_CONTEXT)TmpItem->Context,
  354. CERT_HASH_PROP_ID,
  355. NULL,
  356. &cbHash
  357. )) {
  358. pbHash = (PBYTE)new BYTE[cbHash];
  359. if (pbHash != NULL) {
  360. if (CertGetCertificateContextProperty(
  361. (PCCERT_CONTEXT)TmpItem->Context,
  362. CERT_HASH_PROP_ID,
  363. pbHash,
  364. &cbHash
  365. )) {
  366. HashString = new WCHAR[((((cbHash + 1)/2) * 5) + 1)];
  367. if (HashString) {
  368. ConvertHashToStr(pbHash, cbHash, HashString);
  369. }
  370. }
  371. delete [] pbHash;
  372. }
  373. }
  374. }
  375. CertHash = HashString;
  376. if (HashString){
  377. delete [] HashString;
  378. }
  379. RetPointer = TmpItem->Next;
  380. }
  381. catch (...){
  382. //
  383. // Out of memory
  384. //
  385. TmpItem = NULL;
  386. RetPointer = NULL;
  387. }
  388. break;
  389. }
  390. if ( NULL == TmpItem ){
  391. UserName.Empty();
  392. CertHash.Empty();
  393. }
  394. return RetPointer;
  395. }
  396. DWORD CUsers::GetUserAddedCnt()
  397. {
  398. return m_UserAddedCnt;
  399. }
  400. DWORD CUsers::GetUserRemovedCnt()
  401. {
  402. return m_UserRemovedCnt;
  403. }
  404. PVOID
  405. CUsers::GetNextChangedUser(
  406. PVOID Token,
  407. LPTSTR * UserName,
  408. PSID * UserSid,
  409. PVOID * CertData,
  410. DWORD * Flag
  411. )
  412. //////////////////////////////////////////////////////////////////////
  413. // Routine Description:
  414. // Get the info for changed users. This method is not well behaved in the
  415. // sense of OOP. It exposes internal pointers to the ouside world. The gain
  416. // is performance. At this moment, CUsers is a supporting class and used only
  417. // by USERLIST and CAddSheet (single thread). We can make USERLIST a
  418. // friend of CUsers if such concerns are raised in the future or reimplement this.
  419. // The same issue applies to the enumerate methods.
  420. //
  421. // Arguments:
  422. // Token -- A pointer to the item returned in previous GetNextChangedUser or StartEnum.
  423. // UserName -- User's name
  424. // CertData -- User's certificate blob or hash
  425. // UserSid -- User's ID. Can be NULL
  426. // Flag -- Indicate if the item is existing in the file, to be added or removed
  427. // Return Value:
  428. // Next item pointer.
  429. //
  430. //////////////////////////////////////////////////////////////////////
  431. {
  432. BOOL ChangedUserFound = FALSE;
  433. while ( Token ){
  434. *Flag = ((PUSERSONFILE) Token)->Flag;
  435. if ( ( *Flag & USERADDED ) && !( *Flag & USERREMOVED )){
  436. //
  437. // The user is to to be added to the file
  438. //
  439. *Flag = USERADDED;
  440. ChangedUserFound = TRUE;
  441. } else if ( ( *Flag & USERREMOVED ) && ( *Flag & USERINFILE)){
  442. //
  443. // The user is to be removed from the file
  444. //
  445. *Flag = USERREMOVED;
  446. ChangedUserFound = TRUE;
  447. }
  448. if ( ChangedUserFound ){
  449. *UserName = ((PUSERSONFILE) Token)->UserName;
  450. *UserSid = ((PUSERSONFILE) Token)->UserSid;
  451. *CertData = ((PUSERSONFILE) Token)->Cert;
  452. return ((PUSERSONFILE) Token)->Next;
  453. } else {
  454. Token = ((PUSERSONFILE) Token)->Next;
  455. }
  456. }
  457. *UserName = NULL;
  458. *UserSid = NULL;
  459. *CertData = NULL;
  460. *Flag = 0;
  461. return NULL;
  462. }
  463. void CUsers::Clear()
  464. {
  465. PUSERSONFILE TmpUserItem = m_UsersRoot;
  466. while (TmpUserItem){
  467. m_UsersRoot = TmpUserItem->Next;
  468. delete [] TmpUserItem->UserName;
  469. delete [] TmpUserItem->Cert;
  470. if (TmpUserItem->UserSid){
  471. delete [] TmpUserItem->UserSid;
  472. }
  473. if (TmpUserItem->Context){
  474. CertFreeCertificateContext((PCCERT_CONTEXT)TmpUserItem->Context);
  475. }
  476. delete TmpUserItem;
  477. TmpUserItem = m_UsersRoot;
  478. }
  479. m_UsersRoot = NULL;
  480. m_UserAddedCnt = 0;
  481. m_UserRemovedCnt = 0;
  482. }
  483. void CUsers::ConvertHashToStr(
  484. PBYTE pHashData,
  485. DWORD cbData,
  486. LPWSTR OutHashStr
  487. )
  488. {
  489. DWORD Index = 0;
  490. BOOLEAN NoLastZero = FALSE;
  491. for (; Index < cbData; Index+=2) {
  492. BYTE HashByteLow = pHashData[Index] & 0x0f;
  493. BYTE HashByteHigh = (pHashData[Index] & 0xf0) >> 4;
  494. OutHashStr[Index * 5/2] = HashByteHigh > 9 ? (WCHAR)(HashByteHigh - 9 + 0x40): (WCHAR)(HashByteHigh + 0x30);
  495. OutHashStr[Index * 5/2 + 1] = HashByteLow > 9 ? (WCHAR)(HashByteLow - 9 + 0x40): (WCHAR)(HashByteLow + 0x30);
  496. if (Index + 1 < cbData) {
  497. HashByteLow = pHashData[Index+1] & 0x0f;
  498. HashByteHigh = (pHashData[Index+1] & 0xf0) >> 4;
  499. OutHashStr[Index * 5/2 + 2] = HashByteHigh > 9 ? (WCHAR)(HashByteHigh - 9 + 0x40): (WCHAR)(HashByteHigh + 0x30);
  500. OutHashStr[Index * 5/2 + 3] = HashByteLow > 9 ? (WCHAR)(HashByteLow - 9 + 0x40): (WCHAR)(HashByteLow + 0x30);
  501. OutHashStr[Index * 5/2 + 4] = L' ';
  502. } else {
  503. OutHashStr[Index * 5/2 + 2] = 0;
  504. NoLastZero = TRUE;
  505. }
  506. }
  507. if (!NoLastZero) {
  508. OutHashStr[Index*5/2] = 0;
  509. }
  510. }