Not that this is necessarily a good idea. But after way too much time googling this, I think I'll clear up the fact that it IS possible call the Microsoft RDP client with a username and password parameter.
Now, everyone out there will say, yeah - we know that. You key in the username and password and if you check off the "remember credentials" checkbox it will store that data.
Well the trick is that this data is stored in a place that's not easily accessed -Windows Stored UserNames and Passwords. This area is basically a secured area of windows where passwords are stored for remote connections. You can access this by going Control Panel\User Accounts\Manager your network passwords. Here's a screen snap.
Our management infrastructure tracks this username and password in a seperate secured database, and then when we call MSTSC.exe from our software we want to pass this information. Now, a little more information on how you can pass parameters to MSTSC is in order. When you call MSTSC you can pass as a parameter a .RDP file which contains information about how you want to connect to the remote computer. Same idea as every other document type in Windows, a .RDP file is associated with MSTSC and is used to contain information for that application. Fortunately the .RDP files are basically key value pairs. Check it out...
screen mode id:i:1desktopwidth:i:1024desktopheight:i:768session bpp:i:16winposstr:s:0,1,526,61,1566,865full address:s:SomeIpAddresscompression:i:1keyboardhook:i:2audiomode:i:0redirectdrives:i:0redirectprinters:i:1redirectcomports:i:0redirectsmartcards:i:1displayconnectionbar:i:0autoreconnection enabled:i:1alternate shell:s:shell working directory:s:disable wallpaper:i:0disable full window drag:i:1disable menu anims:i:1disable themes:i:0disable cursor setting:i:0bitmapcachepersistenable:i:1allow desktop composition:i:1allow font smoothing:i:0redirectclipboard:i:1redirectposdevices:i:0authentication level:i:2prompt for credentials:i:0negotiate security layer:i:1remoteapplicationmode:i:0gatewayhostname:s:gatewayusagemethod:i:4gatewaycredentialssource:i:4gatewayprofileusagemethod:i:0promptcredentialonce:i:1drivestoredirect:s:
The imporant pieces here is the "full address" line... this points to the server we're connecting to.
Well... it turns out you can also pass parameters for domain, username, and password. You just need to add them to the bottom of your .rdp file like this
domain:s:MyDomainusername:s:MyUserNamepassword 51:b:MyPassword
But wait a second, saving a password in plain text in a .rdp file sounds like just about the worst security hole you could use! And you're right - you can't actually pass the password parameter as straight text. It needs to be encrypted binary.
(As an aside, that's what the second part of these key/value pairs is... the s, or b, or i character. That indicates whether the data is String, Binary or Integer).
Well it turns out that taking a straight text password and encrypting it ain't too bad. I'm not going to take credit for this code but the following vb dot net code uses the windows api to do just that.
Imports SystemImports System.TextImports System.Runtime.InteropServicesImports System.ComponentModelImports Microsoft.VisualBasicPublic Class DPAPI<DllImport("Crypt32.dll", SetLastError:=True, CharSet:=System.Runtime.InteropServices.CharSet.Auto)> Private Shared Function CryptProtectData( _ByRef pPlainText As DATA_BLOB, _ByVal szDescription As String, _ByRef pEntropy As DATA_BLOB, _ByVal pReserved As IntPtr, _ByRef pPrompt As CRYPTPROTECT_PROMPTSTRUCT, _ByVal dwFlags As Integer, _ByRef pCipherText As DATA_BLOB _) As BooleanEnd Function<DllImport("Crypt32.dll", SetLastError:=True, CharSet:=System.Runtime.InteropServices.CharSet.Auto)> _Private Shared Function CryptUnprotectData( _ByRef pCipherText As DATA_BLOB, _ByRef pszDescription As String, _ByRef pEntropy As DATA_BLOB, _ByVal pReserved As IntPtr, _ByRef pPrompt As CRYPTPROTECT_PROMPTSTRUCT, _ByVal dwFlags As Integer, _ByRef pPlainText As DATA_BLOB _) As BooleanEnd Function<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _Friend Structure DATA_BLOBPublic cbData As IntegerPublic pbData As IntPtrEnd Structure<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _Friend Structure CRYPTPROTECT_PROMPTSTRUCTPublic cbSize As IntegerPublic dwPromptFlags As IntegerPublic hwndApp As IntPtrPublic szPrompt As StringEnd StructurePrivate Const CRYPTPROTECT_UI_FORBIDDEN As Integer = 1Private Const CRYPTPROTECT_LOCAL_MACHINE As Integer = 4Private Shared Sub InitPrompt _( _ByRef ps As CRYPTPROTECT_PROMPTSTRUCT _)ps.cbSize = Marshal.SizeOf(GetType(CRYPTPROTECT_PROMPTSTRUCT))ps.dwPromptFlags = 0ps.hwndApp = IntPtr.Zerops.szPrompt = NothingEnd SubPrivate Shared Sub InitBLOB _( _ByVal data As Byte(), _ByRef blob As DATA_BLOB _)' Use empty array for null parameter.If data Is Nothing Thendata = New Byte(0) {}End If' Allocate memory for the BLOB data.blob.pbData = Marshal.AllocHGlobal(data.Length)' Make sure that memory allocation was successful.If blob.pbData.Equals(IntPtr.Zero) ThenThrow New Exception( _"Unable to allocate data buffer for BLOB structure.")End If' Specify number of bytes in the BLOB.blob.cbData = data.LengthMarshal.Copy(data, 0, blob.pbData, data.Length)End SubPublic Enum KeyTypeUserKey = 1MachineKeyEnd EnumPrivate Shared defaultKeyType As KeyType = KeyType.UserKeyPublic Shared Function Encrypt _( _ByVal keyType As KeyType, _ByVal plainText As String, _ByVal entropy As String, _ByVal description As String _) As StringIf plainText Is Nothing ThenplainText = String.EmptyEnd IfIf entropy Is Nothing Thenentropy = String.EmptyEnd IfDim result As Byte()Dim encrypted As String = ""Dim i As Integerresult = Encrypt(keyType, _Encoding.Unicode.GetBytes(plainText), _Encoding.Unicode.GetBytes(entropy), _description)For i = 0 To result.Length - 1encrypted = encrypted & Convert.ToString(result(i), 16).PadLeft(2, "0").ToUpper()NextReturn encrypted.ToString()End FunctionPublic Shared Function Encrypt _( _ByVal keyType As KeyType, _ByVal plainTextBytes As Byte(), _ByVal entropyBytes As Byte(), _ByVal description As String _) As Byte()If plainTextBytes Is Nothing ThenplainTextBytes = New Byte(0) {}End IfIf entropyBytes Is Nothing ThenentropyBytes = New Byte(0) {}End IfIf description Is Nothing Thendescription = String.EmptyEnd IfDim plainTextBlob As DATA_BLOB = New DATA_BLOBDim cipherTextBlob As DATA_BLOB = New DATA_BLOBDim entropyBlob As DATA_BLOB = New DATA_BLOBDim prompt As _CRYPTPROTECT_PROMPTSTRUCT = New CRYPTPROTECT_PROMPTSTRUCTInitPrompt(prompt)TryTryInitBLOB(plainTextBytes, plainTextBlob)Catch ex As ExceptionThrow New Exception("Cannot initialize plaintext BLOB.", ex)End TryTryInitBLOB(entropyBytes, entropyBlob)Catch ex As ExceptionThrow New Exception("Cannot initialize entropy BLOB.", ex)End TryDim flags As Integer = CRYPTPROTECT_UI_FORBIDDENIf keyType = keyType.MachineKey Thenflags = flags Or (CRYPTPROTECT_LOCAL_MACHINE)End IfDim success As Boolean = CryptProtectData( _plainTextBlob, _description, _entropyBlob, _IntPtr.Zero, _prompt, _flags, _cipherTextBlob)If Not success ThenDim errCode As Integer = Marshal.GetLastWin32Error()Throw New Exception("CryptProtectData failed.", _New Win32Exception(errCode))End IfDim cipherTextBytes(cipherTextBlob.cbData) As ByteMarshal.Copy(cipherTextBlob.pbData, cipherTextBytes, 0, _cipherTextBlob.cbData)Return cipherTextBytesCatch ex As ExceptionThrow New Exception("DPAPI was unable to encrypt data.", ex)FinallyIf Not (plainTextBlob.pbData.Equals(IntPtr.Zero)) ThenMarshal.FreeHGlobal(plainTextBlob.pbData)End IfIf Not (cipherTextBlob.pbData.Equals(IntPtr.Zero)) ThenMarshal.FreeHGlobal(cipherTextBlob.pbData)End IfIf Not (entropyBlob.pbData.Equals(IntPtr.Zero)) ThenMarshal.FreeHGlobal(entropyBlob.pbData)End IfEnd TryEnd FunctionEnd Class
DPAPI.Encrypt(DPAPI.KeyType.MachineKey, "MyPassword", Nothing, "psw")
Now, this is worth stressing again that exposing passwords for RDP clients can be a very dangerous idea. Someone can grab ahold of those and cause all sorts of havoc so your management of these security tools needs to be very well understood. Storage, access, and management of this data is one of the most critical components of your security strategy and anytime your exposing this information you need to be aware of the ramifications of that action.
dasBlog theme by Mads Kristensen
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.