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.

1213 lines
33 KiB

  1. /*++
  2. Copyright (c) 1987-1997 Microsoft Corporation
  3. Module Name:
  4. rassfm.c
  5. Abstract:
  6. This module implements the subauthentication needed by the various RAS
  7. protocols (ARAP, MD5 etc.).
  8. It is adapted from the subauthentication sample from CliffV.
  9. Author:
  10. Shirish Koti 28-Feb-97
  11. Revisions:
  12. 06/02/97 Steve Cobb, Added MD5-CHAP support
  13. --*/
  14. #include <nt.h>
  15. #include <ntrtl.h>
  16. #include <nturtl.h>
  17. #include <ntsam.h>
  18. #include <windows.h>
  19. #include <ntmsv1_0.h>
  20. #include <crypt.h>
  21. #include <samrpc.h>
  22. #include <lsarpc.h>
  23. #define SECURITY_WIN32
  24. #define SECURITY_PACKAGE
  25. #include <security.h>
  26. #include <secint.h>
  27. #include <samisrv.h>
  28. #include <lsaisrv.h>
  29. #include <ntlsa.h>
  30. #include <lmcons.h>
  31. #include <logonmsv.h>
  32. #include <macfile.h>
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include "rasman.h"
  36. #include "rasfmsub.h"
  37. #include "arapio.h"
  38. #include "md5port.h"
  39. #include "cleartxt.h"
  40. #include "rassfm.h"
  41. // Private heap used by the RASSFM module.
  42. PVOID RasSfmPrivateHeap;
  43. // Empty OWF password.
  44. const NT_OWF_PASSWORD EMPTY_OWF_PASSWORD =
  45. {
  46. {
  47. { '\x31', '\xD6', '\xCF', '\xE0', '\xD1', '\x6A', '\xE9', '\x31' },
  48. { '\xB7', '\x3C', '\x59', '\xD7', '\xE0', '\xC0', '\x89', '\xC0' }
  49. }
  50. };
  51. BOOL
  52. RasSfmSubAuthEntry(
  53. IN HANDLE hinstDll,
  54. IN DWORD fdwReason,
  55. IN LPVOID lpReserved
  56. )
  57. /*++
  58. Routine Description:
  59. Entry point into the dll
  60. Arguments:
  61. hinstDll - handle
  62. fdwReason - why the entry
  63. lpReserved -
  64. Return Value:
  65. TRUE
  66. --*/
  67. {
  68. switch (fdwReason)
  69. {
  70. case DLL_PROCESS_ATTACH:
  71. RasSfmPrivateHeap = RtlCreateHeap(
  72. HEAP_GROWABLE,
  73. NULL,
  74. 0,
  75. 0,
  76. NULL,
  77. NULL
  78. );
  79. DisableThreadLibraryCalls( hinstDll );
  80. InitializeCriticalSection( &ArapDesLock );
  81. break;
  82. case DLL_PROCESS_DETACH:
  83. RtlDestroyHeap(RasSfmPrivateHeap);
  84. break;
  85. }
  86. return(TRUE);
  87. }
  88. NTSTATUS
  89. Msv1_0SubAuthenticationRoutineEx(
  90. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  91. IN PVOID LogonInformation,
  92. IN ULONG Flags,
  93. IN PUSER_ALL_INFORMATION UserAll,
  94. IN SAM_HANDLE UserHandle,
  95. IN OUT PMSV1_0_VALIDATION_INFO ValidationInfo,
  96. OUT PULONG ActionsPerformed
  97. )
  98. /*++
  99. Routine Description:
  100. This is the routine called in by the MSV package (if it was requested that
  101. the subauth package be called in), as a result of calling LsaLogonUser.
  102. This routine does RAS protocol specific authentication.
  103. In case of both ARAP and MD5 CHAP, the only thing we do in this routine is
  104. actual password authentication and leave everything else (logon hours, pwd
  105. expiry etc.) to the MSV package.
  106. Arguments:
  107. LogonLevel - we don't use it
  108. LogonInformation - contains the info our client side gave to us
  109. Flags - we don't use this flag
  110. UserAll - we get password creation,expiry times from this
  111. UserHandle - we get the clear text password using this
  112. ValidationInfo - set return info
  113. ActionsPerformed - we always set this to NTLM_SUBAUTH_PASSWORD to indicate
  114. to the package that all we did was check for password
  115. Return Value:
  116. STATUS_SUCCESS: if there was no error.
  117. STATUS_WRONG_PASSWORD: The password was invalid.
  118. --*/
  119. {
  120. NTSTATUS status;
  121. PNETLOGON_NETWORK_INFO pLogonNetworkInfo;
  122. PRAS_SUBAUTH_INFO pRasSubAuthInfo;
  123. pLogonNetworkInfo = (PNETLOGON_NETWORK_INFO) LogonInformation;
  124. pRasSubAuthInfo = (PRAS_SUBAUTH_INFO)
  125. pLogonNetworkInfo->NtChallengeResponse.Buffer;
  126. switch (pRasSubAuthInfo->ProtocolType)
  127. {
  128. //
  129. // do the ARAP-specific authentication
  130. //
  131. case RAS_SUBAUTH_PROTO_ARAP:
  132. status = ArapSubAuthentication(pLogonNetworkInfo,
  133. UserAll,
  134. UserHandle,
  135. ValidationInfo);
  136. ValidationInfo->Authoritative = TRUE;
  137. *ActionsPerformed = MSV1_0_SUBAUTH_PASSWORD;
  138. break;
  139. // MD5 CHAP subauthentication.
  140. //
  141. case RAS_SUBAUTH_PROTO_MD5CHAP:
  142. {
  143. // Subauthenticate the user account.
  144. //
  145. status = MD5ChapSubAuthentication(
  146. UserHandle,
  147. UserAll,
  148. pRasSubAuthInfo
  149. );
  150. // No validation information is returned. Might want to return a
  151. // session key here in the future.
  152. //
  153. ValidationInfo->WhichFields = 0;
  154. ValidationInfo->Authoritative = TRUE;
  155. *ActionsPerformed = MSV1_0_SUBAUTH_PASSWORD;
  156. break;
  157. }
  158. // MD5 CHAP Ex subauthentication.
  159. //
  160. case RAS_SUBAUTH_PROTO_MD5CHAP_EX:
  161. {
  162. // Subauthenticate the user account.
  163. //
  164. status = MD5ChapExSubAuthentication(
  165. UserHandle,
  166. UserAll,
  167. pRasSubAuthInfo
  168. );
  169. // No validation information is returned. Might want to return a
  170. // session key here in the future.
  171. //
  172. ValidationInfo->WhichFields = 0;
  173. ValidationInfo->Authoritative = TRUE;
  174. *ActionsPerformed = MSV1_0_SUBAUTH_PASSWORD;
  175. break;
  176. }
  177. default:
  178. DBGPRINT("RASSFM subauth pkg: bad protocol type %d\n",
  179. pRasSubAuthInfo->ProtocolType);
  180. status = STATUS_WRONG_PASSWORD;
  181. break;
  182. }
  183. return(status);
  184. }
  185. NTSTATUS
  186. Msv1_0SubAuthenticationRoutineGeneric(
  187. IN PVOID SubmitBuffer,
  188. IN ULONG SubmitBufferLength,
  189. OUT PULONG ReturnBufferLength,
  190. OUT PVOID *ReturnBuffer
  191. )
  192. /*++
  193. Routine Description:
  194. This is the routine called in by the MSV package (if it was requested that
  195. the subauth package be called in), as a result of calling
  196. LsaCallAuthenticationPackage. This routine does RAS protocol specific
  197. functions.
  198. In case of ARAP, we implement change password functionality in this routine.
  199. Arguments:
  200. SubmitBuffer - the buffer containing password change info
  201. SubmitBufferLength - length of this buffer
  202. ReturnBufferLength - we don't use it
  203. ReturnBuffer - we don't use it
  204. Return Value:
  205. STATUS_SUCCESS: if there was no error.
  206. --*/
  207. {
  208. PARAP_SUBAUTH_REQ pArapSubAuthInfo;
  209. PUNICODE_STRING pUserName;
  210. PUNICODE_STRING pDomainName;
  211. PRAS_SUBAUTH_INFO pRasSubAuthInfo;
  212. NTSTATUS status;
  213. pRasSubAuthInfo = (PRAS_SUBAUTH_INFO)SubmitBuffer;
  214. switch (pRasSubAuthInfo->ProtocolType)
  215. {
  216. //
  217. // do the ARAP-specific authentication
  218. //
  219. case RAS_SUBAUTH_PROTO_ARAP:
  220. status = ArapChangePassword(pRasSubAuthInfo,
  221. ReturnBufferLength,
  222. ReturnBuffer);
  223. break;
  224. default:
  225. DBGPRINT("Msv1_0SubAuthenticationRoutineGeneric: bad protocol type\n");
  226. ASSERT(0);
  227. status = STATUS_UNSUCCESSFUL;
  228. }
  229. return(status);
  230. }
  231. NTSTATUS
  232. ArapSubAuthentication(
  233. IN OUT PNETLOGON_NETWORK_INFO pLogonNetworkInfo,
  234. IN PUSER_ALL_INFORMATION UserAll,
  235. IN SAM_HANDLE UserHandle,
  236. IN OUT PMSV1_0_VALIDATION_INFO ValidationInfo
  237. )
  238. /*++
  239. Routine Description:
  240. This is the routine that does the actuall authentication. It retrieves
  241. the clear-text password, does the DES encryption of the challenge and
  242. compares with what the Mac client has sent to determine if authentication
  243. succeeded. Also, it returns a response to the challenge sent to us by
  244. the Mac.
  245. Arguments:
  246. pLogonNetworkInfo - ptr to the NETLOGON_NETWORK_INFO struct
  247. UserAll - ptr to the USER_ALL_INFORMATION struct
  248. UserHandle - sam handle for the user
  249. ValidationInfo - what we return to our caller
  250. Return Value:
  251. STATUS_SUCCESS: if authentication succeeded, appropriate error otherwise
  252. --*/
  253. {
  254. NTSTATUS status;
  255. PARAP_SUBAUTH_REQ pArapSubAuthInfo;
  256. ARAP_CHALLENGE Challenge;
  257. PARAP_SUBAUTH_RESP pArapResp;
  258. PUNICODE_STRING pUserName;
  259. PUNICODE_STRING pDomainName;
  260. UNICODE_STRING UnicodePassword;
  261. ANSI_STRING AnsiPassword;
  262. PRAS_SUBAUTH_INFO pRasSubAuthInfo;
  263. DWORD Response1;
  264. DWORD Response2;
  265. UCHAR ClearTextPassword[64];
  266. BOOLEAN fCallerIsArap;
  267. pRasSubAuthInfo = (PRAS_SUBAUTH_INFO)
  268. pLogonNetworkInfo->NtChallengeResponse.Buffer;
  269. pArapSubAuthInfo = (PARAP_SUBAUTH_REQ)&pRasSubAuthInfo->Data[0];
  270. //
  271. // NOTE: this is a quick-n-dirty workaround to returning a clean buffer
  272. // We use the KickoffTime,LogoffTime and SessionKey fields of ValidationInfo
  273. // The SessionKey is a 16 byte field. We use only 12 bytes, but be careful
  274. // not to exceed it!!
  275. ASSERT(sizeof(ARAP_SUBAUTH_RESP) <= sizeof(USER_SESSION_KEY));
  276. //
  277. // store the password create and expiry date: we need to send it to Mac
  278. //
  279. ValidationInfo->KickoffTime = UserAll->PasswordLastSet;
  280. ValidationInfo->LogoffTime = UserAll->PasswordMustChange;
  281. ValidationInfo->WhichFields = ( MSV1_0_VALIDATION_LOGOFF_TIME |
  282. MSV1_0_VALIDATION_KICKOFF_TIME |
  283. MSV1_0_VALIDATION_SESSION_KEY |
  284. MSV1_0_VALIDATION_USER_FLAGS );
  285. ValidationInfo->UserFlags = 0;
  286. pArapResp = (PARAP_SUBAUTH_RESP)&ValidationInfo->SessionKey;
  287. if ((pArapSubAuthInfo->PacketType != ARAP_SUBAUTH_LOGON_PKT) &&
  288. (pArapSubAuthInfo->PacketType != SFM_SUBAUTH_LOGON_PKT))
  289. {
  290. DBGPRINT("ARAPSubAuth: PacketType is not ARAP, returning failure\n");
  291. pArapResp->Result = ARAPERR_BAD_FORMAT;
  292. return(STATUS_UNSUCCESSFUL);
  293. }
  294. fCallerIsArap = (pArapSubAuthInfo->PacketType == ARAP_SUBAUTH_LOGON_PKT);
  295. //
  296. // presently no one calls with fGuestLogon. If in future, we need Guest logon,
  297. // then we will have to check if (Flags & MSV1_0_GUEST_LOGON) is set to allow
  298. // Guest logon. Right now, we fail the request.
  299. //
  300. if (pArapSubAuthInfo->Logon.fGuestLogon)
  301. {
  302. DBGPRINT("ARAPSubAuth: how come guest logon is reaching here??\n");
  303. ASSERT(0);
  304. pArapResp->Result = ARAPERR_AUTH_FAILURE;
  305. return(STATUS_UNSUCCESSFUL);
  306. }
  307. pUserName = &pLogonNetworkInfo->Identity.UserName;
  308. pDomainName = &pLogonNetworkInfo->Identity.LogonDomainName;
  309. status = RetrieveCleartextPassword(UserHandle, UserAll, &UnicodePassword);
  310. if (status != STATUS_SUCCESS)
  311. {
  312. DBGPRINT("ARAPSubAuth: RetrieveCleartextPassword failed %lx\n",status);
  313. pArapResp->Result = ARAPERR_PASSWD_NOT_AVAILABLE;
  314. return(STATUS_UNSUCCESSFUL);
  315. }
  316. RtlZeroMemory(ClearTextPassword, sizeof(ClearTextPassword));
  317. AnsiPassword.Length = AnsiPassword.MaximumLength = sizeof(ClearTextPassword);
  318. AnsiPassword.Buffer = ClearTextPassword;
  319. status = RtlUnicodeStringToAnsiString( &AnsiPassword, &UnicodePassword, FALSE );
  320. ZeroMemory(UnicodePassword.Buffer, UnicodePassword.Length);
  321. // we don't need the unicode password anymore
  322. RtlFreeUnicodeString(&UnicodePassword);
  323. if (!NT_SUCCESS(status))
  324. {
  325. DBGPRINT("ARAPSubAuth: RtlUnicodeStringToAnsiString failed %lx\n",status);
  326. pArapResp->Result = ARAPERR_PASSWD_NOT_AVAILABLE;
  327. return(STATUS_UNSUCCESSFUL);
  328. }
  329. //
  330. // Mac sends challenge to us as well: compute the response
  331. //
  332. Challenge.high = pArapSubAuthInfo->Logon.MacChallenge1;
  333. Challenge.low = pArapSubAuthInfo->Logon.MacChallenge2;
  334. EnterCriticalSection( &ArapDesLock );
  335. if (fCallerIsArap)
  336. {
  337. DoDesInit(ClearTextPassword, TRUE);
  338. }
  339. //
  340. // RandNum expects the low-bit of each byte (of password) to be cleared
  341. // during key-generation
  342. //
  343. else
  344. {
  345. DoDesInit(ClearTextPassword, FALSE);
  346. }
  347. DoTheDESEncrypt((PBYTE)&Challenge);
  348. //
  349. // copy the response that needs to be sent back to the Mac
  350. //
  351. pArapResp->Response = Challenge;
  352. //
  353. // encrypt the challenge that we sent to find out if this Mac is honest
  354. //
  355. Challenge.high = pArapSubAuthInfo->Logon.NTChallenge1;
  356. Challenge.low = pArapSubAuthInfo->Logon.NTChallenge2;
  357. DoTheDESEncrypt((PBYTE)&Challenge);
  358. Response1 = Challenge.high;
  359. Response2 = Challenge.low;
  360. DoDesEnd();
  361. LeaveCriticalSection( &ArapDesLock );
  362. //
  363. // zero the clear text password: we don't need it hanging around
  364. //
  365. RtlZeroMemory(ClearTextPassword, sizeof(ClearTextPassword));
  366. //
  367. // does the response returned by the Mac match ours?
  368. //
  369. if ((Response1 == pArapSubAuthInfo->Logon.MacResponse1) &&
  370. (Response2 == pArapSubAuthInfo->Logon.MacResponse2))
  371. {
  372. pArapResp->Result = ARAPERR_NO_ERROR;
  373. status = STATUS_SUCCESS;
  374. }
  375. else
  376. {
  377. DBGPRINT("ARAPSubAuth: our Challenge: %lx %lx\n",
  378. pArapSubAuthInfo->Logon.NTChallenge1,pArapSubAuthInfo->Logon.NTChallenge2);
  379. DBGPRINT("ARAPSubAuth: Response don't match! (ours %lx %lx vs. Mac's %lx %lx)\n",
  380. Response1,Response2,pArapSubAuthInfo->Logon.MacResponse1,
  381. pArapSubAuthInfo->Logon.MacResponse2);
  382. pArapResp->Response.high = 0;
  383. pArapResp->Response.low = 0;
  384. pArapResp->Result = ARAPERR_AUTH_FAILURE;
  385. status = STATUS_WRONG_PASSWORD;
  386. }
  387. return(status);
  388. }
  389. NTSTATUS
  390. ArapChangePassword(
  391. IN OUT PRAS_SUBAUTH_INFO pRasSubAuthInfo,
  392. OUT PULONG ReturnBufferLength,
  393. OUT PVOID *ReturnBuffer
  394. )
  395. /*++
  396. Routine Description:
  397. This routine is called to change the password of the user in question.
  398. It first retrieves the clear-text password, does the DES decryption of the
  399. munged old password and munged new password to get the clear-text old and
  400. new passwords; makes sure that the old password matches with what we have
  401. as the password and then finally, sets the new password.
  402. Arguments:
  403. pRasSubAuthInfo - ptr to RAS_SUBAUTH_INFO struct: input data
  404. ReturnBufferLength - how much are we returning
  405. ReturnBuffer - what we return: output data
  406. Return Value:
  407. STATUS_SUCCESS: if password change succeeded, appropriate error otherwise
  408. --*/
  409. {
  410. NTSTATUS status;
  411. PARAP_SUBAUTH_REQ pArapSubAuthInfo;
  412. PARAP_SUBAUTH_RESP pArapResp;
  413. UNICODE_STRING UserName;
  414. UNICODE_STRING PackageName;
  415. UNICODE_STRING UnicodePassword;
  416. ANSI_STRING AnsiPassword;
  417. USER_INFORMATION_CLASS UserInformationClass;
  418. USER_ALL_INFORMATION UserAllInfo;
  419. ARAP_CHALLENGE Challenge;
  420. USER_PARAMETERS_INFORMATION *oldParmInfo=NULL;
  421. PSAMPR_USER_ALL_INFORMATION UserParmInfo=NULL;
  422. UCHAR OldPwd[32];
  423. UCHAR NewPwd[32];
  424. UCHAR MacsOldPwd[32];
  425. WCHAR NtPassword[40];
  426. UCHAR NewPwdLen;
  427. UCHAR OldPwdLen;
  428. UCHAR MacOldPwdLen;
  429. SAMPR_HANDLE UserHandle;
  430. PVOID Credentials;
  431. DWORD CredentialSize;
  432. PUCHAR pBufPtr;
  433. BOOLEAN fCallerIsArap;
  434. UCHAR FirstByte;
  435. BOOLEAN fPasswordAvailable=TRUE;
  436. UCHAR i;
  437. *ReturnBuffer = MIDL_user_allocate( sizeof(ARAP_SUBAUTH_RESP) );
  438. if (*ReturnBuffer == NULL)
  439. {
  440. DBGPRINT("ARAPChgPwd: MIDL_alloc failed!\n");
  441. *ReturnBufferLength = 0;
  442. return(STATUS_INSUFFICIENT_RESOURCES);
  443. }
  444. *ReturnBufferLength = sizeof(ARAP_SUBAUTH_RESP);
  445. pArapResp = (PARAP_SUBAUTH_RESP)*ReturnBuffer;
  446. pArapSubAuthInfo = (PARAP_SUBAUTH_REQ)&pRasSubAuthInfo->Data[0];
  447. if ((pArapSubAuthInfo->PacketType != ARAP_SUBAUTH_CHGPWD_PKT) &&
  448. (pArapSubAuthInfo->PacketType != SFM_SUBAUTH_CHGPWD_PKT))
  449. {
  450. DBGPRINT("ARAPChgPwd: bad packet type %d!\n",pArapSubAuthInfo->PacketType);
  451. pArapResp->Result = ARAPERR_BAD_FORMAT;
  452. return(STATUS_UNSUCCESSFUL);
  453. }
  454. fCallerIsArap = (pArapSubAuthInfo->PacketType == ARAP_SUBAUTH_CHGPWD_PKT);
  455. UserName.Length = (sizeof(WCHAR) * wcslen(pArapSubAuthInfo->ChgPwd.UserName));
  456. UserName.MaximumLength = UserName.Length;
  457. UserName.Buffer = pArapSubAuthInfo->ChgPwd.UserName;
  458. status = ArapGetSamHandle(&UserHandle, &UserName);
  459. if (status != STATUS_SUCCESS)
  460. {
  461. DBGPRINT("Arap: ArapGetSamHandle failed with %lx\n", status);
  462. pArapResp->Result = ARAPERR_COULDNT_GET_SAMHANDLE;
  463. return(status);
  464. }
  465. RtlZeroMemory(OldPwd, sizeof(OldPwd));
  466. RtlZeroMemory(MacsOldPwd, sizeof(MacsOldPwd));
  467. RtlZeroMemory(NewPwd, sizeof(NewPwd));
  468. //
  469. // are we on a DS?
  470. //
  471. if (SampUsingDsData())
  472. {
  473. RtlInitUnicodeString( &PackageName, CLEAR_TEXT_PWD_PACKAGE );
  474. //
  475. // get the clear text password
  476. //
  477. status = SamIRetrievePrimaryCredentials( (PVOID)UserHandle,
  478. &PackageName,
  479. &Credentials,
  480. &CredentialSize );
  481. if (status != STATUS_SUCCESS)
  482. {
  483. DBGPRINT("ARAPSubAuth: SamI...Credentials failed %lx\n",status);
  484. pArapResp->Result = ARAPERR_PASSWD_NOT_AVAILABLE;
  485. SamrCloseHandle( &UserHandle );
  486. return(status);
  487. }
  488. //
  489. // if we are returned a null password, it could be that the password is really
  490. // null, or that cleartext password isn't available for this user. If it's
  491. // the latter, we need to bail out!
  492. //
  493. if (CredentialSize == 0)
  494. {
  495. // get the OWF for this user
  496. status = SamrQueryInformationUser( UserHandle,
  497. UserParametersInformation,
  498. (PSAMPR_USER_INFO_BUFFER*)&oldParmInfo);
  499. //
  500. // if the call failed, or if the user's password is not null, bail out!
  501. //
  502. if ( !NT_SUCCESS(status) ||
  503. (oldParmInfo->Parameters.Length != NT_OWF_PASSWORD_LENGTH) ||
  504. (memcmp(oldParmInfo->Parameters.Buffer,
  505. &EMPTY_OWF_PASSWORD,
  506. NT_OWF_PASSWORD_LENGTH)) )
  507. {
  508. fPasswordAvailable = FALSE;
  509. }
  510. if (NT_SUCCESS(status))
  511. {
  512. SamIFree_SAMPR_USER_INFO_BUFFER( (PSAMPR_USER_INFO_BUFFER)oldParmInfo,
  513. UserParametersInformation);
  514. }
  515. if (!fPasswordAvailable)
  516. {
  517. DBGPRINT("ArapChangePassword: password not available\n");
  518. pArapResp->Result = ARAPERR_PASSWD_NOT_AVAILABLE;
  519. LocalFree( Credentials );
  520. SamrCloseHandle( &UserHandle );
  521. return(status);
  522. }
  523. }
  524. // convert to wide-char size
  525. CredentialSize = (CredentialSize/sizeof(WCHAR));
  526. if (CredentialSize > sizeof(OldPwd))
  527. {
  528. DBGPRINT("ArapChangePassword: pwd too long (%d bytes)\n",CredentialSize);
  529. pArapResp->Result = ARAPERR_PASSWORD_TOO_LONG;
  530. LocalFree( Credentials );
  531. SamrCloseHandle( &UserHandle );
  532. return(STATUS_WRONG_PASSWORD);
  533. }
  534. wcstombs(OldPwd, Credentials, CredentialSize);
  535. ZeroMemory( Credentials, CredentialSize );
  536. LocalFree( Credentials );
  537. }
  538. //
  539. // we are not running on the DS, but on a Standalone (workgroup) box. We need to
  540. // retrieve the cleartext pwd differently
  541. //
  542. else
  543. {
  544. // get the user parms
  545. status = SamrQueryInformationUser( UserHandle,
  546. UserAllInformation,
  547. (PSAMPR_USER_INFO_BUFFER *)&UserParmInfo);
  548. if (!NT_SUCCESS(status))
  549. {
  550. DBGPRINT("ARAPSubAuth: SamrQueryInformationUser failed %lx\n",status);
  551. pArapResp->Result = ARAPERR_PASSWD_NOT_AVAILABLE;
  552. SamrCloseHandle( &UserHandle );
  553. return(status);
  554. }
  555. status = IASParmsGetUserPassword(UserParmInfo->Parameters.Buffer,
  556. &UnicodePassword.Buffer);
  557. SamIFree_SAMPR_USER_INFO_BUFFER((PSAMPR_USER_INFO_BUFFER)UserParmInfo,
  558. UserAllInformation);
  559. if ((status != STATUS_SUCCESS) || (UnicodePassword.Buffer == NULL))
  560. {
  561. DBGPRINT("ARAPSubAuth: IASParmsGetUserPassword failed %lx\n",status);
  562. pArapResp->Result = ARAPERR_PASSWD_NOT_AVAILABLE;
  563. SamrCloseHandle( &UserHandle );
  564. return(STATUS_WRONG_PASSWORD);
  565. }
  566. UnicodePassword.MaximumLength =
  567. UnicodePassword.Length = (USHORT)(wcslen(UnicodePassword.Buffer) * sizeof( WCHAR ));
  568. AnsiPassword.Length = AnsiPassword.MaximumLength = sizeof(OldPwd);
  569. AnsiPassword.Buffer = OldPwd;
  570. status = RtlUnicodeStringToAnsiString( &AnsiPassword, &UnicodePassword, FALSE );
  571. ZeroMemory(UnicodePassword.Buffer, UnicodePassword.Length);
  572. // we don't need the unicode password anymore
  573. RtlFreeUnicodeString(&UnicodePassword);
  574. if (!NT_SUCCESS(status))
  575. {
  576. DBGPRINT("ARAPSubAuth: RtlUnicodeStringToAnsiString failed %lx\n",status);
  577. pArapResp->Result = ARAPERR_PASSWD_NOT_AVAILABLE;
  578. SamrCloseHandle( &UserHandle );
  579. return(STATUS_UNSUCCESSFUL);
  580. }
  581. }
  582. //
  583. // password change happens differently for ARAP and SFM: in ARAP, the old pwd
  584. // as well as the new pwd are encrypted using the old pwd. In SFM, old pwd is
  585. // encrypted with the new pwd (the first 8 bytes in after username), and the
  586. // new pwd is encrypted with the old pwd (the next 8 bytes)
  587. //
  588. if (fCallerIsArap)
  589. {
  590. //
  591. // first, get the get the old password out (the way Mac knows it)
  592. //
  593. pBufPtr = &pArapSubAuthInfo->ChgPwd.OldMunge[0];
  594. EnterCriticalSection( &ArapDesLock );
  595. DoDesInit(OldPwd, TRUE);
  596. // first 8 bytes of mangled old password
  597. pBufPtr = &pArapSubAuthInfo->ChgPwd.OldMunge[0];
  598. Challenge.high = (*((DWORD *)(pBufPtr)));
  599. pBufPtr += 4;
  600. Challenge.low = (*((DWORD *)(pBufPtr)));
  601. DoTheDESDecrypt((PBYTE)&Challenge);
  602. RtlCopyMemory(MacsOldPwd, (PBYTE)&Challenge, 8);
  603. // next 8 bytes of mangled old password
  604. pBufPtr += 4;
  605. Challenge.high = (*((DWORD *)(pBufPtr)));
  606. pBufPtr += 4;
  607. Challenge.low = (*((DWORD *)(pBufPtr)));
  608. DoTheDESDecrypt((PBYTE)&Challenge);
  609. RtlCopyMemory(MacsOldPwd+8, (PBYTE)&Challenge, 8);
  610. //
  611. // now, get the new password
  612. //
  613. // first 8 bytes of the mangled new password
  614. pBufPtr = &pArapSubAuthInfo->ChgPwd.NewMunge[0];
  615. Challenge.high = (*((DWORD *)(pBufPtr)));
  616. pBufPtr += 4;
  617. Challenge.low = (*((DWORD *)(pBufPtr)));
  618. DoTheDESDecrypt((PBYTE)&Challenge);
  619. RtlCopyMemory(NewPwd, (PBYTE)&Challenge, 8);
  620. // next 8 bytes of the mangled new password
  621. pBufPtr += 4;
  622. Challenge.high = (*((DWORD *)(pBufPtr)));
  623. pBufPtr += 4;
  624. Challenge.low = (*((DWORD *)(pBufPtr)));
  625. DoTheDESDecrypt((PBYTE)&Challenge);
  626. RtlCopyMemory(NewPwd+8, (PBYTE)&Challenge, 8);
  627. DoDesEnd();
  628. LeaveCriticalSection( &ArapDesLock );
  629. MacOldPwdLen = MacsOldPwd[0];
  630. NewPwdLen = NewPwd[0];
  631. FirstByte = 1;
  632. }
  633. else
  634. {
  635. // using old pwd as the key, get the new pwd out
  636. EnterCriticalSection( &ArapDesLock );
  637. DoDesInit(OldPwd, FALSE); // clear low-bit
  638. pBufPtr = &pArapSubAuthInfo->ChgPwd.NewMunge[0];
  639. Challenge.high = (*((DWORD *)(pBufPtr)));
  640. pBufPtr += 4;
  641. Challenge.low = (*((DWORD *)(pBufPtr)));
  642. DoTheDESDecrypt((PBYTE)&Challenge);
  643. RtlCopyMemory(NewPwd, (PBYTE)&Challenge, 8);
  644. DoDesEnd();
  645. //
  646. // now, we need to get the old pwd out so that we can make sure the
  647. // guy really had the pwd to begin with
  648. //
  649. DoDesInit(NewPwd, FALSE); // clear low-bit
  650. pBufPtr = &pArapSubAuthInfo->ChgPwd.OldMunge[0];
  651. Challenge.high = (*((DWORD *)(pBufPtr)));
  652. pBufPtr += 4;
  653. Challenge.low = (*((DWORD *)(pBufPtr)));
  654. DoTheDESDecrypt((PBYTE)&Challenge);
  655. RtlCopyMemory(MacsOldPwd, (PBYTE)&Challenge, 8);
  656. DoDesEnd();
  657. LeaveCriticalSection( &ArapDesLock );
  658. MacOldPwdLen = (UCHAR)strlen((PBYTE)MacsOldPwd);
  659. NewPwdLen = (UCHAR)strlen((PBYTE)NewPwd);
  660. FirstByte = 0;
  661. }
  662. OldPwdLen = (UCHAR)strlen((PBYTE)OldPwd);
  663. if ((MacOldPwdLen != OldPwdLen) || (MacOldPwdLen > MAX_MAC_PWD_LEN))
  664. {
  665. DBGPRINT("ArapChangePassword: Length mismatch! old len %d, oldMacLen %d\n",
  666. OldPwdLen,MacOldPwdLen);
  667. pArapResp->Result = ARAPERR_PASSWORD_TOO_LONG;
  668. SamrCloseHandle( &UserHandle );
  669. RtlZeroMemory(OldPwd, sizeof(OldPwd));
  670. RtlZeroMemory(MacsOldPwd, sizeof(MacsOldPwd));
  671. RtlZeroMemory(NewPwd, sizeof(NewPwd));
  672. return(STATUS_LOGON_FAILURE);
  673. }
  674. //
  675. // make sure the guy really knew the password to begin with
  676. //
  677. for (i=0; i<MacOldPwdLen ; i++)
  678. {
  679. if (MacsOldPwd[FirstByte+i] != OldPwd[i])
  680. {
  681. DBGPRINT("ArapChgPwd: bad pwd: oldpwd=%s Mac's pwd=%s newpwd=%s\n",
  682. OldPwd,&MacsOldPwd[1],&NewPwd[1]);
  683. pArapResp->Result = ARAPERR_BAD_PASSWORD;
  684. SamrCloseHandle( &UserHandle );
  685. RtlZeroMemory(OldPwd, sizeof(OldPwd));
  686. RtlZeroMemory(MacsOldPwd, sizeof(MacsOldPwd));
  687. RtlZeroMemory(NewPwd, sizeof(NewPwd));
  688. return(STATUS_LOGON_FAILURE);
  689. }
  690. }
  691. RtlZeroMemory(NtPassword, sizeof(NtPassword));
  692. //
  693. // convert the thing to unicode..
  694. // first byte in newpwd is length of the passwd
  695. //
  696. mbstowcs(NtPassword, &NewPwd[FirstByte], NewPwdLen);
  697. NtPassword[NewPwdLen] = 0;
  698. RtlZeroMemory( &UserAllInfo, sizeof(UserAllInfo) );
  699. UserAllInfo.UserName.Length = UserName.Length;
  700. UserAllInfo.UserName.MaximumLength = UserName.MaximumLength;
  701. UserAllInfo.UserName.Buffer = UserName.Buffer;
  702. UserAllInfo.WhichFields = USER_ALL_NTPASSWORDPRESENT;
  703. UserAllInfo.NtPassword.Length = wcslen(NtPassword) * sizeof(WCHAR);
  704. UserAllInfo.NtPassword.MaximumLength = wcslen(NtPassword) * sizeof(WCHAR);
  705. UserAllInfo.NtPassword.Buffer = NtPassword;
  706. status = SamrSetInformationUser( UserHandle,
  707. UserAllInformation,
  708. (PSAMPR_USER_INFO_BUFFER)&UserAllInfo);
  709. SamrCloseHandle( &UserHandle );
  710. //
  711. // wipe out all the clear-text passwords
  712. //
  713. RtlZeroMemory(OldPwd, sizeof(OldPwd));
  714. RtlZeroMemory(NewPwd, sizeof(NewPwd));
  715. RtlZeroMemory((PUCHAR)NtPassword, sizeof(NtPassword));
  716. if (status != STATUS_SUCCESS)
  717. {
  718. DBGPRINT("ARAPSubAuth: SamrSetInfo.. failed %lx\n",status);
  719. pArapResp->Result = ARAPERR_SET_PASSWD_FAILED;
  720. return(STATUS_UNSUCCESSFUL);
  721. }
  722. pArapResp->Result = ARAPERR_NO_ERROR;
  723. return(STATUS_SUCCESS);
  724. }
  725. NTSTATUS
  726. ArapGetSamHandle(
  727. IN PVOID *pUserHandle,
  728. IN PUNICODE_STRING pUserName
  729. )
  730. /*++
  731. Routine Description:
  732. This routine gets sam handle to the specified user (when we get into the
  733. subauth pkg for a password change, we don't have user's sam handle).
  734. Arguments:
  735. pUserHandle - sam handle, on return
  736. pUserName - name of the user in question
  737. Return Value:
  738. STATUS_SUCCESS: if handle retrieved successfully,
  739. appropriate error otherwise
  740. --*/
  741. {
  742. NTSTATUS status;
  743. PLSAPR_POLICY_INFORMATION PolicyInfo = NULL;
  744. SAMPR_HANDLE SamHandle = NULL;
  745. SAMPR_HANDLE DomainHandle = NULL;
  746. SAMPR_ULONG_ARRAY RidArray;
  747. SAMPR_ULONG_ARRAY UseArray;
  748. RidArray.Element = NULL;
  749. UseArray.Element = NULL;
  750. status = LsaIQueryInformationPolicyTrusted(
  751. PolicyAccountDomainInformation,
  752. &PolicyInfo);
  753. if (status != STATUS_SUCCESS)
  754. {
  755. DBGPRINT("ArapGetSamHandle: LsaI...Trusted failed with %lx\n", status);
  756. goto ArapGetSamHandle_Exit;
  757. }
  758. status = SamIConnect(
  759. NULL, // no server name
  760. &SamHandle,
  761. 0, // no desired access
  762. TRUE // trusted caller
  763. );
  764. if (status != STATUS_SUCCESS)
  765. {
  766. DBGPRINT("ArapGetSamHandle: SamIConnect failed with %lx\n", status);
  767. goto ArapGetSamHandle_Exit;
  768. }
  769. status = SamrOpenDomain(
  770. SamHandle,
  771. 0, // no desired access
  772. (PRPC_SID) PolicyInfo->PolicyAccountDomainInfo.DomainSid,
  773. &DomainHandle);
  774. if (status != STATUS_SUCCESS)
  775. {
  776. DBGPRINT("ArapGetSamHandle: SamrOpenDomain failed with %lx\n", status);
  777. goto ArapGetSamHandle_Exit;
  778. }
  779. status = SamrLookupNamesInDomain(
  780. DomainHandle,
  781. 1,
  782. (PRPC_UNICODE_STRING) pUserName,
  783. &RidArray,
  784. &UseArray);
  785. if (status != STATUS_SUCCESS)
  786. {
  787. DBGPRINT("ArapGetSamHandle: Samr..Domain failed with %lx\n", status);
  788. goto ArapGetSamHandle_Exit;
  789. }
  790. if (UseArray.Element[0] != SidTypeUser)
  791. {
  792. DBGPRINT("ArapGetSamHandle: didn't find our user\n");
  793. goto ArapGetSamHandle_Exit;
  794. }
  795. //
  796. // Finally open the user
  797. //
  798. status = SamrOpenUser(
  799. DomainHandle,
  800. 0, // no desired access,
  801. RidArray.Element[0],
  802. pUserHandle);
  803. if (status != STATUS_SUCCESS)
  804. {
  805. DBGPRINT("ArapGetSamHandle: SamrOpenUser failed with %lx\n", status);
  806. goto ArapGetSamHandle_Exit;
  807. }
  808. ArapGetSamHandle_Exit:
  809. if (DomainHandle != NULL)
  810. {
  811. SamrCloseHandle( &DomainHandle );
  812. }
  813. if (SamHandle != NULL)
  814. {
  815. SamrCloseHandle( &SamHandle );
  816. }
  817. if (PolicyInfo != NULL)
  818. {
  819. LsaIFree_LSAPR_POLICY_INFORMATION(
  820. PolicyAccountDomainInformation,
  821. PolicyInfo);
  822. }
  823. SamIFree_SAMPR_ULONG_ARRAY( &UseArray );
  824. SamIFree_SAMPR_ULONG_ARRAY( &RidArray );
  825. return(status);
  826. }
  827. NTSTATUS
  828. DeltaNotify(
  829. IN PSID DomainSid,
  830. IN SECURITY_DB_DELTA_TYPE DeltaType,
  831. IN SECURITY_DB_OBJECT_TYPE ObjectType,
  832. IN ULONG ObjectRid,
  833. IN OPTIONAL PUNICODE_STRING ObjectName,
  834. IN PLARGE_INTEGER ModifiedCount,
  835. IN PSAM_DELTA_DATA DeltaData
  836. )
  837. {
  838. DWORD dwRetCode;
  839. AFP_SERVER_HANDLE hAfpServer;
  840. AFP_SERVER_INFO afpInfo;
  841. // ignore any changes other than those to user
  842. if (ObjectType != SecurityDbObjectSamUser)
  843. {
  844. return(STATUS_SUCCESS);
  845. }
  846. // we only care about guest account: ignore the notification for other users
  847. if (ObjectRid != DOMAIN_USER_RID_GUEST)
  848. {
  849. return(STATUS_SUCCESS);
  850. }
  851. // enable/disable of guest account is all that's interesting to us
  852. if (DeltaType != SecurityDbChange)
  853. {
  854. return(STATUS_SUCCESS);
  855. }
  856. // if there is no DeltaData, account enable/disable hasn't been affected
  857. if (!DeltaData)
  858. {
  859. return(STATUS_SUCCESS);
  860. }
  861. //
  862. // ok, looks like Guest account was enabled (or disabled). Connect to the
  863. // SFM service on this machine. If we fail, that means SFM is not started
  864. // In that case, ignore this change
  865. //
  866. dwRetCode = AfpAdminConnect(NULL, &hAfpServer);
  867. // if we couldn't connect, don't bother: just ignore this notification
  868. if (dwRetCode != NO_ERROR)
  869. {
  870. DBGPRINT("DeltaNotify: AfpAdminConnect failed, dwRetCode = %ld\n",dwRetCode);
  871. return(STATUS_SUCCESS);
  872. }
  873. RtlZeroMemory(&afpInfo, sizeof(AFP_SERVER_INFO));
  874. //
  875. // find out if the guest account is enabled or disabled and set the flag
  876. // appropriately
  877. //
  878. if (!(DeltaData->AccountControl & USER_ACCOUNT_DISABLED))
  879. {
  880. afpInfo.afpsrv_options = AFP_SRVROPT_GUESTLOGONALLOWED;
  881. }
  882. dwRetCode = AfpAdminServerSetInfo(hAfpServer,
  883. (LPBYTE)&afpInfo,
  884. AFP_SERVER_GUEST_ACCT_NOTIFY);
  885. AfpAdminDisconnect(hAfpServer);
  886. return(STATUS_SUCCESS);
  887. }