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.

764 lines
20 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. #include <alloca.h>
  17. //+-----------------------------------------------------------------------
  18. //
  19. // Function: KerbAppendString
  20. //
  21. // Synopsis: Appends to unicode strings together and allocates the output
  22. //
  23. // Effects:
  24. //
  25. // Parameters: Output - Output appended String
  26. // InputTail - Trailing portion of input
  27. // InputHead - Head potion of input
  28. //
  29. //
  30. // Return:
  31. //
  32. // Notes:
  33. //
  34. //------------------------------------------------------------------------
  35. NTSTATUS
  36. KerbAppendString(
  37. OUT PUNICODE_STRING Output,
  38. IN PUNICODE_STRING InputTail,
  39. IN PUNICODE_STRING InputHead
  40. )
  41. {
  42. Output->Buffer = NULL;
  43. Output->Length = InputHead->Length + InputTail->Length;
  44. if ((InputHead->Buffer == NULL) && (InputTail->Buffer == NULL))
  45. {
  46. Output->MaximumLength = 0;
  47. return(STATUS_SUCCESS);
  48. }
  49. else
  50. {
  51. Output->MaximumLength = Output->Length + sizeof(WCHAR);
  52. }
  53. Output->Buffer = (LPWSTR) MIDL_user_allocate(Output->MaximumLength);
  54. if (Output->Buffer == NULL)
  55. {
  56. return(STATUS_INSUFFICIENT_RESOURCES);
  57. }
  58. RtlCopyMemory(
  59. Output->Buffer,
  60. InputHead->Buffer,
  61. InputHead->Length
  62. );
  63. RtlCopyMemory(
  64. Output->Buffer + InputHead->Length / sizeof(WCHAR),
  65. InputTail->Buffer,
  66. InputTail->Length
  67. );
  68. Output->Buffer[Output->Length/sizeof(WCHAR)] = L'\0';
  69. return(STATUS_SUCCESS);
  70. }
  71. typedef enum _KERB_DOMAIN_COMPARISON {
  72. Above,
  73. Below,
  74. Equal,
  75. NotEqual
  76. } KERB_DOMAIN_COMPARISON, *PKERB_DOMAIN_COMPARISON;
  77. //+-----------------------------------------------------------------------
  78. //
  79. // Function: KerbCompareDomains
  80. //
  81. // Synopsis: Compares two domain names and returns whether one is a
  82. // prefix of the other, and the offset of the prefix.
  83. //
  84. // Effects:
  85. //
  86. // Parameters:
  87. //
  88. // Return: Above - domain1 is a postfix of domain2
  89. // Below - domain2 is a postfix of domain1
  90. // Equal - the domains are equal
  91. // NotEqual - the domains are not equal and not above or below
  92. //
  93. // Notes: This does not work for x-500 realm names (/foo/bar)
  94. //
  95. //------------------------------------------------------------------------
  96. KERB_DOMAIN_COMPARISON
  97. KerbCompareDomains(
  98. IN PUNICODE_STRING Domain1,
  99. IN PUNICODE_STRING Domain2,
  100. OUT PULONG PostfixOffset
  101. )
  102. {
  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 = {0};
  354. ULONG OutputRealmLength = 0;
  355. KERBERR KerbErr = KDC_ERR_NONE;
  356. ULONG Index;
  357. ULONG InsertionIndex;
  358. PWCHAR Where;
  359. PUNICODE_STRING PreviousName = NULL;
  360. PUNICODE_STRING CurrentName;
  361. ULONG PostfixOffset;
  362. UNICODE_STRING NameToAdd;
  363. RtlInitUnicodeString(
  364. CompressedRealms,
  365. NULL
  366. );
  367. //
  368. // Initialize all variables
  369. //
  370. Index = 0;
  371. CurrentName = NULL;
  372. InsertionIndex = NewRealmIndex;
  373. //
  374. // First, compute the length of compressed output realms
  375. //
  376. while (Index <= RealmCount)
  377. {
  378. PreviousName = CurrentName;
  379. //
  380. // If this is the index to insert, add the new realm
  381. //
  382. if (InsertionIndex == Index)
  383. {
  384. CurrentName = NewRealm;
  385. }
  386. else if (Index == RealmCount)
  387. {
  388. //
  389. // If we already added all the original realms, get out now
  390. //
  391. break;
  392. }
  393. else
  394. {
  395. CurrentName = &RealmList[Index];
  396. }
  397. NameToAdd = *CurrentName;
  398. //
  399. // If the previous name is above this one, lop off the postfix from
  400. // this name
  401. //
  402. if ((PreviousName != NULL) &&
  403. KerbCompareDomains(
  404. PreviousName,
  405. CurrentName,
  406. &PostfixOffset
  407. ) == Above)
  408. {
  409. NameToAdd.Length = (USHORT) PostfixOffset * sizeof(WCHAR);
  410. }
  411. if ( OutputRealmLength != 0 )
  412. {
  413. OutputRealmLength += sizeof( WCHAR );
  414. }
  415. OutputRealmLength += NameToAdd.Length;
  416. //
  417. // If we inserted the transited realm here, run through the loop
  418. // again with the same index.
  419. //
  420. if (InsertionIndex == Index)
  421. {
  422. InsertionIndex = 0xffffffff;
  423. }
  424. else
  425. {
  426. Index++;
  427. }
  428. }
  429. //
  430. // Account for the trailing NULL
  431. //
  432. OutputRealmLength += sizeof( WCHAR );
  433. //
  434. // Output must fit inside a UNICODE_STRING
  435. //
  436. if ( OutputRealmLength > MAXUSHORT )
  437. {
  438. KerbErr = KRB_ERR_GENERIC;
  439. goto Cleanup;
  440. }
  441. //
  442. // Re-initialize all variables
  443. //
  444. Index = 0;
  445. CurrentName = NULL;
  446. InsertionIndex = NewRealmIndex;
  447. SafeAllocaAllocate( OutputRealms.Buffer, OutputRealmLength );
  448. if ( OutputRealms.Buffer == NULL ) {
  449. KerbErr = KRB_ERR_GENERIC;
  450. goto Cleanup;
  451. }
  452. OutputRealms.MaximumLength = (USHORT)OutputRealmLength;
  453. OutputRealms.Length = 0;
  454. Where = OutputRealms.Buffer;
  455. //
  456. // Now, on to actual copying
  457. //
  458. while (Index <= RealmCount)
  459. {
  460. PreviousName = CurrentName;
  461. //
  462. // If this is the index to insert, add the new realm
  463. //
  464. if (InsertionIndex == Index)
  465. {
  466. CurrentName = NewRealm;
  467. }
  468. else if (Index == RealmCount)
  469. {
  470. //
  471. // If we already added all the original realms, get out now
  472. //
  473. break;
  474. }
  475. else
  476. {
  477. CurrentName = &RealmList[Index];
  478. }
  479. NameToAdd = *CurrentName;
  480. //
  481. // If the previous name is above this one, lop off the postfix from
  482. // this name
  483. //
  484. if ((PreviousName != NULL) &&
  485. KerbCompareDomains(
  486. PreviousName,
  487. CurrentName,
  488. &PostfixOffset
  489. ) == Above)
  490. {
  491. NameToAdd.Length = (USHORT) PostfixOffset * sizeof(WCHAR);
  492. }
  493. if (OutputRealms.Length != 0)
  494. {
  495. *Where++ = L',';
  496. OutputRealms.Length += sizeof(WCHAR);
  497. }
  498. DsysAssert(OutputRealms.Length + NameToAdd.Length < OutputRealms.MaximumLength);
  499. RtlCopyMemory(
  500. Where,
  501. NameToAdd.Buffer,
  502. NameToAdd.Length
  503. );
  504. Where += NameToAdd.Length/sizeof(WCHAR);
  505. OutputRealms.Length = OutputRealms.Length + NameToAdd.Length;
  506. //
  507. // If we inserted the transited realm here, run through the loop
  508. // again with the same index.
  509. //
  510. if (InsertionIndex == Index)
  511. {
  512. InsertionIndex = 0xffffffff;
  513. }
  514. else
  515. {
  516. Index++;
  517. }
  518. }
  519. *Where++ = L'\0';
  520. if (!NT_SUCCESS(KerbDuplicateString(
  521. CompressedRealms,
  522. &OutputRealms)))
  523. {
  524. KerbErr = KRB_ERR_GENERIC;
  525. goto Cleanup;
  526. }
  527. Cleanup:
  528. SafeAllocaFree( OutputRealms.Buffer );
  529. return(KerbErr);
  530. }
  531. //+-----------------------------------------------------------------------
  532. //
  533. // Function: KdcInsertTransitedRealm
  534. //
  535. // Synopsis: Inserts the referree's realm into the tranisted encoding
  536. // in a ticket. This uses domain-x500-compress which
  537. // eliminates redundant information when one domain is the
  538. // prefix or suffix of another.
  539. //
  540. // Effects: Allocates output buffer
  541. //
  542. // Parameters: NewTransitedField - receives the new tranisted field, to
  543. // be freed with KerbFreeString
  544. // OldTransitedField - the existing transited frield.
  545. // ClientRealm - Realm of client (from ticket)
  546. // TransitedRealm - Realm of referring domain
  547. // OurRealm - Our realm name
  548. //
  549. // Return:
  550. //
  551. // Notes:
  552. //
  553. //------------------------------------------------------------------------
  554. KERBERR
  555. KdcInsertTransitedRealm(
  556. OUT PUNICODE_STRING NewTransitedField,
  557. IN PUNICODE_STRING OldTransitedField,
  558. IN PUNICODE_STRING ClientRealm,
  559. IN PUNICODE_STRING TransitedRealm,
  560. IN PUNICODE_STRING OurRealm
  561. )
  562. {
  563. KERBERR KerbErr = KDC_ERR_NONE;
  564. PUNICODE_STRING FullDomainList = NULL;
  565. ULONG CountOfDomains;
  566. ULONG NewEntryIndex = 0xffffffff;
  567. ULONG PostfixOffset;
  568. ULONG Index;
  569. KERB_DOMAIN_COMPARISON Comparison = NotEqual;
  570. KERB_DOMAIN_COMPARISON LastComparison;
  571. //
  572. // The first thing to do is to expand the existing transited field. This
  573. // is because the compression scheme does not allow new domains to simply
  574. // append or insert information - the encoding of existing realms
  575. // can change. For example, going from a domain to its parent means
  576. // that the original domain can be encoded as a prefix of the parent
  577. // whereas originally it was a name unto itself.
  578. //
  579. D_DebugLog((DEB_T_TRANSIT, "Inserted realm %wZ into list %wZ for client fomr %wZ\n",
  580. TransitedRealm, OldTransitedField, ClientRealm ));
  581. KerbErr = KdcExpandTransitedRealms(
  582. &FullDomainList,
  583. &CountOfDomains,
  584. OldTransitedField
  585. );
  586. if (!KERB_SUCCESS(KerbErr))
  587. {
  588. goto Cleanup;
  589. }
  590. //
  591. // Now loop through the domains. Based on the compression, we know that
  592. // higher up domains come first.
  593. //
  594. for (Index = 0; Index < CountOfDomains ; Index++ )
  595. {
  596. LastComparison = Comparison;
  597. Comparison = KerbCompareDomains(
  598. TransitedRealm,
  599. &FullDomainList[Index],
  600. &PostfixOffset
  601. );
  602. if (Comparison == Above)
  603. {
  604. //
  605. // If the new domain is above an existing domain, it gets inserted
  606. // before the existing domain because all the existing domains
  607. // are ordered from top to bottom
  608. //
  609. NewEntryIndex = Index;
  610. break;
  611. }
  612. else if (Comparison == Below)
  613. {
  614. //
  615. // There may be other domains below which are closer, so
  616. // store the result and continue.
  617. //
  618. LastComparison = Comparison;
  619. }
  620. else if (Comparison == NotEqual)
  621. {
  622. //
  623. // The domains aren't above or below each other. If the last
  624. // comparison was below, stick the domain underneath it.
  625. //
  626. if (LastComparison == Below)
  627. {
  628. NewEntryIndex = Index;
  629. break;
  630. }
  631. }
  632. }
  633. //
  634. // If we didn't find a place for it, stick it in at the end.
  635. //
  636. if (NewEntryIndex == 0xffffffff)
  637. {
  638. NewEntryIndex = Index;
  639. }
  640. //
  641. // Now build the new encoding
  642. //
  643. KerbErr = KdcCompressTransitedRealms(
  644. NewTransitedField,
  645. FullDomainList,
  646. CountOfDomains,
  647. TransitedRealm,
  648. NewEntryIndex
  649. );
  650. if (!KERB_SUCCESS(KerbErr))
  651. {
  652. goto Cleanup;
  653. }
  654. Cleanup:
  655. if (FullDomainList != NULL)
  656. {
  657. for (Index = 0; Index < CountOfDomains ; Index++ )
  658. {
  659. KerbFreeString(&FullDomainList[Index]);
  660. }
  661. MIDL_user_free(FullDomainList);
  662. }
  663. return(KerbErr);
  664. }