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.

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