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.

500 lines
12 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2000, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // translate.cpp
  8. //
  9. // SYNOPSIS
  10. //
  11. // Defines the class Translator.
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 02/04/2000 Original version.
  16. // 04/17/2000 Add support for UTCTime.
  17. //
  18. ///////////////////////////////////////////////////////////////////////////////
  19. #include <proxypch.h>
  20. #include <iasutil.h>
  21. #include <attrdnry.h>
  22. #include <radpack.h>
  23. #include <translate.h>
  24. //////////
  25. // The offset between the UNIX and NT epochs.
  26. //////////
  27. const ULONG64 UNIX_EPOCH = 116444736000000000ui64;
  28. ///////////////////////////////////////////////////////////////////////////////
  29. //
  30. // CLASS
  31. //
  32. // ByteSource
  33. //
  34. // DESCRIPTION
  35. //
  36. // Simple class for extracting bytes from an octet string.
  37. //
  38. ///////////////////////////////////////////////////////////////////////////////
  39. class ByteSource
  40. {
  41. public:
  42. ByteSource(const BYTE* buf, ULONG buflen) throw ()
  43. : next(buf), last(buf + buflen) { }
  44. // Returns true if there are any bytes remaining.
  45. bool more() const throw ()
  46. {
  47. return next != last;
  48. }
  49. // Extracts 'nbyte' bytes.
  50. const BYTE* extract(ULONG nbyte)
  51. {
  52. const BYTE* retval = next;
  53. // Update the cursor.
  54. next += nbyte;
  55. // Did we overflow ?
  56. if (next > last) { _com_issue_error(E_INVALIDARG); }
  57. return retval;
  58. }
  59. ULONG remaining() const throw ()
  60. {
  61. return (ULONG)(last - next);
  62. }
  63. protected:
  64. const BYTE* next; // The next byte in the stream.
  65. const BYTE* last; // The end of the stream.
  66. private:
  67. // Not implemented.
  68. ByteSource(const ByteSource&);
  69. ByteSource& operator=(const ByteSource&);
  70. };
  71. HRESULT Translator::FinalConstruct() throw ()
  72. {
  73. return dnary.FinalConstruct();
  74. }
  75. void Translator::toRadius(
  76. IASATTRIBUTE& src,
  77. IASAttributeVector& dst
  78. ) const
  79. {
  80. if (src.dwId > 0 && src.dwId < 256)
  81. {
  82. // This is already a RADIUS attribute, so all we have to do is convert
  83. // the value to an octet string.
  84. if (src.Value.itType == IASTYPE_OCTET_STRING)
  85. {
  86. // It's already an octet string, so just scatter into dst.
  87. scatter(0, src, dst);
  88. }
  89. else
  90. {
  91. // Convert to an octet string ...
  92. IASAttribute attr(true);
  93. encode(0, src, attr);
  94. // ... and scatter into dst.
  95. scatter(0, *attr, dst);
  96. }
  97. }
  98. else
  99. {
  100. // Look up the attribute definition.
  101. const AttributeDefinition* def = dnary.findByID(src.dwId);
  102. // We only process VSAs. At this point, anything else is an internal
  103. // attribute that has no RADIUS representation.
  104. if (def && def->vendorID)
  105. {
  106. // Allocate an attribute for the VSA.
  107. IASAttribute attr(true);
  108. // USR uses a different header than everybody else.
  109. ULONG headerLength = (def->vendorID != 429) ? 6 : 8;
  110. // Encode the data.
  111. ULONG dataLength = encode(headerLength, src, attr);
  112. // Pack the Vendor-Id.
  113. PBYTE buf = attr->Value.OctetString.lpValue;
  114. IASInsertDWORD(buf, def->vendorID);
  115. buf += 4;
  116. // Pack the Vendor-Type and Vendor-Length;
  117. if (def->vendorID != 429)
  118. {
  119. *buf++ = (BYTE)def->vendorType;
  120. *buf++ = (BYTE)(dataLength + 2);
  121. }
  122. else
  123. {
  124. IASInsertDWORD(buf, def->vendorType);
  125. buf += 4;
  126. }
  127. // Mark it as a VSA.
  128. attr->dwId = RADIUS_ATTRIBUTE_VENDOR_SPECIFIC;
  129. // Scatter into multiple attributes if necessary.
  130. scatter(headerLength, *attr, dst);
  131. }
  132. }
  133. }
  134. void Translator::fromRadius(
  135. const RadiusAttribute& src,
  136. DWORD flags,
  137. IASAttributeVector& dst
  138. )
  139. {
  140. if (src.type != RADIUS_ATTRIBUTE_VENDOR_SPECIFIC)
  141. {
  142. // Look this up in the dictionary.
  143. const AttributeDefinition* def = dnary.findByID(src.type);
  144. // If we don't recognize the attribute, treat it as an octet string.
  145. IASTYPE syntax = def ? (IASTYPE)def->syntax : IASTYPE_OCTET_STRING;
  146. // Create the new attribute.
  147. IASAttribute attr(true);
  148. attr->dwId = src.type;
  149. attr->dwFlags = flags;
  150. decode(syntax, src.value, src.length, attr);
  151. // Add to the destination vector.
  152. dst.push_back(attr);
  153. }
  154. else
  155. {
  156. // Create a byte source from the attribute value.
  157. ByteSource bytes(src.value, src.length);
  158. // Extract the vendor ID.
  159. ULONG vendorID = IASExtractDWORD(bytes.extract(4));
  160. // Loop through the value and convert each sub-VSA.
  161. do
  162. {
  163. // Extract the Vendor-Type and the data length.
  164. ULONG type, length;
  165. if (vendorID != 429)
  166. {
  167. type = *bytes.extract(1);
  168. length = *bytes.extract(1) - 2;
  169. }
  170. else
  171. {
  172. type = IASExtractDWORD(bytes.extract(4));
  173. length = bytes.remaining();
  174. }
  175. // Do we have this VSA in our dictionary ?
  176. const AttributeDefinition* def = dnary.findByVendorInfo(
  177. vendorID,
  178. type
  179. );
  180. if (!def)
  181. {
  182. // No, so we'll just leave it 'as is'.
  183. IASAttribute attr(true);
  184. attr->dwId = RADIUS_ATTRIBUTE_VENDOR_SPECIFIC;
  185. attr->dwFlags = flags;
  186. attr.setOctetString(src.length, src.value);
  187. dst.push_back(attr);
  188. break;
  189. }
  190. // Yes, so we can decode this properly.
  191. IASAttribute attr(true);
  192. attr->dwId = def->id;
  193. attr->dwFlags = flags;
  194. decode((IASTYPE)def->syntax, bytes.extract(length), length, attr);
  195. dst.push_back(attr);
  196. } while (bytes.more());
  197. }
  198. }
  199. void Translator::decode(
  200. IASTYPE dstType,
  201. const BYTE* src,
  202. ULONG srclen,
  203. IASAttribute& dst
  204. )
  205. {
  206. // Switch based on the destination type.
  207. switch (dstType)
  208. {
  209. case IASTYPE_BOOLEAN:
  210. {
  211. if (srclen != 4) { _com_issue_error(E_INVALIDARG); }
  212. dst->Value.Boolean = IASExtractDWORD(src) ? TRUE : FALSE;
  213. break;
  214. }
  215. case IASTYPE_INTEGER:
  216. case IASTYPE_ENUM:
  217. case IASTYPE_INET_ADDR:
  218. {
  219. if (srclen != 4) { _com_issue_error(E_INVALIDARG); }
  220. dst->Value.Integer = IASExtractDWORD(src);
  221. break;
  222. }
  223. case IASTYPE_UTC_TIME:
  224. {
  225. if (srclen != 4) { _com_issue_error(E_INVALIDARG); }
  226. // Extract the UNIX time.
  227. ULONG64 val = IASExtractDWORD(src);
  228. // Convert from seconds to 100 nsec intervals.
  229. val *= 10000000;
  230. // Shift to the NT epoch.
  231. val += 116444736000000000ui64;
  232. // Split into the high and low DWORDs.
  233. dst->Value.UTCTime.dwLowDateTime = (DWORD)val;
  234. dst->Value.UTCTime.dwHighDateTime = (DWORD)(val >> 32);
  235. break;
  236. }
  237. case IASTYPE_STRING:
  238. {
  239. dst.setString(srclen, src);
  240. break;
  241. }
  242. default:
  243. {
  244. dst.setOctetString(srclen, src);
  245. break;
  246. }
  247. }
  248. // All went well, so set type attribute type.
  249. dst->Value.itType = dstType;
  250. }
  251. ULONG Translator::getEncodedSize(
  252. const IASATTRIBUTE& src
  253. ) throw ()
  254. {
  255. ULONG size;
  256. switch (src.Value.itType)
  257. {
  258. case IASTYPE_BOOLEAN:
  259. case IASTYPE_INTEGER:
  260. case IASTYPE_ENUM:
  261. case IASTYPE_INET_ADDR:
  262. case IASTYPE_UTC_TIME:
  263. {
  264. size = 4;
  265. break;
  266. }
  267. case IASTYPE_STRING:
  268. {
  269. // Convert the string to ANSI so we can count octets.
  270. IASAttributeAnsiAlloc(const_cast<PIASATTRIBUTE>(&src));
  271. // Allow for NULL strings and don't count the terminator.
  272. if (src.Value.String.pszAnsi)
  273. {
  274. size = strlen(src.Value.String.pszAnsi);
  275. }
  276. else
  277. {
  278. size = 0;
  279. }
  280. break;
  281. }
  282. case IASTYPE_OCTET_STRING:
  283. {
  284. size = src.Value.OctetString.dwLength;
  285. break;
  286. }
  287. default:
  288. // All other types have no wire representation.
  289. size = 0;
  290. }
  291. return size;
  292. }
  293. void Translator::encode(
  294. PBYTE dst,
  295. const IASATTRIBUTE& src
  296. ) throw ()
  297. {
  298. // Switch based on the source's type.
  299. switch (src.Value.itType)
  300. {
  301. case IASTYPE_BOOLEAN:
  302. {
  303. IASInsertDWORD(dst, (src.Value.Boolean ? 1 : 0));
  304. break;
  305. }
  306. case IASTYPE_INTEGER:
  307. case IASTYPE_ENUM:
  308. case IASTYPE_INET_ADDR:
  309. {
  310. IASInsertDWORD(dst, src.Value.Integer);
  311. break;
  312. }
  313. case IASTYPE_STRING:
  314. {
  315. const BYTE* p = (const BYTE*)src.Value.String.pszAnsi;
  316. // Don't use strcpy since we don't want the null terminator.
  317. if (p)
  318. {
  319. while (*p) { *dst++ = *p++; }
  320. }
  321. break;
  322. }
  323. case IASTYPE_UTC_TIME:
  324. {
  325. ULONG64 val;
  326. // Move in the high DWORD.
  327. val = src.Value.UTCTime.dwHighDateTime;
  328. val <<= 32;
  329. // Move in the low DWORD.
  330. val |= src.Value.UTCTime.dwLowDateTime;
  331. // Convert to the UNIX epoch.
  332. val -= UNIX_EPOCH;
  333. // Convert to seconds.
  334. val /= 10000000;
  335. IASInsertDWORD(dst, (DWORD)val);
  336. break;
  337. }
  338. case IASTYPE_OCTET_STRING:
  339. {
  340. memcpy(dst,
  341. src.Value.OctetString.lpValue,
  342. src.Value.OctetString.dwLength);
  343. }
  344. }
  345. }
  346. ULONG Translator::encode(
  347. ULONG headerLength,
  348. const IASATTRIBUTE& src,
  349. IASAttribute& dst
  350. )
  351. {
  352. // Compute the encoded size.
  353. ULONG dataLength = getEncodedSize(src);
  354. ULONG attrLength = dataLength + headerLength;
  355. // Allocate a buffer for the value.
  356. PBYTE buf = (PBYTE)CoTaskMemAlloc(attrLength);
  357. if (!buf) { _com_issue_error(E_OUTOFMEMORY); }
  358. // Encode the data.
  359. encode(buf + headerLength, src);
  360. // Store the buffer in the attribute.
  361. dst->dwId = src.dwId;
  362. dst->dwFlags = src.dwFlags;
  363. dst->Value.itType = IASTYPE_OCTET_STRING;
  364. dst->Value.OctetString.dwLength = attrLength;
  365. dst->Value.OctetString.lpValue = buf;
  366. return dataLength;
  367. }
  368. void Translator::scatter(
  369. ULONG headerLength,
  370. IASATTRIBUTE& src,
  371. IASAttributeVector& dst
  372. )
  373. {
  374. if (src.Value.OctetString.dwLength <= 253)
  375. {
  376. // If the attribute is already small enough, then there's nothing to do.
  377. dst.push_back(&src);
  378. }
  379. else
  380. {
  381. // Maximum length of data that can be store in each attribute.
  382. ULONG maxDataLength = 253 - headerLength;
  383. // Number of bytes remaining to be scattered.
  384. ULONG remaining = src.Value.OctetString.dwLength - headerLength;
  385. // Next byte to be scattered.
  386. PBYTE next = src.Value.OctetString.lpValue + headerLength;
  387. do
  388. {
  389. // Allocate an attribute for the next chunk.
  390. IASAttribute chunk(true);
  391. // Compute the data length and attribute length for this chunk.
  392. ULONG dataLength = min(remaining, maxDataLength);
  393. ULONG attrLength = dataLength + headerLength;
  394. // Allocate a buffer for the value.
  395. PBYTE buf = (PBYTE)CoTaskMemAlloc(attrLength);
  396. if (!buf) { _com_issue_error(E_OUTOFMEMORY); }
  397. // Copy in the header ...
  398. memcpy(buf, src.Value.OctetString.lpValue, headerLength);
  399. // ... and the next chunk of data.
  400. memcpy(buf + headerLength, next, dataLength);
  401. // Store the buffer in the attribute.
  402. chunk->dwId = src.dwId;
  403. chunk->dwFlags = src.dwFlags;
  404. chunk->Value.itType = IASTYPE_OCTET_STRING;
  405. chunk->Value.OctetString.dwLength = attrLength;
  406. chunk->Value.OctetString.lpValue = buf;
  407. // Append to the destination vector.
  408. dst.push_back(chunk);
  409. // Advance to the next chunk.
  410. remaining -= dataLength;
  411. next += dataLength;
  412. } while (remaining);
  413. }
  414. }