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.

383 lines
10 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2000, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // userr.cpp
  8. //
  9. // SYNOPSIS
  10. //
  11. // Defines the class UserRestrictions.
  12. //
  13. ///////////////////////////////////////////////////////////////////////////////
  14. #include <ias.h>
  15. #include <userr.h>
  16. #define IASNAPAPI
  17. #include <xprparse.h>
  18. IASREQUESTSTATUS UserRestrictions::onSyncRequest(IRequest* pRequest) throw ()
  19. {
  20. try
  21. {
  22. IASRequest request(pRequest);
  23. IASRESPONSE response = request.get_Response();
  24. if (response == IAS_RESPONSE_INVALID)
  25. {
  26. PIASATTRIBUTE state = IASPeekAttribute(
  27. request,
  28. RADIUS_ATTRIBUTE_STATE,
  29. IASTYPE_OCTET_STRING
  30. );
  31. if (state && state->Value.OctetString.dwLength)
  32. {
  33. // If we made it here, then we have a Challenge-Reponse that no
  34. // handler recognized, so we discard.
  35. request.SetResponse(
  36. IAS_RESPONSE_DISCARD_PACKET,
  37. IAS_UNEXPECTED_REQUEST
  38. );
  39. return IAS_REQUEST_STATUS_HANDLED;
  40. }
  41. }
  42. IASREASON result = IAS_SUCCESS;
  43. do
  44. {
  45. if (!checkAllowDialin(request))
  46. {
  47. result = IAS_DIALIN_DISABLED;
  48. break;
  49. }
  50. if (!checkTimeOfDay(request))
  51. {
  52. result = IAS_INVALID_DIALIN_HOURS;
  53. break;
  54. }
  55. if (!checkAuthenticationType(request))
  56. {
  57. result = IAS_INVALID_AUTH_TYPE;
  58. break;
  59. }
  60. if (!checkCallingStationId(request))
  61. {
  62. result = IAS_INVALID_CALLING_STATION;
  63. break;
  64. }
  65. if (!checkCalledStationId(request))
  66. {
  67. result = IAS_INVALID_CALLED_STATION;
  68. break;
  69. }
  70. if (!checkAllowedPortType(request))
  71. {
  72. result = IAS_INVALID_PORT_TYPE;
  73. break;
  74. }
  75. if (!checkPasswordMustChange(request))
  76. {
  77. result = IAS_CPW_NOT_ALLOWED;
  78. break;
  79. }
  80. if (!checkCertificateEku(request))
  81. {
  82. result = IAS_INVALID_CERT_EKU;
  83. break;
  84. }
  85. } while (FALSE);
  86. if (result == IAS_SUCCESS)
  87. {
  88. // We apply user restrictions to Access-Rejects as well, so we only
  89. // want to set Access-Accept if the response code is still invalid.
  90. // This should only be true for unauthenticated requests.
  91. if (response == IAS_RESPONSE_INVALID)
  92. {
  93. request.SetResponse(IAS_RESPONSE_ACCESS_ACCEPT, IAS_SUCCESS);
  94. }
  95. }
  96. else
  97. {
  98. request.SetResponse(IAS_RESPONSE_ACCESS_REJECT, result);
  99. }
  100. }
  101. catch (...)
  102. {
  103. pRequest->SetResponse(IAS_RESPONSE_DISCARD_PACKET, IAS_INTERNAL_ERROR);
  104. }
  105. return IAS_REQUEST_STATUS_HANDLED;
  106. }
  107. BOOL UserRestrictions::checkAllowDialin(
  108. IAttributesRaw* request
  109. )
  110. {
  111. PIASATTRIBUTE attr = IASPeekAttribute(
  112. request,
  113. IAS_ATTRIBUTE_ALLOW_DIALIN,
  114. IASTYPE_BOOLEAN
  115. );
  116. return !attr || attr->Value.Boolean;
  117. }
  118. BOOL UserRestrictions::checkTimeOfDay(
  119. IAttributesRaw* request
  120. )
  121. {
  122. AttributeVector attrs;
  123. attrs.load(request, IAS_ATTRIBUTE_NP_TIME_OF_DAY);
  124. if (attrs.empty()) { return TRUE; }
  125. for (AttributeVector::iterator i = attrs.begin(); i != attrs.end(); ++i)
  126. {
  127. IASAttributeUnicodeAlloc(i->pAttribute);
  128. if (!i->pAttribute->Value.String.pszWide) { issue_error(E_OUTOFMEMORY); }
  129. VARIANT_BOOL result;
  130. HRESULT hr = IASEvaluateTimeOfDay(
  131. i->pAttribute->Value.String.pszWide,
  132. &result
  133. );
  134. if (FAILED(hr)) { issue_error(hr); }
  135. // If at least one matches, we let the user in.
  136. if (result) { return TRUE; }
  137. }
  138. // None matched.
  139. return FALSE;
  140. }
  141. BOOL UserRestrictions::checkAuthenticationType(
  142. IAttributesRaw* request
  143. )
  144. {
  145. PIASATTRIBUTE attr = IASPeekAttribute(
  146. request,
  147. IAS_ATTRIBUTE_AUTHENTICATION_TYPE,
  148. IASTYPE_ENUM
  149. );
  150. DWORD authType = attr ? attr->Value.Enumerator : IAS_AUTH_NONE;
  151. // We bypass the check for BaseCamp extensions.
  152. if (authType == IAS_AUTH_CUSTOM) { return TRUE; }
  153. AttributeVector attrs;
  154. attrs.load(request, IAS_ATTRIBUTE_NP_AUTHENTICATION_TYPE);
  155. for (AttributeVector::iterator i = attrs.begin(); i != attrs.end(); ++i)
  156. {
  157. if (i->pAttribute->Value.Integer == authType) { return TRUE; }
  158. }
  159. return FALSE;
  160. }
  161. BOOL UserRestrictions::checkCallingStationId(
  162. IAttributesRaw* request
  163. )
  164. {
  165. return checkStringMatch(
  166. request,
  167. IAS_ATTRIBUTE_NP_CALLING_STATION_ID,
  168. RADIUS_ATTRIBUTE_CALLING_STATION_ID
  169. );
  170. }
  171. BOOL UserRestrictions::checkCalledStationId(
  172. IAttributesRaw* request
  173. )
  174. {
  175. return checkStringMatch(
  176. request,
  177. IAS_ATTRIBUTE_NP_CALLED_STATION_ID,
  178. RADIUS_ATTRIBUTE_CALLED_STATION_ID
  179. );
  180. }
  181. BOOL UserRestrictions::checkAllowedPortType(
  182. IAttributesRaw* request
  183. )
  184. {
  185. AttributeVector attrs;
  186. attrs.load(request, IAS_ATTRIBUTE_NP_ALLOWED_PORT_TYPES);
  187. if (attrs.empty()) { return TRUE; }
  188. PIASATTRIBUTE attr = IASPeekAttribute(
  189. request,
  190. RADIUS_ATTRIBUTE_NAS_PORT_TYPE,
  191. IASTYPE_ENUM
  192. );
  193. if (!attr) { return FALSE; }
  194. for (AttributeVector::iterator i = attrs.begin(); i != attrs.end(); ++i)
  195. {
  196. if (i->pAttribute->Value.Enumerator == attr->Value.Enumerator)
  197. {
  198. return TRUE;
  199. }
  200. }
  201. return FALSE;
  202. }
  203. // If the password must change, we check to see if the subsequent change
  204. // password request would be authorized. This prevents prompting the user for a
  205. // new password when he's not allowed to change it anyway.
  206. BOOL UserRestrictions::checkPasswordMustChange(
  207. IASRequest& request
  208. )
  209. {
  210. if (request.get_Reason() != IAS_PASSWORD_MUST_CHANGE)
  211. {
  212. return TRUE;
  213. }
  214. PIASATTRIBUTE attr = IASPeekAttribute(
  215. request,
  216. IAS_ATTRIBUTE_AUTHENTICATION_TYPE,
  217. IASTYPE_ENUM
  218. );
  219. DWORD cpwType;
  220. switch (attr ? attr->Value.Enumerator : IAS_AUTH_NONE)
  221. {
  222. case IAS_AUTH_MSCHAP:
  223. cpwType = IAS_AUTH_MSCHAP_CPW;
  224. break;
  225. case IAS_AUTH_MSCHAP2:
  226. cpwType = IAS_AUTH_MSCHAP2_CPW;
  227. break;
  228. default:
  229. return TRUE;
  230. }
  231. AttributeVector attrs;
  232. attrs.load(request, IAS_ATTRIBUTE_NP_AUTHENTICATION_TYPE);
  233. for (AttributeVector::iterator i = attrs.begin(); i != attrs.end(); ++i)
  234. {
  235. if (i->pAttribute->Value.Integer == cpwType) { return TRUE; }
  236. }
  237. return FALSE;
  238. }
  239. const char* getAnsiString(IASATTRIBUTE& attr)
  240. {
  241. if (attr.Value.itType != IASTYPE_STRING)
  242. {
  243. return 0;
  244. }
  245. DWORD error = IASAttributeAnsiAlloc(&attr);
  246. if (error != NO_ERROR)
  247. {
  248. IASTL::issue_error(HRESULT_FROM_WIN32(error));
  249. }
  250. return attr.Value.String.pszAnsi;
  251. }
  252. BOOL UserRestrictions::checkCertificateEku(
  253. IASRequest& request
  254. )
  255. {
  256. // Is it an EAP request?
  257. IASAttribute authType;
  258. if (!authType.load(request, IAS_ATTRIBUTE_AUTHENTICATION_TYPE) ||
  259. authType->Value.Enumerator != IAS_AUTH_EAP)
  260. {
  261. return TRUE;
  262. }
  263. // Is it an EAP-TLS request?
  264. IASAttribute eapType;
  265. if (!authType.load(request, IAS_ATTRIBUTE_NP_ALLOWED_EAP_TYPE) ||
  266. authType->Value.Integer != 13)
  267. {
  268. return TRUE;
  269. }
  270. // Are there any constraints on the certificate EKU?
  271. AttributeVector allowed;
  272. allowed.load(request, IAS_ATTRIBUTE_ALLOWED_CERTIFICATE_EKU);
  273. if (allowed.empty())
  274. {
  275. return TRUE;
  276. }
  277. //////////
  278. // Check the constraints.
  279. //////////
  280. AttributeVector actual;
  281. actual.load(request, IAS_ATTRIBUTE_CERTIFICATE_EKU);
  282. for (AttributeVector::iterator i = actual.begin();
  283. i != actual.end();
  284. ++i)
  285. {
  286. const char* actualOid = getAnsiString(*(i->pAttribute));
  287. if (actualOid != 0)
  288. {
  289. for (AttributeVector::iterator j = allowed.begin();
  290. j != allowed.end();
  291. ++j)
  292. {
  293. const char* allowedOid = getAnsiString(*(j->pAttribute));
  294. if ((allowedOid != 0) && (strcmp(allowedOid, actualOid) == 0))
  295. {
  296. return TRUE;
  297. }
  298. }
  299. }
  300. }
  301. return FALSE;
  302. }
  303. BOOL UserRestrictions::checkStringMatch(
  304. IAttributesRaw* request,
  305. DWORD allowedId,
  306. DWORD usedId
  307. )
  308. {
  309. AttributeVector attrs;
  310. attrs.load(request, allowedId);
  311. if (attrs.empty()) { return TRUE; }
  312. PIASATTRIBUTE attr = IASPeekAttribute(
  313. request,
  314. usedId,
  315. IASTYPE_OCTET_STRING
  316. );
  317. if (!attr) { return FALSE; }
  318. PCWSTR used = IAS_OCT2WIDE(attr->Value.OctetString);
  319. for (AttributeVector::iterator i = attrs.begin(); i != attrs.end(); ++i)
  320. {
  321. IASAttributeUnicodeAlloc(i->pAttribute);
  322. if (!i->pAttribute->Value.String.pszWide) { issue_error(E_OUTOFMEMORY); }
  323. if (!_wcsicmp(i->pAttribute->Value.String.pszWide, used)) { return TRUE; }
  324. }
  325. return FALSE;
  326. }