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.

328 lines
9.2 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. encrypt.c
  5. Abstract:
  6. This module implements the routines for the NetWare
  7. redirector to mangle an objectid, challenge key and
  8. password such that a NetWare server will accept the
  9. password as valid.
  10. This program uses information published in Byte Magazine.
  11. Author:
  12. Colin Watson [ColinW] 15-Mar-1993
  13. Revision History:
  14. --*/
  15. #include <nt.h>
  16. #include <ntrtl.h>
  17. #include <nturtl.h>
  18. #include <windows.h>
  19. // #include <nwsutil.h>
  20. #include <usrprop.h>
  21. #include <crypt.h>
  22. // #include <ncpcomm.h>
  23. #define SWAPWORD(w) ((WORD)((w & 0xFF) << 8)|(WORD)(w >> 8))
  24. #define SWAPLONG(l) MAKELONG(SWAPWORD(HIWORD(l)),SWAPWORD(LOWORD(l)))
  25. #define NCP_WELL_KNOWN_SUPERVISOR_ID (ULONG) 0x00000001
  26. #define NCP_WELL_KNOWN_SUPERVISOR_ID_SWAPPED (ULONG) 0x01000000
  27. #define NCP_WELL_KNOWN_SUPERVISOR_ID_CHICAGO (ULONG) 0x00010000
  28. #define NCP_WELL_KNOWN_PSERVER_ID (ULONG) 0x00000002
  29. #define NCP_PSERVER_SIGNATURE L"PS_"
  30. #define SWAP_OBJECT_ID(id) (id == NCP_WELL_KNOWN_SUPERVISOR_ID) ? \
  31. NCP_WELL_KNOWN_SUPERVISOR_ID_SWAPPED : \
  32. MAKELONG(LOWORD(id),SWAPWORD(HIWORD(id)))
  33. UCHAR Table[] =
  34. {0x7,0x8,0x0,0x8,0x6,0x4,0xE,0x4,0x5,0xC,0x1,0x7,0xB,0xF,0xA,0x8,
  35. 0xF,0x8,0xC,0xC,0x9,0x4,0x1,0xE,0x4,0x6,0x2,0x4,0x0,0xA,0xB,0x9,
  36. 0x2,0xF,0xB,0x1,0xD,0x2,0x1,0x9,0x5,0xE,0x7,0x0,0x0,0x2,0x6,0x6,
  37. 0x0,0x7,0x3,0x8,0x2,0x9,0x3,0xF,0x7,0xF,0xC,0xF,0x6,0x4,0xA,0x0,
  38. 0x2,0x3,0xA,0xB,0xD,0x8,0x3,0xA,0x1,0x7,0xC,0xF,0x1,0x8,0x9,0xD,
  39. 0x9,0x1,0x9,0x4,0xE,0x4,0xC,0x5,0x5,0xC,0x8,0xB,0x2,0x3,0x9,0xE,
  40. 0x7,0x7,0x6,0x9,0xE,0xF,0xC,0x8,0xD,0x1,0xA,0x6,0xE,0xD,0x0,0x7,
  41. 0x7,0xA,0x0,0x1,0xF,0x5,0x4,0xB,0x7,0xB,0xE,0xC,0x9,0x5,0xD,0x1,
  42. 0xB,0xD,0x1,0x3,0x5,0xD,0xE,0x6,0x3,0x0,0xB,0xB,0xF,0x3,0x6,0x4,
  43. 0x9,0xD,0xA,0x3,0x1,0x4,0x9,0x4,0x8,0x3,0xB,0xE,0x5,0x0,0x5,0x2,
  44. 0xC,0xB,0xD,0x5,0xD,0x5,0xD,0x2,0xD,0x9,0xA,0xC,0xA,0x0,0xB,0x3,
  45. 0x5,0x3,0x6,0x9,0x5,0x1,0xE,0xE,0x0,0xE,0x8,0x2,0xD,0x2,0x2,0x0,
  46. 0x4,0xF,0x8,0x5,0x9,0x6,0x8,0x6,0xB,0xA,0xB,0xF,0x0,0x7,0x2,0x8,
  47. 0xC,0x7,0x3,0xA,0x1,0x4,0x2,0x5,0xF,0x7,0xA,0xC,0xE,0x5,0x9,0x3,
  48. 0xE,0x7,0x1,0x2,0xE,0x1,0xF,0x4,0xA,0x6,0xC,0x6,0xF,0x4,0x3,0x0,
  49. 0xC,0x0,0x3,0x6,0xF,0x8,0x7,0xB,0x2,0xD,0xC,0x6,0xA,0xA,0x8,0xD};
  50. UCHAR Keys[32] =
  51. {0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D,
  52. 0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35,
  53. 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11,
  54. 0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0};
  55. #define XorArray( DEST, SRC ) { \
  56. PULONG D = (PULONG)DEST; \
  57. PULONG S = (PULONG)SRC; \
  58. int i; \
  59. for ( i = 0; i <= 7 ; i++ ) { \
  60. D[i] ^= S[i]; \
  61. } \
  62. }
  63. int
  64. Scramble(
  65. int iSeed,
  66. UCHAR achBuffer[32]
  67. );
  68. VOID
  69. Shuffle(
  70. UCHAR *achObjectId,
  71. UCHAR *szUpperPassword,
  72. int iPasswordLen,
  73. UCHAR *achOutputBuffer
  74. )
  75. /*++
  76. Routine Description:
  77. This routine shuffles around the object ID with the password
  78. Arguments:
  79. IN achObjectId - Supplies the 4 byte user's bindery object id
  80. IN szUpperPassword - Supplies the user's uppercased password on the
  81. first call to process the password. On the second and third calls
  82. this parameter contains the OutputBuffer from the first call
  83. IN iPasswordLen - length of uppercased password
  84. OUT achOutputBuffer - Returns the 8 byte sub-calculation
  85. Return Value:
  86. none.
  87. --*/
  88. {
  89. int iTempIndex;
  90. int iOutputIndex;
  91. UCHAR achTemp[32];
  92. //
  93. // Truncate all trailing zeros from the password.
  94. //
  95. while (iPasswordLen > 0 && szUpperPassword[iPasswordLen-1] == 0 ) {
  96. iPasswordLen--;
  97. }
  98. //
  99. // Initialize the achTemp buffer. Initialization consists of taking
  100. // the password and dividing it up into chunks of 32. Any bytes left
  101. // over are the remainder and do not go into the initialization.
  102. //
  103. // achTemp[0] = szUpperPassword[0] ^ szUpperPassword[32] ^ szUpper...
  104. // achTemp[1] = szUpperPassword[1] ^ szUpperPassword[33] ^ szUpper...
  105. // etc.
  106. //
  107. if ( iPasswordLen > 32) {
  108. // At least one chunk of 32. Set the buffer to the first chunk.
  109. RtlCopyMemory( achTemp, szUpperPassword, 32 );
  110. szUpperPassword +=32; // Remove the first chunk
  111. iPasswordLen -=32;
  112. while ( iPasswordLen >= 32 ) {
  113. //
  114. // Xor this chunk with the characters already loaded into
  115. // achTemp.
  116. //
  117. XorArray( achTemp, szUpperPassword);
  118. szUpperPassword +=32; // Remove this chunk
  119. iPasswordLen -=32;
  120. }
  121. } else {
  122. // No chunks of 32 so set the buffer to zero's
  123. RtlZeroMemory( achTemp, sizeof(achTemp));
  124. }
  125. //
  126. // achTemp is now initialized. Load the remainder into achTemp.
  127. // The remainder is repeated to fill achTemp.
  128. //
  129. // The corresponding character from Keys is taken to seperate
  130. // each repitition.
  131. //
  132. // As an example, take the remainder "ABCDEFG". The remainder is expanded
  133. // to "ABCDEFGwABCDEFGxABCDEFGyABCDEFGz" where w is Keys[7],
  134. // x is Keys[15], y is Keys[23] and z is Keys[31].
  135. //
  136. //
  137. if (iPasswordLen > 0) {
  138. int iPasswordOffset = 0;
  139. for (iTempIndex = 0; iTempIndex < 32; iTempIndex++) {
  140. if (iPasswordLen == iPasswordOffset) {
  141. iPasswordOffset = 0;
  142. achTemp[iTempIndex] ^= Keys[iTempIndex];
  143. } else {
  144. achTemp[iTempIndex] ^= szUpperPassword[iPasswordOffset++];
  145. }
  146. }
  147. }
  148. //
  149. // achTemp has been loaded with the users password packed into 32
  150. // bytes. Now take the objectid that came from the server and use
  151. // that to munge every byte in achTemp.
  152. //
  153. for (iTempIndex = 0; iTempIndex < 32; iTempIndex++)
  154. achTemp[iTempIndex] ^= achObjectId[ iTempIndex & 3];
  155. Scramble( Scramble( 0, achTemp ), achTemp );
  156. //
  157. // Finally take pairs of bytes in achTemp and return the two
  158. // nibbles obtained from Table. The pairs of bytes used
  159. // are achTemp[n] and achTemp[n+16].
  160. //
  161. for (iOutputIndex = 0; iOutputIndex < 16; iOutputIndex++) {
  162. achOutputBuffer[iOutputIndex] =
  163. Table[achTemp[iOutputIndex << 1]] |
  164. (Table[achTemp[(iOutputIndex << 1) + 1]] << 4);
  165. }
  166. return;
  167. }
  168. int
  169. Scramble(
  170. int iSeed,
  171. UCHAR achBuffer[32]
  172. )
  173. /*++
  174. Routine Description:
  175. This routine scrambles around the contents of the buffer. Each buffer
  176. position is updated to include the contents of at least two character
  177. positions plus an EncryptKey value. The buffer is processed left to right
  178. and so if a character position chooses to merge with a buffer position
  179. to its left then this buffer position will include bits derived from at
  180. least 3 bytes of the original buffer contents.
  181. Arguments:
  182. IN iSeed
  183. IN OUT achBuffer[32]
  184. Return Value:
  185. none.
  186. --*/
  187. {
  188. int iBufferIndex;
  189. for (iBufferIndex = 0; iBufferIndex < 32; iBufferIndex++) {
  190. achBuffer[iBufferIndex] =
  191. (UCHAR)(
  192. ((UCHAR)(achBuffer[iBufferIndex] + iSeed)) ^
  193. ((UCHAR)( achBuffer[(iBufferIndex+iSeed) & 31] -
  194. Keys[iBufferIndex] )));
  195. iSeed += achBuffer[iBufferIndex];
  196. }
  197. return iSeed;
  198. }
  199. NTSTATUS
  200. ReturnNetwareForm(
  201. const char * pszSecretValue,
  202. DWORD dwUserId,
  203. const WCHAR * pchNWPassword,
  204. UCHAR * pchEncryptedNWPassword
  205. )
  206. /*++
  207. Routine Description:
  208. This routine takes the ObjectId and encrypts it with the user
  209. supplied password to develop a credential for the intermediate form.
  210. Arguments:
  211. DWORD dwUserId - Supplies the 4 byte user's object id
  212. const WCHAR * pchNWPassword - Supplies the user's password
  213. UCHAR * pchEncryptedNWPassword - 16 characters where the result goes.
  214. Return Value:
  215. none.
  216. --*/
  217. {
  218. DWORD dwStatus;
  219. DWORD chObjectId = SWAP_OBJECT_ID (dwUserId);
  220. UNICODE_STRING uniNWPassword;
  221. OEM_STRING oemNWPassword;
  222. //
  223. // shuffle actually uses 32 bytes, not just 16. It only returns 16 though.
  224. //
  225. UCHAR pszShuffledNWPassword[NT_OWF_PASSWORD_LENGTH * 2];
  226. uniNWPassword.Buffer = (WCHAR *) pchNWPassword;
  227. uniNWPassword.Length = (USHORT)(lstrlenW (pchNWPassword)*sizeof(WCHAR));
  228. uniNWPassword.MaximumLength = uniNWPassword.Length;
  229. if ((dwStatus = RtlUpcaseUnicodeStringToOemString (&oemNWPassword,
  230. &uniNWPassword,
  231. TRUE)) == STATUS_SUCCESS)
  232. {
  233. Shuffle((UCHAR *) &chObjectId, oemNWPassword.Buffer, oemNWPassword.Length, pszShuffledNWPassword);
  234. // Encrypt with LSA secret.
  235. dwStatus = RtlEncryptNtOwfPwdWithUserKey(
  236. (PNT_OWF_PASSWORD) pszShuffledNWPassword,
  237. (PUSER_SESSION_KEY) pszSecretValue,
  238. (PENCRYPTED_NT_OWF_PASSWORD) pchEncryptedNWPassword);
  239. }
  240. return (dwStatus);
  241. }