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.

882 lines
24 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. exchcli2.cxx
  5. Abstract:
  6. IIS Crypto client-side key exchange test with security impersonation.
  7. Author:
  8. Keith Moore (keithmo) 14-Oct-1997
  9. Revision History:
  10. --*/
  11. #include "precomp.hxx"
  12. #pragma hdrstop
  13. //
  14. // Private constants.
  15. //
  16. #define TEST_HRESULT(api) \
  17. if( FAILED(result) ) { \
  18. \
  19. printf( \
  20. "%s:%lu failed, error %08lx\n", \
  21. api, \
  22. __LINE__, \
  23. result \
  24. ); \
  25. \
  26. goto cleanup; \
  27. \
  28. } else
  29. #define TEST_SOCKERR(api) \
  30. if( sockerr != NO_ERROR ) { \
  31. \
  32. printf( \
  33. "%s:%lu failed, error %d\n", \
  34. api, \
  35. __LINE__, \
  36. sockerr \
  37. ); \
  38. \
  39. goto cleanup; \
  40. \
  41. } else
  42. #define FREE_BLOB(b) \
  43. if( b != NULL ) { \
  44. \
  45. HRESULT _result; \
  46. \
  47. _result = IISCryptoFreeBlob( b ); \
  48. \
  49. if( FAILED(_result) ) { \
  50. \
  51. printf( \
  52. "IISCryptoFreeBlob( %08lx ):%lu failed, error %08lx\n", \
  53. b, \
  54. __LINE__, \
  55. _result \
  56. ); \
  57. \
  58. } \
  59. \
  60. (b) = NULL; \
  61. \
  62. }
  63. #define PACKAGE_NAME L"NTLM"
  64. #define KEY_CTRL_C '\x03'
  65. #define KEY_BACKSPACE '\x08'
  66. #define KEY_ENTER '\x0d'
  67. #define KEY_EOF '\x1a'
  68. #define KEY_ESCAPE '\x1b'
  69. #define STR_BEEP "\x07"
  70. #define STR_HIDDEN "*"
  71. #define STR_BACKSPACE "\x08 \x08"
  72. #define ALLOC_MEM(cb) (PVOID)LocalAlloc( LPTR, (cb) )
  73. #define FREE_MEM(p) (VOID)LocalFree( (HLOCAL)(p) )
  74. #define DIM(x) (sizeof(x) / sizeof(x[0]))
  75. //
  76. // Private types.
  77. //
  78. //
  79. // Private globals.
  80. //
  81. DECLARE_DEBUG_PRINTS_OBJECT()
  82. #include <initguid.h>
  83. DEFINE_GUID(IisCrypt2Guid,
  84. 0x784d8928, 0xaa8c, 0x11d2, 0x92, 0x5e, 0x00, 0xc0, 0x4f, 0x72, 0xd9, 0x0e);
  85. CHAR ClientPlainText[] = "Client Client Client Client Client Client";
  86. //
  87. // Private prototypes.
  88. //
  89. SECURITY_STATUS
  90. MyLogonUser(
  91. IN LPWSTR UserName,
  92. IN LPWSTR UserDomain,
  93. IN LPWSTR UserPassword,
  94. OUT PCtxtHandle ServerContext,
  95. OUT PCredHandle ServerCredential
  96. );
  97. BOOL
  98. GetStringFromUser(
  99. LPSTR Prompt,
  100. LPWSTR String,
  101. ULONG MaxLength,
  102. BOOL Echo
  103. );
  104. //
  105. // Public functions.
  106. //
  107. INT
  108. __cdecl
  109. main(
  110. INT argc,
  111. CHAR * argv[]
  112. )
  113. {
  114. INT sockerr;
  115. HRESULT result;
  116. IIS_CRYPTO_EXCHANGE_CLIENT * pclient;
  117. BUFFERED_SOCKET * psocket;
  118. PIIS_CRYPTO_BLOB clientKeyExchangeKeyBlob;
  119. PIIS_CRYPTO_BLOB clientSignatureKeyBlob;
  120. PIIS_CRYPTO_BLOB serverKeyExchangeKeyBlob;
  121. PIIS_CRYPTO_BLOB serverSignatureKeyBlob;
  122. PIIS_CRYPTO_BLOB serverSessionKeyBlob;
  123. PIIS_CRYPTO_BLOB clientSessionKeyBlob;
  124. PIIS_CRYPTO_BLOB clientHashBlob;
  125. PIIS_CRYPTO_BLOB serverHashBlob;
  126. PIIS_CRYPTO_BLOB dataBlob;
  127. IIS_CRYPTO_STORAGE * clientStorage;
  128. IIS_CRYPTO_STORAGE * serverStorage;
  129. PVOID buffer;
  130. DWORD bufferLength;
  131. DWORD bufferType;
  132. PSecurityFunctionTable ftab;
  133. CtxtHandle serverContext;
  134. CredHandle serverCredential;
  135. WCHAR name[128];
  136. WCHAR domain[128];
  137. WCHAR password[128];
  138. if( argc != 2 ) {
  139. printf(
  140. "use: exchcli2 target_server\n"
  141. );
  142. return 1;
  143. }
  144. //
  145. // Initialize debug stuff.
  146. //
  147. #ifndef _NO_TRACING_
  148. CREATE_DEBUG_PRINT_OBJECT( "iiscrypt", IisCrypt2Guid );
  149. CREATE_INITIALIZE_DEBUG();
  150. #else
  151. CREATE_DEBUG_PRINT_OBJECT( "iiscrypt" );
  152. #endif
  153. //
  154. // Setup our locals so we know how to cleanup on exit.
  155. //
  156. pclient = NULL;
  157. psocket = NULL;
  158. clientKeyExchangeKeyBlob = NULL;
  159. clientSignatureKeyBlob = NULL;
  160. serverKeyExchangeKeyBlob = NULL;
  161. serverSignatureKeyBlob = NULL;
  162. serverSessionKeyBlob = NULL;
  163. clientSessionKeyBlob = NULL;
  164. clientHashBlob = NULL;
  165. serverHashBlob = NULL;
  166. dataBlob = NULL;
  167. clientStorage = NULL;
  168. serverStorage = NULL;
  169. //
  170. // Initialize SSPI.
  171. //
  172. ftab = InitSecurityInterface();
  173. if( ftab == NULL ) {
  174. sockerr = GetLastError();
  175. TEST_SOCKERR( "InitSecurityInterface()" );
  176. }
  177. //
  178. // Prompt for the user name, domain, and password.
  179. //
  180. if( !GetStringFromUser( "name: ", name, DIM(name), TRUE ) ||
  181. !GetStringFromUser( "domain: ", domain, DIM(domain), TRUE ) ||
  182. !GetStringFromUser( "password: ", password, DIM(password), FALSE ) ) {
  183. goto cleanup;
  184. }
  185. //
  186. // Logon.
  187. //
  188. result = MyLogonUser(
  189. name,
  190. domain,
  191. password,
  192. &serverContext,
  193. &serverCredential
  194. );
  195. RtlZeroMemory(
  196. password,
  197. sizeof(password)
  198. );
  199. TEST_HRESULT( "MyLogonUser" );
  200. result = ImpersonateSecurityContext( &serverContext );
  201. TEST_HRESULT( "ImpersonateSecurityContext" );
  202. //
  203. // Initialize the crypto package.
  204. //
  205. printf( "exchcli2: Initializing...\n" );
  206. result = IISCryptoInitialize();
  207. TEST_HRESULT( "IISCryptoInitialize()" );
  208. //
  209. // Create & initialize the client-side key exchange object.
  210. //
  211. pclient = new IIS_CRYPTO_EXCHANGE_CLIENT;
  212. if( pclient == NULL ) {
  213. printf( "out of memory\n" );
  214. goto cleanup;
  215. }
  216. result = pclient->Initialize(
  217. CRYPT_NULL,
  218. CRYPT_NULL,
  219. CRYPT_NULL,
  220. TRUE
  221. );
  222. TEST_HRESULT( "pclient->Initialize()" );
  223. //
  224. // Create & initialize the buffered socket object.
  225. //
  226. psocket = new BUFFERED_SOCKET;
  227. if( psocket == NULL ) {
  228. printf( "out of memory\n" );
  229. goto cleanup;
  230. }
  231. result = psocket->InitializeClient( argv[1], SERVER_PORT );
  232. TEST_HRESULT( "psocket->Initialize()" );
  233. //
  234. // 1. CLIENT(1)
  235. //
  236. printf( "exchcli2: Phase 1...\n" );
  237. result = pclient->ClientPhase1(
  238. &clientKeyExchangeKeyBlob,
  239. &clientSignatureKeyBlob
  240. );
  241. TEST_HRESULT( "pclient->ClientPhase1()" );
  242. sockerr = psocket->SendBlob( clientKeyExchangeKeyBlob );
  243. TEST_SOCKERR( "psocket->SendBlob()" );
  244. sockerr = psocket->SendBlob( clientSignatureKeyBlob );
  245. TEST_SOCKERR( "psocket->SendBlob()" );
  246. //
  247. // 3. CLIENT(2)
  248. //
  249. printf( "exchcli2: Phase 2...\n" );
  250. sockerr = psocket->RecvBlob( &serverKeyExchangeKeyBlob );
  251. TEST_SOCKERR( "psocket->RecvBlob()" );
  252. sockerr = psocket->RecvBlob( &serverSignatureKeyBlob );
  253. TEST_SOCKERR( "psocket->RecvBlob()" );
  254. sockerr = psocket->RecvBlob( &serverSessionKeyBlob );
  255. TEST_SOCKERR( "psocket->RecvBlob()" );
  256. result = pclient->ClientPhase2(
  257. serverKeyExchangeKeyBlob,
  258. serverSignatureKeyBlob,
  259. serverSessionKeyBlob,
  260. &clientSessionKeyBlob,
  261. &clientHashBlob
  262. );
  263. TEST_HRESULT( "pclient->ClientPhase2()" );
  264. sockerr = psocket->SendBlob( clientSessionKeyBlob );
  265. TEST_SOCKERR( "psocket->SendBlob()" );
  266. sockerr = psocket->SendBlob( clientHashBlob );
  267. TEST_SOCKERR( "psocket->SendBlob()" );
  268. //
  269. // 5. CLIENT(3)
  270. //
  271. printf( "exchcli2: Phase 3...\n" );
  272. sockerr = psocket->RecvBlob( &serverHashBlob );
  273. TEST_SOCKERR( "psocket->RecvBlob()" );
  274. result = pclient->ClientPhase3(
  275. serverHashBlob
  276. );
  277. TEST_HRESULT( "pclient->ClientPhase3()" );
  278. //
  279. // Create the storage objects.
  280. //
  281. printf( "exchcli2: Creating storage objects...\n" );
  282. clientStorage = new IIS_CRYPTO_STORAGE;
  283. if( clientStorage == NULL ) {
  284. printf( "out of memory\n" );
  285. goto cleanup;
  286. }
  287. result = clientStorage->Initialize(
  288. pclient->QueryProviderHandle(),
  289. pclient->AssumeClientSessionKey(),
  290. CRYPT_NULL,
  291. CRYPT_NULL,
  292. TRUE
  293. );
  294. TEST_HRESULT( "clientStorage->Initialize()" );
  295. serverStorage = new IIS_CRYPTO_STORAGE;
  296. if( serverStorage == NULL ) {
  297. printf( "out of memory\n" );
  298. goto cleanup;
  299. }
  300. result = serverStorage->Initialize(
  301. pclient->QueryProviderHandle(),
  302. pclient->AssumeServerSessionKey(),
  303. CRYPT_NULL,
  304. pclient->AssumeServerSignatureKey(),
  305. TRUE
  306. );
  307. TEST_HRESULT( "serverStorage->Initialize()" );
  308. //
  309. // Send some encrypted data.
  310. //
  311. printf(
  312. "exchcli2: Encrypting '%s'...\n",
  313. ClientPlainText
  314. );
  315. result = clientStorage->EncryptData(
  316. &dataBlob,
  317. ClientPlainText,
  318. sizeof(ClientPlainText),
  319. REG_SZ
  320. );
  321. TEST_HRESULT( "clientStorage->EncryptData()" );
  322. printf( "exchcli2: Sending encrypted data...\n" );
  323. sockerr = psocket->SendBlob( dataBlob );
  324. TEST_SOCKERR( "psocket->SendBlob()" );
  325. FREE_BLOB( dataBlob );
  326. //
  327. // Receive some encrypted data.
  328. //
  329. printf( "exchcli2: Receiving encrypted data...\n" );
  330. sockerr = psocket->RecvBlob( &dataBlob );
  331. TEST_SOCKERR( "psocket->RecvBlob()" );
  332. result = serverStorage->DecryptData(
  333. &buffer,
  334. &bufferLength,
  335. &bufferType,
  336. dataBlob
  337. );
  338. TEST_HRESULT( "serverStorage->DecryptData()" );
  339. printf(
  340. "exchcli2: Received data[%lu] = '%s'\n",
  341. bufferLength,
  342. buffer
  343. );
  344. //
  345. // Tests complete.
  346. //
  347. printf( "exchcli2: Done!\n" );
  348. cleanup:
  349. FREE_BLOB( dataBlob );
  350. FREE_BLOB( serverHashBlob );
  351. FREE_BLOB( clientHashBlob );
  352. FREE_BLOB( clientSessionKeyBlob );
  353. FREE_BLOB( serverSessionKeyBlob );
  354. FREE_BLOB( serverSignatureKeyBlob );
  355. FREE_BLOB( serverKeyExchangeKeyBlob );
  356. FREE_BLOB( clientSignatureKeyBlob );
  357. FREE_BLOB( clientKeyExchangeKeyBlob );
  358. delete psocket;
  359. delete clientStorage;
  360. delete serverStorage;
  361. delete pclient;
  362. (VOID)IISCryptoTerminate();
  363. DELETE_DEBUG_PRINT_OBJECT();
  364. return 0;
  365. } // main
  366. //
  367. // Private functions.
  368. //
  369. SECURITY_STATUS
  370. MyLogonUser(
  371. IN LPWSTR UserName,
  372. IN LPWSTR UserDomain,
  373. IN LPWSTR UserPassword,
  374. OUT PCtxtHandle ServerContext,
  375. OUT PCredHandle ServerCredential
  376. )
  377. {
  378. SECURITY_STATUS status;
  379. PSecPkgInfoW packageInfo;
  380. PSEC_WINNT_AUTH_IDENTITY_W additionalCredentials;
  381. ULONG additionalCredentialsLength;
  382. LPWSTR next;
  383. CredHandle clientCredential;
  384. TimeStamp expiration;
  385. SecBufferDesc tokenBuffer1Desc;
  386. SecBuffer tokenBuffer1;
  387. SecBufferDesc tokenBuffer2Desc;
  388. SecBuffer tokenBuffer2;
  389. ULONG contextAttributes;
  390. CtxtHandle clientContext;
  391. PVOID rawTokenBuffer1;
  392. PVOID rawTokenBuffer2;
  393. BOOL haveClientCredential;
  394. //
  395. // Setup locals so we know how to cleanup on exit.
  396. //
  397. packageInfo = NULL;
  398. additionalCredentials = NULL;
  399. rawTokenBuffer1 = NULL;
  400. rawTokenBuffer2 = NULL;
  401. haveClientCredential = FALSE;
  402. //
  403. // Get the package info. We must do this to get the maximum
  404. // token length.
  405. //
  406. status = QuerySecurityPackageInfoW(
  407. PACKAGE_NAME, // pszPackageName
  408. &packageInfo // ppPackageInfo
  409. );
  410. if( FAILED(status) ) {
  411. goto Cleanup;
  412. }
  413. //
  414. // Allocate the token buffers.
  415. //
  416. rawTokenBuffer1 = ALLOC_MEM( packageInfo->cbMaxToken );
  417. rawTokenBuffer2 = ALLOC_MEM( packageInfo->cbMaxToken );
  418. if( rawTokenBuffer1 == NULL ||
  419. rawTokenBuffer2 == NULL ) {
  420. status = ERROR_NOT_ENOUGH_MEMORY;
  421. goto Cleanup;
  422. }
  423. //
  424. // Build the credential info containing the cleartext user name,
  425. // domain name, and password.
  426. //
  427. additionalCredentialsLength = sizeof(*additionalCredentials);
  428. if( UserName != NULL ) {
  429. additionalCredentialsLength += ( wcslen( UserName ) + 1 ) * sizeof(WCHAR);
  430. }
  431. if( UserDomain != NULL ) {
  432. additionalCredentialsLength += ( wcslen( UserDomain ) + 1 ) * sizeof(WCHAR);
  433. }
  434. if( UserPassword != NULL ) {
  435. additionalCredentialsLength += ( wcslen( UserPassword ) + 1 ) * sizeof(WCHAR);
  436. }
  437. additionalCredentials = (PSEC_WINNT_AUTH_IDENTITY_W)ALLOC_MEM( additionalCredentialsLength );
  438. if( additionalCredentials == NULL ) {
  439. status = ERROR_NOT_ENOUGH_MEMORY;
  440. goto Cleanup;
  441. }
  442. RtlZeroMemory(
  443. additionalCredentials,
  444. additionalCredentialsLength
  445. );
  446. next = (LPWSTR)( additionalCredentials + 1 );
  447. additionalCredentials->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
  448. if( UserName != NULL ) {
  449. additionalCredentials->User = (unsigned short *)next;
  450. additionalCredentials->UserLength = wcslen( UserName );
  451. wcscpy( next, UserName );
  452. next += additionalCredentials->UserLength + 1;
  453. }
  454. if( UserDomain != NULL ) {
  455. additionalCredentials->Domain = (unsigned short *)next;
  456. additionalCredentials->DomainLength = wcslen( UserDomain );
  457. wcscpy( next, UserDomain );
  458. next += additionalCredentials->DomainLength + 1;
  459. }
  460. if( UserPassword != NULL ) {
  461. additionalCredentials->Password = (unsigned short *)next;
  462. additionalCredentials->PasswordLength = wcslen( UserPassword );
  463. wcscpy( next, UserPassword );
  464. next += additionalCredentials->PasswordLength + 1;
  465. }
  466. //
  467. // Get the client-side credentials.
  468. //
  469. status = AcquireCredentialsHandleW(
  470. NULL, // pszPrincipal
  471. PACKAGE_NAME, // pszPackage
  472. SECPKG_CRED_OUTBOUND, // fCredentialUse
  473. NULL, // pvLogonID
  474. additionalCredentials, // pAuthData
  475. NULL, // pGetKeyFn
  476. NULL, // pvGetKeyArgument
  477. &clientCredential, // phCredential
  478. &expiration // ptsExpiry
  479. );
  480. RtlZeroMemory(
  481. additionalCredentials->Password,
  482. additionalCredentials->PasswordLength
  483. );
  484. if( FAILED(status) ) {
  485. goto Cleanup;
  486. }
  487. haveClientCredential = TRUE;
  488. //
  489. // Get the server-side credentials.
  490. //
  491. status = AcquireCredentialsHandleW(
  492. NULL, // pszPrincipal
  493. PACKAGE_NAME, // pszPackage
  494. SECPKG_CRED_INBOUND, // fCredentialUse
  495. NULL, // pvLogonID
  496. NULL, // pAuthData
  497. NULL, // pGetKeyFn
  498. NULL, // pvGetKeyArgument
  499. ServerCredential, // phCredential
  500. &expiration // ptsExpiry
  501. );
  502. if( FAILED(status) ) {
  503. goto Cleanup;
  504. }
  505. //
  506. // Initialize the client-side security context.
  507. //
  508. tokenBuffer1Desc.cBuffers = 1;
  509. tokenBuffer1Desc.pBuffers = &tokenBuffer1;
  510. tokenBuffer1Desc.ulVersion = SECBUFFER_VERSION;
  511. tokenBuffer1.BufferType = SECBUFFER_TOKEN;
  512. tokenBuffer1.cbBuffer = packageInfo->cbMaxToken;
  513. tokenBuffer1.pvBuffer = rawTokenBuffer1;
  514. status = InitializeSecurityContext(
  515. &clientCredential, // phCredential
  516. NULL, // phContext
  517. NULL, // pszTargetName
  518. ISC_REQ_REPLAY_DETECT, // fContextReq
  519. 0, // Reserved1
  520. SECURITY_NATIVE_DREP, // TargetDataRep,
  521. NULL, // pInput
  522. 0, // Reserved2
  523. &clientContext, // phNewContext
  524. &tokenBuffer1Desc, // pOutput,
  525. &contextAttributes, // pfContextAttr,
  526. &expiration // ptsExpiry
  527. );
  528. if( FAILED(status) ) {
  529. goto Cleanup;
  530. }
  531. //
  532. // Import the client-side context into the server side.
  533. //
  534. tokenBuffer2Desc.cBuffers = 1;
  535. tokenBuffer2Desc.pBuffers = &tokenBuffer2;
  536. tokenBuffer2Desc.ulVersion = SECBUFFER_VERSION;
  537. tokenBuffer2.BufferType = SECBUFFER_TOKEN;
  538. tokenBuffer2.cbBuffer = packageInfo->cbMaxToken;
  539. tokenBuffer2.pvBuffer = rawTokenBuffer2;
  540. status = AcceptSecurityContext(
  541. ServerCredential, // phCredential
  542. NULL, // phContext
  543. &tokenBuffer1Desc, // pInput
  544. 0, // fContextReq
  545. SECURITY_NATIVE_DREP, // TargetDataRep
  546. ServerContext, // phNewContext
  547. &tokenBuffer2Desc, // pOutput
  548. &contextAttributes, // pfContextAttr
  549. &expiration // ptsExpiry
  550. );
  551. if( FAILED(status) ) {
  552. goto Cleanup;
  553. }
  554. //
  555. // Pass it back into the client.
  556. //
  557. tokenBuffer1Desc.cBuffers = 1;
  558. tokenBuffer1Desc.pBuffers = &tokenBuffer1;
  559. tokenBuffer1Desc.ulVersion = SECBUFFER_VERSION;
  560. tokenBuffer1.BufferType = SECBUFFER_TOKEN;
  561. tokenBuffer1.cbBuffer = packageInfo->cbMaxToken;
  562. tokenBuffer1.pvBuffer = rawTokenBuffer1;
  563. status = InitializeSecurityContext(
  564. &clientCredential, // phCredential
  565. &clientContext, // phContext
  566. NULL, // pszTargetName
  567. 0, // fContextReq
  568. 0, // Reserved1
  569. SECURITY_NATIVE_DREP, // TargetDataRep,
  570. &tokenBuffer2Desc, // pInput
  571. 0, // Reserved2
  572. &clientContext, // phNewContext
  573. &tokenBuffer1Desc, // pOutput,
  574. &contextAttributes, // pfContextAttr,
  575. &expiration // ptsExpiry
  576. );
  577. if( FAILED(status) ) {
  578. goto Cleanup;
  579. }
  580. //
  581. // And one last time back into the server.
  582. //
  583. tokenBuffer2Desc.cBuffers = 1;
  584. tokenBuffer2Desc.pBuffers = &tokenBuffer2;
  585. tokenBuffer2Desc.ulVersion = SECBUFFER_VERSION;
  586. tokenBuffer2.BufferType = SECBUFFER_TOKEN;
  587. tokenBuffer2.cbBuffer = packageInfo->cbMaxToken;
  588. tokenBuffer2.pvBuffer = rawTokenBuffer2;
  589. status = AcceptSecurityContext(
  590. ServerCredential, // phCredential
  591. ServerContext, // phContext
  592. &tokenBuffer1Desc, // pInput
  593. 0, // fContextReq
  594. SECURITY_NATIVE_DREP, // TargetDataRep
  595. ServerContext, // phNewContext
  596. NULL, // pOutput
  597. &contextAttributes, // pfContextAttr
  598. &expiration // ptsExpiry
  599. );
  600. if( FAILED(status) ) {
  601. goto Cleanup;
  602. }
  603. Cleanup:
  604. if( haveClientCredential ) {
  605. FreeCredentialsHandle( &clientCredential );
  606. }
  607. if( rawTokenBuffer2 != NULL ) {
  608. FREE_MEM( rawTokenBuffer2 );
  609. }
  610. if( rawTokenBuffer1 != NULL ) {
  611. FREE_MEM( rawTokenBuffer1 );
  612. }
  613. if( additionalCredentials != NULL ) {
  614. FREE_MEM( additionalCredentials );
  615. }
  616. if( packageInfo != NULL ) {
  617. FreeContextBuffer( packageInfo );
  618. }
  619. return status;
  620. } // MyLogonUser
  621. BOOL
  622. GetStringFromUser(
  623. LPSTR Prompt,
  624. LPWSTR String,
  625. ULONG MaxLength,
  626. BOOL Echo
  627. )
  628. {
  629. ULONG length;
  630. INT ch;
  631. printf( "%s", Prompt );
  632. length = 0;
  633. for( ; ; ) {
  634. ch = _getch();
  635. switch( ch ) {
  636. case KEY_CTRL_C :
  637. return FALSE;
  638. case KEY_BACKSPACE :
  639. if( length == 0 ) {
  640. printf( STR_BEEP );
  641. } else {
  642. length--;
  643. printf( STR_BACKSPACE );
  644. }
  645. break;
  646. case KEY_ENTER :
  647. case KEY_EOF :
  648. String[length] = L'\0';
  649. printf( "\n" );
  650. return TRUE;
  651. case KEY_ESCAPE :
  652. while( length > 0 ) {
  653. length--;
  654. printf( STR_BACKSPACE );
  655. }
  656. break;
  657. default :
  658. if( length < MaxLength ) {
  659. String[length++] = (WCHAR)ch;
  660. if( Echo ) {
  661. printf( "%c", ch );
  662. } else {
  663. printf( STR_HIDDEN );
  664. }
  665. } else {
  666. printf( STR_BEEP );
  667. }
  668. break;
  669. }
  670. }
  671. return TRUE;
  672. } // GetStringFromUser