Форум: "Прочее";
Текущий архив: 2014.09.28;
Скачать: [xml.tar.bz2];
ВнизДоменная авторизация Найти похожие ветки
← →
Mikki (2013-10-01 18:16) [0]Имеем Дельфи клиент + БД.
В БД хранится список пользователей, своя таблица.
Хочется прикрутить windows авторизацию. То есть, если компьютер в домене, запуск из под пользователя - чтобы программа не спрашивала логин пароль.
Первоначальная мысль - в своей таблице также записать имя пользователя доменного, например: mydomen\myuser и если пройдет авторизация для этого доменного юзера - присвоить пользователю права, соответствующие id вычисленного пользователя.
Но вот дальше.. куда копать? Что передавать с клиента, как проверять валидность на сервере.. и в каком порядке вообще осуществлять коннект.. наверняка у кого то есть опыт - поделитесь плиз
← →
brother © (2013-10-01 18:35) [1]> Что передавать с клиента
id, имя mydomen\myuser, хэш этих данных...
> как проверять валидность на сервере
=
> и в каком порядке вообще осуществлять коннект
это о чем?
← →
Необычный порошок (2013-10-01 18:55) [2]зачем доменная, если права будут раздаваться вручную?
и зачем раздавать права в вручную, если юзер прошел доменную авторизацию и права можно дать доменным ролям/группам/пользователям?
← →
brother © (2013-10-01 19:59) [3]я так понимаю, идет речь о правах на доступа к таблицам БД
← →
Mikki (2013-10-01 20:15) [4]
> id, имя mydomen\myuser, хэш этих данных..
что такое Id?
Хм, допустим я передам mydomen\myuser, но что мешает передать имя любого пользователя?
> зачем доменная, если права будут раздаваться вручную?
чтобы пользователь не вводил логин пароль
> и зачем раздавать права в вручную, если юзер прошел доменную
> авторизацию и права можно дать доменным ролям/группам/пользователям?
это уже другой вопрос. В любом случае, своя система прав в программе есть. Связывать её с доменными правами - следующий, возможно, этап. Пока хотя бы авторизацию понять.
← →
vuk © (2013-10-01 20:44) [5]to Mikki (01.10.13 18:16):
> Хочется прикрутить windows авторизацию.
Что за БД-то хоть? А то кой-где такая автоматическая авторизация уже встроена.
← →
Необычный Порошок (2013-10-01 20:45) [6]чтобы пользователь не вводил логин пароль
Он и не будет вводить пароль если будет доменная.
это уже другой вопрос. В любом случае, своя система прав в программе есть. Связывать её с доменными правами - следующий, возможно, этап.
своя система прав лишняя при доменной авторизации.
Пока хотя бы авторизацию понять.
Для начала надо дать понять аудитории что у тебя за сервер.
Так как ответ на твой сокровенный вопрос напрямую зависит от этого.
← →
vuk © (2013-10-01 20:52) [7]to Необычный Порошок (01.10.13 20:45) [6]:
> своя система прав лишняя при доменной авторизации.
Не лишняя. Своя система прав может реализовывать отличную от реализованной в сервере БД логику. К примеру, построчные права и т.д.
← →
Необычный Порошок (2013-10-01 20:58) [8]да да.
именно ради rls автор и задумал смешать свое и доменное.
а нам всем только показалось что главная причина в том, что он не хочет чтобы юзер вводил пароль.
← →
vuk © (2013-10-01 21:03) [9]Причин для написания своей системы безопасности может быть много. И это вообще мало зависит от того, какая модель аутентификации используется для соединения с сервером.
← →
Необычный Порошок (2013-10-01 21:09) [10]у него все именно так и будет. главное верить.
← →
vuk © (2013-10-01 21:14) [11]to Необычный Порошок (01.10.13 21:09) [10]
> именно так и будет
Так - это как?
← →
Необычный Порошок (2013-10-01 21:17) [12]да так и будет.
человек, который:
- не догадывается, что ответ на его вопрос зависит от его сервера
- не знает как использовать доменную авторизацию
будет заморочен вопросом роу левел секьюрити
← →
vuk © (2013-10-01 21:28) [13]Еще раз повторяю, причины могут быть и другие, помимо rls.
← →
Необычный Порошок (2013-10-01 21:32) [14]яж разве спорю.
например желание чтобы юзер не вводил пароль - чем не и другая причина
← →
Mikki (2013-10-01 22:19) [15]vuk, хотелось бы понять алгоритм / действия, не опираясь на базу MS SQL.
Конечно, хотелось бы опыта человека, который прикручивал AD авторизацию в приложении windows на delphi.
По сути, как я понимаю, нужно на сервер отправить некую штуку, основанную на логине/пароле доменного юзера, а сервер эту штуку смог проверить. В чистом виде логин пароль передавать нельзя по понятным причинам, да и взять его клиентской программе неоткуда.
Насчет первого коннекта - вполне можно, чтобы программа соединилась под каким-то дефолтным прошитым логином паролем, с урезанными правами и возможностью только вызывать хранимку аля "Loging(..)"
← →
Необычный Порошок (2013-10-01 22:21) [16]ну и как тебе это, вук?
я был безнадежно далек от истины?
← →
Anatoly Podgoretsky © (2013-10-01 22:26) [17]> Mikki (01.10.2013 22:19:15) [15]
В MS SQL встроенная и не отключается.
← →
Необычный Порошок (2013-10-01 22:30) [18]Насчет первого коннекта - вполне можно, чтобы программа соединилась под каким-то дефолтным прошитым логином паролем, с урезанными правами и возможностью только вызывать хранимку аля "Loging(..)"
ну вот и все.
и нет у тебя никакой доменной авторизации.
будь хранимка хоть аля, хоть траляля.
PS
TAdoConnection
свойство connectionstring
тыкнув в него откроешь мастер настройки подключения.
и будет тебе там доменная авторизация.
← →
DVM © (2013-10-01 22:40) [19]
> Необычный Порошок (01.10.13 22:30) [18]
> TAdoConnection
> свойство connectionstring
> тыкнув в него откроешь мастер настройки подключения.
> и будет тебе там доменная авторизация.
Не будет, если СУБД не поддерживает. Ему же для абстрактной СУБД в вакууме надо.
← →
DVM © (2013-10-01 22:46) [20]
> Mikki (01.10.13 22:19) [15]
> хотелось бы понять алгоритм / действия, не опираясь на базу
> MS SQL.
>
> Конечно, хотелось бы опыта человека, который прикручивал
> AD авторизацию в приложении windows на delphi.
Читай про NTLM например
← →
Необычный Порошок (2013-10-01 23:00) [21]Не будет, если СУБД не поддерживает. Ему же для абстрактной СУБД в вакууме надо.
будет и абстрактная поддерживать.
немного абстрактно, правда, но будет.
← →
DVM © (2013-10-01 23:04) [22]
> Необычный Порошок (01.10.13 23:00) [21]
> будет и абстрактная поддерживать.
каким образом? Сервер субд при такой аутентификации должен сам обращаться в каталог, если он это не умеет, то кто?
← →
Inovet © (2013-10-01 23:18) [23]> [22] DVM © (01.10.13 23:04)
Он будет обращаться
> [21] Необычный Порошок (01.10.13 23:00)
> немного абстрактно
← →
Необычный Порошок (2013-10-01 23:19) [24]абстрактным образом. абстракным.
у нас же кроме абстрактного сервера еще и асбстрактная библиотека доступа к серверу.
в вакууме.
но думаю что через пару дней мы и про нее узнаем что-то конкретное тоже.
← →
Mikki (2013-10-02 00:03) [25]
> Читай про NTLM например
это слишком расплывчато. Я могу еще вспомнить слова "читай про kerberos". Так можно читать следующий год весь(
Интересует реальный опыт, кто может такое делал или видел, может дать конкретное направление. Можно вообще рассмотреть даже без БД - например, есть самописный сервер и есть самописный клиент. Как серверу сделать авторизацию клиента по AD?
Подозреваю, что это достаточно сложно, поэтому вопрос, наверное, сводится к тому - есть ли какие-то готовые инструменты / API или их нету. Желательно в контексте прикручиванию к дельфи.
← →
DVM © (2013-10-02 10:13) [26]
> Mikki (02.10.13 00:03) [25]
> это слишком расплывчато. Я могу еще вспомнить слова "читай
> про kerberos". Так можно читать следующий год весь(
Читать тебе все равно придется. Нет волшебного компонента "NTLM аутентификация в абстрактной СУБД". Тебе уже неоднократно намекали, что хорошо бы назвать СУБД, ибо в большинстве мне известных такая аутентификация имеется, чаще всего она фигурирует там под именем Trust Authentication.
Оракл - да, MSSQL - да, Firebird - да, PostgreSQL -да.
← →
Anatoly Podgoretsky © (2013-10-02 11:13) [27]Раз не признается, то у него MS SQL
← →
Eraser © (2013-10-02 17:12) [28]
> Mikki (02.10.13 00:03) [25]
если безотносительно к СУБД, то такие компоненты в делифи есть, более того, в стандартной поставке.
← →
Mikki (2013-10-02 17:32) [29]Eraser, ясно, спасибо большое.
← →
sniknik © (2013-10-02 22:43) [30]когда винда стартует программу она определяет "токен" (паспорт) процесса... оттуда можно узнать из под какого юзера запущена, в каких группах он состоит (доменные тоже определяются)... возможно еще что-то но этого хватит для "авторизации", просто прочитай в клиенте и отправь на сервер, а тот пусть в ответ список прав со всех групп/юзера...
понадобятся функции
OpenThreadToken
OpenProcessToken
GetTokenInformation
ну и наверное
ConvertSidToStringSid
т.к. там все сидами представлено, чтобы было наглядно для админа твоей системы нужно чтобы группы имели привычный вид (а оперировать в программе лучше тоже сид-ами).
← →
DVM © (2013-10-02 23:01) [31]
> sniknik © (02.10.13 22:43) [30]
> возможно еще что-то но этого хватит для "авторизации"
Для авторизации то хватит, но не хватит для аутентификации. Нужна полноценная многоходовая проверка подлинности пользователя контроллером домена на основании присланных пользователем данных. И обращаться к контроллеру должен сервер, к которому хочет получить доступ пользователь.
← →
Mikki (2013-10-02 23:39) [32]sniknik, спасибо! Но тут DVM правду говорит, в описанном тобой варианте это все будет держаться на честном слове, а по хорошему серверу нужно как-то проверить, что присылаемые данные - верные. Иначе зная API можно подставить доменное имя любого компа (ну или там sid)
← →
Дмитрий СС (2013-10-03 01:24) [33]Вот заголовочный файл для функций, которые позволяют это делать.
unit uSSPI;
interface
uses Windows, SysUtils;
type
// Secur32.dll function prototypes
TQueryPackageInfo = function(PackageName : PChar;
var PackageInfo : pointer) : integer; stdcall;
TFreeContextBuffer = function(pBuffer : pointer) : integer; stdcall;
TFreeCredentialsHandle = function(var hCred : Int64) : integer; stdcall;
TDeleteSecurityContext = function(var hCred : Int64) : integer; stdcall;
TAcquireCredentialsHandle = function(pszPrincipal : PChar;
pszPackage : PChar;
fCredentialUse : DWORD;
pvLogonID : DWORD;
pAuthData : pointer;
pGetKeyFn : DWORD;
pvGetKeyArgument : pointer;
var phCredential : Int64;
var ptsExpiry : TTimeStamp) : integer; stdcall;
TInitializeSecurityContext = function(var phCredential : Int64;
phContext : pointer;
pszTargetName : PChar;
fContextReq : DWORD;
Reserved1 : DWORD;
TargetDataRep : DWORD;
pInput : pointer;
Reserved2 : DWORD;
var phNewContext : Int64;
pOutput : pointer;
var pfContextAttr : Int64;
var ptsExpiry : TTimeStamp) : integer; stdcall;
TAcceptSecurityContext = function(var phCredential : Int64;
phContext : pointer;
pInput : pointer;
fContextReq : DWORD;
TargetDataRep : DWORD;
var phNewContext : Int64;
pOutput : pointer;
var pfContextAttr : Int64;
var ptsExpiry : TTimeStamp) : integer; stdcall;
TQuerySecurityContextToken = function (phContext : pointer; var Token: THandle): Integer; stdcall;
// AcquireCredentialsHandle() Internal Structure
PAuthIdentity = ^TAuthIdentity;
TAuthIdentity = packed record
User : PChar;
UserLength : DWORD;
Domain : PChar;
DomainLength : DWORD;
Password : PChar;
PasswordLength : DWORD;
Flags : DWORD;
end;
// QuerySecurityPackageInfo Internal Structure
PSecPkgInfo = ^TSecPkgInfo;
TSecPkgInfo = packed record
Capabilities : DWORD;
Version : WORD;
RPCID : WORD;
MaxToken : DWORD;
Name : PChar;
Comment : PChar;
end;
// InitializeSecurityContext() Internal structure
PSecBuffer = ^TSecBuffer;
TSecBuffer = packed record
cbBuffer : DWORD;
BufferType : DWORD;
pvBuffer : pointer;
end;
PSecBuffDesc = ^TSecBuffDesc;
TSecBuffDesc = packed record
ulVersion : DWORD;
cBuffers : DWORD;
pBuffers : PSecBuffer;
end;
var
FQueryPackageInfo : TQueryPackageInfo;
FFreeContextBuffer : TFreeContextBuffer;
FAcquireCredHandle : TAcquireCredentialsHandle;
FFreeCredHandle : TFreeCredentialsHandle;
FInitSecContext : TInitializeSecurityContext;
FDelSecContext : TDeleteSecurityContext;
FAcceptSecContext : TAcceptSecurityContext;
QuerySecurityContextToken: TQuerySecurityContextToken;
FSecHandle : THandle;
implementation
initialization
FSecHandle := LoadLibrary("SECUR32.DLL");
FQueryPackageInfo := nil;
FFreeContextBuffer := nil;
FAcquireCredHandle := nil;
FFreeCredHandle := nil;
FInitSecContext := nil;
FDelSecContext := nil;
FAcceptSecContext := nil;
if FSecHandle <> 0 then begin
@FQueryPackageInfo := GetProcAddress(FSecHandle,"QuerySecurityPackageInfoW");
@FFreeContextBuffer := GetProcAddress(FSecHandle,"FreeContextBuffer");
@FAcquireCredHandle := GetProcAddress(FSecHandle,"AcquireCredentialsHandleW");
@FInitSecContext := GetProcAddress(FSecHandle,"InitializeSecurityContextW");
@FFreeCredHandle := GetProcAddress(FSecHandle,"FreeCredentialsHandle");
@FDelSecContext := GetProcAddress(FSecHandle,"DeleteSecurityContext");
@FAcceptSecContext := GetProcAddress(FSecHandle,"AcceptSecurityContext");
@QuerySecurityContextToken := GetProcAddress(FSecHandle,"QuerySecurityContextToken");
end else begin
raise Exception.Create("Невозможно загрузить SECUR32.DLL.");
end;
finalization
if FSecHandle <> 0 then
FreeLibrary(FSecHandle);
end.
← →
Дмитрий СС (2013-10-03 01:29) [34]Вот тут можно наковырять пример клиента http://yadi.sk/d/C890RkuBAJR99
Сервер, к сожалению, не сохранился =(
← →
Eraser © (2013-10-03 03:32) [35]Вот код со стороны сервера, в на стороне клиента почти тоже самое.
function XXX.ServerHandshakeAuth(AContext: TIdContext;
ASessionKey: THandle; var AllowedRights: DWORD): Boolean;
var
hContext: CtxtHandle;
phTempContext: PCtxtHandle;
tsExpires: TimeStamp;
pszPackage: PSecWChar;
ss, CrendHandleStat: SECURITY_STATUS;
bFirstPass: Boolean;
pbTokenBuf: System.PByte;
lSize, lAttributes: ULONG;
msAuth: TMemoryStream;
// Declare in and out buffers.
secBufferOut, secBufferIn: array [0..0] of SecBuffer;
secBufDescriptorOut, secBufDescriptorIn: SecBufferDesc;
hCredentials: CredHandle;
pSD: JwaWinNT.PSECURITY_DESCRIPTOR;
hToken: THandle;
begin
Result := False;
bFirstPass := True;
hToken := 0;
AllowedRights := 0;
FillChar(hCredentials, SizeOf(hCredentials), 0);
ss := SEC_I_CONTINUE_NEEDED;
lAttributes := ISC_REQ_STREAM;
msAuth := TMemoryStream.Create;
try
pszPackage := PSecWChar(PChar(NTLMSP_NAME));
EnterCriticalSection(csCrypto);
try
CrendHandleStat := AcquireCredentialsHandle(nil,
pszPackage,
SECPKG_CRED_BOTH,
nil, nil, nil, nil,
@hCredentials, tsExpires);
if CrendHandleStat <> SEC_E_OK then
begin
Exit;
end;
finally
LeaveCriticalSection(csCrypto);
end;
while ss = SEC_I_CONTINUE_NEEDED do
begin
msAuth.Clear;
lSize := AContext.Connection.IOHandler.ReadLongInt;
AContext.Connection.IOHandler.ReadStream(msAuth, lSize);
msAuth.Position := 0;
//DecryptData(ASessionKey, msAuth);
GetMem(pbTokenBuf, lSize);
msAuth.Read(pbTokenBuf^, msAuth.Size);
// Point "In Buffer" to blob.
secBufferIn[0].BufferType := SECBUFFER_TOKEN;
secBufferIn[0].cbBuffer := lSize;
secBufferIn[0].pvBuffer := pbTokenBuf;
// Point "In" BufDesc to in buffer.
secBufDescriptorIn.cBuffers := 1;
secBufDescriptorIn.pBuffers := @secBufferIn;
secBufDescriptorIn.ulVersion := SECBUFFER_VERSION;
// Set up out buffer,
// (The SSPI will be allocating buffers for us).
secBufferOut[0].BufferType := SECBUFFER_TOKEN;
secBufferOut[0].cbBuffer := 0;
secBufferOut[0].pvBuffer := nil;
// Point "Out" Bufdesc to out buffer.
secBufDescriptorOut.cBuffers := 1;
secBufDescriptorOut.pBuffers := @secBufferOut;
secBufDescriptorOut.ulVersion := SECBUFFER_VERSION;
if bFirstPass then
phTempContext := nil
else
phTempContext := @hContext;
EnterCriticalSection(csCrypto);
try
ss := AcceptSecurityContext(@hCredentials,
phTempContext,
@secBufDescriptorIn,
lAttributes or ISC_REQ_ALLOCATE_MEMORY,
SECURITY_NETWORK_DREP,
@hContext,
@secBufDescriptorOut,
lAttributes,
nil);
finally
LeaveCriticalSection(csCrypto);
end;
// No longer first pass through the loop
bFirstPass := False;
// Was a blob output? If so, send it.
if secBufferOut[0].cbBuffer <> 0 then
begin
// Client communication!
msAuth.Clear;
msAuth.Write(secBufferOut[0].pvBuffer^, secBufferOut[0].cbBuffer);
msAuth.Position := 0;
AContext.Connection.IOHandler.Write(Integer(msAuth.Size));
AContext.Connection.IOHandler.Write(msAuth, msAuth.Size);
EnterCriticalSection(csCrypto);
try
// Free out buffer.
FreeContextBuffer(secBufferOut[0].pvBuffer);
finally
LeaveCriticalSection(csCrypto);
end;
end;
if pbTokenBuf <> nil then
FreeMem(pbTokenBuf);
end;
if ss <> SEC_E_OK then
Exit;
pSD := GetSD;
if pSD = nil then
begin
Exit;
end;
EnterCriticalSection(csCrypto);
try
ss := QuerySecurityContextToken(@hContext, @hToken);
if ss = SEC_E_OK then
begin
try
// Тут проверка результата. см. ф-ю AccessCheck
Result := ZZZZZZZZZ(hToken, pSD, AllowedRights);
finally
if hToken <> 0 then
CloseHandle(hToken);
end;
end;
finally
LeaveCriticalSection(csCrypto);
end;
finally
EnterCriticalSection(csCrypto);
try
DeleteSecurityContext(@hContext);
FreeCredentialsHandle(@hCredentials);
finally
LeaveCriticalSection(csCrypto);
end;
msAuth.Free;
end;
end;
← →
sniknik © (2013-10-03 08:23) [36]> нужно как-то проверить, что присылаемые данные - верные.
> доменное имя любого компа (ну или там sid)
не компа, а юзера/его групп, на сервере проверка будет почти "автоматом", тебе же там придется делать соответствие сид -> имя при чтении/задании прав (админы прописывают у имен, ты работаешь/присылаешь сиды), а это получится ТОЛЬКО если программа/юзер в домене, сиды уникальны (есть еще предопределенные, локальные, стандартные везде, но не доменные), т.е. даже одноименные например юзер сделан локально и в домене... не найдет локального.
в общем не все так просто для "честного слова" даже зная АПИ (клиент твой? или другие пишут, зачем АПИ в смысле). вот если "в разрыв" где нибудь через прокси пустить, там чужое подставить можно, да... но во первых, тебе насколько серьезную защиту нужно? будут "ломать" или "от дурака"? а то нужна калитка в сад, а пытаются сейфовую дверь банка спроектировать...
ну и во вторых можно же пустить авторизацию по шифрованному каналу.
p.s. но вообще ладно, не подходит так не подходит.
← →
sniknik © (2013-10-03 10:36) [37]+
> Для авторизации то хватит, но не хватит для аутентификации.
для аутентификации вроде вот это подойдет
http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1341
помнил что есть статья, когда свое делал натыкался, но пока нашел... мне по какой то причине, не помню, не подошло. (вроде из-за того что тут аутентификация на сервере по данным с клиента, а мне нужно было идентифицировать именно клиента, на локальной машине... права в программе давались именно на него, просто хранятся на сервере)
← →
DVM © (2013-10-03 10:45) [38]
> sniknik © (03.10.13 10:36) [37]
> помнил что есть статья, когда свое делал натыкался, но пока
> нашел...
вот и я про нее подумал сразу, помню Набережных автор, а где видел как называется забыл совсем.
← →
Петрович (2014-02-17 23:40) [39]Возможно я не совсем понял что хочет автор темы, но если о
> а по хорошему серверу нужно как-то проверить, что присылаемые
> данные - верные.
то например в MSSQL есть системная функция SYSTEM_USER возвращающая имя пользователя "подлогиненного" к серверу. Причем, если использовалась Windows-авторизация то SYSTEM_USER вернет DOMAIN\user_login_name а если серверная, то имя серверного юзверя.
Вот тебе и карты в руки - хочешь сравнивай что тебе прислали, хочешь вообще опирайся на SYSTEM_USER и не смотри на то что тебе присылает хитрый враг прикидывающийся юзверем.
Правда, здесь есть одно НО. Опираться на DOMAIN\user_login_name вообще-то не хорошо. Поскольку "DOMAIN\Vasya" сегодня, и "DOMAIN\Vasya" завтра, это не всегда один и тот же юзверь. Правильнее работать с SID пользователя.
Например можно взгянуть сюда http://habrahabr.ru/post/111978/
Страницы: 1 вся ветка
Форум: "Прочее";
Текущий архив: 2014.09.28;
Скачать: [xml.tar.bz2];
Память: 0.59 MB
Время: 0.002 c