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.

711 lines
15 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1997.
  5. //
  6. // File: protocol.c
  7. //
  8. // Contents: Implements the XTCB protocol
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 3-01-00 RichardW Created
  15. //
  16. //----------------------------------------------------------------------------
  17. #include "xtcbpkg.h"
  18. #include "md5.h"
  19. #include "hmac.h"
  20. #include <cryptdll.h>
  21. //
  22. // The protocol is very straight-forward. For any group, we have the following:
  23. //
  24. // A Group Key (G)
  25. // A Client Key (C)
  26. // A Server Key (S)
  27. //
  28. //
  29. // The protocol is as follows:
  30. //
  31. // Client (C) sends a message to the server, consisting of:
  32. // A random seed [ R ]
  33. // The client's PAC
  34. // Name of the client and group
  35. // HMAC( G, S, R, PAC, Name )
  36. //
  37. // Both sides create CS and SC keys by:
  38. // CS = HMAC( [S], G, R, "string1")
  39. // SC = HMAC( [C], G, R, "string2")
  40. //
  41. // The server (S) verifies the HMAC, and replies with:
  42. // A different seed [ R2 ]
  43. // HMAC( G, C, R2 )
  44. //
  45. #define CS_HMAC_STRING "Fairly long string for the client-server session key derivation"
  46. #define SC_HMAC_STRING "Equally long string to derive server-client session key for now"
  47. LARGE_INTEGER XtcbExpirationTime = { 0xFFFFFFFF, 0x6FFFFFFF };
  48. typedef struct _XTCB_HMAC {
  49. HMACMD5_CTX Context ;
  50. } XTCB_HMAC, * PXTCB_HMAC;
  51. PXTCB_HMAC
  52. XtcbInitHmac(
  53. PUCHAR IndividualKey,
  54. PUCHAR GroupKey
  55. )
  56. {
  57. PXTCB_HMAC HMac;
  58. UCHAR Key[ XTCB_SEED_LENGTH * 2 ];
  59. HMac = LocalAlloc( LMEM_FIXED, sizeof( XTCB_HMAC ) );
  60. if ( HMac )
  61. {
  62. RtlCopyMemory( Key,
  63. IndividualKey,
  64. XTCB_SEED_LENGTH );
  65. RtlCopyMemory( Key+XTCB_SEED_LENGTH,
  66. GroupKey,
  67. XTCB_SEED_LENGTH );
  68. HMACMD5Init( &HMac->Context,
  69. Key,
  70. XTCB_SEED_LENGTH * 2 );
  71. }
  72. return HMac ;
  73. }
  74. PXTCB_HMAC
  75. XtcbPrepareHmac(
  76. PXTCB_HMAC HMac
  77. )
  78. {
  79. PXTCB_HMAC Working ;
  80. Working = LocalAlloc( LMEM_FIXED, sizeof( XTCB_HMAC ) );
  81. return Working ;
  82. }
  83. #define XtcbHmacUpdate( H, p, s ) \
  84. HMACMD5Update( &((PXTCB_HMAC)H)->Context, p, s )
  85. VOID
  86. XtcbDeriveKeys(
  87. PXTCB_CONTEXT Context,
  88. PUCHAR ServerKey,
  89. PUCHAR GroupKey,
  90. PUCHAR ClientKey,
  91. PUCHAR RandomSeed
  92. )
  93. {
  94. HMACMD5_CTX HMac ;
  95. HMACMD5Init( &HMac, ServerKey, XTCB_SEED_LENGTH );
  96. HMACMD5Update( &HMac, GroupKey, XTCB_SEED_LENGTH );
  97. HMACMD5Update( &HMac, RandomSeed, XTCB_SEED_LENGTH );
  98. HMACMD5Update( &HMac, CS_HMAC_STRING, sizeof( CS_HMAC_STRING ) );
  99. if ( Context->Core.Type == XtcbContextServer )
  100. {
  101. HMACMD5Final( &HMac, Context->Core.InboundKey );
  102. }
  103. else
  104. {
  105. HMACMD5Final( &HMac, Context->Core.OutboundKey );
  106. }
  107. HMACMD5Init( &HMac, ClientKey, XTCB_SEED_LENGTH );
  108. HMACMD5Update( &HMac, GroupKey, XTCB_SEED_LENGTH );
  109. HMACMD5Update( &HMac, RandomSeed, XTCB_SEED_LENGTH );
  110. HMACMD5Update( &HMac, SC_HMAC_STRING, sizeof( SC_HMAC_STRING ) );
  111. if ( Context->Core.Type == XtcbContextServer )
  112. {
  113. HMACMD5Final( &HMac, Context->Core.OutboundKey );
  114. }
  115. else
  116. {
  117. HMACMD5Final( &HMac, Context->Core.InboundKey );
  118. }
  119. }
  120. SECURITY_STATUS
  121. XtcbBuildInitialToken(
  122. PXTCB_CREDS Creds,
  123. PXTCB_CONTEXT Context,
  124. PSECURITY_STRING Target,
  125. PSECURITY_STRING Group,
  126. PUCHAR ServerKey,
  127. PUCHAR GroupKey,
  128. PUCHAR ClientKey,
  129. PUCHAR * Token,
  130. PULONG TokenLen
  131. )
  132. {
  133. PXTCB_HMAC HMac ;
  134. PXTCB_INIT_MESSAGE Message ;
  135. PUCHAR CopyTo ;
  136. PUCHAR Base ;
  137. Message = LsaTable->AllocateLsaHeap( sizeof( XTCB_INIT_MESSAGE ) +
  138. Creds->Pac->Length +
  139. XtcbUnicodeDnsName.Length +
  140. Group->Length );
  141. if ( !Message )
  142. {
  143. return SEC_E_INSUFFICIENT_MEMORY ;
  144. }
  145. CDGenerateRandomBits( Message->Seed, XTCB_SEED_LENGTH );
  146. //
  147. // Create keys in the context
  148. //
  149. XtcbDeriveKeys(
  150. Context,
  151. ServerKey,
  152. GroupKey,
  153. ClientKey,
  154. Message->Seed );
  155. //
  156. // Set random seed in the context
  157. //
  158. RtlCopyMemory(
  159. Context->Core.RootKey,
  160. Message->Seed,
  161. XTCB_SEED_LENGTH );
  162. //
  163. // Fill it in:
  164. //
  165. Message->Version = 1 ;
  166. Message->Length = sizeof( XTCB_INIT_MESSAGE ) +
  167. Creds->Pac->Length +
  168. XtcbUnicodeDnsName.Length +
  169. Group->Length ;
  170. RtlZeroMemory( Message->HMAC, XTCB_HMAC_LENGTH );
  171. CopyTo = (PUCHAR) ( Message + 1 );
  172. Base = (PUCHAR) Message;
  173. RtlCopyMemory(
  174. CopyTo,
  175. Creds->Pac,
  176. Creds->Pac->Length );
  177. Message->PacOffset = (ULONG) (CopyTo - Base);
  178. Message->PacLength = Creds->Pac->Length ;
  179. CopyTo += Creds->Pac->Length ;
  180. RtlCopyMemory(
  181. CopyTo,
  182. XtcbUnicodeDnsName.Buffer,
  183. XtcbUnicodeDnsName.Length );
  184. Message->OriginatingNode.Buffer = (ULONG) (CopyTo - Base );
  185. Message->OriginatingNode.Length = XtcbUnicodeDnsName.Length ;
  186. Message->OriginatingNode.MaximumLength = XtcbUnicodeDnsName.Length ;
  187. CopyTo+= XtcbUnicodeDnsName.Length ;
  188. RtlCopyMemory(
  189. CopyTo,
  190. Group->Buffer,
  191. Group->Length );
  192. Message->Group.Buffer = (ULONG) (CopyTo - Base );
  193. Message->Group.Length = Group->Length ;
  194. Message->Group.MaximumLength = Group->Length ;
  195. //
  196. // Structure complete.
  197. //
  198. //
  199. // Do HMAC
  200. //
  201. *Token = (PUCHAR) Message ;
  202. *TokenLen = Message->Length ;
  203. return SEC_I_CONTINUE_NEEDED ;
  204. }
  205. BOOL
  206. XtcbParseInputToken(
  207. IN PUCHAR Token,
  208. IN ULONG TokenLength,
  209. OUT PSECURITY_STRING Client,
  210. OUT PSECURITY_STRING Group
  211. )
  212. {
  213. PXTCB_INIT_MESSAGE Message ;
  214. PWSTR Scan ;
  215. PUCHAR End ;
  216. ULONG Chars;
  217. UNICODE_STRING String = { 0 };
  218. BOOL Success = FALSE ;
  219. *Client = String ;
  220. *Group = String ;
  221. if ( TokenLength < sizeof( XTCB_INIT_MESSAGE ) )
  222. {
  223. goto ParseExit;
  224. }
  225. Message = (PXTCB_INIT_MESSAGE) Token ;
  226. if ( Message->Length != TokenLength )
  227. {
  228. goto ParseExit;
  229. }
  230. End = Token + Message->Length ;
  231. String.Length = Message->OriginatingNode.Length ;
  232. String.Buffer = (PWSTR) (Token + Message->OriginatingNode.Buffer );
  233. String.MaximumLength = String.Length ;
  234. if ( (PUCHAR) String.Buffer + String.Length > End )
  235. {
  236. goto ParseExit;
  237. }
  238. if ( !XtcbDupSecurityString( Client, &String ) )
  239. {
  240. goto ParseExit;
  241. }
  242. String.Length = Message->Group.Length ;
  243. String.Buffer = (PWSTR) (Token + Message->Group.Buffer );
  244. String.MaximumLength = String.Length ;
  245. if ( (PUCHAR) String.Buffer + String.Length > End )
  246. {
  247. goto ParseExit;
  248. }
  249. if ( !XtcbDupSecurityString( Group, &String ))
  250. {
  251. goto ParseExit ;
  252. }
  253. Success = TRUE ;
  254. ParseExit:
  255. if ( !Success )
  256. {
  257. if ( Client->Buffer )
  258. {
  259. LocalFree( Client->Buffer );
  260. }
  261. if ( Group->Buffer )
  262. {
  263. LocalFree( Group->Buffer );
  264. }
  265. }
  266. return Success ;
  267. }
  268. SECURITY_STATUS
  269. XtcbAuthenticateClient(
  270. PXTCB_CONTEXT Context,
  271. PUCHAR Token,
  272. ULONG TokenLength,
  273. PUCHAR ClientKey,
  274. PUCHAR GroupKey,
  275. PUCHAR MyKey
  276. )
  277. {
  278. PXTCB_INIT_MESSAGE Message ;
  279. PXTCB_PAC Pac ;
  280. PLSA_TOKEN_INFORMATION_V2 TokenInfo ;
  281. ULONG Size ;
  282. PTOKEN_GROUPS Groups ;
  283. PUCHAR Scan ;
  284. PUCHAR Sid1 ;
  285. NTSTATUS Status = SEC_E_INVALID_TOKEN ;
  286. PSID Sid ;
  287. PUCHAR Target ;
  288. ULONG i ;
  289. LUID LogonId ;
  290. UNICODE_STRING UserName ;
  291. UNICODE_STRING DomainName ;
  292. HANDLE hToken ;
  293. NTSTATUS SubStatus ;
  294. //
  295. // On entry, we know that the message is in general ok, that the general
  296. // bounds are ok, but not the PAC. So validate the PAC first before using
  297. // it.
  298. //
  299. Message = (PXTCB_INIT_MESSAGE) Token ;
  300. XtcbDeriveKeys(
  301. Context,
  302. MyKey,
  303. GroupKey,
  304. ClientKey,
  305. Message->Seed );
  306. //
  307. // Got the keys. Let's examine the PAC/
  308. //
  309. Pac = (PXTCB_PAC) ( Token + Message->PacOffset );
  310. if ( ( Pac->Length != Message->PacLength ) ||
  311. ( Message->PacLength > TokenLength ) ||
  312. ( Pac->Length > TokenLength ) )
  313. {
  314. return SEC_E_INVALID_TOKEN ;
  315. }
  316. //
  317. // Make sure offsets are within bounds. Each area
  318. // will still have to confirm that offset + length
  319. // within limits
  320. //
  321. if ( ( Pac->UserOffset > Pac->Length ) ||
  322. ( Pac->GroupOffset > Pac->Length ) ||
  323. ( Pac->RestrictionOffset > Pac->Length ) ||
  324. ( Pac->NameOffset > Pac->Length ) ||
  325. ( Pac->DomainOffset > Pac->Length ) ||
  326. ( Pac->CredentialOffset > Pac->Length ) )
  327. {
  328. return SEC_E_INVALID_TOKEN ;
  329. }
  330. //
  331. // 1000 is the current LSA limit. This is not exported to the packages
  332. // for some reason. This is hard coded right now, but needs to be
  333. // a global define, or a queryable value out of the LSA.
  334. //
  335. if ( Pac->GroupCount > 1000 )
  336. {
  337. return SEC_E_INVALID_TOKEN ;
  338. }
  339. //
  340. // Looks good, lets start assembling the token information.
  341. //
  342. if ( Pac->GroupLength + Pac->GroupOffset > Pac->Length )
  343. {
  344. return SEC_E_INVALID_TOKEN ;
  345. }
  346. Size = sizeof( LSA_TOKEN_INFORMATION_V2 ) + // Basic info
  347. ( Pac->GroupLength ) + // All the group SIDs
  348. ( Pac->UserLength ) + // User SID
  349. ROUND_UP_COUNT( ( ( Pac->GroupCount * sizeof( SID_AND_ATTRIBUTES ) ) +
  350. sizeof( TOKEN_GROUPS ) ), ALIGN_LPVOID ) ;
  351. TokenInfo = LsaTable->AllocateLsaHeap( Size );
  352. if ( TokenInfo == NULL )
  353. {
  354. return SEC_E_INSUFFICIENT_MEMORY ;
  355. }
  356. //
  357. // Now fill in this structure.
  358. //
  359. TokenInfo->ExpirationTime = XtcbExpirationTime ;
  360. TokenInfo->DefaultDacl.DefaultDacl = NULL ;
  361. TokenInfo->Privileges = NULL ;
  362. TokenInfo->Owner.Owner = NULL ;
  363. //
  364. // Set up initial pointers:
  365. //
  366. Groups = (PTOKEN_GROUPS) ( TokenInfo + 1 );
  367. Target = (PSID) ( (PUCHAR) Groups +
  368. ROUND_UP_COUNT( ( ( Pac->GroupCount * sizeof( SID_AND_ATTRIBUTES ) ) +
  369. sizeof( TOKEN_GROUPS ) ), ALIGN_LPVOID ) );
  370. //
  371. // Copy over the user SID
  372. //
  373. if ( Pac->UserOffset + Pac->UserLength > Pac->Length )
  374. {
  375. Status = SEC_E_INVALID_TOKEN ;
  376. goto Cleanup ;
  377. }
  378. Sid = (PSID) ((PUCHAR) Pac + Pac->UserOffset) ;
  379. if ( !RtlValidSid( Sid ) )
  380. {
  381. Status = SEC_E_INVALID_TOKEN ;
  382. goto Cleanup ;
  383. }
  384. if ( RtlLengthSid( Sid ) != Pac->UserLength )
  385. {
  386. Status = SEC_E_INVALID_TOKEN ;
  387. goto Cleanup ;
  388. }
  389. RtlCopySid( Pac->UserLength,
  390. (PSID) Target,
  391. Sid );
  392. Target += RtlLengthSid( Sid );
  393. TokenInfo->User.User.Sid = (PSID) Target ;
  394. TokenInfo->User.User.Attributes = 0 ;
  395. //
  396. // Now, do the groups. Since all the SIDs are in one
  397. // contiguous block, the plan is to copy them over
  398. // whole, then iterate through the list and fix up the
  399. // pointers in the group list.
  400. //
  401. RtlCopyMemory(
  402. Target,
  403. (PUCHAR) Pac + Pac->GroupOffset,
  404. Pac->GroupLength );
  405. Scan = Target ;
  406. Target += Pac->GroupLength ;
  407. i = 0 ;
  408. while ( Scan < Target )
  409. {
  410. Sid = (PSID) Scan ;
  411. if ( RtlValidSid( Sid ) )
  412. {
  413. //
  414. // This is an ok SID.
  415. //
  416. Groups->Groups[ i ].Sid = Sid ;
  417. Groups->Groups[ i ].Attributes = SE_GROUP_MANDATORY |
  418. SE_GROUP_ENABLED |
  419. SE_GROUP_ENABLED_BY_DEFAULT ;
  420. if ( i == 0 )
  421. {
  422. TokenInfo->PrimaryGroup.PrimaryGroup = Sid ;
  423. }
  424. i++ ;
  425. Scan += RtlLengthSid( Sid );
  426. }
  427. else
  428. {
  429. break;
  430. }
  431. }
  432. //
  433. // On exit, if Scan is less than Target, then we failed to
  434. // process all the SIDs. Bail out
  435. //
  436. if ( Scan < Target )
  437. {
  438. Status = SEC_E_INVALID_TOKEN ;
  439. goto Cleanup ;
  440. }
  441. //
  442. // Pull out the user name/etc
  443. //
  444. if ( Pac->NameLength + Pac->NameOffset > Pac->Length )
  445. {
  446. Status = SEC_E_INVALID_TOKEN ;
  447. goto Cleanup ;
  448. }
  449. UserName.Buffer = (PWSTR) ((PUCHAR) Pac + Pac->NameOffset);
  450. UserName.Length = (WORD) Pac->NameLength ;
  451. UserName.MaximumLength = UserName.Length ;
  452. if ( Pac->DomainLength + Pac->DomainOffset > Pac->Length )
  453. {
  454. Status = SEC_E_INVALID_TOKEN ;
  455. goto Cleanup ;
  456. }
  457. DomainName.Buffer = (PWSTR) ((PUCHAR) Pac + Pac->DomainOffset );
  458. DomainName.Length = (WORD) Pac->DomainLength ;
  459. DomainName.MaximumLength = DomainName.Length ;
  460. //
  461. // We've assembled the token info. Now, create the logon session
  462. //
  463. DebugLog(( DEB_TRACE, "Creating logon for %wZ\\%wZ\n", &DomainName,
  464. &UserName ));
  465. AllocateLocallyUniqueId( &LogonId );
  466. Status = LsaTable->CreateLogonSession( &LogonId );
  467. if ( !NT_SUCCESS( Status ) )
  468. {
  469. goto Cleanup ;
  470. }
  471. //
  472. // Create the token to represent this user:
  473. //
  474. Status = LsaTable->CreateToken(
  475. &LogonId,
  476. &XtcbSource,
  477. Network,
  478. TokenImpersonation,
  479. LsaTokenInformationV2,
  480. TokenInfo,
  481. NULL,
  482. &UserName,
  483. &DomainName,
  484. &XtcbComputerName,
  485. NULL,
  486. &hToken,
  487. &SubStatus );
  488. TokenInfo = NULL ;
  489. if ( NT_SUCCESS( Status ) )
  490. {
  491. Status = SubStatus ;
  492. }
  493. if ( NT_SUCCESS( Status ) )
  494. {
  495. Context->Token = hToken ;
  496. }
  497. Cleanup:
  498. if ( TokenInfo )
  499. {
  500. LsaTable->FreeLsaHeap( TokenInfo );
  501. }
  502. return Status ;
  503. }
  504. SECURITY_STATUS
  505. XtcbBuildReplyToken(
  506. PXTCB_CONTEXT Context,
  507. ULONG fContextReq,
  508. PSecBuffer pOutput
  509. )
  510. {
  511. PXTCB_INIT_MESSAGE_REPLY Reply ;
  512. NTSTATUS Status ;
  513. if ( fContextReq & ASC_REQ_ALLOCATE_MEMORY )
  514. {
  515. Reply = LsaTable->AllocateLsaHeap( sizeof( XTCB_INIT_MESSAGE_REPLY ) );
  516. }
  517. else
  518. {
  519. if ( pOutput->cbBuffer >= sizeof( XTCB_INIT_MESSAGE_REPLY ) )
  520. {
  521. Reply = pOutput->pvBuffer ;
  522. }
  523. else
  524. {
  525. Reply = NULL ;
  526. }
  527. }
  528. if ( Reply == NULL )
  529. {
  530. Status = SEC_E_INSUFFICIENT_MEMORY ;
  531. goto Cleanup ;
  532. }
  533. Reply->Version = 1;
  534. Reply->Length = sizeof( XTCB_INIT_MESSAGE_REPLY );
  535. CDGenerateRandomBits(
  536. Reply->ReplySeed,
  537. XTCB_SEED_LENGTH );
  538. pOutput->cbBuffer = sizeof( XTCB_INIT_MESSAGE_REPLY );
  539. pOutput->pvBuffer = Reply ;
  540. Reply = NULL ;
  541. Cleanup:
  542. if ( Reply )
  543. {
  544. LsaTable->FreeLsaHeap( Reply );
  545. }
  546. return Status ;
  547. }