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.

419 lines
11 KiB

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