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.

794 lines
22 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) Microsoft Corporation
  4. //
  5. // SYNOPSIS
  6. //
  7. // Defines the class RadiusProxy.
  8. //
  9. ///////////////////////////////////////////////////////////////////////////////
  10. #include <proxypch.h>
  11. #include <datastore2.h>
  12. #include <proxy.h>
  13. #include <radpack.h>
  14. #include <iasevent.h>
  15. #include <iasinfo.h>
  16. #include <iasutil.h>
  17. #include <dsobj.h>
  18. ///////////////////////////////////////////////////////////////////////////////
  19. //
  20. // CLASS
  21. //
  22. // Resolver
  23. //
  24. // DESCRIPTION
  25. //
  26. // Utility class for resolving hostnames and iterating through the results.
  27. //
  28. ///////////////////////////////////////////////////////////////////////////////
  29. class Resolver
  30. {
  31. public:
  32. Resolver() throw ()
  33. : first(NULL), last(NULL)
  34. { }
  35. ~Resolver() throw ()
  36. { if (first != &addr) delete[] first; }
  37. // Returns true if the result set contains the specified address.
  38. bool contains(ULONG address) const throw ()
  39. {
  40. for (const ULONG* i = first; i != last; ++i)
  41. {
  42. if (*i == address) { return true; }
  43. }
  44. return false;
  45. }
  46. // Resolves the given name. The return value is the error code.
  47. ULONG resolve(const PCWSTR name = NULL) throw ()
  48. {
  49. // Clear out the existing result set.
  50. if (first != &addr)
  51. {
  52. delete[] first;
  53. first = last = NULL;
  54. }
  55. if (name)
  56. {
  57. // First try for a quick score on dotted decimal.
  58. addr = ias_inet_wtoh(name);
  59. if (addr != INADDR_NONE)
  60. {
  61. addr = htonl(addr);
  62. first = &addr;
  63. last = first + 1;
  64. return NO_ERROR;
  65. }
  66. }
  67. // That didn't work, so look up the name.
  68. PHOSTENT he = IASGetHostByName(name);
  69. if (!he) { return GetLastError(); }
  70. // Count the number of addresses returned.
  71. ULONG naddr = 0;
  72. while (he->h_addr_list[naddr]) { ++naddr; }
  73. // Allocate an array to hold them.
  74. first = last = new (std::nothrow) ULONG[naddr];
  75. if (first)
  76. {
  77. for (ULONG i = 0; i < naddr; ++i)
  78. {
  79. *last++ = *(PULONG)he->h_addr_list[i];
  80. }
  81. }
  82. LocalFree(he);
  83. return first ? NO_ERROR : WSA_NOT_ENOUGH_MEMORY;
  84. }
  85. const ULONG* begin() const throw ()
  86. { return first; }
  87. const ULONG* end() const throw ()
  88. { return last; }
  89. private:
  90. ULONG addr, *first, *last;
  91. // Not implemented.
  92. Resolver(const Resolver&);
  93. Resolver& operator=(const Resolver&);
  94. };
  95. ///////////////////////////////////////////////////////////////////////////////
  96. //
  97. // CLASS
  98. //
  99. // IASRemoteServer
  100. //
  101. // DESCRIPTION
  102. //
  103. // Extends RemoteServer to add IAS specific server information.
  104. //
  105. ///////////////////////////////////////////////////////////////////////////////
  106. class IASRemoteServer : public RemoteServer
  107. {
  108. public:
  109. IASRemoteServer(
  110. const RemoteServerConfig& config,
  111. RadiusRemoteServerEntry* entry
  112. )
  113. : RemoteServer(config),
  114. counters(entry)
  115. {
  116. // Create the Remote-Server-Address attribute.
  117. IASAttribute name(true);
  118. name->dwId = IAS_ATTRIBUTE_REMOTE_SERVER_ADDRESS;
  119. name->Value.itType = IASTYPE_INET_ADDR;
  120. name->Value.InetAddr = ntohl(config.ipAddress);
  121. attrs.push_back(name);
  122. // Update our PerfMon entry.
  123. if (counters)
  124. {
  125. counters->dwCounters[radiusAuthClientServerPortNumber] =
  126. ntohs(config.authPort);
  127. counters->dwCounters[radiusAccClientServerPortNumber] =
  128. ntohs(config.acctPort);
  129. }
  130. }
  131. // Attributes to be added to each request.
  132. IASAttributeVectorWithBuffer<1> attrs;
  133. // PerfMon counters.
  134. RadiusRemoteServerEntry* counters;
  135. };
  136. RadiusProxy::RadiusProxy()
  137. : engine(this)
  138. {
  139. }
  140. RadiusProxy::~RadiusProxy() throw ()
  141. {
  142. }
  143. HRESULT RadiusProxy::FinalConstruct() throw ()
  144. {
  145. IAS_PRODUCT_LIMITS limits;
  146. DWORD error = IASGetProductLimits(0, &limits);
  147. if (error != NO_ERROR)
  148. {
  149. return HRESULT_FROM_WIN32(error);
  150. }
  151. maxServerGroups = limits.maxServerGroups;
  152. HRESULT hr = counters.FinalConstruct();
  153. if (SUCCEEDED(hr))
  154. {
  155. hr = translator.FinalConstruct();
  156. if (SUCCEEDED(hr))
  157. {
  158. hr = engine.finalConstruct();
  159. }
  160. }
  161. return hr;
  162. }
  163. STDMETHODIMP RadiusProxy::PutProperty(LONG Id, VARIANT* pValue)
  164. {
  165. if (pValue == NULL) { return E_INVALIDARG; }
  166. HRESULT hr;
  167. switch (Id)
  168. {
  169. case PROPERTY_RADIUSPROXY_SERVERGROUPS:
  170. {
  171. if (V_VT(pValue) != VT_DISPATCH) { return DISP_E_TYPEMISMATCH; }
  172. try
  173. {
  174. configure(V_UNKNOWN(pValue));
  175. hr = S_OK;
  176. }
  177. catch (const _com_error& ce)
  178. {
  179. hr = ce.Error();
  180. }
  181. break;
  182. }
  183. default:
  184. {
  185. hr = DISP_E_MEMBERNOTFOUND;
  186. }
  187. }
  188. return hr;
  189. }
  190. void RadiusProxy::onEvent(
  191. const RadiusEvent& event
  192. ) throw ()
  193. {
  194. // Convert the event context to an IASRemoteServer.
  195. IASRemoteServer* server = static_cast<IASRemoteServer*>(event.context);
  196. // Update the counters.
  197. counters.updateCounters(
  198. event.portType,
  199. event.eventType,
  200. (server ? server->counters : NULL),
  201. event.data
  202. );
  203. // We always use the address as an insertion string.
  204. WCHAR addr[16], misc[16];
  205. ias_inet_htow(ntohl(event.ipAddress), addr);
  206. // Set up the default parameters for event reporting.
  207. DWORD eventID = 0;
  208. DWORD numStrings = 1;
  209. DWORD dataSize = 0;
  210. PCWSTR strings[2] = { addr, misc };
  211. const void* rawData = NULL;
  212. // Map the RADIUS event to an IAS event ID.
  213. switch (event.eventType)
  214. {
  215. case eventInvalidAddress:
  216. eventID = PROXY_E_INVALID_ADDRESS;
  217. _itow(ntohs(event.ipPort), misc, 10);
  218. numStrings = 2;
  219. break;
  220. case eventMalformedPacket:
  221. eventID = PROXY_E_MALFORMED_RESPONSE;
  222. dataSize = event.packetLength;
  223. rawData = event.packet;
  224. break;
  225. case eventBadAuthenticator:
  226. eventID = PROXY_E_BAD_AUTHENTICATOR;
  227. break;
  228. case eventBadSignature:
  229. eventID = PROXY_E_BAD_SIGNATURE;
  230. break;
  231. case eventMissingSignature:
  232. eventID = PROXY_E_MISSING_SIGNATURE;
  233. break;
  234. case eventUnknownType:
  235. eventID = PROXY_E_UNKNOWN_TYPE;
  236. _itow(event.packet[0], misc, 10);
  237. numStrings = 2;
  238. break;
  239. case eventUnexpectedResponse:
  240. eventID = PROXY_E_UNEXPECTED_RESPONSE;
  241. dataSize = event.packetLength;
  242. rawData = event.packet;
  243. break;
  244. case eventSendError:
  245. eventID = PROXY_E_SEND_ERROR;
  246. _itow(event.data, misc, 10);
  247. numStrings = 2;
  248. break;
  249. case eventReceiveError:
  250. eventID = PROXY_E_RECV_ERROR;
  251. _itow(event.data, misc, 10);
  252. numStrings = 2;
  253. break;
  254. case eventServerAvailable:
  255. eventID = PROXY_S_SERVER_AVAILABLE;
  256. break;
  257. case eventServerUnavailable:
  258. eventID = PROXY_E_SERVER_UNAVAILABLE;
  259. _itow(server->maxEvents, misc, 10);
  260. numStrings = 2;
  261. break;
  262. }
  263. if (eventID)
  264. {
  265. IASReportEvent(
  266. eventID,
  267. numStrings,
  268. dataSize,
  269. strings,
  270. (void*)rawData
  271. );
  272. }
  273. }
  274. void RadiusProxy::onComplete(
  275. RadiusProxyEngine::Result result,
  276. PVOID context,
  277. RemoteServer* server,
  278. BYTE code,
  279. const RadiusAttribute* begin,
  280. const RadiusAttribute* end
  281. ) throw ()
  282. {
  283. IRequest* comreq = (IRequest*)context;
  284. IASRESPONSE response = IAS_RESPONSE_DISCARD_PACKET;
  285. // Map the result to a reason code.
  286. IASREASON reason;
  287. switch (result)
  288. {
  289. case RadiusProxyEngine::resultSuccess:
  290. reason = IAS_SUCCESS;
  291. break;
  292. case RadiusProxyEngine::resultNotEnoughMemory:
  293. reason = IAS_INTERNAL_ERROR;
  294. break;
  295. case RadiusProxyEngine::resultUnknownServerGroup:
  296. reason = IAS_PROXY_UNKNOWN_GROUP;
  297. break;
  298. case RadiusProxyEngine::resultUnknownServer:
  299. reason = IAS_PROXY_UNKNOWN_SERVER;
  300. break;
  301. case RadiusProxyEngine::resultInvalidRequest:
  302. reason = IAS_PROXY_PACKET_TOO_LONG;
  303. break;
  304. case RadiusProxyEngine::resultSendError:
  305. reason = IAS_PROXY_SEND_ERROR;
  306. break;
  307. case RadiusProxyEngine::resultRequestTimeout:
  308. reason = IAS_PROXY_TIMEOUT;
  309. break;
  310. case RadiusProxyEngine::resultCryptoError:
  311. reason = IAS_INTERNAL_ERROR;
  312. break;
  313. default:
  314. reason = IAS_INTERNAL_ERROR;
  315. }
  316. try
  317. {
  318. IASRequest request(comreq);
  319. // Always store the server attributes if available.
  320. if (server)
  321. {
  322. static_cast<IASRemoteServer*>(server)->attrs.store(request);
  323. }
  324. if (reason == IAS_SUCCESS)
  325. {
  326. // Set the response code and determine the flags used for returned
  327. // attributes.
  328. DWORD flags = 0;
  329. switch (code)
  330. {
  331. case RADIUS_ACCESS_ACCEPT:
  332. {
  333. response = IAS_RESPONSE_ACCESS_ACCEPT;
  334. flags = IAS_INCLUDE_IN_ACCEPT;
  335. break;
  336. }
  337. case RADIUS_ACCESS_REJECT:
  338. {
  339. response = IAS_RESPONSE_ACCESS_REJECT;
  340. reason = IAS_PROXY_REJECT;
  341. flags = IAS_INCLUDE_IN_REJECT;
  342. break;
  343. }
  344. case RADIUS_ACCESS_CHALLENGE:
  345. {
  346. response = IAS_RESPONSE_ACCESS_CHALLENGE;
  347. flags = IAS_INCLUDE_IN_CHALLENGE;
  348. break;
  349. }
  350. case RADIUS_ACCOUNTING_RESPONSE:
  351. {
  352. response = IAS_RESPONSE_ACCOUNTING;
  353. flags = IAS_INCLUDE_IN_ACCEPT;
  354. break;
  355. }
  356. default:
  357. {
  358. // The RadiusProxyEngine should never do this.
  359. _com_issue_error(E_FAIL);
  360. }
  361. }
  362. // Convert the received attributes to IAS format.
  363. AttributeVector incoming;
  364. for (const RadiusAttribute* src = begin; src != end; ++src)
  365. {
  366. // Temporary hack to workaround bug in the protocol.
  367. if (src->type != RADIUS_SIGNATURE)
  368. {
  369. translator.fromRadius(*src, flags, incoming);
  370. }
  371. }
  372. if (!incoming.empty())
  373. {
  374. // Get the existing attributes.
  375. AttributeVector existing;
  376. existing.load(request);
  377. // Erase any attributes that are already in the request.
  378. AttributeIterator i, j;
  379. for (i = existing.begin(); i != existing.end(); ++i)
  380. {
  381. // Both the flags ...
  382. if (i->pAttribute->dwFlags & flags)
  383. {
  384. for (j = incoming.begin(); j != incoming.end(); )
  385. {
  386. // ... and the ID have to match.
  387. if (j->pAttribute->dwId == i->pAttribute->dwId)
  388. {
  389. j = incoming.erase(j);
  390. }
  391. else
  392. {
  393. ++j;
  394. }
  395. }
  396. }
  397. }
  398. // Store the remaining attributes.
  399. incoming.store(request);
  400. }
  401. }
  402. }
  403. catch (const _com_error& ce)
  404. {
  405. response = IAS_RESPONSE_DISCARD_PACKET;
  406. if (ce.Error() == E_INVALIDARG)
  407. {
  408. // We must have had an error translating from RADIUS to IAS format.
  409. reason = IAS_PROXY_MALFORMED_RESPONSE;
  410. }
  411. else
  412. {
  413. // Probably memory allocation.
  414. reason = IAS_INTERNAL_ERROR;
  415. }
  416. }
  417. // Give it back to the pipeline.
  418. comreq->SetResponse(response, reason);
  419. comreq->ReturnToSource(IAS_REQUEST_STATUS_HANDLED);
  420. // This balances the AddRef we did before calling forwardRequest.
  421. comreq->Release();
  422. }
  423. void RadiusProxy::onAsyncRequest(IRequest* pRequest) throw ()
  424. {
  425. try
  426. {
  427. IASRequest request(pRequest);
  428. // Set the packet code based on the request type.
  429. BYTE packetCode;
  430. switch (request.get_Request())
  431. {
  432. case IAS_REQUEST_ACCESS_REQUEST:
  433. {
  434. packetCode = RADIUS_ACCESS_REQUEST;
  435. break;
  436. }
  437. case IAS_REQUEST_ACCOUNTING:
  438. {
  439. packetCode = RADIUS_ACCOUNTING_REQUEST;
  440. break;
  441. }
  442. default:
  443. {
  444. // The pipeline should never give us a request of the wrong type.
  445. _com_issue_error(E_FAIL);
  446. }
  447. }
  448. // Get the attributes from the request.
  449. AttributeVector all, outgoing;
  450. all.load(request);
  451. for (AttributeIterator i = all.begin(); i != all.end(); ++i)
  452. {
  453. // Send all the attributes received from the client except Proxy-State.
  454. if (i->pAttribute->dwFlags & IAS_RECVD_FROM_CLIENT &&
  455. i->pAttribute->dwId != RADIUS_ATTRIBUTE_PROXY_STATE)
  456. {
  457. translator.toRadius(*(i->pAttribute), outgoing);
  458. }
  459. }
  460. // If the request authenticator contains the CHAP challenge:
  461. // it must be used so get the request authenticator (always to
  462. // simplify the code)
  463. PBYTE requestAuthenticator = 0;
  464. IASAttribute radiusHeader;
  465. if (radiusHeader.load(
  466. request,
  467. IAS_ATTRIBUTE_CLIENT_PACKET_HEADER,
  468. IASTYPE_OCTET_STRING
  469. ))
  470. {
  471. requestAuthenticator = radiusHeader->Value.OctetString.lpValue + 4;
  472. }
  473. // Allocate an array of RadiusAttributes.
  474. size_t nbyte = outgoing.size() * sizeof(RadiusAttribute);
  475. RadiusAttribute* begin = (RadiusAttribute*)_alloca(nbyte);
  476. RadiusAttribute* end = begin;
  477. // Load the individual attributes.
  478. for (AttributeIterator j = outgoing.begin(); j != outgoing.end(); ++j)
  479. {
  480. end->type = (BYTE)(j->pAttribute->dwId);
  481. end->length = (BYTE)(j->pAttribute->Value.OctetString.dwLength);
  482. end->value = j->pAttribute->Value.OctetString.lpValue;
  483. ++end;
  484. }
  485. // Get the RADIUS Server group. This may be NULL since NAS-State bypasses
  486. // proxy policy.
  487. PIASATTRIBUTE group = IASPeekAttribute(
  488. request,
  489. IAS_ATTRIBUTE_PROVIDER_NAME,
  490. IASTYPE_STRING
  491. );
  492. // AddRef the request because we're giving it to the engine.
  493. pRequest->AddRef();
  494. // Add the request authenticator to the parameters of forwardRequest
  495. // That can be NULL
  496. engine.forwardRequest(
  497. (PVOID)pRequest,
  498. (group ? group->Value.String.pszWide : L""),
  499. packetCode,
  500. requestAuthenticator,
  501. begin,
  502. end
  503. );
  504. }
  505. catch (const _com_error&)
  506. {
  507. // We weren't able to forward it to the engine.
  508. pRequest->SetResponse(IAS_RESPONSE_DISCARD_PACKET, IAS_INTERNAL_ERROR);
  509. pRequest->ReturnToSource(IAS_REQUEST_STATUS_HANDLED);
  510. }
  511. }
  512. void RadiusProxy::configure(IUnknown* root)
  513. {
  514. // Get our IP addresses. We don't care if this fails.
  515. Resolver localAddress;
  516. localAddress.resolve();
  517. // Open the RADIUS Server Groups container. If it's not there, we'll just
  518. // assume there's nothing to configure.
  519. DataStoreObject inGroups(
  520. root,
  521. L"RADIUS Server Groups\0"
  522. );
  523. if (inGroups.empty()) { return; }
  524. LONG numGroups = inGroups.numChildren();
  525. if (numGroups > maxServerGroups)
  526. {
  527. IASTracePrintf(
  528. "License Violation: %ld Remote RADIUS Server Groups are "
  529. "configured, but only %lu are allowed for this product type.",
  530. numGroups,
  531. maxServerGroups
  532. );
  533. IASReportLicenseViolation();
  534. _com_issue_error(IAS_E_LICENSE_VIOLATION);
  535. }
  536. // Reserve space for each group.
  537. ServerGroups outGroups(numGroups);
  538. // Iterate through the groups.
  539. DataStoreObject inGroup;
  540. while (inGroups.nextChild(inGroup))
  541. {
  542. // Get the group name.
  543. CComBSTR groupName;
  544. inGroup.getValue(L"Name", &groupName);
  545. // Reserve space for each server. This is really a guess since a server
  546. // may resolve to multiple IP addresses.
  547. RemoteServers outServers(inGroup.numChildren());
  548. // Iterate through the servers.
  549. DataStoreObject inServer;
  550. while (inGroup.nextChild(inServer))
  551. {
  552. configureServer(localAddress, groupName, inServer, outServers);
  553. }
  554. // Ignore any empty groups.
  555. if (outServers.empty()) { continue; }
  556. // Create the new group.
  557. ServerGroupPtr outGroup(new ServerGroup(
  558. groupName,
  559. outServers.begin(),
  560. outServers.end()
  561. ));
  562. outGroups.push_back(outGroup);
  563. }
  564. // Wow, we're finally done.
  565. if (engine.setServerGroups(
  566. outGroups.begin(),
  567. outGroups.end()
  568. ) == false)
  569. {
  570. // most likely reason for failure is out of memory error
  571. _com_issue_error(E_OUTOFMEMORY);
  572. }
  573. }
  574. void RadiusProxy::configureServer(
  575. const Resolver& localAddress,
  576. const wchar_t* groupName,
  577. DataStoreObject& inServer,
  578. RemoteServers& outServers
  579. )
  580. {
  581. USES_CONVERSION;
  582. // Populate the RemoteServerConfig. It has a lot of fields.
  583. RemoteServerConfig config;
  584. CComBSTR name;
  585. inServer.getValue(L"Name", &name);
  586. CLSIDFromString(name, &config.guid);
  587. ULONG port;
  588. inServer.getValue(L"Server Authentication Port", &port, 1812);
  589. config.authPort = htons((USHORT)port);
  590. inServer.getValue(L"Server Accounting Port", &port, 1813);
  591. config.acctPort = htons((USHORT)port);
  592. CComBSTR bstrAuth;
  593. inServer.getValue(L"Authentication Secret", &bstrAuth);
  594. config.authSecret = W2A(bstrAuth);
  595. CComBSTR bstrAcct;
  596. inServer.getValue(L"Accounting Secret", &bstrAcct, bstrAuth);
  597. config.acctSecret = W2A(bstrAcct);
  598. inServer.getValue(L"Priority", &config.priority, 1);
  599. inServer.getValue(L"Weight", &config.weight, 50);
  600. // Ignore any zero weight servers.
  601. if (config.weight == 0) { return; }
  602. // We don't use this feature for now.
  603. config.sendSignature = false;
  604. inServer.getValue(
  605. L"Forward Accounting On/Off",
  606. &config.sendAcctOnOff,
  607. true
  608. );
  609. inServer.getValue(L"Timeout", &config.timeout, 3);
  610. // Don't allow zero for timeout
  611. if (config.timeout == 0) { config.timeout = 1; }
  612. inServer.getValue(L"Maximum Lost Packets", &config.maxLost, 5);
  613. // Don't allow zero for maxLost.
  614. if (config.maxLost == 0) { config.maxLost = 1; }
  615. inServer.getValue(
  616. L"Blackout Interval",
  617. &config.blackout,
  618. 10 * config.timeout
  619. );
  620. if (config.blackout < config.timeout)
  621. {
  622. // Blackout interval must be >= request timeout.
  623. config.blackout = config.timeout;
  624. }
  625. // These need to be in msec.
  626. config.timeout *= 1000;
  627. config.blackout *= 1000;
  628. // Now we have to resolve the server name to an IP address.
  629. CComBSTR address;
  630. inServer.getValue(L"Address", &address);
  631. Resolver serverAddress;
  632. ULONG error = serverAddress.resolve(address);
  633. if (error)
  634. {
  635. WCHAR errorCode[16];
  636. _itow(GetLastError(), errorCode, 10);
  637. PCWSTR strings[3] = { address, groupName, errorCode };
  638. IASReportEvent(
  639. PROXY_E_HOST_NOT_FOUND,
  640. 3,
  641. 0,
  642. strings,
  643. NULL
  644. );
  645. }
  646. // Create a server entry for each address.
  647. for (const ULONG* addr = serverAddress.begin();
  648. addr != serverAddress.end();
  649. ++addr)
  650. {
  651. // Don't allow them to proxy locally.
  652. if (localAddress.contains(*addr))
  653. {
  654. WCHAR ipAddress[16];
  655. ias_inet_htow(ntohl(*addr), ipAddress);
  656. PCWSTR strings[3] = { address, groupName, ipAddress };
  657. IASReportEvent(
  658. PROXY_E_LOCAL_SERVER,
  659. 3,
  660. 0,
  661. strings,
  662. NULL
  663. );
  664. continue;
  665. }
  666. // Look up the PerfMon counters.
  667. RadiusRemoteServerEntry* entry = counters.getRemoteServerEntry(
  668. *addr
  669. );
  670. // Create the new server
  671. config.ipAddress = *addr;
  672. RemoteServerPtr outServer(new IASRemoteServer(
  673. config,
  674. entry
  675. ));
  676. outServers.push_back(outServer);
  677. }
  678. }