Тема: VB.NET: Авторизация OpenVPN через учётку Windows
Есть в природе OpenVPN, умеющий при авторизации пользователей запускать скрипт для дополнительной авторизации. Под линуксы скриптов навалом, под Windows нашел одну портянку на vbs, которая к тому же не заработала на моей ОС(так как AD там нет и быть не может).
Как оказалось, всё гораздо проще можно реализовать воспользовавшись .NET. Под данную реализацию потребуется версия 3.5(и выше - требуется наличие System.DirectoryServices.AccountManagement.PrincipalContext).
Для тех кто не сталкивался с OpenVPN в кратце поясню. При авторизации пользователя(помимо сертификатов и пр.) можно запросить у пользователя логин-пароль и проверить его в системных учётках ОС. Для этого в конфиге указывается скрипт, которому при запуске в виде параметра передаётся имя файла. В файле(временный, на время авторизации) содержится всего две строки - логин и пароль. В результате работы скрипта должен вернуться код завершения 0(успешная авторизация) или 1(не удалось авторизоваться).
Протестировано на:
Windows XP Pro SP3, VS2008(.NET 3.5)
Windows 2003 Server Web Edition, VB.NET 2010 Express(.Net 4.0 - на сайте MS 2008-ую эспресс студию уже не нашел)
Приложение консольное. В проекте нужно добавить ссылку на System.DirectoryServices.AccountManagement.
Module WinAuth
Dim logfile As System.IO.StreamWriter
Dim pwdfile As System.IO.StreamReader
Function Main(ByVal cmdArgs() As String) As Integer
Dim instance As System.DirectoryServices.AccountManagement.PrincipalContext
Dim usrName As String
Dim usrPass As String
Dim returnValue As Boolean
logfile = My.Computer.FileSystem.OpenTextFileWriter("winauth.log", True, _
System.Text.Encoding.GetEncoding(1251))
logfile.AutoFlush = True
If cmdArgs.Count <> 1 Then
Echo("Wrong parametrs.")
Return 1
Else
Try
pwdfile = My.Computer.FileSystem.OpenTextFileReader(cmdArgs(0).ToString, _
System.Text.Encoding.GetEncoding(1251))
Catch ex As Exception
Echo(ex.Message.ToString)
Echo("pwdfile not found.")
End Try
Try
usrName = pwdfile.ReadLine.ToString
Catch ex As Exception
Echo(ex.Message.ToString)
Echo("No data in pwdfile.")
Return 1
End Try
Try
usrPass = pwdfile.ReadLine.ToString
Catch ex As Exception
Echo(ex.Message.ToString)
Echo("No password in pwdfile.")
Return 1
End Try
End If
pwdfile.Close()
instance = New System.DirectoryServices.AccountManagement.PrincipalContext( _
System.DirectoryServices.AccountManagement.ContextType.Machine, _
System.Environment.MachineName.ToString)
Try
returnValue = instance.ValidateCredentials(usrName, usrPass)
Catch ex As Exception
Echo(ex.Message.ToString)
Return 1
'Error Codes list for Microsoft technologies:
'http://www.symantec.com/business/support/index?page=content&id=TECH12638
'"HRESULT: 0x80070533" == "Logon failure: account currently disabled."
End Try
Select Case returnValue
Case False
Echo("Logon failure: username or password incorrect.")
Return 1
Case True
Echo("Logon is successful.")
Return 0
Case Else
Echo("Unknown Error?")
Return 1
End Select
End Function
Sub Echo(ByVal text)
Console.WriteLine(text)
Debug.Print(text)
logfile.WriteLine(Now() & ":> " & text)
End Sub
End Module
На всякий случай прокоментирую один момент:
instance = New System.DirectoryServices.AccountManagement.PrincipalContext( _
System.DirectoryServices.AccountManagement.ContextType.Machine, _
System.Environment.MachineName.ToString)
В качестве второго параметра доступно:
System.DirectoryServices.AccountManagement.ContextType.Machine
System.DirectoryServices.AccountManagement.ContextType.Domain
System.DirectoryServices.AccountManagement.ContextType.ApplicationDirectory
Вторым параметром идёт сервер для авторизации. На Win2003WE сработало "localhost", на WinXP по какой-то причине не прокатило - но работало по IP(127.0.0.1). Посмотрел чем богат дотнет, и поставил имя машины. Работает на обоих компьютерах.
Конечно, как минимум вывод сообщений лучше допилить под себя(или убрать совсем) - вариант выше в некоторой степени beta .
Надеюсь кому нибудь пригодится.
P.S. В найденном vbs-варианте так же проверялась принадлежность пользователя конкретной группе. В моём коде это не реализовано(так как пока такой задачи не стоит), но не думаю что в .NET с этим будут проблемы.