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.

385 lines
9.3 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) Microsoft Corporation
  4. //
  5. // SYNOPSIS
  6. //
  7. // Defines the class AccountValidation.
  8. //
  9. ///////////////////////////////////////////////////////////////////////////////
  10. #include <ias.h>
  11. #include <ntsamuser.h>
  12. #include <autohdl.h>
  13. #include <iaslsa.h>
  14. #include <iasntds.h>
  15. #include <iastlutl.h>
  16. #include <lmaccess.h>
  17. #include <samutil.h>
  18. #include <sdoias.h>
  19. //////////
  20. // Attributes that should be retrieved for each user.
  21. //////////
  22. const PCWSTR PER_USER_ATTRS[] =
  23. {
  24. L"userAccountControl",
  25. L"accountExpires",
  26. L"logonHours",
  27. L"tokenGroups",
  28. L"objectSid",
  29. NULL
  30. };
  31. //////////
  32. //
  33. // Process an LDAP response.
  34. //
  35. // Based on the empirical evidence, it seems that userAccountControl and
  36. // accountExpires are always present while logonHours is optional. However,
  37. // none of these attributes are marked as mandatory in the LDAP schema. Since
  38. // we have already done a rudimentary access check at bind, we will allow any
  39. // of these attributes to be absent.
  40. //
  41. //////////
  42. inline
  43. DWORD
  44. ValidateLdapResponse(
  45. IASTL::IASRequest& request,
  46. LDAPMessage* msg
  47. )
  48. {
  49. // Retrieve the connection for this message.
  50. LDAP* ld = ldap_conn_from_msg(NULL, msg);
  51. PWCHAR *str;
  52. PLDAP_BERVAL *data1, *data2;
  53. // There is exactly one entry.
  54. LDAPMessage* e = ldap_first_entry(ld, msg);
  55. //////////
  56. // Check the UserAccountControl flags.
  57. //////////
  58. ULONG userAccountControl;
  59. str = ldap_get_valuesW(ld, e, L"userAccountControl");
  60. if (str)
  61. {
  62. userAccountControl = (ULONG)_wtoi64(*str);
  63. ldap_value_freeW(str);
  64. if (userAccountControl & UF_ACCOUNTDISABLE)
  65. {
  66. return ERROR_ACCOUNT_DISABLED;
  67. }
  68. if (userAccountControl & UF_LOCKOUT)
  69. {
  70. return ERROR_ACCOUNT_LOCKED_OUT;
  71. }
  72. }
  73. //////////
  74. // Retrieve AccountExpires.
  75. //////////
  76. LARGE_INTEGER accountExpires;
  77. str = ldap_get_valuesW(ld, e, L"accountExpires");
  78. if (str)
  79. {
  80. accountExpires.QuadPart = _wtoi64(*str);
  81. ldap_value_freeW(str);
  82. }
  83. else
  84. {
  85. accountExpires.QuadPart = 0;
  86. }
  87. //////////
  88. // Retrieve LogonHours.
  89. //////////
  90. IAS_LOGON_HOURS logonHours;
  91. data1 = ldap_get_values_lenW(ld, e, L"logonHours");
  92. if (data1 != NULL)
  93. {
  94. logonHours.UnitsPerWeek = 8 * (USHORT)data1[0]->bv_len;
  95. logonHours.LogonHours = (PUCHAR)data1[0]->bv_val;
  96. }
  97. else
  98. {
  99. logonHours.UnitsPerWeek = 0;
  100. logonHours.LogonHours = NULL;
  101. }
  102. //////////
  103. // Check the account restrictions.
  104. //////////
  105. DWORD status;
  106. LARGE_INTEGER sessionTimeout;
  107. status = IASCheckAccountRestrictions(
  108. &accountExpires,
  109. &logonHours,
  110. &sessionTimeout
  111. );
  112. InsertInternalTimeout(request, sessionTimeout);
  113. ldap_value_free_len(data1);
  114. if (status != NO_ERROR) { return status; }
  115. //////////
  116. // Retrieve tokenGroups and objectSid.
  117. //////////
  118. data1 = ldap_get_values_lenW(ld, e, L"tokenGroups");
  119. data2 = ldap_get_values_lenW(ld, e, L"objectSid");
  120. PTOKEN_GROUPS allGroups;
  121. ULONG length;
  122. if (data1 && data2)
  123. {
  124. // Allocate memory for the TOKEN_GROUPS struct.
  125. ULONG numGroups = ldap_count_values_len(data1);
  126. PTOKEN_GROUPS tokenGroups =
  127. (PTOKEN_GROUPS)_alloca(
  128. FIELD_OFFSET(TOKEN_GROUPS, Groups) +
  129. sizeof(SID_AND_ATTRIBUTES) * numGroups
  130. );
  131. // Store the number of groups.
  132. tokenGroups->GroupCount = numGroups;
  133. // Store the group SIDs.
  134. for (ULONG i = 0; i < numGroups; ++i)
  135. {
  136. tokenGroups->Groups[i].Sid = (PSID)data1[i]->bv_val;
  137. tokenGroups->Groups[i].Attributes = SE_GROUP_ENABLED;
  138. }
  139. // Expand the group membership locally.
  140. status = IASGetAliasMembership(
  141. (PSID)data2[0]->bv_val,
  142. tokenGroups,
  143. CoTaskMemAlloc,
  144. &allGroups,
  145. &length
  146. );
  147. }
  148. else
  149. {
  150. status = ERROR_ACCESS_DENIED;
  151. }
  152. ldap_value_free_len(data1);
  153. ldap_value_free_len(data2);
  154. if (status != NO_ERROR) { return status; }
  155. //////////
  156. // Initialize and store the attribute.
  157. //////////
  158. IASTL::IASAttribute attr(true);
  159. attr->dwId = IAS_ATTRIBUTE_TOKEN_GROUPS;
  160. attr->Value.itType = IASTYPE_OCTET_STRING;
  161. attr->Value.OctetString.dwLength = length;
  162. attr->Value.OctetString.lpValue = (PBYTE)allGroups;
  163. attr.store(request);
  164. return NO_ERROR;
  165. }
  166. HRESULT AccountValidation::Initialize()
  167. {
  168. return IASNtdsInitialize();
  169. }
  170. HRESULT AccountValidation::Shutdown() throw ()
  171. {
  172. IASNtdsUninitialize();
  173. return S_OK;
  174. }
  175. IASREQUESTSTATUS AccountValidation::onSyncRequest(IRequest* pRequest) throw ()
  176. {
  177. try
  178. {
  179. IASTL::IASRequest request(pRequest);
  180. //////////
  181. // Only process requests that don't have Token-Groups already.
  182. //////////
  183. IASTL::IASAttribute tokenGroups;
  184. if (!tokenGroups.load(
  185. request,
  186. IAS_ATTRIBUTE_TOKEN_GROUPS,
  187. IASTYPE_OCTET_STRING
  188. ))
  189. {
  190. //////////
  191. // Extract the NT4-Account-Name attribute.
  192. //////////
  193. IASTL::IASAttribute identity;
  194. if (identity.load(
  195. request,
  196. IAS_ATTRIBUTE_NT4_ACCOUNT_NAME,
  197. IASTYPE_STRING
  198. ))
  199. {
  200. //////////
  201. // Convert the User-Name to SAM format.
  202. //////////
  203. SamExtractor extractor(*identity);
  204. PCWSTR domain = extractor.getDomain();
  205. PCWSTR username = extractor.getUsername();
  206. IASTracePrintf(
  207. "Validating Windows account %S\\%S.",
  208. domain,
  209. username
  210. );
  211. //////////
  212. // Validate the account.
  213. //////////
  214. if (!tryNativeMode(request, domain, username))
  215. {
  216. doDownlevel(request, domain, username);
  217. }
  218. }
  219. }
  220. }
  221. catch (const _com_error& ce)
  222. {
  223. IASTraceExcept();
  224. IASProcessFailure(pRequest, ce.Error());
  225. }
  226. return IAS_REQUEST_STATUS_CONTINUE;
  227. }
  228. void AccountValidation::doDownlevel(
  229. IASTL::IASRequest& request,
  230. PCWSTR domainName,
  231. PCWSTR username
  232. )
  233. {
  234. IASTraceString("Using downlevel APIs to validate account.");
  235. //////////
  236. // Inject the user's groups.
  237. //////////
  238. IASTL::IASAttribute groups(true);
  239. DWORD status;
  240. LARGE_INTEGER sessionTimeout;
  241. status = IASGetGroupsForUser(
  242. username,
  243. domainName,
  244. &CoTaskMemAlloc,
  245. (PTOKEN_GROUPS*)&groups->Value.OctetString.lpValue,
  246. &groups->Value.OctetString.dwLength,
  247. &sessionTimeout
  248. );
  249. InsertInternalTimeout(request, sessionTimeout);
  250. if (status == NO_ERROR)
  251. {
  252. // Insert the groups.
  253. groups->dwId = IAS_ATTRIBUTE_TOKEN_GROUPS;
  254. groups->Value.itType = IASTYPE_OCTET_STRING;
  255. groups.store(request);
  256. IASTraceString("Successfully validated account.");
  257. }
  258. else
  259. {
  260. IASTraceFailure("IASGetGroupsForUser", status);
  261. status = IASMapWin32Error(status, IAS_SERVER_UNAVAILABLE);
  262. IASProcessFailure(request, status);
  263. }
  264. }
  265. bool AccountValidation::tryNativeMode(
  266. IASTL::IASRequest& request,
  267. PCWSTR domainName,
  268. PCWSTR username
  269. )
  270. {
  271. //////////
  272. // Only handle domain users.
  273. //////////
  274. if (IASGetRole() != IAS_ROLE_DC && IASIsDomainLocal(domainName))
  275. {
  276. return false;
  277. }
  278. //////////
  279. // Query the DS.
  280. //////////
  281. DWORD error;
  282. IASNtdsResult result;
  283. error = IASNtdsQueryUserAttributes(
  284. domainName,
  285. username,
  286. LDAP_SCOPE_BASE,
  287. const_cast<PWCHAR*>(PER_USER_ATTRS),
  288. &result
  289. );
  290. switch (error)
  291. {
  292. case NO_ERROR:
  293. {
  294. // We got something back, so validate the response.
  295. error = ValidateLdapResponse(request, result.msg);
  296. if (error == NO_ERROR)
  297. {
  298. IASTraceString("Successfully validated account.");
  299. return true;
  300. }
  301. IASTraceFailure("ValidateLdapResponse", error);
  302. break;
  303. }
  304. case ERROR_DS_NOT_INSTALLED:
  305. case ERROR_INVALID_DOMAIN_ROLE:
  306. {
  307. // No DS, so we can't handle.
  308. return false;
  309. }
  310. default:
  311. {
  312. // We have a DS for this user, but we can't talk to it.
  313. break;
  314. }
  315. }
  316. IASProcessFailure(
  317. request,
  318. IASMapWin32Error(error, IAS_DOMAIN_UNAVAILABLE)
  319. );
  320. return true;
  321. }