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.

502 lines
12 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // radpack.cpp
  8. //
  9. // SYNOPSIS
  10. //
  11. // Defines functions for packing and unpacking RADIUS packets.
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 02/01/2000 Original version.
  16. //
  17. ///////////////////////////////////////////////////////////////////////////////
  18. #include <proxypch.h>
  19. #include <align.h>
  20. #include <md5.h>
  21. #include <hmac.h>
  22. #include <radpack.h>
  23. // Return the number of bytes required to round 'length' to a multiple of 16.
  24. inline ULONG GetPaddingLength16(ULONG length) throw ()
  25. {
  26. return ROUND_UP_COUNT(length, 16) - length;
  27. }
  28. // Returns 'true' if attr is a Microsoft VSA. The attribute must be of type 26.
  29. inline bool isMicrosoftVSA(const RadiusAttribute& attr) throw ()
  30. {
  31. return attr.length >= 6 && !memcmp(attr.value, "\x00\x00\x01\x37", 4);
  32. }
  33. // Returns 'true' if attr is a Cisco AV-Pair VSA containing a LEAP session key.
  34. // The attribute must be of type 26.
  35. inline bool isCiscoLeapSessionKey(const RadiusAttribute& attr) throw ()
  36. {
  37. return (attr.length == 57) &&
  38. (memcmp(
  39. attr.value,
  40. "\x00\x00\x00\x09\x01\x35leap:session-key=",
  41. 23
  42. ) == 0);
  43. }
  44. // Pack a 16-bit integer into a buffer.
  45. inline void InsertUInt16(PBYTE p, USHORT value) throw ()
  46. {
  47. p[0] = (BYTE)(value >> 8);
  48. p[1] = (BYTE)value;
  49. }
  50. // Unpack a 16-bit integer into a buffer.
  51. inline USHORT ExtractUInt16(const BYTE* p) throw ()
  52. {
  53. return (USHORT)(p[0] << 8) | (USHORT)p[1];
  54. }
  55. // Returns the number of bytes of padding that should be added when packing the
  56. // attribute.
  57. ULONG
  58. WINAPI
  59. GetPaddingLength(
  60. const RadiusAttribute& attr
  61. ) throw ()
  62. {
  63. switch (attr.type)
  64. {
  65. case RADIUS_USER_PASSWORD:
  66. return GetPaddingLength16(attr.length);
  67. case RADIUS_TUNNEL_PASSWORD:
  68. // Subtract 1 byte for the tag and 2 for the salt.
  69. return GetPaddingLength16(attr.length - 3);
  70. case RADIUS_VENDOR_SPECIFIC:
  71. {
  72. if (isMicrosoftVSA(attr))
  73. {
  74. switch (attr.value[4])
  75. {
  76. case MS_CHAP_MPPE_SEND_KEYS:
  77. case MS_CHAP_MPPE_RECV_KEYS:
  78. // Vendor-Id = 4 bytes
  79. // Vendor-Type = 1 byte
  80. // Vendor-Length = 1 byte
  81. // Salt = 2 bytes
  82. return GetPaddingLength16(attr.length - 8);
  83. }
  84. }
  85. break;
  86. }
  87. }
  88. return 0;
  89. }
  90. // Returns information about how to encrypt/decrypt an attribute.
  91. VOID
  92. WINAPI
  93. GetCryptParameters(
  94. const RadiusAttribute& attr,
  95. CryptParameters& parms
  96. ) throw ()
  97. {
  98. memset(&parms, 0, sizeof(parms));
  99. switch (attr.type)
  100. {
  101. case RADIUS_USER_PASSWORD:
  102. {
  103. parms.encrypted = TRUE;
  104. break;
  105. }
  106. case RADIUS_TUNNEL_PASSWORD:
  107. {
  108. parms.encrypted = TRUE;
  109. parms.salted = TRUE;
  110. parms.offset = 1; // Skip the tag.
  111. break;
  112. }
  113. case RADIUS_VENDOR_SPECIFIC:
  114. {
  115. if (isMicrosoftVSA(attr))
  116. {
  117. switch (attr.value[4])
  118. {
  119. case MS_CHAP_MPPE_KEYS:
  120. {
  121. parms.encrypted = TRUE;
  122. parms.offset = 6; // Skip the VSA header.
  123. break;
  124. }
  125. case MS_CHAP_MPPE_SEND_KEYS:
  126. case MS_CHAP_MPPE_RECV_KEYS:
  127. {
  128. parms.encrypted = TRUE;
  129. parms.salted = TRUE;
  130. parms.offset = 6; // Skip the VSA header.
  131. break;
  132. }
  133. }
  134. }
  135. else if (isCiscoLeapSessionKey(attr))
  136. {
  137. parms.encrypted = TRUE;
  138. parms.salted = TRUE;
  139. parms.offset = 23;
  140. }
  141. break;
  142. }
  143. }
  144. }
  145. ULONG
  146. WINAPI
  147. GetBufferSizeRequired(
  148. const RadiusPacket& packet,
  149. const RadiusAttribute* proxyState,
  150. BOOL alwaysSign
  151. ) throw ()
  152. {
  153. // We'll look for the signature as we iterate through the attributes.
  154. BOOL hasSignature = FALSE;
  155. // We always need 20 bytes for the header.
  156. ULONG nbyte = 20;
  157. // Iterate through the attributes.
  158. for (const RadiusAttribute* attr = packet.begin; attr != packet.end; ++attr)
  159. {
  160. nbyte += 2; // Two bytes for type & length.
  161. nbyte += attr->length;
  162. nbyte += GetPaddingLength(*attr);
  163. if (attr->type == RADIUS_SIGNATURE)
  164. {
  165. hasSignature = TRUE;
  166. }
  167. else if (attr->type == RADIUS_EAP_MESSAGE)
  168. {
  169. alwaysSign = TRUE;
  170. }
  171. }
  172. // Reserve space for the Proxy-State attribute.
  173. if (proxyState) { nbyte += proxyState->length + 2; }
  174. // Reserve space for the signature if necessary.
  175. if (alwaysSign && !hasSignature && packet.code == RADIUS_ACCESS_REQUEST)
  176. {
  177. nbyte += 18;
  178. }
  179. return nbyte <= 4096 ? nbyte : 0;
  180. }
  181. VOID
  182. WINAPI
  183. PackBuffer(
  184. const BYTE* secret,
  185. ULONG secretLength,
  186. RadiusPacket& packet,
  187. const RadiusAttribute* proxyState,
  188. BOOL alwaysSign,
  189. BYTE* buffer
  190. ) throw ()
  191. {
  192. // Set up a cursor into the buffer.
  193. BYTE* dst = buffer;
  194. // Pack the header.
  195. *dst++ = packet.code;
  196. *dst++ = packet.identifier;
  197. InsertUInt16(dst, packet.length);
  198. dst += 2;
  199. // Pack the authenticator.
  200. if (packet.code == RADIUS_ACCESS_REQUEST)
  201. {
  202. memcpy(dst, packet.authenticator, 16);
  203. }
  204. else
  205. {
  206. memset(dst, 0, 16);
  207. }
  208. dst += 16;
  209. // We'll look for the signature as we iterate through the attributes.
  210. BYTE* signature = NULL;
  211. for (const RadiusAttribute* attr = packet.begin; attr != packet.end; ++attr)
  212. {
  213. // Pack the type.
  214. *dst++ = attr->type;
  215. // Pack the length.
  216. ULONG paddingLength = GetPaddingLength(*attr);
  217. ULONG valueLength = attr->length + paddingLength;
  218. *dst++ = (BYTE)(2 + valueLength);
  219. if (attr->type == RADIUS_SIGNATURE)
  220. {
  221. signature = dst;
  222. }
  223. else if (attr->type == RADIUS_EAP_MESSAGE)
  224. {
  225. alwaysSign = TRUE;
  226. }
  227. // Pack the value ...
  228. memcpy(dst, attr->value, attr->length);
  229. // ... and add the padding.
  230. memset(dst + attr->length, 0, paddingLength);
  231. // Do we need to encrypt this attribute ?
  232. CryptParameters parms;
  233. GetCryptParameters(*attr, parms);
  234. if (parms.encrypted)
  235. {
  236. // Yes.
  237. IASRadiusCrypt(
  238. TRUE,
  239. parms.salted,
  240. secret,
  241. secretLength,
  242. buffer + 4,
  243. dst + parms.offset,
  244. valueLength - parms.offset
  245. );
  246. }
  247. dst += valueLength;
  248. }
  249. // Add the Proxy-State
  250. if (proxyState)
  251. {
  252. *dst++ = proxyState->type;
  253. *dst++ = proxyState->length + 2;
  254. memcpy(dst, proxyState->value, proxyState->length);
  255. dst += proxyState->length;
  256. }
  257. if (packet.code == RADIUS_ACCESS_REQUEST)
  258. {
  259. if (alwaysSign && (signature == 0))
  260. {
  261. *dst++ = RADIUS_SIGNATURE;
  262. *dst++ = 18;
  263. signature = dst;
  264. }
  265. if (signature != 0)
  266. {
  267. // Compute the signature.
  268. memset(signature, 0, 16);
  269. HMACMD5_CTX context;
  270. HMACMD5Init(&context, (BYTE*)secret, secretLength);
  271. HMACMD5Update(&context, buffer, packet.length);
  272. HMACMD5Final(&context, signature);
  273. }
  274. }
  275. else
  276. {
  277. // For everything but Access-Request, we compute the authenticator.
  278. MD5_CTX context;
  279. MD5Init(&context);
  280. MD5Update(&context, buffer, packet.length);
  281. MD5Update(&context, secret, secretLength);
  282. MD5Final(&context);
  283. memcpy(buffer + 4, context.digest, 16);
  284. }
  285. }
  286. RadiusAttribute*
  287. WINAPI
  288. FindAttribute(
  289. const RadiusPacket& packet,
  290. BYTE type
  291. )
  292. {
  293. for (const RadiusAttribute* i = packet.begin; i != packet.end; ++i)
  294. {
  295. if (i->type == type) { return const_cast<RadiusAttribute*>(i); }
  296. }
  297. return NULL;
  298. }
  299. ULONG
  300. WINAPI
  301. GetAttributeCount(
  302. const BYTE* buffer,
  303. ULONG bufferLength
  304. ) throw ()
  305. {
  306. if (bufferLength >= 20 && ExtractUInt16(buffer + 2) == bufferLength)
  307. {
  308. ULONG count = 0;
  309. const BYTE* end = buffer + bufferLength;
  310. for (const BYTE* p = buffer + 20; p < end; p += p[1])
  311. {
  312. ++count;
  313. }
  314. if (p == end) { return count; }
  315. }
  316. return MALFORMED_PACKET;
  317. }
  318. VOID
  319. WINAPI
  320. UnpackBuffer(
  321. BYTE* buffer,
  322. ULONG bufferLength,
  323. RadiusPacket& packet
  324. ) throw ()
  325. {
  326. // Set up a cursor into the buffer.
  327. BYTE* src = buffer;
  328. packet.code = *src++;
  329. packet.identifier = *src++;
  330. packet.length = ExtractUInt16(src);
  331. src +=2;
  332. packet.authenticator = src;
  333. src += 16;
  334. RadiusAttribute* dst = packet.begin;
  335. const BYTE* end = buffer + bufferLength;
  336. while (src < end)
  337. {
  338. dst->type = *src++;
  339. dst->length = *src++ - 2;
  340. dst->value = src;
  341. src += dst->length;
  342. ++dst;
  343. }
  344. }
  345. BYTE*
  346. WINAPI
  347. FindRawAttribute(
  348. BYTE type,
  349. BYTE* buffer,
  350. ULONG bufferLength
  351. )
  352. {
  353. BYTE* end = buffer + bufferLength;
  354. for (BYTE* p = buffer + 20; p < buffer + bufferLength; p += p[1])
  355. {
  356. if (*p == type) { return p; }
  357. }
  358. return NULL;
  359. }
  360. AuthResult
  361. WINAPI
  362. AuthenticateAndDecrypt(
  363. const BYTE* requestAuthenticator,
  364. const BYTE* secret,
  365. ULONG secretLength,
  366. BYTE* buffer,
  367. ULONG bufferLength,
  368. RadiusPacket& packet
  369. ) throw ()
  370. {
  371. AuthResult result = AUTH_UNKNOWN;
  372. if (!requestAuthenticator) { requestAuthenticator = buffer + 4; }
  373. // Check the authenticator for everything but Access-Request.
  374. if (buffer[0] != RADIUS_ACCESS_REQUEST)
  375. {
  376. MD5_CTX context;
  377. MD5Init(&context);
  378. MD5Update(&context, buffer, 4);
  379. MD5Update(&context, requestAuthenticator, 16);
  380. MD5Update(&context, buffer + 20, bufferLength - 20);
  381. MD5Update(&context, secret, secretLength);
  382. MD5Final(&context);
  383. if (memcmp(context.digest, buffer + 4, 16))
  384. {
  385. return AUTH_BAD_AUTHENTICATOR;
  386. }
  387. result = AUTH_AUTHENTIC;
  388. }
  389. // Look for a signature.
  390. BYTE* signature = FindRawAttribute(
  391. RADIUS_SIGNATURE,
  392. buffer,
  393. bufferLength
  394. );
  395. if (signature)
  396. {
  397. if (signature[1] != 18) { return AUTH_BAD_SIGNATURE; }
  398. signature += 2;
  399. BYTE sent[16];
  400. memcpy(sent, signature, 16);
  401. memset(signature, 0, 16);
  402. HMACMD5_CTX context;
  403. HMACMD5Init(&context, (BYTE*)secret, secretLength);
  404. HMACMD5Update(&context, buffer, 4);
  405. HMACMD5Update(&context, (BYTE*)requestAuthenticator, 16);
  406. HMACMD5Update(&context, buffer + 20, bufferLength - 20);
  407. HMACMD5Final(&context, signature);
  408. if (memcmp(signature, sent, 16)) { return AUTH_BAD_SIGNATURE; }
  409. result = AUTH_AUTHENTIC;
  410. }
  411. else if (FindRawAttribute(RADIUS_EAP_MESSAGE, buffer, bufferLength))
  412. {
  413. return AUTH_MISSING_SIGNATURE;
  414. }
  415. // The buffer is authentic, so decrypt the attributes.
  416. for (const RadiusAttribute* attr = packet.begin; attr != packet.end; ++attr)
  417. {
  418. // Do we need to decrypt this attribute ?
  419. CryptParameters parms;
  420. GetCryptParameters(*attr, parms);
  421. if (parms.encrypted)
  422. {
  423. // Yes.
  424. IASRadiusCrypt(
  425. FALSE,
  426. parms.salted,
  427. secret,
  428. secretLength,
  429. requestAuthenticator,
  430. attr->value + parms.offset,
  431. attr->length - parms.offset
  432. );
  433. }
  434. }
  435. return result;
  436. }