Protecting MySQL Sessions With SSH Port Forwarding (Part 2)

Introduction

In my last article, I described how to secure connections to a MySQL server using SSH port forwarding (or SSH tunneling). Responses to the article included requests for information on connecting to an SSH server running under Windows and instructions on closing a tunnel opened from within Visual Basic. In this article I will show how to use the OpenSSH For Windows tool to host SSH tunnels on a Windows server, and will also cover how to use the shellexecuteex, terminateprocess and closeHandle API calls to open and close an SSH tunnel from within VB. The instructions given will be based on a Windows 2000 installation, but should be adaptable to any NT based system.

SSH For Windows

One of the advantages of the OpenSSH project is that it is designed for portability and variations exist for a variety of platforms. Michael Johnson maintains a Windows version of OpenSSH at http://lexa.mckenna.edu/sshwindows/ which I will be using in this article. OpenSSH for Windows can be downloaded at http://lexa.mckenna.edu/sshwindows/download/releases/

OpenSSH uses components from the Cygwin project, a Linux emulator that runs on most varieties of Windows. It should be noted that OpenSSH for Windows does not create a full Cygwin installation, and will not be compatible with systems that have Cygwin already installed.

Installing SSH For Windows

Once you download and extract the OpenSSH zip archive at http://lexa.mckenna.edu/sshwindows/download/releases/ you will have a single executable with a name like setupssh371-20031015.exe which you can double-click to install OpenSSH for Windows on your system. I would recommend installing OpenSSH to the default location of c:\program files\openssh.

Creating An NT User

OpenSSH for Windows relies on the Windows NT authentication system to validate users, and as such we need to create an NT user for OpenSSH to validate against.

Open the control panel with Start > Settings > Control Panel and double-click on the Users And Passwords icon. If you login automatically without specifying a password you will find most of the form grayed out, but you can gain access to the controls by checking the box titled “Users must enter a user name and password to access this computer.”

The checkbox

We will need to create a user which has password-protected access to the system. If you do not plan to do anything beyond opening SSH tunnels with the account then creating a user with Guest-level permissions will be adequate. In this case I will be creating a user named ‘tunnel’ as a member of the guests group. You can create a new user by clicking the Add… button on the Users and Passwords panel.

Configuring The passwd And group Files

Configuration for OpenSSH For Windows is done through the command shell. To open a command shell select Start > Run… then enter cmd in the dialogue box and click Ok. Once in the command shell enter the following:

 cd c:\program files\openssh\bin
mkgroup -l >> ..\etc\group

This command will add your user groups (Administrator, Guests, etc.) to the openssh\etc\group file. Please note that this will import local groups only. If your NT security is based on a domain controller you will need to reference the openssh\docs\readme.txt file for instructions on using domain users and groups.

Once your groups are imported you can use the following command to import users into the openssh\etc\passwd file:

 mkpasswd -l -u tunnel >> ..\etc\passwd

This will import the tunnel user into the passwd file. While you can import all Windows users at once by omitting the -u argument and username, I would recommend only importing the specific users you intend to use for SSH access.

Starting The Server

Once our server is configured we can start the OpenSSH server with the following command:

 net start opensshd

We can also use the services manager to start the OpenSSH server and configure it to start when the system is booting. Right-click on the My Computer icon on the desktop and select Manage from the context menu. You can then see a list of available services by choosing the Services entry of the Services And Applications tree.

Click To Enlarge

You can then right-click on the OpenSSH Server entry and choose the Start option to start the OpenSSH server. You can also choose the Properties option and change the Startup type: option from Manual to Automatic to have OpenSSH start during your server’s boot sequence.

Once your server is running you can then connect using tools like Putty and Plink as discussed in my previous article.

Opening And Closing Tunnels In Visual Basic

In my previous article I discussed how to open a tunnel in Visual Basic using the Plink utility and the shellexecute API call. While this worked quite well for opening SSH tunnels, I failed to cover how to close the tunnels from within VB. In order to open and also close tunnels from within Visual Basic we’ll be using the shellexecuteex, terminateprocess, and closeHandle API calls.

