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.

422 lines
12 KiB

  1. #include "precomp.hpp"
  2. //#include <nt.h>
  3. //#include <ntrtl.h>
  4. //#include <nturtl.h>
  5. //#include <windows.h>
  6. #include <wincred.h>
  7. #include <align.h>
  8. #include <lm.h>
  9. #include <ntsecapi.h>
  10. #include <dsgetdc.h>
  11. #include <stdlib.h>
  12. //#include <stdio.h>
  13. #include <string.h>
  14. // !!!!!
  15. // this file is a duplicate of a nearly identical file in the keymgr project. It should be removed when
  16. // the implementation of NetUserChangePassword() is updated to handle unc names and MIT Kerberos
  17. // realms properly. For now, it wraps NetUserChangePassword() to handle the extra cases.
  18. // Dependent libraries:
  19. // secur32.lib, netapi32.lib
  20. // external fn: NET_API_STATUS NetUserChangePasswordEy(LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR)
  21. BOOL
  22. IsMITName (
  23. LPCWSTR UserName
  24. )
  25. {
  26. BOOL fReturn = FALSE;
  27. HKEY MitKey;
  28. DWORD Index;
  29. PWSTR Realms;
  30. DWORD RealmSize;
  31. int err;
  32. DWORD NumRealms;
  33. DWORD MaxRealmLength;
  34. FILETIME KeyTime;
  35. WCHAR *szUncTail;
  36. if (NULL == UserName) return FALSE;
  37. szUncTail = wcschr(UserName,'@');
  38. if (NULL == szUncTail) return FALSE;
  39. szUncTail++; // point to char following @
  40. err = RegOpenKeyEx(
  41. HKEY_LOCAL_MACHINE,
  42. TEXT("System\\CurrentControlSet\\Control\\Lsa\\Kerberos\\Domains"),
  43. 0,
  44. KEY_READ,
  45. &MitKey );
  46. if ( err == 0 )
  47. {
  48. #ifdef LOUDLY
  49. CreduiDebugLog("Kerberos domains key opened\n");
  50. #endif
  51. err = RegQueryInfoKey( MitKey,
  52. NULL,
  53. NULL,
  54. NULL,
  55. &NumRealms,
  56. &MaxRealmLength,
  57. NULL,
  58. NULL,
  59. NULL,
  60. NULL,
  61. NULL,
  62. NULL );
  63. MaxRealmLength++ ;
  64. MaxRealmLength *= sizeof( WCHAR );
  65. Realms = (PWSTR) malloc(MaxRealmLength );
  66. if ( Realms)
  67. {
  68. #ifdef LOUDLY
  69. CreduiDebugLog("Kerberos realms found\n");
  70. #endif
  71. for ( Index = 0 ; Index < NumRealms ; Index++ )
  72. {
  73. RealmSize = MaxRealmLength ;
  74. err = RegEnumKeyEx( MitKey,
  75. Index,
  76. Realms,
  77. &RealmSize,
  78. NULL,
  79. NULL,
  80. NULL,
  81. &KeyTime );
  82. if (err == 0)
  83. {
  84. #ifdef LOUDLY
  85. CreduiDebugLog("Fetched realm: ");
  86. CreduiDebugLog(Realms);
  87. CreduiDebugLog("\n");
  88. CreduiDebugLog("Username suffix: ");
  89. CreduiDebugLog(szUncTail);
  90. CreduiDebugLog("\n");
  91. #endif
  92. if (0 == _wcsicmp(szUncTail, Realms))
  93. {
  94. #ifdef LOUDLY
  95. CreduiDebugLog("Username maps to an MIT realm\n");
  96. #endif
  97. fReturn = TRUE;
  98. break;
  99. }
  100. }
  101. }
  102. }
  103. free(Realms);
  104. }
  105. return fReturn;
  106. }
  107. NTSTATUS
  108. MitChangePasswordEy(
  109. LPCWSTR DomainName,
  110. LPCWSTR UserName,
  111. LPCWSTR OldPassword,
  112. LPCWSTR NewPassword,
  113. NTSTATUS *pSubStatus
  114. )
  115. {
  116. HANDLE hLsa = NULL;
  117. NTSTATUS Status;
  118. NTSTATUS SubStatus;
  119. STRING Name;
  120. ULONG PackageId;
  121. PVOID Response = NULL ;
  122. ULONG ResponseSize;
  123. PKERB_CHANGEPASSWORD_REQUEST ChangeRequest = NULL;
  124. ULONG ChangeSize = 0;
  125. UNICODE_STRING User,Domain,OldPass,NewPass;
  126. Status = LsaConnectUntrusted(&hLsa);
  127. if (!SUCCEEDED(Status)) goto Cleanup;
  128. #ifdef LOUDLY
  129. CreduiDebugLog("We have an LSA handle\n");
  130. #endif
  131. RtlInitString(
  132. &Name,
  133. MICROSOFT_KERBEROS_NAME_A
  134. );
  135. Status = LsaLookupAuthenticationPackage(
  136. hLsa,
  137. &Name,
  138. &PackageId
  139. );
  140. if (!NT_SUCCESS(Status))
  141. {
  142. goto Cleanup;
  143. }
  144. #ifdef LOUDLY
  145. CreduiDebugLog("Authentication package found\n");
  146. #endif
  147. RtlInitUnicodeString(
  148. &User,
  149. UserName
  150. );
  151. RtlInitUnicodeString(
  152. &Domain,
  153. DomainName
  154. );
  155. RtlInitUnicodeString(
  156. &OldPass,
  157. OldPassword
  158. );
  159. RtlInitUnicodeString(
  160. &NewPass,
  161. NewPassword
  162. );
  163. ChangeSize = ROUND_UP_COUNT(sizeof(KERB_CHANGEPASSWORD_REQUEST),4)+
  164. User.Length +
  165. Domain.Length +
  166. OldPass.Length +
  167. NewPass.Length ;
  168. ChangeRequest = (PKERB_CHANGEPASSWORD_REQUEST) LocalAlloc(LMEM_ZEROINIT, ChangeSize );
  169. if ( ChangeRequest == NULL )
  170. {
  171. Status = STATUS_NO_MEMORY ;
  172. goto Cleanup ;
  173. }
  174. ChangeRequest->MessageType = KerbChangePasswordMessage;
  175. ChangeRequest->AccountName = User;
  176. ChangeRequest->AccountName.Buffer = (LPWSTR) ROUND_UP_POINTER(sizeof(KERB_CHANGEPASSWORD_REQUEST) + (PBYTE) ChangeRequest,4);
  177. RtlCopyMemory(
  178. ChangeRequest->AccountName.Buffer,
  179. User.Buffer,
  180. User.Length
  181. );
  182. ChangeRequest->DomainName = Domain;
  183. ChangeRequest->DomainName.Buffer = ChangeRequest->AccountName.Buffer + ChangeRequest->AccountName.Length / sizeof(WCHAR);
  184. RtlCopyMemory(
  185. ChangeRequest->DomainName.Buffer,
  186. Domain.Buffer,
  187. Domain.Length
  188. );
  189. ChangeRequest->OldPassword = OldPass;
  190. ChangeRequest->OldPassword.Buffer = ChangeRequest->DomainName.Buffer + ChangeRequest->DomainName.Length / sizeof(WCHAR);
  191. RtlCopyMemory(
  192. ChangeRequest->OldPassword.Buffer,
  193. OldPass.Buffer,
  194. OldPass.Length
  195. );
  196. ChangeRequest->NewPassword = NewPass;
  197. ChangeRequest->NewPassword.Buffer = ChangeRequest->OldPassword.Buffer + ChangeRequest->OldPassword.Length / sizeof(WCHAR);
  198. RtlCopyMemory(
  199. ChangeRequest->NewPassword.Buffer,
  200. NewPass.Buffer,
  201. NewPass.Length
  202. );
  203. //
  204. // We are running as the caller, so state we are impersonating
  205. //
  206. //ChangeRequest->Impersonating = TRUE;
  207. #ifdef LOUDLY
  208. CreduiDebugLog("Attempting to call the authentication package\n");
  209. #endif
  210. Status = LsaCallAuthenticationPackage(
  211. hLsa,
  212. PackageId,
  213. ChangeRequest,
  214. ChangeSize,
  215. &Response,
  216. &ResponseSize,
  217. &SubStatus
  218. );
  219. if (!NT_SUCCESS(Status) || !NT_SUCCESS(SubStatus))
  220. {
  221. #ifdef LOUDLY
  222. WCHAR szsz[200];
  223. swprintf(szsz,L"Call failed. Status %x SubStatus %x\n",Status, SubStatus);
  224. CreduiDebugLog(szsz);
  225. #endif
  226. if (NT_SUCCESS(Status))
  227. {
  228. Status = SubStatus;
  229. *pSubStatus = STATUS_UNSUCCESSFUL ;
  230. }
  231. else
  232. {
  233. *pSubStatus = SubStatus;
  234. }
  235. }
  236. Cleanup:
  237. if (hLsa) LsaDeregisterLogonProcess(hLsa);
  238. if (Response != NULL) LsaFreeReturnBuffer(Response);
  239. if (ChangeRequest != NULL)
  240. {
  241. SecureZeroMemory(ChangeRequest, ChangeSize);
  242. ChangeSize = 0;
  243. LocalFree(ChangeRequest);
  244. }
  245. return(Status);
  246. }
  247. /*
  248. NetUserChangePasswordEy()
  249. A wrapper function to superset the functionality of NetUserChangePassword(), specifically
  250. by adding support for changing the account password for an MIT Kerberos principal.
  251. This routine accepts:
  252. 1. uncracked username, with NULL domain
  253. 2. cracked username, with domain portion routed to the domain argument
  254. In case 1, it handles all cases, including MIT realm password changes
  255. In case 2, it will not handle MIT realms.
  256. Case 2 is provided for backwards compatibility with NetUserChangePassword(). It is intended
  257. that callers should pass the uncracked name, and remove the cracking code from the client.
  258. */
  259. NET_API_STATUS
  260. NetUserChangePasswordEy (
  261. LPCWSTR domainname,
  262. LPCWSTR username,
  263. LPCWSTR oldpassword,
  264. LPCWSTR newpassword
  265. )
  266. {
  267. NTSTATUS ns; // status from call
  268. NET_API_STATUS nas;
  269. NTSTATUS ss; // substatus
  270. #ifdef LOUDLY
  271. CreduiDebugLog("NetUserChangePasswordEy called for ");
  272. CreduiDebugLog(username);
  273. CreduiDebugLog("\n");
  274. #endif
  275. // domainname may be a kerberos realm
  276. // If not a UNC name, call through to NetUserChangePassword
  277. // else
  278. // locate UNC suffix
  279. // search all domains returned by DsEnumerateDomainTrusts() for a match
  280. // On match, if is kerberos realm, call MitChangePasswordEy()
  281. // else call NetUserChangePassword
  282. if ((domainname == NULL) && IsMITName(username))
  283. {
  284. ns = MitChangePasswordEy(domainname, username, oldpassword, newpassword, &ss);
  285. // remap certain errors returned by MitChangePasswordEy to coincide with those of NetUserChangePassword
  286. if (NT_SUCCESS(ns)) nas = NERR_Success;
  287. else
  288. {
  289. switch (ns)
  290. {
  291. case STATUS_CANT_ACCESS_DOMAIN_INFO:
  292. case STATUS_NO_SUCH_DOMAIN:
  293. {
  294. nas = NERR_InvalidComputer;
  295. break;
  296. }
  297. case STATUS_NO_SUCH_USER:
  298. case STATUS_WRONG_PASSWORD_CORE:
  299. case STATUS_WRONG_PASSWORD:
  300. {
  301. nas = ERROR_INVALID_PASSWORD;
  302. break;
  303. }
  304. case STATUS_ACCOUNT_RESTRICTION:
  305. case STATUS_ACCESS_DENIED:
  306. case STATUS_BACKUP_CONTROLLER:
  307. {
  308. nas = ERROR_ACCESS_DENIED;
  309. break;
  310. }
  311. case STATUS_PASSWORD_RESTRICTION:
  312. {
  313. nas = NERR_PasswordTooShort;
  314. break;
  315. }
  316. default:
  317. nas = -1; // will produce omnibus error message when found (none of the above)
  318. break;
  319. }
  320. }
  321. }
  322. else if (NULL == domainname)
  323. {
  324. WCHAR RetUserName[CRED_MAX_USERNAME_LENGTH + 1];
  325. WCHAR RetDomainName[CRED_MAX_USERNAME_LENGTH + 1];
  326. RetDomainName[0] = 0;
  327. DWORD Status = CredUIParseUserNameW(
  328. username,
  329. RetUserName,
  330. CRED_MAX_USERNAME_LENGTH,
  331. RetDomainName,
  332. CRED_MAX_USERNAME_LENGTH);
  333. switch (Status)
  334. {
  335. case NO_ERROR:
  336. {
  337. #ifdef LOUDLY
  338. CreduiDebugLog("Non-MIT password change for ");
  339. CreduiDebugLog(RetUserName);
  340. CreduiDebugLog(" of domain ");
  341. CreduiDebugLog(RetDomainName);
  342. CreduiDebugLog("\n");
  343. #endif
  344. nas = NetUserChangePassword(RetDomainName,RetUserName,oldpassword,newpassword);
  345. break;
  346. }
  347. case ERROR_INSUFFICIENT_BUFFER:
  348. nas = ERROR_INVALID_PARAMETER;
  349. break;
  350. case ERROR_INVALID_ACCOUNT_NAME:
  351. default:
  352. nas = NERR_UserNotFound;
  353. break;
  354. }
  355. }
  356. else
  357. {
  358. // both username and domainname passed.
  359. nas = NetUserChangePassword(domainname,username,oldpassword,newpassword);
  360. }
  361. #ifdef LOUDLY
  362. WCHAR szsz[200];
  363. swprintf(szsz,L"NUCPEy returns %x\n",nas);
  364. CreduiDebugLog(szsz);
  365. #endif
  366. return nas;
  367. }