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.

595 lines
14 KiB

  1. /*++
  2. Copyright (c) 1987-1996 Microsoft Corporation
  3. Module Name:
  4. joincrypt.c
  5. Abstract:
  6. Authentication related functions required by netjoin
  7. Author:
  8. kumarp 29-May-1999
  9. Notes:
  10. The functions in this file used to be made avaialbe by
  11. including net\svcdlls\logonsrv\server\ssiauth.c. This led to several
  12. problems due to which, the functions are now copied from that file
  13. into this separate file.
  14. --*/
  15. #pragma hdrstop
  16. #define WKSTA_NETLOGON
  17. #define NETSETUP_JOIN
  18. #include <netsetp.h>
  19. #include <crypt.h>
  20. #include <ntsam.h>
  21. #include <logonmsv.h>
  22. #include <lmshare.h>
  23. #include <wincrypt.h>
  24. #include <netlogon.h>
  25. #include <logonp.h>
  26. #include <logonmsv.h>
  27. #include <ssi.h>
  28. #include <wchar.h>
  29. #include "joinp.h"
  30. HCRYPTPROV NlGlobalCryptProvider = (HCRYPTPROV)NULL;
  31. #define NlPrint(x)
  32. BOOLEAN
  33. NlGenerateRandomBits(
  34. PUCHAR Buffer,
  35. ULONG BufferLen
  36. );
  37. VOID
  38. NlComputeChallenge(
  39. OUT PNETLOGON_CREDENTIAL Challenge
  40. );
  41. VOID
  42. NlComputeCredentials(
  43. IN PNETLOGON_CREDENTIAL Challenge,
  44. OUT PNETLOGON_CREDENTIAL Credential,
  45. IN PNETLOGON_SESSION_KEY SessionKey
  46. );
  47. NTSTATUS
  48. NlMakeSessionKey(
  49. IN ULONG NegotiatedFlags,
  50. IN PNT_OWF_PASSWORD CryptKey,
  51. IN PNETLOGON_CREDENTIAL ClientChallenge,
  52. IN PNETLOGON_CREDENTIAL ServerChallenge,
  53. OUT PNETLOGON_SESSION_KEY SessionKey
  54. )
  55. /*++
  56. Routine Description:
  57. Build an encryption key for use in authentication for
  58. this RequestorName.
  59. Arguments:
  60. NegotiatedFlags - Determines the strength of the key.
  61. CryptKey -- The OWF password of the user account being used.
  62. ClientChallenge -- 8 byte (64 bit) number generated by caller
  63. ServerChallenge -- 8 byte (64 bit) number generated by primary
  64. SessionKey -- 16 byte (128 bit) number generated at both ends
  65. If the key strength is weak, the last 64 bits will be zero.
  66. Return Value:
  67. TRUE: Success
  68. FALSE: Failure
  69. NT status code.
  70. --*/
  71. {
  72. NTSTATUS Status;
  73. BLOCK_KEY BlockKey;
  74. NETLOGON_SESSION_KEY TempSessionKey;
  75. #ifndef NETSETUP_JOIN
  76. PCHECKSUM_BUFFER CheckBuffer = NULL;
  77. PCHECKSUM_FUNCTION Check;
  78. #endif // NETSETUP_JOIN
  79. //
  80. // Start with a zero key
  81. //
  82. RtlZeroMemory(SessionKey, sizeof(NETLOGON_SESSION_KEY));
  83. #ifdef NETSETUP_JOIN
  84. UNREFERENCED_PARAMETER( NegotiatedFlags );
  85. #else // NETSETUP_JOIN
  86. //
  87. // If the caller wants a strong key,
  88. // Compute it.
  89. //
  90. if ( NegotiatedFlags & NETLOGON_SUPPORTS_STRONG_KEY ) {
  91. // PCRYPTO_SYSTEM CryptSystem;
  92. UCHAR LocalChecksum[sizeof(*SessionKey)];
  93. // ULONG OutputSize;
  94. //
  95. // Initialize the checksum routines.
  96. //
  97. Status = CDLocateCheckSum( KERB_CHECKSUM_MD5_HMAC, &Check);
  98. if (!NT_SUCCESS(Status)) {
  99. NlPrint(( NL_CRITICAL,"NlMakeSessionKey: Failed to load checksum routines: 0x%x\n", Status));
  100. goto Cleanup;
  101. }
  102. ASSERT(Check->CheckSumSize <= sizeof(LocalChecksum));
  103. Status = Check->InitializeEx(
  104. (LPBYTE)CryptKey,
  105. sizeof( *CryptKey ),
  106. 0, // no message type
  107. &CheckBuffer );
  108. if (!NT_SUCCESS(Status)) {
  109. NlPrint(( NL_CRITICAL,"NlMakeSessionKey: Failed to initialize checksum routines: 0x%x\n", Status));
  110. goto Cleanup;
  111. }
  112. //
  113. // Sum in the client challenge, a constant, and the server challenge
  114. //
  115. Check->Sum( CheckBuffer,
  116. sizeof(*ClientChallenge),
  117. (PUCHAR)ClientChallenge );
  118. Check->Sum( CheckBuffer,
  119. sizeof(*ServerChallenge),
  120. (PUCHAR)ServerChallenge );
  121. //
  122. // Finish the checksum
  123. //
  124. (void) Check->Finalize(CheckBuffer, LocalChecksum);
  125. //
  126. // Copy the checksum into the message.
  127. //
  128. ASSERT( sizeof(LocalChecksum) >= sizeof(*SessionKey) );
  129. RtlCopyMemory( SessionKey, LocalChecksum, sizeof(*SessionKey) );
  130. //
  131. // Compute weaker (but backward compatible key)
  132. //
  133. } else {
  134. #endif // NETSETUP_JOIN
  135. //
  136. // we will have a 128 bit key (64 bit encrypted rest padded with 0s)
  137. //
  138. // SessionKey = C + P (arithmetic sum ignore carry)
  139. //
  140. *((unsigned long * ) SessionKey) =
  141. *((unsigned long * ) ClientChallenge) +
  142. *((unsigned long * ) ServerChallenge);
  143. *((unsigned long * )((LPBYTE)SessionKey + 4)) =
  144. *((unsigned long * )((LPBYTE)ClientChallenge + 4)) +
  145. *((unsigned long * )((LPBYTE)ServerChallenge + 4));
  146. //
  147. // CryptKey is our 16 byte key to be used as described in codespec
  148. // use first 7 bytes of CryptKey for first encryption
  149. //
  150. RtlCopyMemory( &BlockKey, CryptKey, BLOCK_KEY_LENGTH );
  151. Status = RtlEncryptBlock(
  152. (PCLEAR_BLOCK) SessionKey, // Clear text
  153. &BlockKey, // Key
  154. (PCYPHER_BLOCK) &TempSessionKey); // Cypher Block
  155. if ( !NT_SUCCESS( Status ) ) {
  156. goto Cleanup;
  157. }
  158. //
  159. // Further encrypt the encrypted "SessionKey" using upper 7 bytes
  160. //
  161. ASSERT( LM_OWF_PASSWORD_LENGTH == 2*BLOCK_KEY_LENGTH+2 );
  162. RtlCopyMemory( &BlockKey,
  163. ((PUCHAR)CryptKey) + 2 + BLOCK_KEY_LENGTH,
  164. BLOCK_KEY_LENGTH );
  165. Status = RtlEncryptBlock(
  166. (PCLEAR_BLOCK) &TempSessionKey, // Clear text
  167. &BlockKey, // Key
  168. (PCYPHER_BLOCK) SessionKey); // Cypher Block
  169. if ( !NT_SUCCESS( Status ) ) {
  170. goto Cleanup;
  171. }
  172. #ifndef NETSETUP_JOIN
  173. }
  174. #endif // NETSETUP_JOIN
  175. Cleanup:
  176. #ifndef NETSETUP_JOIN
  177. if (CheckBuffer != NULL) {
  178. Status = Check->Finish(&CheckBuffer);
  179. if (!NT_SUCCESS(Status)) {
  180. NlPrint(( NL_CRITICAL,"NlMakeSessionKey: Failed to finish checksum: 0x%x\n", Status));
  181. }
  182. }
  183. #endif // NETSETUP_JOIN
  184. return Status;
  185. }
  186. VOID
  187. NlComputeChallenge(
  188. OUT PNETLOGON_CREDENTIAL Challenge
  189. )
  190. /*++
  191. Routine Description:
  192. Generates a 64 bit challenge
  193. Arguments:
  194. Challenge - Returns the computed challenge
  195. Return Value:
  196. None.
  197. --*/
  198. {
  199. //
  200. // Use an ideal random bit generator.
  201. //
  202. if (!NlGenerateRandomBits( (LPBYTE)Challenge, sizeof(*Challenge) )) {
  203. NlPrint((NL_CRITICAL, "Can't NlGenerateRandomBits\n" ));
  204. }
  205. return;
  206. }
  207. VOID
  208. NlComputeCredentials(
  209. IN PNETLOGON_CREDENTIAL Challenge,
  210. OUT PNETLOGON_CREDENTIAL Credential,
  211. IN PNETLOGON_SESSION_KEY SessionKey
  212. )
  213. /*++
  214. Routine Description:
  215. Calculate the credentials by encrypting the 8 byte
  216. challenge with first 7 bytes of sessionkey and then
  217. further encrypting it by next 7 bytes of sessionkey.
  218. Arguments:
  219. Challenge - Supplies the 8 byte (64 bit) challenge
  220. Credential - Returns the 8 byte (64 bit) number generated
  221. SessionKey - Supplies 14 byte (112 bit) encryption key
  222. The buffer is 16 bytes (128 bits) long. For a weak key, the trailing 8 bytes
  223. are zero. For a strong key, this routine ingored that trailing 2 bytes of
  224. useful key.
  225. Return Value:
  226. NONE
  227. --*/
  228. {
  229. NTSTATUS Status;
  230. BLOCK_KEY BlockKey;
  231. CYPHER_BLOCK IntermediateBlock;
  232. RtlZeroMemory(Credential, sizeof(*Credential));
  233. //
  234. // use first 7 bytes of SessionKey for first encryption
  235. //
  236. RtlCopyMemory( &BlockKey, SessionKey, BLOCK_KEY_LENGTH );
  237. Status = RtlEncryptBlock( (PCLEAR_BLOCK) Challenge, // Cleartext
  238. &BlockKey, // Key
  239. &IntermediateBlock ); // Cypher Block
  240. ASSERT( NT_SUCCESS(Status) );
  241. //
  242. // further encrypt the encrypted Credential using next 7 bytes
  243. //
  244. RtlCopyMemory( &BlockKey,
  245. ((PUCHAR)SessionKey) + BLOCK_KEY_LENGTH,
  246. BLOCK_KEY_LENGTH );
  247. Status = RtlEncryptBlock( (PCLEAR_BLOCK) &IntermediateBlock, // Cleartext
  248. &BlockKey, // Key
  249. Credential ); // Cypher Block
  250. ASSERT( NT_SUCCESS(Status) );
  251. return;
  252. }
  253. BOOLEAN
  254. NlGenerateRandomBits(
  255. PUCHAR Buffer,
  256. ULONG BufferLen
  257. )
  258. /*++
  259. Routine Description:
  260. Generates random bits
  261. Arguments:
  262. pBuffer - Buffer to fill
  263. cbBuffer - Number of bytes in buffer
  264. Return Value:
  265. Status of the operation.
  266. --*/
  267. {
  268. if( !CryptGenRandom( NlGlobalCryptProvider, BufferLen, ( LPBYTE )Buffer ) )
  269. {
  270. NlPrint((NL_CRITICAL, "CryptGenRandom failed with %lu\n", GetLastError() ));
  271. return FALSE;
  272. }
  273. return TRUE;
  274. }
  275. NET_API_STATUS
  276. NET_API_FUNCTION
  277. NetpValidateMachineAccount(
  278. IN LPWSTR lpDc,
  279. IN LPWSTR lpDomain,
  280. IN LPWSTR lpMachine,
  281. IN LPWSTR lpPassword
  282. )
  283. /*++
  284. Routine Description:
  285. Performs validation that the machine account exists and has the same password we expect
  286. The internals of this function were lifted completely from SimulateFullSync() in
  287. ..\svcdlls\logonsrv\server\nltest.c,
  288. Arguments:
  289. lpDc -- Name of the Dc
  290. lpDomain -- Name of the domain
  291. lpMachine -- Current machine (Netbios name)
  292. lpPassword -- Password that should be on the account.
  293. Returns:
  294. NERR_Success -- Success
  295. --*/
  296. {
  297. NTSTATUS Status = STATUS_SUCCESS;
  298. NETLOGON_CREDENTIAL ServerChallenge;
  299. NETLOGON_CREDENTIAL ClientChallenge;
  300. NETLOGON_CREDENTIAL ComputedServerCredential;
  301. NETLOGON_CREDENTIAL ReturnedServerCredential;
  302. NETLOGON_CREDENTIAL AuthenticationSeed;
  303. NETLOGON_SESSION_KEY SessionKey;
  304. WCHAR AccountName[SSI_ACCOUNT_NAME_LENGTH+1];
  305. UNICODE_STRING Password;
  306. NT_OWF_PASSWORD NtOwfPassword;
  307. UNREFERENCED_PARAMETER( lpDomain );
  308. ASSERT( lpPassword );
  309. //
  310. // Validate the machine name length to avoid buffer overrun
  311. //
  312. if ( lpMachine == NULL || wcslen(lpMachine) > CNLEN ) {
  313. return ERROR_INVALID_PARAMETER;
  314. }
  315. //
  316. // initialize Crypto Provider.
  317. // (required for NlComputeChallenge).
  318. //
  319. if ( !CryptAcquireContext(
  320. &NlGlobalCryptProvider,
  321. NULL,
  322. NULL,
  323. PROV_RSA_FULL,
  324. CRYPT_VERIFYCONTEXT
  325. ))
  326. {
  327. NlGlobalCryptProvider = (HCRYPTPROV)NULL;
  328. return (NET_API_STATUS)GetLastError();
  329. }
  330. //
  331. // Prepare our challenge
  332. //
  333. NlComputeChallenge( &ClientChallenge );
  334. //
  335. // free cryptographic service provider.
  336. //
  337. if ( NlGlobalCryptProvider ) {
  338. CryptReleaseContext( NlGlobalCryptProvider, 0 );
  339. NlGlobalCryptProvider = (HCRYPTPROV)NULL;
  340. }
  341. //
  342. // Get the primary's challenge
  343. //
  344. Status = I_NetServerReqChallenge(lpDc,
  345. lpMachine,
  346. &ClientChallenge,
  347. &ServerChallenge );
  348. if ( !NT_SUCCESS( Status ) ) {
  349. goto ValidateMachineAccountError;
  350. }
  351. Password.Length = Password.MaximumLength = wcslen(lpPassword) * sizeof(WCHAR);
  352. Password.Buffer = lpPassword;
  353. //
  354. // Compute the NT OWF password for this user.
  355. //
  356. Status = RtlCalculateNtOwfPassword( &Password, &NtOwfPassword );
  357. if ( !NT_SUCCESS( Status ) ) {
  358. goto ValidateMachineAccountError;
  359. }
  360. //
  361. // Actually compute the session key given the two challenges and the
  362. // password.
  363. //
  364. NlMakeSessionKey(
  365. #if(_WIN32_WINNT >= 0x0500)
  366. 0,
  367. #endif
  368. &NtOwfPassword,
  369. &ClientChallenge,
  370. &ServerChallenge,
  371. &SessionKey );
  372. //
  373. // Prepare credentials using our challenge.
  374. //
  375. NlComputeCredentials( &ClientChallenge,
  376. &AuthenticationSeed,
  377. &SessionKey );
  378. //
  379. // Send these credentials to primary. The primary will compute
  380. // credentials using the challenge supplied by us and compare
  381. // with these. If both match then it will compute credentials
  382. // using its challenge and return it to us for verification
  383. //
  384. wcscpy( AccountName, lpMachine );
  385. wcscat( AccountName, SSI_ACCOUNT_NAME_POSTFIX);
  386. Status = I_NetServerAuthenticate( lpDc,
  387. AccountName,
  388. WorkstationSecureChannel,
  389. lpMachine,
  390. &AuthenticationSeed,
  391. &ReturnedServerCredential );
  392. if ( !NT_SUCCESS( Status ) ) {
  393. goto ValidateMachineAccountError;
  394. }
  395. //
  396. // The DC returned a server credential to us,
  397. // ensure the server credential matches the one we would compute.
  398. //
  399. NlComputeCredentials( &ServerChallenge,
  400. &ComputedServerCredential,
  401. &SessionKey);
  402. if (RtlCompareMemory( &ReturnedServerCredential,
  403. &ComputedServerCredential,
  404. sizeof(ReturnedServerCredential)) !=
  405. sizeof(ReturnedServerCredential)) {
  406. Status = STATUS_ACCESS_DENIED;
  407. }
  408. ValidateMachineAccountError:
  409. if ( Status == STATUS_ACCESS_DENIED ) {
  410. Status = STATUS_LOGON_FAILURE;
  411. }
  412. if ( !NT_SUCCESS( Status ) ) {
  413. NetpLog(( "Failed to validate machine account for %ws against %ws: 0x%lx\n",
  414. lpMachine, lpDc, Status ));
  415. }
  416. return( RtlNtStatusToDosError( Status ) );
  417. }