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.

371 lines
11 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1998, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // NTSamNames.cpp
  8. //
  9. // SYNOPSIS
  10. //
  11. // This file defines the class NTSamNames.
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 04/13/1998 Original version.
  16. // 04/25/1998 Reject non-local users under WKS or stand-alone SRV.
  17. // 07/22/1998 Don't abort non-local users.
  18. // 08/21/1998 Trace/error improvements.
  19. // 09/08/1998 Added realm stripping.
  20. // 09/10/1998 Remove localOnly flag.
  21. // 09/16/1998 Apply all realm stripping rules.
  22. // 10/22/1998 Allow more name types.
  23. // 01/20/1999 Add support for DNIS/ANI/Guest.
  24. // 02/18/1999 Move registry values.
  25. // 03/19/1999 Add new realms support & overrideUsername flag.
  26. // 01/25/2000 Leave stripped username on the heap.
  27. // 02/15/2000 Remove realms support.
  28. //
  29. ///////////////////////////////////////////////////////////////////////////////
  30. #include <ias.h>
  31. #include <iaslsa.h>
  32. #include <memory>
  33. #include <sdoias.h>
  34. #include <samutil.h>
  35. #include <ntsamnames.h>
  36. #include <varvec.h>
  37. /////////
  38. // Registry keys and values.
  39. /////////
  40. const WCHAR PARAMETERS_KEY[] =
  41. L"SYSTEM\\CurrentControlSet\\Services\\RemoteAccess\\Policy";
  42. const WCHAR IDENTITY_ATTR_VALUE[] = L"User Identity Attribute";
  43. const WCHAR DEFAULT_IDENTITY_VALUE[] = L"Default User Identity";
  44. const WCHAR OVERRIDE_USERNAME_VALUE[] = L"Override User-Name";
  45. //////////
  46. // Determines whether an identity can be cracked through DsCrackNames.
  47. //////////
  48. inline BOOL isCrackable(PCWSTR szIdentity) throw ()
  49. {
  50. return wcschr(szIdentity, L'@') || // DS_USER_PRINCIPAL_NAME
  51. wcschr(szIdentity, L'=') || // DS_FQDN_1779_NAME
  52. wcschr(szIdentity, L'/') ; // DS_CANONICAL_NAME
  53. }
  54. STDMETHODIMP NTSamNames::Initialize()
  55. {
  56. // Initialize the LSA API.
  57. DWORD error = IASLsaInitialize();
  58. if (error) { return HRESULT_FROM_WIN32(error); }
  59. LONG status;
  60. DWORD cbData, type;
  61. // Open the Parameters registry key.
  62. HKEY hKey;
  63. status = RegOpenKeyW(
  64. HKEY_LOCAL_MACHINE,
  65. PARAMETERS_KEY,
  66. &hKey
  67. );
  68. if (status == NO_ERROR)
  69. {
  70. // Query the Identity Attribute.
  71. DWORD cbData = sizeof(DWORD);
  72. status = RegQueryValueExW(
  73. hKey,
  74. IDENTITY_ATTR_VALUE,
  75. NULL,
  76. &type,
  77. (LPBYTE)&identityAttr,
  78. &cbData
  79. );
  80. // If we didn't successfully read a DWORD, set to default.
  81. if (status != NO_ERROR || type != REG_DWORD || cbData != sizeof(DWORD))
  82. {
  83. identityAttr = RADIUS_ATTRIBUTE_USER_NAME;
  84. }
  85. // Query the Override User-Name flag.
  86. cbData = sizeof(DWORD);
  87. status = RegQueryValueExW(
  88. hKey,
  89. OVERRIDE_USERNAME_VALUE,
  90. NULL,
  91. &type,
  92. (LPBYTE)&overrideUsername,
  93. &cbData
  94. );
  95. // If we didn't successfully read a DWORD, set to default.
  96. if (status != NO_ERROR || type != REG_DWORD || cbData != sizeof(DWORD))
  97. {
  98. overrideUsername = FALSE;
  99. }
  100. // Query the length of the Default Identity.
  101. defaultLength = 0;
  102. status = RegQueryValueExW(
  103. hKey,
  104. DEFAULT_IDENTITY_VALUE,
  105. NULL,
  106. &type,
  107. NULL,
  108. &defaultLength
  109. );
  110. if (status == NO_ERROR &&
  111. type == REG_SZ &&
  112. defaultLength > 2 * sizeof(WCHAR))
  113. {
  114. // Allocate memory to hold the Default Identity.
  115. defaultIdentity = new (std::nothrow)
  116. WCHAR[defaultLength / sizeof(WCHAR)];
  117. if (defaultIdentity)
  118. {
  119. // Query the value of the Default Identity.
  120. status = RegQueryValueExW(
  121. hKey,
  122. DEFAULT_IDENTITY_VALUE,
  123. NULL,
  124. &type,
  125. (LPBYTE)defaultIdentity,
  126. &defaultLength
  127. );
  128. if (status != NO_ERROR ||
  129. type != REG_SZ ||
  130. defaultLength < 2 * sizeof(WCHAR))
  131. {
  132. delete[] defaultIdentity;
  133. defaultIdentity = NULL;
  134. defaultLength = 0;
  135. }
  136. }
  137. }
  138. RegCloseKey(hKey);
  139. }
  140. IASTracePrintf("User identity attribute: %lu", identityAttr);
  141. IASTracePrintf("Override User-Name: %s",
  142. overrideUsername ? "TRUE" : "FALSE");
  143. IASTracePrintf("Default user identity: %S",
  144. (defaultIdentity ? defaultIdentity : L"<Guest>"));
  145. // Small optimization.
  146. if (identityAttr == RADIUS_ATTRIBUTE_USER_NAME) { overrideUsername = TRUE; }
  147. return S_OK;
  148. }
  149. STDMETHODIMP NTSamNames::Shutdown()
  150. {
  151. IASLsaUninitialize();
  152. delete[] defaultIdentity;
  153. defaultIdentity = NULL;
  154. defaultLength = 0;
  155. return S_OK;
  156. }
  157. IASREQUESTSTATUS NTSamNames::onSyncRequest(IRequest* pRequest) throw ()
  158. {
  159. // I had to stick this at function scope, so it can be freed in
  160. // the catch block.
  161. PDS_NAME_RESULTW result = NULL;
  162. try
  163. {
  164. IASRequest request(pRequest);
  165. WCHAR name[DNLEN + UNLEN + 2], *identity;
  166. // Get the identity attribute.
  167. IASAttribute attr;
  168. if (overrideUsername ||
  169. !attr.load(request, RADIUS_ATTRIBUTE_USER_NAME, IASTYPE_OCTET_STRING))
  170. {
  171. attr.load(request, identityAttr, IASTYPE_OCTET_STRING);
  172. }
  173. if (attr != NULL && attr->Value.OctetString.dwLength != 0)
  174. {
  175. // Convert it to a UNICODE string.
  176. identity = IAS_OCT2WIDE(attr->Value.OctetString);
  177. IASTracePrintf(
  178. "NT-SAM Names handler received request with user identity %S.",
  179. identity
  180. );
  181. }
  182. else
  183. {
  184. // No identity attribute.
  185. if (defaultIdentity)
  186. {
  187. // Use the default identity if set.
  188. identity = defaultIdentity;
  189. }
  190. else
  191. {
  192. // Otherwise use the guest account for the default domain.
  193. IASGetGuestAccountName(identity = name);
  194. }
  195. IASTracePrintf(
  196. "NT-SAM Names handler using default user identity %S.",
  197. identity
  198. );
  199. }
  200. // Allocate an attribute to hold the NT4 Account Name.
  201. IASAttribute nt4Name(true);
  202. nt4Name->dwId = IAS_ATTRIBUTE_NT4_ACCOUNT_NAME;
  203. // (1) If it already contains a backslash, then use as is.
  204. PWCHAR delim = wcschr(identity, L'\\');
  205. if (delim)
  206. {
  207. if (IASGetRole() == IAS_ROLE_STANDALONE ||
  208. IASGetProductType() == IAS_PRODUCT_WORKSTATION)
  209. {
  210. // Strip out the domain.
  211. *delim = L'\0';
  212. // Make sure this is a local user.
  213. if (!IASIsDomainLocal(identity))
  214. {
  215. IASTraceString("Non-local users are not allowed -- rejecting.");
  216. return IASProcessFailure(request, IAS_LOCAL_USERS_ONLY);
  217. }
  218. // Restore the delimiter.
  219. *delim = L'\\';
  220. }
  221. IASTraceString("Username is already an NT4 account name.");
  222. nt4Name.setString(identity);
  223. }
  224. // (2) If we're stand-alone, then we don't support DsCrackNames.
  225. // (3) If it doesn't look crackable, then don't waste the network I/O.
  226. else if (IASGetRole() == IAS_ROLE_STANDALONE || !isCrackable(identity))
  227. {
  228. IASTraceString("Prepending default domain.");
  229. nt4Name->Value.String.pszWide = prependDefaultDomain(identity);
  230. nt4Name->Value.String.pszAnsi = NULL;
  231. nt4Name->Value.itType = IASTYPE_STRING;
  232. }
  233. else
  234. {
  235. // (4) Call DsCrackNames.
  236. DWORD dwErr = cracker.crackNames(
  237. DS_NAME_FLAG_EVAL_AT_DC,
  238. DS_UNKNOWN_NAME,
  239. DS_NT4_ACCOUNT_NAME,
  240. identity,
  241. &result
  242. );
  243. if (dwErr != NO_ERROR)
  244. {
  245. IASTraceFailure("DsCrackNames", dwErr);
  246. return IASProcessFailure(request, IAS_GLOBAL_CATALOG_UNAVAILABLE);
  247. }
  248. if (result->rItems->status == DS_NAME_NO_ERROR)
  249. {
  250. IASTraceString("Successfully cracked username.");
  251. // (5) DsCrackNames returned an NT4 Account Name, so use it.
  252. nt4Name.setString(result->rItems->pName);
  253. }
  254. else
  255. {
  256. IASTraceString("Global Catalog could not crack username; "
  257. "prepending default domain.");
  258. // (6) If it can't be cracked we'll assume that it's a flat
  259. // username with some weird characters.
  260. nt4Name->Value.String.pszWide = prependDefaultDomain(identity);
  261. nt4Name->Value.String.pszAnsi = NULL;
  262. nt4Name->Value.itType = IASTYPE_STRING;
  263. }
  264. cracker.freeNameResult(result);
  265. }
  266. // Convert the domain name to uppercase.
  267. delim = wcschr(nt4Name->Value.String.pszWide, L'\\');
  268. *delim = L'\0';
  269. _wcsupr(nt4Name->Value.String.pszWide);
  270. *delim = L'\\';
  271. nt4Name.store(request);
  272. // For now, we'll use this as the FQDN as well.
  273. IASStoreFQUserName(
  274. request,
  275. DS_NT4_ACCOUNT_NAME,
  276. nt4Name->Value.String.pszWide
  277. );
  278. IASTracePrintf("SAM-Account-Name is \"%S\".",
  279. nt4Name->Value.String.pszWide);
  280. }
  281. catch (const _com_error& ce)
  282. {
  283. IASTraceExcept();
  284. if (result) { cracker.freeNameResult(result); }
  285. return IASProcessFailure(pRequest, ce.Error());
  286. }
  287. return IAS_REQUEST_STATUS_HANDLED;
  288. }
  289. PWSTR NTSamNames::prependDefaultDomain(PCWSTR username)
  290. {
  291. _ASSERT(username != NULL);
  292. // Figure out how long everything is.
  293. PCWSTR domain = IASGetDefaultDomain();
  294. ULONG domainLen = wcslen(domain);
  295. ULONG usernameLen = wcslen(username) + 1;
  296. // Allocate the needed memory.
  297. ULONG needed = domainLen + usernameLen + 1;
  298. PWSTR retval = (PWSTR)CoTaskMemAlloc(needed * sizeof(WCHAR));
  299. if (!retval) { _com_issue_error(E_OUTOFMEMORY); }
  300. // Set up the cursor used for packing the strings.
  301. PWSTR dst = retval;
  302. // Copy in the domain name.
  303. memcpy(dst, domain, domainLen * sizeof(WCHAR));
  304. dst += domainLen;
  305. // Add the delimiter.
  306. *dst++ = L'\\';
  307. // Copy in the username.
  308. // Note: usernameLen includes the null-terminator.
  309. memcpy(dst, username, usernameLen * sizeof(WCHAR));
  310. return retval;
  311. }