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.

663 lines
18 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 1992 - 1997
  6. //
  7. // File: transit.cxx
  8. //
  9. // Contents: Code for compressing transitive realm list
  10. //
  11. //
  12. // History:
  13. //
  14. //------------------------------------------------------------------------
  15. #include "kdcsvr.hxx"
  16. //+-----------------------------------------------------------------------
  17. //
  18. // Function: KerbAppendString
  19. //
  20. // Synopsis: Appends to unicode strings together and allocates the output
  21. //
  22. // Effects:
  23. //
  24. // Parameters: Output - Output appended String
  25. // InputTail - Trailing portion of input
  26. // InputHead - Head potion of input
  27. //
  28. //
  29. // Return:
  30. //
  31. // Notes:
  32. //
  33. //------------------------------------------------------------------------
  34. NTSTATUS
  35. KerbAppendString(
  36. OUT PUNICODE_STRING Output,
  37. IN PUNICODE_STRING InputTail,
  38. IN PUNICODE_STRING InputHead
  39. )
  40. {
  41. Output->Buffer = NULL;
  42. Output->Length = InputHead->Length + InputTail->Length;
  43. if ((InputHead->Buffer == NULL) && (InputTail->Buffer == NULL))
  44. {
  45. Output->MaximumLength = 0;
  46. return(STATUS_SUCCESS);
  47. }
  48. else
  49. {
  50. Output->MaximumLength = Output->Length + sizeof(WCHAR);
  51. }
  52. Output->Buffer = (LPWSTR) MIDL_user_allocate(Output->MaximumLength);
  53. if (Output->Buffer == NULL)
  54. {
  55. return(STATUS_INSUFFICIENT_RESOURCES);
  56. }
  57. RtlCopyMemory(
  58. Output->Buffer,
  59. InputHead->Buffer,
  60. InputHead->Length
  61. );
  62. RtlCopyMemory(
  63. Output->Buffer + InputHead->Length / sizeof(WCHAR),
  64. InputTail->Buffer,
  65. InputTail->Length
  66. );
  67. Output->Buffer[Output->Length/sizeof(WCHAR)] = L'\0';
  68. return(STATUS_SUCCESS);
  69. }
  70. typedef enum _KERB_DOMAIN_COMPARISON {
  71. Above,
  72. Below,
  73. Equal,
  74. NotEqual
  75. } KERB_DOMAIN_COMPARISON, *PKERB_DOMAIN_COMPARISON;
  76. //+-----------------------------------------------------------------------
  77. //
  78. // Function: KerbCompareDomains
  79. //
  80. // Synopsis: Compares two domain names and returns whether one is a
  81. // prefix of the other, and the offset of the prefix.
  82. //
  83. // Effects:
  84. //
  85. // Parameters:
  86. //
  87. // Return: Above - domain1 is a postfix of domain2
  88. // Below - domain2 is a postfix of domain1
  89. // Equal - the domains are equal
  90. // NotEqual - the domains are not equal and not above or below
  91. //
  92. // Notes: This does not work for x-500 realm names (/foo/bar)
  93. //
  94. //------------------------------------------------------------------------
  95. KERB_DOMAIN_COMPARISON
  96. KerbCompareDomains(
  97. IN PUNICODE_STRING Domain1,
  98. IN PUNICODE_STRING Domain2,
  99. OUT PULONG PostfixOffset
  100. )
  101. {
  102. ULONG Index;
  103. UNICODE_STRING TempString;
  104. if (Domain1->Length > Domain2->Length)
  105. {
  106. TempString.Length = TempString.MaximumLength = Domain2->Length;
  107. TempString.Buffer = Domain1->Buffer + (Domain1->Length - Domain2->Length) / sizeof(WCHAR);
  108. if (RtlEqualUnicodeString(
  109. &TempString,
  110. Domain2,
  111. TRUE) && TempString.Buffer[-1] == '.')
  112. {
  113. *PostfixOffset = (Domain1->Length - Domain2->Length) / sizeof(WCHAR);
  114. return(Below);
  115. }
  116. else
  117. {
  118. return(NotEqual);
  119. }
  120. }
  121. else if (Domain1->Length < Domain2->Length)
  122. {
  123. TempString.Length = TempString.MaximumLength = Domain1->Length;
  124. TempString.Buffer = Domain2->Buffer + (Domain2->Length - Domain1->Length) / sizeof(WCHAR);
  125. if (RtlEqualUnicodeString(
  126. &TempString,
  127. Domain1,
  128. TRUE) && TempString.Buffer[-1] == '.')
  129. {
  130. *PostfixOffset = (Domain2->Length - Domain1->Length) / sizeof(WCHAR);
  131. return(Above);
  132. }
  133. else
  134. {
  135. return(NotEqual);
  136. }
  137. }
  138. else if (RtlEqualUnicodeString(Domain1,Domain2, TRUE))
  139. {
  140. *PostfixOffset = 0;
  141. return(Equal);
  142. }
  143. else
  144. {
  145. return(NotEqual);
  146. }
  147. }
  148. //+-----------------------------------------------------------------------
  149. //
  150. // Function: KdcExpandTranistedRealms
  151. //
  152. // Synopsis: Expands the transited realm field into an array of realms
  153. //
  154. // Effects: Allocates an array of realm names
  155. //
  156. // Parameters: FullRealmList - receives the full list of realms
  157. // CountOfRealms - receveies the number of entries in the list
  158. // TranistedList - The transited field to expand
  159. //
  160. // Return:
  161. //
  162. // Notes:
  163. //
  164. //------------------------------------------------------------------------
  165. KERBERR
  166. KdcExpandTransitedRealms(
  167. OUT PUNICODE_STRING * FullRealmList,
  168. OUT PULONG CountOfRealms,
  169. IN PUNICODE_STRING TransitedList
  170. )
  171. {
  172. KERBERR KerbErr = KDC_ERR_NONE;
  173. ULONG RealmCount;
  174. ULONG Index;
  175. ULONG RealmIndex;
  176. PUNICODE_STRING RealmList = NULL;
  177. UNICODE_STRING CurrentRealm;
  178. *FullRealmList = NULL;
  179. *CountOfRealms = 0;
  180. //
  181. // First count the number of realms in the tranisted list. We can do
  182. // this by counting the number of ',' in the list. Note: if the encoding
  183. // is compressed by using a null entry to include all domains in a path
  184. // up or down a hierarchy, this code will fail.
  185. //
  186. if (TransitedList->Length == 0)
  187. {
  188. return(KDC_ERR_NONE);
  189. }
  190. RealmCount = 1;
  191. for (Index = 0; Index < TransitedList->Length / sizeof(WCHAR) ; Index++ )
  192. {
  193. if (TransitedList->Buffer[Index] == ',')
  194. {
  195. RealmCount++;
  196. //
  197. // Check for a ',,' compression indicating that all the
  198. // realms between two names have been traversed.
  199. // BUG 453997: we don't handle this yet.
  200. //
  201. if ( (Index == 0) ||
  202. (Index == (TransitedList->Length / sizeof(WCHAR)) -1) ||
  203. (TransitedList->Buffer[Index-1] == L','))
  204. {
  205. DebugLog((DEB_ERROR,"BUG 453997:: hit overcompressed transited encoding: %wZ\n",
  206. TransitedList ));
  207. KerbErr = KRB_ERR_GENERIC;
  208. goto Cleanup;
  209. }
  210. }
  211. }
  212. //
  213. // We now have a the count of realms. Allocate an array of UNICODE_STRING
  214. // structures to hold the realms.
  215. //
  216. RealmList = (PUNICODE_STRING) MIDL_user_allocate(RealmCount * sizeof(UNICODE_STRING));
  217. if (RealmList == NULL)
  218. {
  219. KerbErr = KRB_ERR_GENERIC;
  220. goto Cleanup;
  221. }
  222. RtlZeroMemory(
  223. RealmList,
  224. RealmCount * sizeof(UNICODE_STRING)
  225. );
  226. //
  227. // Now loop through and insert the full names of all the domains into
  228. // the list
  229. //
  230. RealmIndex = 0;
  231. CurrentRealm = *TransitedList;
  232. for (Index = 0; Index <= TransitedList->Length / sizeof(WCHAR) ; Index++ )
  233. {
  234. //
  235. // If we hit the end of the string or found a ',', split off a
  236. // new realm.
  237. //
  238. if ((Index == TransitedList->Length / sizeof(WCHAR)) ||
  239. (TransitedList->Buffer[Index] == ',' ))
  240. {
  241. //
  242. // Check for a ',,' compression indicating that all the
  243. // realms between two names have been traversed.
  244. // BUG 453997:: we don't handle this yet.
  245. //
  246. if ( (Index == 0) ||
  247. (Index == (TransitedList->Length / sizeof(WCHAR)) -1) ||
  248. (TransitedList->Buffer[Index-1] == L','))
  249. {
  250. DebugLog((DEB_ERROR,"BUG 453997:: hit overcompressed transited encoding: %wZ\n",
  251. TransitedList ));
  252. KerbErr = KRB_ERR_GENERIC;
  253. goto Cleanup;
  254. }
  255. CurrentRealm.Length = CurrentRealm.MaximumLength =
  256. (USHORT)(&TransitedList->Buffer[Index] - CurrentRealm.Buffer) * sizeof(WCHAR);
  257. //
  258. // Check for a trailing '.' - if so, append it
  259. // to the parent
  260. //
  261. if (TransitedList->Buffer[Index-1] == '.')
  262. {
  263. //
  264. // This is a compressed name, so append it to the previous
  265. // name
  266. //
  267. if (RealmIndex == 0)
  268. {
  269. DebugLog((DEB_ERROR,"First element in transited encoding has a trailing '.': %wZ\n",
  270. TransitedList ));
  271. KerbErr = KRB_ERR_GENERIC;
  272. goto Cleanup;
  273. }
  274. if (!NT_SUCCESS(KerbAppendString(
  275. &RealmList[RealmIndex],
  276. &RealmList[RealmIndex-1],
  277. &CurrentRealm)))
  278. {
  279. KerbErr = KRB_ERR_GENERIC;
  280. goto Cleanup;
  281. }
  282. }
  283. else if ((RealmIndex != 0) && (CurrentRealm.Buffer[0] == '/'))
  284. {
  285. if (!NT_SUCCESS(KerbAppendString(
  286. &RealmList[RealmIndex],
  287. &CurrentRealm,
  288. &RealmList[RealmIndex-1]
  289. )))
  290. {
  291. KerbErr = KRB_ERR_GENERIC;
  292. goto Cleanup;
  293. }
  294. }
  295. else if (!NT_SUCCESS(KerbDuplicateString(
  296. &RealmList[RealmIndex],
  297. &CurrentRealm)))
  298. {
  299. KerbErr = KRB_ERR_GENERIC;
  300. goto Cleanup;
  301. }
  302. CurrentRealm.Buffer =CurrentRealm.Buffer + 1 + CurrentRealm.Length/sizeof(WCHAR);
  303. RealmIndex++;
  304. }
  305. }
  306. DsysAssert(RealmIndex == RealmCount);
  307. *FullRealmList = RealmList;
  308. *CountOfRealms = RealmCount;
  309. Cleanup:
  310. if (!KERB_SUCCESS(KerbErr))
  311. {
  312. if (RealmList != NULL)
  313. {
  314. for (RealmIndex = 0; RealmIndex < RealmCount ; RealmIndex++ )
  315. {
  316. KerbFreeString(&RealmList[RealmIndex]);
  317. }
  318. MIDL_user_free(RealmList);
  319. }
  320. }
  321. return(KerbErr);
  322. }
  323. //+-----------------------------------------------------------------------
  324. //
  325. // Function: KdcCompressTransitedRealms
  326. //
  327. // Synopsis: Compresses an ordered list of realms by removing
  328. // redundant information.
  329. //
  330. // Effects: Allocates an output string
  331. //
  332. // Parameters: CompressedRealms - receives the compressed list of realms
  333. // RealmList - List of domains to compress
  334. // RealmCount - number of entries in realm list
  335. // NewRealm - new realm to add to the lsit
  336. // NewRealmIndex - Location before which to insert the new
  337. // realm
  338. //
  339. // Return:
  340. //
  341. // Notes:
  342. //
  343. //------------------------------------------------------------------------
  344. KERBERR
  345. KdcCompressTransitedRealms(
  346. OUT PUNICODE_STRING CompressedRealms,
  347. IN PUNICODE_STRING RealmList,
  348. IN ULONG RealmCount,
  349. IN PUNICODE_STRING NewRealm,
  350. IN ULONG NewRealmIndex
  351. )
  352. {
  353. UNICODE_STRING OutputRealms;
  354. WCHAR OutputRealmBuffer[1000];
  355. KERBERR KerbErr = KDC_ERR_NONE;
  356. ULONG Index;
  357. ULONG InsertionIndex = NewRealmIndex;
  358. PWCHAR Where;
  359. PUNICODE_STRING PreviousName = NULL;
  360. PUNICODE_STRING CurrentName = NULL;
  361. ULONG PostfixOffset;
  362. KERB_DOMAIN_COMPARISON Comparison;
  363. UNICODE_STRING NameToAdd;
  364. RtlInitUnicodeString(
  365. CompressedRealms,
  366. NULL
  367. );
  368. OutputRealms.Buffer = OutputRealmBuffer;
  369. OutputRealms.MaximumLength = sizeof(OutputRealmBuffer);
  370. OutputRealms.Length = 0;
  371. Where = OutputRealms.Buffer;
  372. Index = 0;
  373. while (Index <= RealmCount)
  374. {
  375. PreviousName = CurrentName;
  376. //
  377. // If this is the index to insert, add the new realm
  378. //
  379. if (InsertionIndex == Index)
  380. {
  381. CurrentName = NewRealm;
  382. }
  383. else if (Index == RealmCount)
  384. {
  385. //
  386. // If we already added all the original realms, get out now
  387. //
  388. break;
  389. }
  390. else
  391. {
  392. CurrentName = &RealmList[Index];
  393. }
  394. NameToAdd = *CurrentName;
  395. //
  396. // If the previous name is above this one, lop off the postfix from
  397. // this name
  398. //
  399. if ((PreviousName != NULL) &&
  400. KerbCompareDomains(
  401. PreviousName,
  402. CurrentName,
  403. &PostfixOffset
  404. ) == Above)
  405. {
  406. NameToAdd.Length = (USHORT) PostfixOffset * sizeof(WCHAR);
  407. }
  408. DsysAssert(OutputRealms.Length + NameToAdd.Length + sizeof(WCHAR) < OutputRealms.MaximumLength);
  409. if (OutputRealms.Length + NameToAdd.Length + sizeof(WCHAR) > OutputRealms.MaximumLength)
  410. {
  411. //
  412. // BUG 453652: wrong error
  413. //
  414. KerbErr = KRB_ERR_GENERIC;
  415. goto Cleanup;
  416. }
  417. if (OutputRealms.Length != 0)
  418. {
  419. *Where++ = L',';
  420. OutputRealms.Length += sizeof(WCHAR);
  421. }
  422. RtlCopyMemory(
  423. Where,
  424. NameToAdd.Buffer,
  425. NameToAdd.Length
  426. );
  427. Where += NameToAdd.Length/sizeof(WCHAR);
  428. OutputRealms.Length += NameToAdd.Length;
  429. //
  430. // If we inserted the transited realm here, run through the loop
  431. // again with the same index.
  432. //
  433. if (InsertionIndex == Index)
  434. {
  435. InsertionIndex = 0xffffffff;
  436. }
  437. else
  438. {
  439. Index++;
  440. }
  441. }
  442. *Where++ = L'\0';
  443. if (!NT_SUCCESS(KerbDuplicateString(
  444. CompressedRealms,
  445. &OutputRealms)))
  446. {
  447. KerbErr = KRB_ERR_GENERIC;
  448. goto Cleanup;
  449. }
  450. Cleanup:
  451. return(KerbErr);
  452. }
  453. //+-----------------------------------------------------------------------
  454. //
  455. // Function: KdcInsertTransitedRealm
  456. //
  457. // Synopsis: Inserts the referree's realm into the tranisted encoding
  458. // in a ticket. This uses domain-x500-compress which
  459. // eliminates redundant information when one domain is the
  460. // prefix or suffix of another.
  461. //
  462. // Effects: Allocates output buffer
  463. //
  464. // Parameters: NewTransitedField - receives the new tranisted field, to
  465. // be freed with KerbFreeString
  466. // OldTransitedField - the existing transited frield.
  467. // ClientRealm - Realm of client (from ticket)
  468. // TransitedRealm - Realm of referring domain
  469. // OurRealm - Our realm name
  470. //
  471. // Return:
  472. //
  473. // Notes:
  474. //
  475. //------------------------------------------------------------------------
  476. KERBERR
  477. KdcInsertTransitedRealm(
  478. OUT PUNICODE_STRING NewTransitedField,
  479. IN PUNICODE_STRING OldTransitedField,
  480. IN PUNICODE_STRING ClientRealm,
  481. IN PUNICODE_STRING TransitedRealm,
  482. IN PUNICODE_STRING OurRealm
  483. )
  484. {
  485. KERBERR KerbErr = KDC_ERR_NONE;
  486. PUNICODE_STRING FullDomainList = NULL;
  487. ULONG CountOfDomains;
  488. ULONG NewEntryIndex = 0xffffffff;
  489. UNICODE_STRING NewDomain;
  490. ULONG PostfixOffset;
  491. ULONG Index;
  492. KERB_DOMAIN_COMPARISON Comparison = NotEqual;
  493. KERB_DOMAIN_COMPARISON LastComparison;
  494. //
  495. // The first thing to do is to expand the existing transited field. This
  496. // is because the compression scheme does not allow new domains to simply
  497. // append or insert information - the encoding of existing realms
  498. // can change. For example, going from a domain to its parent means
  499. // that the original domain can be encoded as a prefix of the parent
  500. // whereas originally it was a name unto itself.
  501. //
  502. D_DebugLog((DEB_T_TRANSIT, "Inserted realm %wZ into list %wZ for client fomr %wZ\n",
  503. TransitedRealm, OldTransitedField, ClientRealm ));
  504. KerbErr = KdcExpandTransitedRealms(
  505. &FullDomainList,
  506. &CountOfDomains,
  507. OldTransitedField
  508. );
  509. if (!KERB_SUCCESS(KerbErr))
  510. {
  511. goto Cleanup;
  512. }
  513. //
  514. // Now loop through the domains. Based on the compression, we know that
  515. // higher up domains come first.
  516. //
  517. for (Index = 0; Index < CountOfDomains ; Index++ )
  518. {
  519. LastComparison = Comparison;
  520. Comparison = KerbCompareDomains(
  521. TransitedRealm,
  522. &FullDomainList[Index],
  523. &PostfixOffset
  524. );
  525. if (Comparison == Above)
  526. {
  527. //
  528. // If the new domain is above an existing domain, it gets inserted
  529. // before the existing domain because all the existing domains
  530. // are ordered from top to bottom
  531. //
  532. NewEntryIndex = Index;
  533. break;
  534. }
  535. else if (Comparison == Below)
  536. {
  537. //
  538. // There may be other domains below which are closer, so
  539. // store the result and continue.
  540. //
  541. LastComparison = Comparison;
  542. }
  543. else if (Comparison == NotEqual)
  544. {
  545. //
  546. // The domains aren't above or below each other. If the last
  547. // comparison was below, stick the domain underneath it.
  548. //
  549. if (LastComparison == Below)
  550. {
  551. NewEntryIndex = Index;
  552. break;
  553. }
  554. }
  555. }
  556. //
  557. // If we didn't find a place for it, stick it in at the end.
  558. //
  559. if (NewEntryIndex == 0xffffffff)
  560. {
  561. NewEntryIndex = Index;
  562. }
  563. //
  564. // Now build the new encoding
  565. //
  566. KerbErr = KdcCompressTransitedRealms(
  567. NewTransitedField,
  568. FullDomainList,
  569. CountOfDomains,
  570. TransitedRealm,
  571. NewEntryIndex
  572. );
  573. if (!KERB_SUCCESS(KerbErr))
  574. {
  575. goto Cleanup;
  576. }
  577. Cleanup:
  578. if (FullDomainList != NULL)
  579. {
  580. for (Index = 0; Index < CountOfDomains ; Index++ )
  581. {
  582. KerbFreeString(&FullDomainList[Index]);
  583. }
  584. MIDL_user_free(FullDomainList);
  585. }
  586. return(KerbErr);
  587. }