Leaked source code of windows server 2003
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.

657 lines
16 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 1992 - 1997
  6. //
  7. // File: userlist.cxx
  8. //
  9. // Contents: Routines for logging a client with a PAC onto an existing
  10. // NT account.
  11. //
  12. //
  13. // History: 21-Febuary-1997 Created MikeSw
  14. //
  15. //------------------------------------------------------------------------
  16. #include <kerb.hxx>
  17. #include <kerbp.h>
  18. #ifndef WIN32_CHICAGO
  19. extern "C"
  20. {
  21. #include <samrpc.h>
  22. #include <lsaisrv.h>
  23. #include <samisrv.h>
  24. }
  25. #include <userall.h>
  26. //+-------------------------------------------------------------------------
  27. //
  28. // Function: KerbReadRegistryString
  29. //
  30. // Synopsis: Reads & allocates a string from the registry
  31. //
  32. // Effects:
  33. //
  34. // Arguments:
  35. //
  36. // Requires:
  37. //
  38. // Returns: STATUS_SUCCESS - the value was found
  39. // STATUS_NO_SUCH_USER - the value was not found
  40. // STATUS_INSUFFICIENT_RESOURCES - memory allocation failure
  41. //
  42. // Notes:
  43. //
  44. //
  45. //--------------------------------------------------------------------------
  46. NTSTATUS
  47. KerbReadRegistryString(
  48. IN HKEY Key,
  49. IN LPWSTR Value,
  50. IN OUT PUNICODE_STRING String
  51. )
  52. {
  53. ULONG Type;
  54. ULONG WinError = ERROR_SUCCESS;
  55. ULONG StringSize = 0;
  56. NTSTATUS Status = STATUS_NO_SUCH_USER;
  57. WinError = RegQueryValueEx(
  58. Key,
  59. Value,
  60. NULL,
  61. &Type,
  62. NULL,
  63. &StringSize
  64. );
  65. if ((WinError == ERROR_MORE_DATA) || (WinError == ERROR_SUCCESS))
  66. {
  67. //
  68. // The value exists - get the name from it
  69. //
  70. String->Buffer = (LPWSTR) KerbAllocate(StringSize);
  71. if (String->Buffer == NULL)
  72. {
  73. Status = STATUS_INSUFFICIENT_RESOURCES;
  74. goto Cleanup;
  75. }
  76. WinError = RegQueryValueEx(
  77. Key,
  78. Value,
  79. NULL,
  80. &Type,
  81. (PUCHAR) String->Buffer,
  82. &StringSize
  83. );
  84. if (WinError != ERROR_SUCCESS)
  85. {
  86. goto Cleanup;
  87. }
  88. RtlInitUnicodeString(
  89. String,
  90. String->Buffer
  91. );
  92. Status = STATUS_SUCCESS;
  93. }
  94. Cleanup:
  95. if (!NT_SUCCESS(Status) && (String->Buffer != NULL))
  96. {
  97. KerbFreeString(String);
  98. }
  99. return(Status);
  100. }
  101. //+-------------------------------------------------------------------------
  102. //
  103. // Function: KerbMapClientName
  104. //
  105. // Synopsis: Maps a Kerberos client name to an NT user name. First it
  106. // tries converting the name to a string and looking for a
  107. // value with that name. If that fails, it looks for a
  108. // value with the client realm name.
  109. //
  110. // Effects:
  111. //
  112. // Arguments: MappedName - receives the name of the local account the
  113. // client maps to
  114. // ClientName - Kerberos principal name of the client
  115. // ClientRealm - Kerberos realm of the client
  116. //
  117. // Requires:
  118. //
  119. // Returns:
  120. //
  121. // Notes:
  122. //
  123. //
  124. //--------------------------------------------------------------------------
  125. NTSTATUS
  126. KerbMapClientName(
  127. OUT PUNICODE_STRING MappedName,
  128. IN PKERB_INTERNAL_NAME ClientName,
  129. IN PUNICODE_STRING ClientRealm
  130. )
  131. {
  132. LPWSTR UserName = NULL;
  133. DWORD WinError;
  134. NTSTATUS Status = STATUS_NO_SUCH_USER;
  135. HKEY Key = NULL;
  136. UNICODE_STRING ClientString = {0};
  137. EMPTY_UNICODE_STRING( MappedName );
  138. //
  139. // First convert the MIT client name to a registry value name. We do
  140. // this by adding a '/' between every component of the client name
  141. // and appending "@ClientRealm"
  142. //
  143. //
  144. // Make sure the client realm is null terminated
  145. //
  146. DsysAssert(ClientRealm->Length == 0 || (ClientRealm->MaximumLength >= ClientRealm->Length + sizeof(WCHAR)));
  147. DsysAssert(ClientRealm->Length == 0 || (ClientRealm->Buffer[ClientRealm->Length/sizeof(WCHAR)] == L'\0'));
  148. //
  149. // The value length is the length of all the components of the names,
  150. // all the '/' separtors, and the name of the domain name plus '@'
  151. //
  152. if (!KERB_SUCCESS(KerbConvertKdcNameToString(
  153. &ClientString,
  154. ClientName,
  155. ClientRealm)))
  156. {
  157. Status = STATUS_INSUFFICIENT_RESOURCES;
  158. goto Cleanup;
  159. }
  160. D_DebugLog((DEB_TRACE,"Mapping client name %wZ\n",&ClientString ));
  161. //
  162. // Also build just the username, which is used when users are mapped
  163. // back to their own name
  164. //
  165. UserName = (LPWSTR) KerbAllocate(ClientName->Names[0].Length + sizeof(WCHAR));
  166. if (UserName == NULL)
  167. {
  168. Status = STATUS_INSUFFICIENT_RESOURCES;
  169. goto Cleanup;
  170. }
  171. RtlCopyMemory(
  172. UserName,
  173. ClientName->Names[0].Buffer,
  174. ClientName->Names[0].Length
  175. );
  176. UserName[ClientName->Names[0].Length / sizeof(WCHAR)] = L'\0';
  177. //
  178. // Now check the registry for a mapping for this name
  179. //
  180. WinError = RegOpenKey(
  181. HKEY_LOCAL_MACHINE,
  182. KERB_USERLIST_KEY,
  183. &Key
  184. );
  185. if (WinError != ERROR_SUCCESS)
  186. {
  187. goto Cleanup;
  188. }
  189. //
  190. // Read out the value
  191. //
  192. Status = KerbReadRegistryString(
  193. Key,
  194. ClientString.Buffer,
  195. MappedName
  196. );
  197. if (Status == STATUS_NO_SUCH_USER)
  198. {
  199. //
  200. // Try again with just the domain name - this allows all users in
  201. // a domain to be mapped
  202. //
  203. Status = KerbReadRegistryString(
  204. Key,
  205. ClientRealm->Buffer,
  206. MappedName
  207. );
  208. }
  209. if (Status == STATUS_NO_SUCH_USER)
  210. {
  211. Status = KerbReadRegistryString(
  212. Key,
  213. KERB_ALL_USERS_VALUE,
  214. MappedName
  215. );
  216. }
  217. if (!NT_SUCCESS(Status))
  218. {
  219. goto Cleanup;
  220. }
  221. //
  222. // If the mapped name is '*', then use the client's name
  223. //
  224. if (_wcsicmp(MappedName->Buffer,KERB_MATCH_ALL_NAME) == 0)
  225. {
  226. KerbFree(MappedName->Buffer);
  227. RtlInitUnicodeString(
  228. MappedName,
  229. UserName
  230. );
  231. UserName = NULL;
  232. }
  233. Status = STATUS_SUCCESS;
  234. Cleanup:
  235. if (!NT_SUCCESS(Status))
  236. {
  237. KerbFreeString(MappedName);
  238. }
  239. KerbFreeString(&ClientString);
  240. if (UserName != NULL)
  241. {
  242. KerbFree(UserName);
  243. }
  244. if (Key != NULL)
  245. {
  246. RegCloseKey(Key);
  247. }
  248. return(Status);
  249. }
  250. //+-------------------------------------------------------------------------
  251. //
  252. // Function: KerbCreatePacForKerbClient
  253. //
  254. // Synopsis: Creates a PAC structure for a kerb client without a PAC
  255. //
  256. // Effects:
  257. //
  258. // Arguments:
  259. //
  260. // Requires:
  261. //
  262. // Returns:
  263. //
  264. // Notes:
  265. //
  266. //
  267. //--------------------------------------------------------------------------
  268. NTSTATUS
  269. KerbCreatePacForKerbClient(
  270. OUT PPACTYPE * Pac,
  271. IN PKERB_INTERNAL_NAME ClientName,
  272. IN PUNICODE_STRING ClientRealm,
  273. IN OPTIONAL PUNICODE_STRING MappedClientName
  274. )
  275. {
  276. NTSTATUS Status = STATUS_SUCCESS;
  277. PLSAPR_POLICY_INFORMATION PolicyInfo = NULL;
  278. SAMPR_HANDLE SamHandle = NULL;
  279. SAMPR_HANDLE DomainHandle = NULL;
  280. SAMPR_HANDLE UserHandle = NULL;
  281. PSAMPR_GET_GROUPS_BUFFER Groups = NULL;
  282. SID_AND_ATTRIBUTES_LIST TransitiveGroups = {0};
  283. PSAMPR_USER_INFO_BUFFER UserInfo = NULL;
  284. PPACTYPE LocalPac = NULL;
  285. SAMPR_ULONG_ARRAY RidArray;
  286. SAMPR_ULONG_ARRAY UseArray;
  287. SECPKG_CLIENT_INFO ClientInfo;
  288. //
  289. // local variables containing copy of globals.
  290. //
  291. UNICODE_STRING LocalMachineName;
  292. UNICODE_STRING LocalDomainName;
  293. UNICODE_STRING LocalAccountName = NULL_UNICODE_STRING;
  294. KERBEROS_MACHINE_ROLE LocalRole = KerbRoleWorkstation;
  295. BOOLEAN GlobalsLocked = FALSE;
  296. RidArray.Element = NULL;
  297. UseArray.Element = NULL;
  298. LocalMachineName.Buffer = NULL;
  299. LocalDomainName.Buffer = NULL;
  300. //
  301. // Verify that the caller has TCB privilege. Otherwise anyone can forge
  302. // a ticket to themselves to logon with any name in the list.
  303. //
  304. Status = LsaFunctions->GetClientInfo(&ClientInfo);
  305. if (!NT_SUCCESS(Status))
  306. {
  307. return(Status);
  308. }
  309. if (!ClientInfo.HasTcbPrivilege)
  310. {
  311. return(STATUS_PRIVILEGE_NOT_HELD);
  312. }
  313. //
  314. // If we are a domain controller, call SAM to do the mapping.
  315. // Otherwise, do it ourselves.
  316. //
  317. //
  318. // Common code for both wksta and DC - open SAM
  319. // However, if we're a realmless wksta, we know we have a client
  320. // mapping to a local account so skip lookup on DC
  321. //
  322. //
  323. // Call the LSA to get our domain sid
  324. //
  325. Status = LsaIQueryInformationPolicyTrusted(
  326. PolicyAccountDomainInformation,
  327. &PolicyInfo
  328. );
  329. if (!NT_SUCCESS(Status))
  330. {
  331. goto Cleanup;
  332. }
  333. //
  334. // Open SAM to get the account information
  335. //
  336. Status = SamIConnect(
  337. NULL, // no server name
  338. &SamHandle,
  339. 0, // no desired access
  340. TRUE // trusted caller
  341. );
  342. if (!NT_SUCCESS(Status))
  343. {
  344. goto Cleanup;
  345. }
  346. Status = SamrOpenDomain(
  347. SamHandle,
  348. 0, // no desired access
  349. (PRPC_SID) PolicyInfo->PolicyAccountDomainInfo.DomainSid,
  350. &DomainHandle
  351. );
  352. if (!NT_SUCCESS(Status))
  353. {
  354. goto Cleanup;
  355. }
  356. //
  357. // grab the globals while holding the lock.
  358. // ... then release the lock prior to making the call!
  359. //
  360. KerbGlobalReadLock();
  361. GlobalsLocked = TRUE;
  362. LocalRole = KerbGlobalRole;
  363. Status = KerbDuplicateString( &LocalMachineName, &KerbGlobalMachineName );
  364. if(!NT_SUCCESS(Status))
  365. {
  366. DebugLog((DEB_ERROR, "Failed to duplicate KerbGlobalMachineName\n"));
  367. goto Cleanup;
  368. }
  369. if( LocalRole == KerbRoleDomainController )
  370. {
  371. Status = KerbDuplicateString( &LocalDomainName, &KerbGlobalDomainName );
  372. if(!NT_SUCCESS(Status))
  373. {
  374. DebugLog((DEB_ERROR, "Failed to duplicate KerbGlobalDomainName\n"));
  375. goto Cleanup;
  376. }
  377. }
  378. KerbGlobalReleaseLock();
  379. GlobalsLocked = FALSE;
  380. //
  381. // If the is a DC, try to look up the name in SAM as an AltSecId.
  382. // If that fails, we will try looking at the registry mapping.
  383. //
  384. if (LocalRole == KerbRoleDomainController)
  385. {
  386. UNICODE_STRING AltSecId = {0};
  387. KERBERR KerbErr;
  388. KerbErr = KerbBuildAltSecId(
  389. &AltSecId,
  390. ClientName,
  391. NULL, // no unicode realm
  392. ClientRealm
  393. );
  394. if (!KERB_SUCCESS(KerbErr))
  395. {
  396. Status = KerbMapKerbError(KerbErr);
  397. goto Cleanup;
  398. }
  399. Status = SamIGetUserLogonInformationEx(
  400. SamHandle,
  401. SAM_OPEN_BY_ALTERNATE_ID,
  402. &AltSecId,
  403. USER_ALL_PAC_INIT,
  404. &UserInfo,
  405. &TransitiveGroups,
  406. NULL // no user handle
  407. );
  408. KerbFreeString(&AltSecId);
  409. }
  410. if (!NT_SUCCESS(Status) || (UserInfo == NULL))
  411. {
  412. if (!ARGUMENT_PRESENT(MappedClientName) || MappedClientName->Buffer == NULL)
  413. {
  414. Status = KerbMapClientName(
  415. &LocalAccountName,
  416. ClientName,
  417. ClientRealm
  418. );
  419. if (!NT_SUCCESS(Status))
  420. {
  421. goto Cleanup;
  422. }
  423. }
  424. else
  425. {
  426. KerbDuplicateString(
  427. &LocalAccountName,
  428. MappedClientName
  429. );
  430. }
  431. Status = SamrLookupNamesInDomain(
  432. DomainHandle,
  433. 1,
  434. (PRPC_UNICODE_STRING) &LocalAccountName,
  435. &RidArray,
  436. &UseArray
  437. );
  438. if (!NT_SUCCESS(Status))
  439. {
  440. goto Cleanup;
  441. }
  442. if ((UseArray.Element[0] != SidTypeUser) &&
  443. (UseArray.Element[0] != SidTypeComputer))
  444. {
  445. Status = STATUS_NONE_MAPPED;
  446. goto Cleanup;
  447. }
  448. //
  449. // Finally open the user
  450. //
  451. Status = SamrOpenUser(
  452. DomainHandle,
  453. 0, // no desired access,
  454. RidArray.Element[0],
  455. &UserHandle
  456. );
  457. if (!NT_SUCCESS(Status))
  458. {
  459. goto Cleanup;
  460. }
  461. Status = SamrQueryInformationUser(
  462. UserHandle,
  463. UserAllInformation,
  464. &UserInfo
  465. );
  466. if (!NT_SUCCESS(Status))
  467. {
  468. goto Cleanup;
  469. }
  470. Status = SamrGetGroupsForUser(
  471. UserHandle,
  472. &Groups
  473. );
  474. if (!NT_SUCCESS(Status))
  475. {
  476. goto Cleanup;
  477. }
  478. }
  479. //
  480. // This is common code
  481. //
  482. //
  483. // Set the password must changes time to inifinite because we don't
  484. // want spurious password must change popups
  485. //
  486. UserInfo->All.PasswordMustChange = *(POLD_LARGE_INTEGER) &KerbGlobalWillNeverTime;
  487. //
  488. // Finally build the PAC
  489. //
  490. Status = PAC_Init(
  491. &UserInfo->All,
  492. Groups,
  493. &TransitiveGroups, // no extra groups
  494. PolicyInfo->PolicyAccountDomainInfo.DomainSid,
  495. ((LocalRole == KerbRoleDomainController) ?
  496. &LocalDomainName : &LocalMachineName),
  497. &LocalMachineName,
  498. 0, // no signature
  499. 0, // no additional data
  500. NULL, // no additional data
  501. &LocalPac
  502. );
  503. if (!NT_SUCCESS(Status))
  504. {
  505. goto Cleanup;
  506. }
  507. *Pac = LocalPac;
  508. LocalPac = NULL;
  509. Cleanup:
  510. if( GlobalsLocked )
  511. {
  512. KerbGlobalReleaseLock();
  513. }
  514. KerbFreeString( &LocalMachineName );
  515. KerbFreeString( &LocalDomainName );
  516. KerbFreeString( &LocalAccountName );
  517. if (UserHandle != NULL)
  518. {
  519. SamrCloseHandle( &UserHandle );
  520. }
  521. if (DomainHandle != NULL)
  522. {
  523. SamrCloseHandle( &DomainHandle );
  524. }
  525. if (SamHandle != NULL)
  526. {
  527. SamrCloseHandle( &SamHandle );
  528. }
  529. if (Groups != NULL)
  530. {
  531. SamIFree_SAMPR_GET_GROUPS_BUFFER( Groups );
  532. }
  533. SamIFreeSidAndAttributesList(&TransitiveGroups);
  534. if (UserInfo != NULL)
  535. {
  536. SamIFree_SAMPR_USER_INFO_BUFFER( UserInfo, UserAllInformation );
  537. }
  538. if (PolicyInfo != NULL)
  539. {
  540. LsaIFree_LSAPR_POLICY_INFORMATION(
  541. PolicyAccountDomainInformation,
  542. PolicyInfo
  543. );
  544. }
  545. SamIFree_SAMPR_ULONG_ARRAY( &UseArray );
  546. SamIFree_SAMPR_ULONG_ARRAY( &RidArray );
  547. if (LocalPac != NULL)
  548. {
  549. MIDL_user_free(LocalPac);
  550. }
  551. return(Status);
  552. }
  553. #endif // WIN32_CHICAGO