/*++ Copyright (c) 1996 Microsoft Corporation Module Name: exchcli2.cxx Abstract: IIS Crypto client-side key exchange test with security impersonation. Author: Keith Moore (keithmo) 14-Oct-1997 Revision History: --*/ #include "precomp.hxx" #pragma hdrstop // // Private constants. // #define TEST_HRESULT(api) \ if( FAILED(result) ) { \ \ printf( \ "%s:%lu failed, error %08lx\n", \ api, \ __LINE__, \ result \ ); \ \ goto cleanup; \ \ } else #define TEST_SOCKERR(api) \ if( sockerr != NO_ERROR ) { \ \ printf( \ "%s:%lu failed, error %d\n", \ api, \ __LINE__, \ sockerr \ ); \ \ goto cleanup; \ \ } else #define FREE_BLOB(b) \ if( b != NULL ) { \ \ HRESULT _result; \ \ _result = IISCryptoFreeBlob( b ); \ \ if( FAILED(_result) ) { \ \ printf( \ "IISCryptoFreeBlob( %08lx ):%lu failed, error %08lx\n", \ b, \ __LINE__, \ _result \ ); \ \ } \ \ (b) = NULL; \ \ } #define PACKAGE_NAME L"NTLM" #define KEY_CTRL_C '\x03' #define KEY_BACKSPACE '\x08' #define KEY_ENTER '\x0d' #define KEY_EOF '\x1a' #define KEY_ESCAPE '\x1b' #define STR_BEEP "\x07" #define STR_HIDDEN "*" #define STR_BACKSPACE "\x08 \x08" #define ALLOC_MEM(cb) (PVOID)LocalAlloc( LPTR, (cb) ) #define FREE_MEM(p) (VOID)LocalFree( (HLOCAL)(p) ) #define DIM(x) (sizeof(x) / sizeof(x[0])) // // Private types. // // // Private globals. // DECLARE_DEBUG_PRINTS_OBJECT() #include DEFINE_GUID(IisCrypt2Guid, 0x784d8928, 0xaa8c, 0x11d2, 0x92, 0x5e, 0x00, 0xc0, 0x4f, 0x72, 0xd9, 0x0e); CHAR ClientPlainText[] = "Client Client Client Client Client Client"; // // Private prototypes. // SECURITY_STATUS MyLogonUser( IN LPWSTR UserName, IN LPWSTR UserDomain, IN LPWSTR UserPassword, OUT PCtxtHandle ServerContext, OUT PCredHandle ServerCredential ); BOOL GetStringFromUser( LPSTR Prompt, LPWSTR String, ULONG MaxLength, BOOL Echo ); // // Public functions. // INT __cdecl main( INT argc, CHAR * argv[] ) { INT sockerr; HRESULT result; IIS_CRYPTO_EXCHANGE_CLIENT * pclient; BUFFERED_SOCKET * psocket; PIIS_CRYPTO_BLOB clientKeyExchangeKeyBlob; PIIS_CRYPTO_BLOB clientSignatureKeyBlob; PIIS_CRYPTO_BLOB serverKeyExchangeKeyBlob; PIIS_CRYPTO_BLOB serverSignatureKeyBlob; PIIS_CRYPTO_BLOB serverSessionKeyBlob; PIIS_CRYPTO_BLOB clientSessionKeyBlob; PIIS_CRYPTO_BLOB clientHashBlob; PIIS_CRYPTO_BLOB serverHashBlob; PIIS_CRYPTO_BLOB dataBlob; IIS_CRYPTO_STORAGE * clientStorage; IIS_CRYPTO_STORAGE * serverStorage; PVOID buffer; DWORD bufferLength; DWORD bufferType; PSecurityFunctionTable ftab; CtxtHandle serverContext; CredHandle serverCredential; WCHAR name[128]; WCHAR domain[128]; WCHAR password[128]; if( argc != 2 ) { printf( "use: exchcli2 target_server\n" ); return 1; } // // Initialize debug stuff. // #ifndef _NO_TRACING_ CREATE_DEBUG_PRINT_OBJECT( "iiscrypt", IisCrypt2Guid ); CREATE_INITIALIZE_DEBUG(); #else CREATE_DEBUG_PRINT_OBJECT( "iiscrypt" ); #endif // // Setup our locals so we know how to cleanup on exit. // pclient = NULL; psocket = NULL; clientKeyExchangeKeyBlob = NULL; clientSignatureKeyBlob = NULL; serverKeyExchangeKeyBlob = NULL; serverSignatureKeyBlob = NULL; serverSessionKeyBlob = NULL; clientSessionKeyBlob = NULL; clientHashBlob = NULL; serverHashBlob = NULL; dataBlob = NULL; clientStorage = NULL; serverStorage = NULL; // // Initialize SSPI. // ftab = InitSecurityInterface(); if( ftab == NULL ) { sockerr = GetLastError(); TEST_SOCKERR( "InitSecurityInterface()" ); } // // Prompt for the user name, domain, and password. // if( !GetStringFromUser( "name: ", name, DIM(name), TRUE ) || !GetStringFromUser( "domain: ", domain, DIM(domain), TRUE ) || !GetStringFromUser( "password: ", password, DIM(password), FALSE ) ) { goto cleanup; } // // Logon. // result = MyLogonUser( name, domain, password, &serverContext, &serverCredential ); RtlZeroMemory( password, sizeof(password) ); TEST_HRESULT( "MyLogonUser" ); result = ImpersonateSecurityContext( &serverContext ); TEST_HRESULT( "ImpersonateSecurityContext" ); // // Initialize the crypto package. // printf( "exchcli2: Initializing...\n" ); result = IISCryptoInitialize(); TEST_HRESULT( "IISCryptoInitialize()" ); // // Create & initialize the client-side key exchange object. // pclient = new IIS_CRYPTO_EXCHANGE_CLIENT; if( pclient == NULL ) { printf( "out of memory\n" ); goto cleanup; } result = pclient->Initialize( CRYPT_NULL, CRYPT_NULL, CRYPT_NULL, TRUE ); TEST_HRESULT( "pclient->Initialize()" ); // // Create & initialize the buffered socket object. // psocket = new BUFFERED_SOCKET; if( psocket == NULL ) { printf( "out of memory\n" ); goto cleanup; } result = psocket->InitializeClient( argv[1], SERVER_PORT ); TEST_HRESULT( "psocket->Initialize()" ); // // 1. CLIENT(1) // printf( "exchcli2: Phase 1...\n" ); result = pclient->ClientPhase1( &clientKeyExchangeKeyBlob, &clientSignatureKeyBlob ); TEST_HRESULT( "pclient->ClientPhase1()" ); sockerr = psocket->SendBlob( clientKeyExchangeKeyBlob ); TEST_SOCKERR( "psocket->SendBlob()" ); sockerr = psocket->SendBlob( clientSignatureKeyBlob ); TEST_SOCKERR( "psocket->SendBlob()" ); // // 3. CLIENT(2) // printf( "exchcli2: Phase 2...\n" ); sockerr = psocket->RecvBlob( &serverKeyExchangeKeyBlob ); TEST_SOCKERR( "psocket->RecvBlob()" ); sockerr = psocket->RecvBlob( &serverSignatureKeyBlob ); TEST_SOCKERR( "psocket->RecvBlob()" ); sockerr = psocket->RecvBlob( &serverSessionKeyBlob ); TEST_SOCKERR( "psocket->RecvBlob()" ); result = pclient->ClientPhase2( serverKeyExchangeKeyBlob, serverSignatureKeyBlob, serverSessionKeyBlob, &clientSessionKeyBlob, &clientHashBlob ); TEST_HRESULT( "pclient->ClientPhase2()" ); sockerr = psocket->SendBlob( clientSessionKeyBlob ); TEST_SOCKERR( "psocket->SendBlob()" ); sockerr = psocket->SendBlob( clientHashBlob ); TEST_SOCKERR( "psocket->SendBlob()" ); // // 5. CLIENT(3) // printf( "exchcli2: Phase 3...\n" ); sockerr = psocket->RecvBlob( &serverHashBlob ); TEST_SOCKERR( "psocket->RecvBlob()" ); result = pclient->ClientPhase3( serverHashBlob ); TEST_HRESULT( "pclient->ClientPhase3()" ); // // Create the storage objects. // printf( "exchcli2: Creating storage objects...\n" ); clientStorage = new IIS_CRYPTO_STORAGE; if( clientStorage == NULL ) { printf( "out of memory\n" ); goto cleanup; } result = clientStorage->Initialize( pclient->QueryProviderHandle(), pclient->AssumeClientSessionKey(), CRYPT_NULL, CRYPT_NULL, TRUE ); TEST_HRESULT( "clientStorage->Initialize()" ); serverStorage = new IIS_CRYPTO_STORAGE; if( serverStorage == NULL ) { printf( "out of memory\n" ); goto cleanup; } result = serverStorage->Initialize( pclient->QueryProviderHandle(), pclient->AssumeServerSessionKey(), CRYPT_NULL, pclient->AssumeServerSignatureKey(), TRUE ); TEST_HRESULT( "serverStorage->Initialize()" ); // // Send some encrypted data. // printf( "exchcli2: Encrypting '%s'...\n", ClientPlainText ); result = clientStorage->EncryptData( &dataBlob, ClientPlainText, sizeof(ClientPlainText), REG_SZ ); TEST_HRESULT( "clientStorage->EncryptData()" ); printf( "exchcli2: Sending encrypted data...\n" ); sockerr = psocket->SendBlob( dataBlob ); TEST_SOCKERR( "psocket->SendBlob()" ); FREE_BLOB( dataBlob ); // // Receive some encrypted data. // printf( "exchcli2: Receiving encrypted data...\n" ); sockerr = psocket->RecvBlob( &dataBlob ); TEST_SOCKERR( "psocket->RecvBlob()" ); result = serverStorage->DecryptData( &buffer, &bufferLength, &bufferType, dataBlob ); TEST_HRESULT( "serverStorage->DecryptData()" ); printf( "exchcli2: Received data[%lu] = '%s'\n", bufferLength, buffer ); // // Tests complete. // printf( "exchcli2: Done!\n" ); cleanup: FREE_BLOB( dataBlob ); FREE_BLOB( serverHashBlob ); FREE_BLOB( clientHashBlob ); FREE_BLOB( clientSessionKeyBlob ); FREE_BLOB( serverSessionKeyBlob ); FREE_BLOB( serverSignatureKeyBlob ); FREE_BLOB( serverKeyExchangeKeyBlob ); FREE_BLOB( clientSignatureKeyBlob ); FREE_BLOB( clientKeyExchangeKeyBlob ); delete psocket; delete clientStorage; delete serverStorage; delete pclient; (VOID)IISCryptoTerminate(); DELETE_DEBUG_PRINT_OBJECT(); return 0; } // main // // Private functions. // SECURITY_STATUS MyLogonUser( IN LPWSTR UserName, IN LPWSTR UserDomain, IN LPWSTR UserPassword, OUT PCtxtHandle ServerContext, OUT PCredHandle ServerCredential ) { SECURITY_STATUS status; PSecPkgInfoW packageInfo; PSEC_WINNT_AUTH_IDENTITY_W additionalCredentials; ULONG additionalCredentialsLength; LPWSTR next; CredHandle clientCredential; TimeStamp expiration; SecBufferDesc tokenBuffer1Desc; SecBuffer tokenBuffer1; SecBufferDesc tokenBuffer2Desc; SecBuffer tokenBuffer2; ULONG contextAttributes; CtxtHandle clientContext; PVOID rawTokenBuffer1; PVOID rawTokenBuffer2; BOOL haveClientCredential; // // Setup locals so we know how to cleanup on exit. // packageInfo = NULL; additionalCredentials = NULL; rawTokenBuffer1 = NULL; rawTokenBuffer2 = NULL; haveClientCredential = FALSE; // // Get the package info. We must do this to get the maximum // token length. // status = QuerySecurityPackageInfoW( PACKAGE_NAME, // pszPackageName &packageInfo // ppPackageInfo ); if( FAILED(status) ) { goto Cleanup; } // // Allocate the token buffers. // rawTokenBuffer1 = ALLOC_MEM( packageInfo->cbMaxToken ); rawTokenBuffer2 = ALLOC_MEM( packageInfo->cbMaxToken ); if( rawTokenBuffer1 == NULL || rawTokenBuffer2 == NULL ) { status = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } // // Build the credential info containing the cleartext user name, // domain name, and password. // additionalCredentialsLength = sizeof(*additionalCredentials); if( UserName != NULL ) { additionalCredentialsLength += ( wcslen( UserName ) + 1 ) * sizeof(WCHAR); } if( UserDomain != NULL ) { additionalCredentialsLength += ( wcslen( UserDomain ) + 1 ) * sizeof(WCHAR); } if( UserPassword != NULL ) { additionalCredentialsLength += ( wcslen( UserPassword ) + 1 ) * sizeof(WCHAR); } additionalCredentials = (PSEC_WINNT_AUTH_IDENTITY_W)ALLOC_MEM( additionalCredentialsLength ); if( additionalCredentials == NULL ) { status = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } RtlZeroMemory( additionalCredentials, additionalCredentialsLength ); next = (LPWSTR)( additionalCredentials + 1 ); additionalCredentials->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; if( UserName != NULL ) { additionalCredentials->User = (unsigned short *)next; additionalCredentials->UserLength = wcslen( UserName ); wcscpy( next, UserName ); next += additionalCredentials->UserLength + 1; } if( UserDomain != NULL ) { additionalCredentials->Domain = (unsigned short *)next; additionalCredentials->DomainLength = wcslen( UserDomain ); wcscpy( next, UserDomain ); next += additionalCredentials->DomainLength + 1; } if( UserPassword != NULL ) { additionalCredentials->Password = (unsigned short *)next; additionalCredentials->PasswordLength = wcslen( UserPassword ); wcscpy( next, UserPassword ); next += additionalCredentials->PasswordLength + 1; } // // Get the client-side credentials. // status = AcquireCredentialsHandleW( NULL, // pszPrincipal PACKAGE_NAME, // pszPackage SECPKG_CRED_OUTBOUND, // fCredentialUse NULL, // pvLogonID additionalCredentials, // pAuthData NULL, // pGetKeyFn NULL, // pvGetKeyArgument &clientCredential, // phCredential &expiration // ptsExpiry ); RtlZeroMemory( additionalCredentials->Password, additionalCredentials->PasswordLength ); if( FAILED(status) ) { goto Cleanup; } haveClientCredential = TRUE; // // Get the server-side credentials. // status = AcquireCredentialsHandleW( NULL, // pszPrincipal PACKAGE_NAME, // pszPackage SECPKG_CRED_INBOUND, // fCredentialUse NULL, // pvLogonID NULL, // pAuthData NULL, // pGetKeyFn NULL, // pvGetKeyArgument ServerCredential, // phCredential &expiration // ptsExpiry ); if( FAILED(status) ) { goto Cleanup; } // // Initialize the client-side security context. // tokenBuffer1Desc.cBuffers = 1; tokenBuffer1Desc.pBuffers = &tokenBuffer1; tokenBuffer1Desc.ulVersion = SECBUFFER_VERSION; tokenBuffer1.BufferType = SECBUFFER_TOKEN; tokenBuffer1.cbBuffer = packageInfo->cbMaxToken; tokenBuffer1.pvBuffer = rawTokenBuffer1; status = InitializeSecurityContext( &clientCredential, // phCredential NULL, // phContext NULL, // pszTargetName ISC_REQ_REPLAY_DETECT, // fContextReq 0, // Reserved1 SECURITY_NATIVE_DREP, // TargetDataRep, NULL, // pInput 0, // Reserved2 &clientContext, // phNewContext &tokenBuffer1Desc, // pOutput, &contextAttributes, // pfContextAttr, &expiration // ptsExpiry ); if( FAILED(status) ) { goto Cleanup; } // // Import the client-side context into the server side. // tokenBuffer2Desc.cBuffers = 1; tokenBuffer2Desc.pBuffers = &tokenBuffer2; tokenBuffer2Desc.ulVersion = SECBUFFER_VERSION; tokenBuffer2.BufferType = SECBUFFER_TOKEN; tokenBuffer2.cbBuffer = packageInfo->cbMaxToken; tokenBuffer2.pvBuffer = rawTokenBuffer2; status = AcceptSecurityContext( ServerCredential, // phCredential NULL, // phContext &tokenBuffer1Desc, // pInput 0, // fContextReq SECURITY_NATIVE_DREP, // TargetDataRep ServerContext, // phNewContext &tokenBuffer2Desc, // pOutput &contextAttributes, // pfContextAttr &expiration // ptsExpiry ); if( FAILED(status) ) { goto Cleanup; } // // Pass it back into the client. // tokenBuffer1Desc.cBuffers = 1; tokenBuffer1Desc.pBuffers = &tokenBuffer1; tokenBuffer1Desc.ulVersion = SECBUFFER_VERSION; tokenBuffer1.BufferType = SECBUFFER_TOKEN; tokenBuffer1.cbBuffer = packageInfo->cbMaxToken; tokenBuffer1.pvBuffer = rawTokenBuffer1; status = InitializeSecurityContext( &clientCredential, // phCredential &clientContext, // phContext NULL, // pszTargetName 0, // fContextReq 0, // Reserved1 SECURITY_NATIVE_DREP, // TargetDataRep, &tokenBuffer2Desc, // pInput 0, // Reserved2 &clientContext, // phNewContext &tokenBuffer1Desc, // pOutput, &contextAttributes, // pfContextAttr, &expiration // ptsExpiry ); if( FAILED(status) ) { goto Cleanup; } // // And one last time back into the server. // tokenBuffer2Desc.cBuffers = 1; tokenBuffer2Desc.pBuffers = &tokenBuffer2; tokenBuffer2Desc.ulVersion = SECBUFFER_VERSION; tokenBuffer2.BufferType = SECBUFFER_TOKEN; tokenBuffer2.cbBuffer = packageInfo->cbMaxToken; tokenBuffer2.pvBuffer = rawTokenBuffer2; status = AcceptSecurityContext( ServerCredential, // phCredential ServerContext, // phContext &tokenBuffer1Desc, // pInput 0, // fContextReq SECURITY_NATIVE_DREP, // TargetDataRep ServerContext, // phNewContext NULL, // pOutput &contextAttributes, // pfContextAttr &expiration // ptsExpiry ); if( FAILED(status) ) { goto Cleanup; } Cleanup: if( haveClientCredential ) { FreeCredentialsHandle( &clientCredential ); } if( rawTokenBuffer2 != NULL ) { FREE_MEM( rawTokenBuffer2 ); } if( rawTokenBuffer1 != NULL ) { FREE_MEM( rawTokenBuffer1 ); } if( additionalCredentials != NULL ) { FREE_MEM( additionalCredentials ); } if( packageInfo != NULL ) { FreeContextBuffer( packageInfo ); } return status; } // MyLogonUser BOOL GetStringFromUser( LPSTR Prompt, LPWSTR String, ULONG MaxLength, BOOL Echo ) { ULONG length; INT ch; printf( "%s", Prompt ); length = 0; for( ; ; ) { ch = _getch(); switch( ch ) { case KEY_CTRL_C : return FALSE; case KEY_BACKSPACE : if( length == 0 ) { printf( STR_BEEP ); } else { length--; printf( STR_BACKSPACE ); } break; case KEY_ENTER : case KEY_EOF : String[length] = L'\0'; printf( "\n" ); return TRUE; case KEY_ESCAPE : while( length > 0 ) { length--; printf( STR_BACKSPACE ); } break; default : if( length < MaxLength ) { String[length++] = (WCHAR)ch; if( Echo ) { printf( "%c", ch ); } else { printf( STR_HIDDEN ); } } else { printf( STR_BEEP ); } break; } } return TRUE; } // GetStringFromUser