For this project I am going to split the API calls into a separate module that will contain the following:

Option Explicit
Dim si As SHELLEXECUTEINFO

Private Sub cmdClose_Click()
    Dim exitCode As Long
    If TerminateProcess(si.hProcess, exitCode) <> 0 Then
        CloseHandle (si.hProcess)
        MsgBox "Tunnel Closed!"
    End If
End Sub

Private Sub cmdOpen_Click()
    si.cbSize = Len(si)
    si.fMask = SEE_MASK_NOCLOSEPROCESS
    si.hwnd = frmMain.hwnd
    si.lpVerb = "open"
    si.lpFile = App.Path & "\plink.exe"
    si.lpParameters = "-ssh -l tunnel -pw 123456 -L 3306:127.0.0.1:3306 -batch 127.0.0.1"
    si.lpDirectory = App.Path
    si.nShow = SW_HIDE

    If ShellExecuteEx(si) Then
        MsgBox "Tunnel Open!"
    Else
        MsgBox "Opening Failed!"
    End If
End Sub

In addition I will create a form with two command buttons named cmdOpen and cmdClose (see attached project). These buttons will handle the actual opening and closing of the tunnel. Place the following code in your form:

Option Explicit
Dim si As SHELLEXECUTEINFO

Private Sub cmdClose_Click()
    Dim exitCode As Long
    If TerminateProcess(si.hProcess, exitCode) <> 0 Then
        CloseHandle (si.hProcess)
        MsgBox "Tunnel Closed!"
    End If
End Sub

Private Sub cmdOpen_Click()
    si.cbSize = Len(si)
    si.fMask = SEE_MASK_NOCLOSEPROCESS
    si.hwnd = frmMain.hwnd
    si.lpVerb = "open"
    si.lpFile = App.Path & "\plink.exe"
    si.lpParameters = "-ssh -l tunnel -pw 123456 -L 3306:127.0.0.1:3306 -batch 127.0.0.1"
    si.lpDirectory = App.Path
    si.nShow = SW_HIDE

    If ShellExecuteEx(si) Then
        MsgBox "Tunnel Open!"
    Else
        MsgBox "Opening Failed!"
    End If
End Sub

This code will open the Plink tunnel and store it’s process ID in si.hProcess, allowing us to later kill the process by referencing it’s process ID with the TerminateProcess API call.

Conclusion

OpenSSH For Windows is an excellent tool for providing secure access to Windows based servers through SSH tunnels. The shellexecuteex and terminateprocess API functions allow us to not only open an SSH tunnel from within Visual Basic, but close it as well.

