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.

734 lines
19 KiB

  1. /*************************************************************************
  2. *
  3. * nw.c
  4. *
  5. * Netware security support
  6. *
  7. * Copyright Microsoft Corporation, 1998
  8. *
  9. *
  10. *************************************************************************/
  11. /*
  12. * Includes
  13. */
  14. #include "precomp.h"
  15. #pragma hdrstop
  16. #include <ntlsa.h>
  17. #include <rpc.h>
  18. #if DBG
  19. ULONG
  20. DbgPrint(
  21. PCH Format,
  22. ...
  23. );
  24. #define DBGPRINT(x) DbgPrint x
  25. #if DBGTRACE
  26. #define TRACE0(x) DbgPrint x
  27. #define TRACE1(x) DbgPrint x
  28. #else
  29. #define TRACE0(x)
  30. #define TRACE1(x)
  31. #endif
  32. #else
  33. #define DBGPRINT(x)
  34. #define TRACE0(x)
  35. #define TRACE1(x)
  36. #endif
  37. /*
  38. * This is the prefix for the secret object name.
  39. */
  40. #define CITRIX_NW_SECRET_NAME L"CTX_NW_INFO_"
  41. /*=============================================================================
  42. == Public functions
  43. =============================================================================*/
  44. /*=============================================================================
  45. == Functions Used
  46. =============================================================================*/
  47. NTSTATUS CreateSecretInLsa(
  48. PWCHAR pSecretName,
  49. PWCHAR pSecretData
  50. );
  51. NTSTATUS
  52. QuerySecretInLsa(
  53. PWCHAR pSecretName,
  54. PWCHAR pSecretData,
  55. DWORD ByteCount
  56. );
  57. BOOL
  58. IsCallerSystem( VOID );
  59. BOOL
  60. IsCallerAdmin( VOID );
  61. BOOL
  62. TestUserForAdmin( VOID );
  63. NTSTATUS
  64. IsZeroterminateStringA(
  65. PBYTE pString,
  66. DWORD dwLength
  67. );
  68. NTSTATUS
  69. IsZeroterminateStringW(
  70. PWCHAR pwString,
  71. DWORD dwLength
  72. ) ;
  73. /*=============================================================================
  74. == Global data
  75. =============================================================================*/
  76. /*******************************************************************************
  77. *
  78. * RpcServerNWLogonSetAdmin (UNICODE)
  79. *
  80. * Creates or updates the specified server's NWLogon Domain Administrator
  81. * UserID and Password in the SAM secret objects of the specified server.
  82. *
  83. * The caller must be ADMIN.
  84. *
  85. * ENTRY:
  86. * pServerName (input)
  87. * Server to store info for. This server is typically a domain controller.
  88. *
  89. * pNWLogon (input)
  90. * Pointer to a NWLOGONADMIN structure containing specified server's
  91. * domain admin and password.
  92. *
  93. * EXIT:
  94. * ERROR_SUCCESS - no error
  95. * ERROR_INSUFFICIENT_BUFFER - pUserConfig buffer too small
  96. * otherwise: the error code
  97. *
  98. ******************************************************************************/
  99. BOOLEAN
  100. RpcServerNWLogonSetAdmin(
  101. HANDLE hServer,
  102. DWORD *pResult,
  103. PWCHAR pServerName,
  104. DWORD ServerNameSize,
  105. PNWLOGONADMIN pNWLogon,
  106. DWORD ByteCount
  107. )
  108. {
  109. DWORD Size;
  110. DWORD Result;
  111. PWCHAR pDomain;
  112. UINT LocalFlag;
  113. PWCHAR pSecretName;
  114. RPC_STATUS RpcStatus;
  115. WCHAR UserPass[ USERNAME_LENGTH + PASSWORD_LENGTH + DOMAIN_LENGTH + 3 ];
  116. // Do minimal buffer validation
  117. if (pNWLogon == NULL ) {
  118. *pResult = STATUS_INVALID_USER_BUFFER;
  119. return FALSE;
  120. }
  121. if( pServerName == NULL ) {
  122. DBGPRINT(("NWLogonSetAdmin: No ServerName\n"));
  123. *pResult = (ULONG)STATUS_INVALID_PARAMETER;
  124. return( FALSE );
  125. }
  126. *pResult = IsZeroterminateStringW(pServerName, ServerNameSize );
  127. if (*pResult != STATUS_SUCCESS) {
  128. return FALSE;
  129. }
  130. pNWLogon->Username[USERNAME_LENGTH] = (WCHAR) 0;
  131. pNWLogon->Password[PASSWORD_LENGTH] = (WCHAR) 0;
  132. pNWLogon->Domain[DOMAIN_LENGTH] = (WCHAR) 0;
  133. //
  134. // Only a SYSTEM mode caller (IE: Winlogon) is allowed
  135. // to query this value.
  136. //
  137. RpcStatus = RpcImpersonateClient( NULL );
  138. if( RpcStatus != RPC_S_OK ) {
  139. DBGPRINT(("RpcServerNWLogonSetAdmin: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
  140. *pResult = (ULONG)STATUS_CANNOT_IMPERSONATE;
  141. return( FALSE );
  142. }
  143. //
  144. // Inquire if local RPC call
  145. //
  146. RpcStatus = I_RpcBindingIsClientLocal(
  147. 0, // Active RPC call we are servicing
  148. &LocalFlag
  149. );
  150. if( RpcStatus != RPC_S_OK ) {
  151. DBGPRINT(("NWLogonSetAdmin Could not query local client RpcStatus 0x%x\n",RpcStatus));
  152. RpcRevertToSelf();
  153. *pResult = (ULONG)STATUS_ACCESS_DENIED;
  154. return( FALSE );
  155. }
  156. if( !LocalFlag ) {
  157. DBGPRINT(("NWLogonSetAdmin Not a local client call\n"));
  158. RpcRevertToSelf();
  159. *pResult = (ULONG)STATUS_ACCESS_DENIED;
  160. return( FALSE );
  161. }
  162. if( !IsCallerAdmin() ) {
  163. RpcRevertToSelf();
  164. DBGPRINT(("RpcServerNWLogonSetAdmin: Caller Not SYSTEM\n"));
  165. *pResult = (ULONG)STATUS_ACCESS_DENIED;
  166. return( FALSE );
  167. }
  168. RpcRevertToSelf();
  169. if( ByteCount < sizeof(NWLOGONADMIN) ) {
  170. DBGPRINT(("NWLogonSetAdmin: Bad size %d\n",ByteCount));
  171. *pResult = (ULONG)STATUS_INFO_LENGTH_MISMATCH;
  172. return( FALSE );
  173. }
  174. // check for username, and if there is one then encrypt username and pw
  175. TRACE0(("NWLogonSetAdmin: UserName %ws\n",pNWLogon->Username));
  176. // concatenate the username, password, and domain together
  177. wcscpy(UserPass, pNWLogon->Username);
  178. wcscat(UserPass, L"/");
  179. wcscat(UserPass, pNWLogon->Password);
  180. wcscat(UserPass, L"/");
  181. // Skip over any \\ backslashes (if a machine name was passed in)
  182. pDomain = pNWLogon->Domain;
  183. while (*pDomain == L'\\') {
  184. pDomain++;
  185. }
  186. wcscat(UserPass, pDomain);
  187. //
  188. // Build the secret name from the server name.
  189. //
  190. // This is because each domain will have a different entry.
  191. //
  192. // Skip over any \\ backslashes (if a machine name was passed in)
  193. while (*pServerName == L'\\') {
  194. pServerName++;
  195. }
  196. Size = wcslen(pServerName) + 1;
  197. Size *= sizeof(WCHAR);
  198. Size += sizeof(CITRIX_NW_SECRET_NAME);
  199. pSecretName = MemAlloc( Size );
  200. if( pSecretName == NULL ) {
  201. DBGPRINT(("NWLogonSetAdmin: No memory\n"));
  202. *pResult = (ULONG)STATUS_NO_MEMORY;
  203. return( FALSE );
  204. }
  205. wcscpy(pSecretName, CITRIX_NW_SECRET_NAME );
  206. wcscat(pSecretName, pServerName );
  207. // check for username, and if there is one then encrypt username and pw
  208. if ( wcslen( pNWLogon->Username ) ) {
  209. // store encrypted username
  210. Result = CreateSecretInLsa( pSecretName, UserPass );
  211. } else {
  212. // If there wasn't a username, clear this secret object.
  213. Result = CreateSecretInLsa( pSecretName, L"");
  214. DBGPRINT(("TERMSRV: RpcServerNWLogonSetAdmin: UserName not supplied\n"));
  215. }
  216. MemFree( pSecretName );
  217. *pResult = Result;
  218. return( Result == STATUS_SUCCESS );
  219. }
  220. /*******************************************************************************
  221. *
  222. * RpcServerQueryNWLogonAdmin
  223. *
  224. * Query NWLOGONADMIN structure from the SAM Secret object on the given
  225. * WinFrame server.
  226. *
  227. * The caller must be SYSTEM context, IE: WinLogon.
  228. *
  229. * ENTRY:
  230. * hServer (input)
  231. * Rpc handle
  232. *
  233. * pServerName (input)
  234. * Server to store info for. This server is typically a domain controller.
  235. *
  236. * pNWLogon (output)
  237. * pointer to NWLOGONADMIN structure
  238. *
  239. * EXIT:
  240. * nothing
  241. *
  242. ******************************************************************************/
  243. BOOLEAN
  244. RpcServerNWLogonQueryAdmin(
  245. HANDLE hServer,
  246. DWORD *pResult,
  247. PWCHAR pServerName,
  248. DWORD ServerNameSize,
  249. PNWLOGONADMIN pNWLogon,
  250. DWORD ByteCount
  251. )
  252. {
  253. PWCHAR pwch;
  254. DWORD Size;
  255. ULONG ulcsep;
  256. UINT LocalFlag;
  257. NTSTATUS Status;
  258. PWCHAR pSecretName;
  259. RPC_STATUS RpcStatus;
  260. WCHAR encString[ USERNAME_LENGTH + PASSWORD_LENGTH + DOMAIN_LENGTH + 3 ];
  261. BOOLEAN SystemCaller = FALSE;
  262. // Do minimal buffer validation
  263. if (pNWLogon == NULL) {
  264. *pResult = STATUS_INVALID_USER_BUFFER;
  265. return FALSE;
  266. }
  267. if( pServerName == NULL ) {
  268. DBGPRINT(("NWLogonQueryAdmin: No ServerName\n"));
  269. *pResult = (ULONG)STATUS_INVALID_PARAMETER;
  270. return( FALSE );
  271. }
  272. *pResult = IsZeroterminateStringW(pServerName, ServerNameSize );
  273. if (*pResult != STATUS_SUCCESS) {
  274. return FALSE;
  275. }
  276. pNWLogon->Username[USERNAME_LENGTH] = (WCHAR) 0;
  277. pNWLogon->Password[PASSWORD_LENGTH] = (WCHAR) 0;
  278. pNWLogon->Domain[DOMAIN_LENGTH] = (WCHAR) 0;
  279. //
  280. //
  281. // Only a SYSTEM mode caller (IE: Winlogon) is allowed
  282. // to query this value.
  283. //
  284. RpcStatus = RpcImpersonateClient( NULL );
  285. if( RpcStatus != RPC_S_OK ) {
  286. DBGPRINT(("RpcServerNWLogonQueryAdmin: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
  287. *pResult = (ULONG)STATUS_CANNOT_IMPERSONATE;
  288. return( FALSE );
  289. }
  290. //
  291. // Inquire if local RPC call
  292. //
  293. RpcStatus = I_RpcBindingIsClientLocal(
  294. 0, // Active RPC call we are servicing
  295. &LocalFlag
  296. );
  297. if( RpcStatus != RPC_S_OK ) {
  298. DBGPRINT(("NWLogonQueryAdmin Could not query local client RpcStatus 0x%x\n",RpcStatus));
  299. RpcRevertToSelf();
  300. *pResult = (ULONG)STATUS_ACCESS_DENIED;
  301. return( FALSE );
  302. }
  303. if( !LocalFlag ) {
  304. DBGPRINT(("NWLogonQueryAdmin Not a local client call\n"));
  305. RpcRevertToSelf();
  306. *pResult = (ULONG)STATUS_ACCESS_DENIED;
  307. return( FALSE );
  308. }
  309. /* find out who is calling us system has complete access, admin can't get password, user is kicked out */
  310. if( IsCallerSystem() ) {
  311. SystemCaller = TRUE;
  312. }
  313. if( !TestUserForAdmin() && (SystemCaller != TRUE) ) {
  314. RpcRevertToSelf();
  315. DBGPRINT(("RpcServerNWLogonQueryAdmin: Caller Not SYSTEM or Admin\n"));
  316. *pResult = (ULONG)STATUS_ACCESS_DENIED;
  317. return( FALSE );
  318. }
  319. RpcRevertToSelf();
  320. if( ByteCount < sizeof(NWLOGONADMIN) ) {
  321. DBGPRINT(("NWLogonQueryAdmin: Bad size %d\n",ByteCount));
  322. *pResult = (ULONG)STATUS_INFO_LENGTH_MISMATCH;
  323. return( FALSE );
  324. }
  325. //
  326. // Build the secret name from the server name.
  327. //
  328. // This is because each domain will have a different entry.
  329. //
  330. // Skip over any \\ backslashes (if a machine name was passed in)
  331. while (*pServerName == L'\\') {
  332. pServerName++;
  333. }
  334. Size = wcslen(pServerName) + 1;
  335. Size *= sizeof(WCHAR);
  336. Size += sizeof(CITRIX_NW_SECRET_NAME);
  337. pSecretName = MemAlloc( Size );
  338. if( pSecretName == NULL ) {
  339. DBGPRINT(("NWLogonSetAdmin: No memory\n"));
  340. *pResult = (ULONG)STATUS_NO_MEMORY;
  341. return( FALSE );
  342. }
  343. wcscpy(pSecretName, CITRIX_NW_SECRET_NAME );
  344. wcscat(pSecretName, pServerName );
  345. Status = QuerySecretInLsa(
  346. pSecretName,
  347. encString,
  348. sizeof(encString)
  349. );
  350. MemFree( pSecretName );
  351. if( !NT_SUCCESS(Status) ) {
  352. *pResult = Status;
  353. DBGPRINT(("NWLogonQueryAdmin: Error 0x%x querying secret object\n",Status));
  354. return( FALSE );
  355. }
  356. // check for username/password if there is one then decrypt it
  357. if ( wcslen( encString ) ) {
  358. // Change the '/' seperator to null
  359. pwch = &encString[0];
  360. ulcsep = 0;
  361. while (pwch && *pwch) {
  362. pwch = wcschr(pwch, L'/');
  363. if (pwch) {
  364. *pwch = L'\0';
  365. pwch++;
  366. ulcsep++;
  367. }
  368. }
  369. // get clear text username
  370. wcscpy( pNWLogon->Username, &encString[0] );
  371. if (ulcsep >= 1) {
  372. // Skip to the password
  373. pwch = &encString[0] + wcslen(&encString[0]) + 1;
  374. if( SystemCaller == TRUE ){
  375. // get clear text password
  376. wcscpy( pNWLogon->Password, pwch);
  377. } else {
  378. *pNWLogon->Password = L'\0';
  379. }
  380. } else {
  381. *pNWLogon->Password = L'\0';
  382. }
  383. if (ulcsep >= 2) {
  384. // Skip to the domain string
  385. pwch = pwch + wcslen(pwch) + 1;
  386. // get clear text domain
  387. wcscpy( pNWLogon->Domain, pwch);
  388. } else {
  389. *pNWLogon->Domain = L'\0';
  390. }
  391. TRACE0(("NwLogonQueryAdmin :%ws:%ws:%ws:\n",pNWLogon->Username,pNWLogon->Domain,pNWLogon->Password));
  392. *pResult = STATUS_SUCCESS;
  393. return( TRUE );
  394. }
  395. else {
  396. DBGPRINT(("RpcServerNWLogonQueryAdmin: zero length data\n"));
  397. // set to username and password to NULL strings
  398. pNWLogon->Password[0] = L'\0';
  399. pNWLogon->Username[0] = L'\0';
  400. pNWLogon->Domain[0] = L'\0';
  401. *pResult = STATUS_SUCCESS;
  402. return( TRUE );
  403. }
  404. }
  405. /*******************************************************************************
  406. *
  407. * CreateSecretInLsa
  408. *
  409. * Create the secret object in the LSA to keep it from prying eyes.
  410. *
  411. * NOTE: There is no need to encode the data since it is RSA encrypted
  412. * by the LSA secret routines.
  413. *
  414. * ENTRY:
  415. * pSecretName (input)
  416. * Secret name to create.
  417. *
  418. * pSecretData (input)
  419. * Data to store in secret
  420. *
  421. * EXIT:
  422. * NTSTATUS
  423. *
  424. ******************************************************************************/
  425. NTSTATUS
  426. CreateSecretInLsa(
  427. PWCHAR pSecretName,
  428. PWCHAR pSecretData
  429. )
  430. {
  431. NTSTATUS Status;
  432. OBJECT_ATTRIBUTES ObjectAttributes;
  433. SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
  434. LSA_HANDLE PolicyHandle;
  435. UNICODE_STRING SecretName;
  436. UNICODE_STRING SecretValue;
  437. LSA_HANDLE SecretHandle;
  438. ACCESS_MASK DesiredAccess;
  439. if( pSecretName == NULL ) {
  440. DBGPRINT(("CreateSecretInLsa: NULL SecretName\n"));
  441. return( STATUS_INVALID_PARAMETER );
  442. }
  443. SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
  444. SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
  445. SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  446. SecurityQualityOfService.EffectiveOnly = FALSE;
  447. InitializeObjectAttributes(
  448. &ObjectAttributes,
  449. NULL,
  450. 0L,
  451. NULL,
  452. NULL
  453. );
  454. ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
  455. Status = LsaOpenPolicy(
  456. NULL, // SystemName (Local)
  457. &ObjectAttributes,
  458. GENERIC_ALL,
  459. &PolicyHandle
  460. );
  461. if( !NT_SUCCESS(Status) ) {
  462. DBGPRINT(("Error 0x%x Opening Policy\n",Status));
  463. return( Status );
  464. }
  465. RtlInitUnicodeString( &SecretName, pSecretName );
  466. DesiredAccess = GENERIC_ALL;
  467. TRACE0(("Creating Secret name :%ws:\n",pSecretName));
  468. Status = LsaCreateSecret(
  469. PolicyHandle,
  470. &SecretName,
  471. DesiredAccess,
  472. &SecretHandle
  473. );
  474. // Its OK if the name already exits, we will set a new value or delete
  475. if( Status == STATUS_OBJECT_NAME_COLLISION ) {
  476. TRACE0(("CreateSecretInLsa: Existing Entry, Opening\n"));
  477. Status = LsaOpenSecret(
  478. PolicyHandle,
  479. &SecretName,
  480. DesiredAccess,
  481. &SecretHandle
  482. );
  483. }
  484. if( !NT_SUCCESS(Status) ) {
  485. DBGPRINT(("Error 0x%x Creating Secret\n",Status));
  486. /* makarp; Close Policy Handle in case of LsaCreateSecrete, LsaopenSecret failures. #182787 */
  487. LsaClose( PolicyHandle );
  488. return( Status );
  489. }
  490. TRACE0(("CreateSecretInLsa: Status 0x%x\n",Status));
  491. if ( wcslen(pSecretData) != 0 ){
  492. RtlInitUnicodeString( &SecretValue, pSecretData );
  493. Status = LsaSetSecret( SecretHandle, &SecretValue, NULL );
  494. TRACE0(("CreateSecretInLsa: LsaSetSecret Status 0x%x\n",Status));
  495. LsaClose(SecretHandle);
  496. }
  497. else{
  498. Status = LsaDelete(SecretHandle);
  499. }
  500. LsaClose( PolicyHandle );
  501. return( Status );
  502. }
  503. /*******************************************************************************
  504. *
  505. * QuerySecretInLsa
  506. *
  507. * Query the secret object in the LSA.
  508. *
  509. * ENTRY:
  510. * pSecretName (input)
  511. * Secret name to create.
  512. *
  513. * pSecretData (output)
  514. * Buffer to store secret data.
  515. *
  516. * ByteCount (input)
  517. * Maximum size of buffer to store result.
  518. *
  519. * EXIT:
  520. * NTSTATUS
  521. *
  522. ******************************************************************************/
  523. NTSTATUS
  524. QuerySecretInLsa(
  525. PWCHAR pSecretName,
  526. PWCHAR pSecretData,
  527. DWORD ByteCount
  528. )
  529. {
  530. NTSTATUS Status;
  531. OBJECT_ATTRIBUTES ObjectAttributes;
  532. SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
  533. LSA_HANDLE PolicyHandle;
  534. UNICODE_STRING SecretName;
  535. LSA_HANDLE SecretHandle;
  536. ACCESS_MASK DesiredAccess;
  537. LARGE_INTEGER CurrentTime;
  538. PUNICODE_STRING pCurrentValue = NULL;
  539. SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
  540. SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
  541. SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  542. SecurityQualityOfService.EffectiveOnly = FALSE;
  543. InitializeObjectAttributes(
  544. &ObjectAttributes,
  545. NULL,
  546. 0L,
  547. NULL,
  548. NULL
  549. );
  550. ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
  551. Status = LsaOpenPolicy(
  552. NULL, // SystemName (Local)
  553. &ObjectAttributes,
  554. GENERIC_ALL,
  555. &PolicyHandle
  556. );
  557. if( !NT_SUCCESS(Status) ) {
  558. DBGPRINT(("Error 0x%x Opening Policy\n",Status));
  559. return( Status );
  560. }
  561. RtlInitUnicodeString( &SecretName, pSecretName );
  562. DesiredAccess = GENERIC_ALL;
  563. Status = LsaOpenSecret(
  564. PolicyHandle,
  565. &SecretName,
  566. DesiredAccess,
  567. &SecretHandle
  568. );
  569. if( !NT_SUCCESS(Status) ) {
  570. DBGPRINT(("Error 0x%x Opening Secret :%ws:\n",Status,pSecretName));
  571. /* makarp; Close Policy Handle in case of LsaopenSecret failures. #182787 */
  572. LsaClose( PolicyHandle );
  573. return( Status );
  574. }
  575. Status = LsaQuerySecret(
  576. SecretHandle,
  577. &pCurrentValue,
  578. &CurrentTime,
  579. NULL,
  580. NULL
  581. );
  582. TRACE0(("QuerySecretInLsa: Status 0x%x\n",Status));
  583. if( NT_SUCCESS(Status) ) {
  584. if (pCurrentValue != NULL) {
  585. if( (pCurrentValue->Length+sizeof(WCHAR)) > ByteCount ) {
  586. Status = STATUS_INFO_LENGTH_MISMATCH;
  587. }
  588. else {
  589. RtlMoveMemory( pSecretData, pCurrentValue->Buffer, pCurrentValue->Length );
  590. pSecretData[pCurrentValue->Length/sizeof(WCHAR)] = 0;
  591. }
  592. LsaFreeMemory( pCurrentValue );
  593. } else {
  594. pSecretData[0] = (WCHAR) 0;
  595. }
  596. }
  597. LsaClose(SecretHandle);
  598. LsaClose( PolicyHandle );
  599. TRACE0(("QuerySecretInLsa: Final Status 0x%x\n",Status));
  600. return( Status );
  601. }