15 Responses to “Protecting MySQL Sessions With SSH Port Forwarding (Part 2)”

  1. Toni Says:

    Hi,
    This looks just the code what I’m looking for, but I’m having little difficulties to implement this, since I’m quite new about this.
    There is a reference to see attached project, but I can’t see it. I was just thinking, if I could take look that and notice what is wrong. Thanks for awesome examples!!

  2. Sanj Says:

    Excellent tutorial, thanks. The first part of the tutorial has worked a treat for me, but the second part regarding opening and closing through VB is a little confusing. It looks like the two code windows at the bottom of the page duplicate each other. What are SHELLEXECUTEINFO and SEE_MASK_NOCLOSEPROCESS? Is there separate code available for the API declarations, as in the first part of the tutorial? Thanks again for a VERY useful tutorial.

  3. MitMacher Says:

    Exactly what I’ve searched for! Many thanks!!! :-)

    And the missing declarations can be found here:

    http://www.vbarchiv.net/vbapi/ShellExecuteEx.php

    http://www.vbarchiv.net/faq/allg_exitprocess.php

  4. Eddie Says:

    Hi,

    This is what I am looking for. I was able to get the first tutorial working, but am not able to get the 2nd part … I am new to VB … The below code will compile and nothing … I was wondering if anyone has any feedback to help me out … thanks so much ..

    Natasha

    Option Explicit

    Dim si As SHELLEXECUTEINFO

    Private Type SHELLEXECUTEINFO
    cbSize As Long
    fMask As Long
    hwnd As Long
    lpVerb As String
    lpFile As String
    lpParameters As String
    lpDirectory As String
    nShow As Long
    hInstApp As Long
    lpIDList As Long
    lpClass As String
    hkeyClass As Long
    dwHotKey As Long
    hIcon As Long
    hProcess As Long
    End Type

    Private Declare Function ShellExecuteEx Lib “shell32.dll” Alias “ShellExecuteExA” _
    (si As SHELLEXECUTEINFO) As Long

    Private Declare Function TerminateProcess Lib “kernel32″ (ByVal _
    hProcess As Long, ByVal uExitCode As Long) As Long

    Private Declare Function CloseHandle Lib “kernel32″ (ByVal _
    hObject As Long) As Long

    Private Sub cmdClose_Click()
    Dim exitCode As Long
    If TerminateProcess(si.hProcess, exitCode) 0 Then
    CloseHandle (si.hProcess)
    MsgBox “Tunnel Closed!”
    End If
    End Sub

    Private Sub cmdOpen_Click()
    si.cbSize = Len(si)
    si.fMask = SEE_MASK_NOCLOSEPROCESS
    si.hwnd = frmMain.hwnd
    si.lpVerb = “open”
    si.lpFile = “c:\plink.exe”
    si.lpParameters = “-ssh -l tunnel -pw Cyber4me 192.168.10.25″
    si.lpDirectory = “c:\”
    si.nShow = SW_HIDE

    If ShellExecuteEx(si) Then
    MsgBox “Tunnel Open!”
    Else
    MsgBox “Opening Failed!”
    End If
    End Sub

  5. WaQas Says:

    i just want to know that if we use plink.exe with command line .. so i want to know that is our SSH password will safe when we publish our software to general public????

    please try to reply me as soon as possible..

  6. MitMacher Says:

    Hm, perhaps it would be quite easy to dissassemble your software to get the cleartext password, that’s right. Therefore I suggest to use certificates instead of passwords in general, e.g.:

    plink.exe -ssh -P 45678 -l tunnel -i mycert.ppk -L 3306:127.0.0.1:3306 -N 12.34.56.78

    But your problem will be the same on the following connections through this tunnel. Where and how to store e.g., database passwords? We choose the way to store them into ini-files if the customer enters it through the program GUI. You MUSTN’T deliver your program with hardcoded passwords!

    Further It should be no security risk to have a ssh-tunnel opened for some people because it isn’t a shell and without a valid database login nobody is able to access your data… :-)

    greetings

  7. WaQas Says:

    sir, i hope that my software can’t b open .. because it is sucure with the

    http://themida.com/themida.php

    i also no worry that anybody change the plink.exe with there file to get command … because i check MD5 hash to veryfy plink.exe …

    i just worry that is anyone can get the ssh password run time or with any runtime debuger???

    Thanks in advance…

  8. MitMacher Says:

    OK, themida sounds good but I haven’t used it so far.
    And as far as I know the ssh password will be transmitted AFTER the handshake has been done and the connection is already secured, so you don’t have to bother with that.

  9. WaQas Says:

    ahaan, its kool to know about SSH..

    its mean .. tunnel is the safest way to communicate with any type data…

    dear.. actually my clients keep the money records.. so you can think about hackers … and they always want to destroy security to incriment their money … so i am frightened..

  10. WaQas Says:

    send me your email address… i will send you themida … you should check it .. it is very very good security ..
    in vb6 if you use its macros .. so .. it will fully protected…

  11. WaQas Says:

    sorry dear, i do lot of posts same time ….

    bro, will you please tell me how can i check that tunnel is opened successfully… because some time on slow connection it take some time to establish the connection ..

    please tell me the way that how can we check that connection successfully established and then we can open our database through the tunnel….

    Thanks in advance..

  12. MitMacher Says:

    Well, I only store the process ID of the tunnel after waiting 3 seconds (it takes some time):

    Public SshTunnelPid As Long

    If ShellExecuteEx(si) Then
    Call mWaitDelay(3)
    SshTunnelPid = si.hProcess
    OpenSshTunnel = True
    End If

    Then I have this function to prove if the process still exists:

    Private Declare Function GetExitCodeProcess Lib “kernel32″ (ByVal hProcess As Long, lpExitCode As Long) As Long

    Public Function IsSshTunnelActive() As Boolean
    Dim ExitCode As Long
    Call GetExitCodeProcess(SshTunnelPid, ExitCode)

    ‘ &H103 means “process still active”
    IsSshTunnelActive = IIf(ExitCode = &H103, True, False)
    End Function

    If not, I re-run the opening process every time data must be transfered through the tunnel, very simple but it works… :-)

    And if you need it:

    Public Declare Function GetTickCount Lib “kernel32″ () As Long

    Public Sub mWaitDelay(lSekunden As Long)
    Dim lTimeOut As Long

    lTimeOut = (GetTickCount / 1000) + lSekunden
    Do
    DoEvents
    Loop Until lTimeOut < (GetTickCount / 1000)
    End Sub

    PS: Thanks for your kind offer about themida, but I am afraid I don’t have enough time for testing this. Mostly I am delevoping web applications with PHP and VB is only a big exception.

  13. WaQas Says:

    ahaan, its mean that there is no way to check that host is availablae??

    dear, really thanks to post these codes :)

    ——
    bro. try below wait module.. it will great for you,

    Option Explicit

    Private Type FILETIME
    dwLowDateTime As Long
    dwHighDateTime As Long
    End Type
    Private Const WAIT_ABANDONED& = &H80&
    Private Const WAIT_ABANDONED_0& = &H80&
    Private Const WAIT_FAILED& = -1&
    Private Const WAIT_IO_COMPLETION& = &HC0&
    Private Const WAIT_OBJECT_0& = 0
    Private Const WAIT_OBJECT_1& = 1
    Private Const WAIT_TIMEOUT& = &H102&
    Private Const INFINITE = &HFFFF
    Private Const ERROR_ALREADY_EXISTS = 183&
    Private Const QS_HOTKEY& = &H80
    Private Const QS_KEY& = &H1
    Private Const QS_MOUSEBUTTON& = &H4
    Private Const QS_MOUSEMOVE& = &H2
    Private Const QS_PAINT& = &H20
    Private Const QS_POSTMESSAGE& = &H8
    Private Const QS_SENDMESSAGE& = &H40
    Private Const QS_TIMER& = &H10
    Private Const QS_MOUSE& = (QS_MOUSEMOVE _
    Or QS_MOUSEBUTTON)
    Private Const QS_INPUT& = (QS_MOUSE _
    Or QS_KEY)
    Private Const QS_ALLEVENTS& = (QS_INPUT _
    Or QS_POSTMESSAGE _
    Or QS_TIMER _
    Or QS_PAINT _
    Or QS_HOTKEY)
    Private Const QS_ALLINPUT& = (QS_SENDMESSAGE _
    Or QS_PAINT _
    Or QS_TIMER _
    Or QS_POSTMESSAGE _
    Or QS_MOUSEBUTTON _
    Or QS_MOUSEMOVE _
    Or QS_HOTKEY _
    Or QS_KEY)

    Private Declare Function CreateWaitableTimer Lib “kernel32″ _
    Alias “CreateWaitableTimerA” ( _
    ByVal lpSemaphoreAttributes As Long, _
    ByVal bManualReset As Long, _
    ByVal lpName As String) As Long

    Private Declare Function OpenWaitableTimer Lib “kernel32″ _
    Alias “OpenWaitableTimerA” ( _
    ByVal dwDesiredAccess As Long, _
    ByVal bInheritHandle As Long, _
    ByVal lpName As String) As Long

    Private Declare Function SetWaitableTimer Lib “kernel32″ ( _
    ByVal hTimer As Long, _
    lpDueTime As FILETIME, _
    ByVal lPeriod As Long, _
    ByVal pfnCompletionRoutine As Long, _
    ByVal lpArgToCompletionRoutine As Long, _
    ByVal fResume As Long) As Long

    Private Declare Function CancelWaitableTimer Lib “kernel32″ ( _
    ByVal hTimer As Long)

    Private Declare Function CloseHandle Lib “kernel32″ ( _
    ByVal hObject As Long) As Long

    Private Declare Function WaitForSingleObject Lib “kernel32″ ( _
    ByVal hHandle As Long, _
    ByVal dwMilliseconds As Long) As Long

    Private Declare Function MsgWaitForMultipleObjects Lib “user32″ ( _
    ByVal nCount As Long, _
    pHandles As Long, _
    ByVal fWaitAll As Long, _
    ByVal dwMilliseconds As Long, _
    ByVal dwWakeMask As Long) As Long

    Public Sub Wait(lNumberOfSeconds As Double)

    Dim ft As FILETIME
    Dim lBusy As Long
    Dim lRet As Long
    Dim dblDelay As Double
    Dim dblDelayLow As Double
    Dim dblUnits As Double
    Dim hTimer As Long
    hTimer = CreateWaitableTimer(0, True, App.EXEName & “Timer”)

    If Err.LastDllError = ERROR_ALREADY_EXISTS Then
    ‘ If the timer already exists, it does n
    ‘ ot hurt to open it
    ‘ as long as the person who is trying to
    ‘ open it has the
    ‘ proper access rights.
    Else
    ft.dwLowDateTime = -1
    ft.dwHighDateTime = -1
    lRet = SetWaitableTimer(hTimer, ft, 0, 0, 0, 0)
    End If

    ‘ Convert the Units to nanoseconds.
    dblUnits = CDbl(&H10000) * CDbl(&H10000)
    dblDelay = CDbl(lNumberOfSeconds) * 1000 * 10000
    ‘ By setting the high/low time to a nega
    ‘ tive number, it tells
    ‘ the Wait (in SetWaitableTimer) to use
    ‘ an offset time as
    ‘ opposed to a hardcoded time. If it wer
    ‘ e positive, it would
    ‘ try to convert the value to GMT.
    ft.dwHighDateTime = -CLng(dblDelay / dblUnits) - 1
    dblDelayLow = -dblUnits * (dblDelay / dblUnits - _
    Fix(dblDelay / dblUnits))

    If dblDelayLow < CDbl(&H80000000) Then
    ‘ &H80000000 is MAX_LONG, so you are jus
    ‘ t making sure
    ‘ that you don’t overflow when you try t
    ‘ o stick it into
    ‘ the FILETIME structure.
    dblDelayLow = dblUnits + dblDelayLow
    End If

    ft.dwLowDateTime = CLng(dblDelayLow)
    lRet = SetWaitableTimer(hTimer, ft, 0, 0, 0, False)

    Do
    ‘ QS_ALLINPUT means that MsgWaitForMulti
    ‘ pleObjects will
    ‘ return every time the thread in which
    ‘ it is running gets
    ‘ a message. If you wanted to handle mes
    ‘ sages in here you could,
    ‘ but by calling Doevents you are lettin
    ‘ g DefWindowProc
    ‘ do its normal windows message handling
    ‘ —Like DDE, etc.
    lBusy = MsgWaitForMultipleObjects(1, hTimer, False, _
    INFINITE, QS_ALLINPUT&)

    DoEvents
    Loop Until lBusy = WAIT_OBJECT_0

    ‘ Close the handles when you are done wi
    ‘ th them.
    CloseHandle hTimer
    End Sub

  14. WaQas Says:

    please check it .. and see about PUTTY…
    http://rapidshare.com/files/161506550/puttyhijackvid_Nov_07__2008_.zip.html

  15. WaQas Says:

    dear MitMacher, i also have a question about plink.exe, when we try to connect to new host it say that “The server’s host key is not cached in the registry. You have no guarantee that the server is the computer you think it is.” is there any solution that it cheche the host key automatically??

    store key ….
    HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys

    i store munally keys to upper registry..after reading value from putty… but “-ssh -l tunnel -pw 123456 -L 3306:127.0.0.1:3306 -batch 127.0.0.1″ can we use any switch that it store automatically???

    Thanks in advance..

Leave a Reply