视窗模拟Apache模块的拉撒路 [英] Windows Impersonation Apache Module in Lazarus
问题描述
我目前在移植几个Windows桌面应用程序到一个网站的过程。
I currently am in the process of porting several Windows desktop applications to a single web site.
目前的设置包括多个SQL服务器后端数据库,使用Windows身份验证(SSPI)只,每个用户/组/角色对特定对象的特定的权利配置。这是方便的,因为应用层不必实现任何访问控制。
The current setup includes several SQL server backend databases, configured with Windows Authentication (SSPI) only, and every user/group/role has specific rights on specific objects. Which is convenient, because the application layer doesn't have to implement any access control.
我想以同样的方式把它与Web服务器,Apache的Windows机器上。但对数据库的每一个连接正在使用Apache的帐户进行。这是可以理解和预期,实际上Apache是故意给访问公共数据,才能够提供公共的内容。
I'd like to keep it the same way with the web server, an Apache on a Windows machine. But every connection to the databases is being made using Apache's account. That's understandable and expected, in fact Apache is deliberately given access to public data, to be able to deliver public content.
但在情况下,域用户登录(登录过程已经实现)我想的Apache进程处理请求的冒充用户,从而在整个过程中充当他们请求。
But in case a domain user logs in (the login process is already implemented) I'd like the Apache process that handles the request to impersonate that user, and thus act as them during the whole request.
起初,我试过php的<一个href=\"http://www.iis.net/learn/application-frameworks/install-and-configure-php-applications-on-iis/using-fastcgi-to-host-php-applications-on-iis\"相对=nofollow> fastcgi.impersonate 伎俩,使用IIS作为Web服务器。但是,我最终放弃了上,主要是因为:(1)我们必须端口到Apache无论如何,(2)它是PHP特有的,事实证明,我们应该作为一个整体来针对Web服务器...
At first, I tried php's fastcgi.impersonate trick, using IIS as the web server. But I eventually gave up on that, mainly because (1) we had to port to Apache anyway and (2) it was php-specific, and it turned out we should be targeting the web server as a whole...
所以我重定向我的搜索Apache模块。几个月的研究没有给出水果,比 mod_auth_sspi 之类的,这显然ISN等吨什么我正在寻找(身份验证和模拟是两回事)。
So I redirected my search to Apache modules. Months of research gave no fruits, other than mod_auth_sspi and the like, which apparently isn't what I'm looking for (authentication and impersonation are two different things).
最后,我决定把我自己的模块。大多数的101的例子我能找到的都写在C,但我设法找到那些2-3中的拉撒路/ FPC ,这是我一直使用的是什么相当长一段时间,现在,但从来没有这样的事。
Finally I decided to make my own module. Most of the "101" examples I could find are written in C, but I managed to find 2-3 ones in Lazarus/FPC, which is what I've been using for quite a while now, but never for such a task.
我知道我必须建立一个.dll的项目,我知道(或多或少)使用什么单位,我知道像函数的LogonUser()
和 ImpersonateLoggedOnUser()
应该是我的工具箱。
I know I have to build a .dll project, I know (more or less) what units to use and I know functions like LogonUser()
and ImpersonateLoggedOnUser()
should be in my toolbox.
有没有人做过类似的事情?任何人都可以点我朝着正确的方向?
Has anyone done anything similar? Can anyone point me to the right direction?
一个例子是AP preciated,即使它的概念的一个简单证明。这个问题还远远没有要求一个最终的,最终的解决办法。
An example would be appreciated, even if it's a simple proof of concept. This question is far from asking for a final, definitive solution.
推荐答案
我终于想出了以下内容:
I eventually came up with the following:
library mod_winimpersonate;
{$mode objfpc}{$H+}
uses SysUtils, Windows, httpd, apr, Classes;
function DefaultHandler(r: Prequest_rec): Integer; cdecl;
Var
cookies:TStringList;
logindata,username,password:String;
p:Integer;
begin
RevertToSelf;
cookies:=TStringList.Create;
cookies.Delimiter:=';';
cookies.DelimitedText:=apr_table_get(r^.headers_in,'COOKIE');
logindata:=URLDecode(cookies.Values['WinImpersonate']);
If Length(logindata)>0 then
Begin
p:=Pos(':',logindata);
username:=LeftStr(logindata,p-1);
password:=RightStr(logindata,Length(logindata)-p);
ChangeLoggedInUser(username,password,'');
End;
Result:=DECLINED;
end;
procedure RegisterHooks(p: Papr_pool_t); cdecl;
begin
ap_hook_handler(@DefaultHandler, nil, nil, APR_HOOK_REALLY_FIRST);
end;
var
TheModule: module;
exports TheModule name 'winimpersonate_module';
begin
FillChar(TheModule, sizeof(TheModule), 0);
STANDARD20_MODULE_STUFF(TheModule);
with TheModule do
begin
name := 'mod_winimpersonate.dll';
register_hooks := @RegisterHooks;
end;
end.
这绝不是最终的解决方案,但它是一个开始。逻辑如下:
This is by no means a final solution, but it's a start. The logic is the following:
-
恢复到Apache的帐户。这是的一绝的,如果我们使用的是回收的阿帕奇跟帖说previously模仿别人。
Revert to the Apache account. This is a must, in case we are using a recycled Apache thread that previously impersonate someone else.
这是一个名为'WinImpersonatecookie中检索用户的凭据,在的形式,用户名:密码
。这需要更多的工作(也许加密凭据,或将其存放在安全的(?)放置在服务器或一些更安全)
Retrieve the user's credentials from a cookie named 'WinImpersonate', in the form of username:password
. This needs more work (maybe encrypt the credentials, or store them in a safe(?) place up on the server or something even more secure)
模拟用户,用窗口的帮助
单元。
返回 DECLINED
,以便Apache知道我们没有处理该请求,并应继续要求模块正确的处理程序。
Return DECLINED
, so Apache knows we didn't handle the request, and it should continue asking modules for the right handler.
有很多顾虑,才能为这个来实现一个体面的安全级别加以解决。其中,保护的凭据,以及浏览器缓存。但正如我所说,这是一个开始,还有一个概念证明。
There are many concerns to be addressed, in order for this to achieve a decent security level. Among others, the protection of the credentials, and the browser cache. But as I said, it's a start, as well as a proof of concept.
您会注意到有两个实用功能,从上面的清单中丢失:
You'll notice that there are two utility functions missing from the above listing:
URLDe code
德codeS一个url-CN codeD字符串:
URLDecode
decodes a url-encoded string:
// Convert URLEncoded string to utf8 string
function URLDecode(const s: String): String;
var
sAnsi: String;
sUtf8: String;
sWide: WideString;
i, len: Cardinal;
ESC: string[2];
CharCode: integer;
c: char;
begin
sAnsi := PChar(s);
SetLength(sUtf8, Length(sAnsi));
i := 1;
len := 1;
while (i <= Cardinal(Length(sAnsi))) do
begin
if (sAnsi[i] <> '%') then
begin
if (sAnsi[i] = '+') then c := ' ' else c := sAnsi[i];
sUtf8[len] := c;
Inc(len);
end
else
begin
Inc(i);
ESC := Copy(sAnsi, i, 2);
Inc(i, 1);
try
CharCode := StrToInt('$' + ESC);
c := Char(CharCode);
sUtf8[len] := c;
Inc(len);
except
end;
end;
Inc(i);
end;
Dec(len);
SetLength(sUtf8, len);
sWide := UTF8Decode(sUtf8);
len := Length(sWide);
Result := sWide;
end;
ChangeLoggedInUser
尝试登录使用提供的凭据的用户,并在成功尝试模仿他:
ChangeLoggedInUser
tries to login the user using the credentials supplied, and upon success it tries to impersonate him:
Function ChangeLoggedInUser(username, password, domain: string):Boolean;
var
creds: Cardinal;
begin
Result:=False;
try
if LogonUser(PChar(username)
,PChar(domain)
,PChar(password)
,LOGON32_LOGON_NETWORK_CLEARTEXT
,LOGON32_PROVIDER_DEFAULT
,creds
) then
begin
ImpersonateLoggedOnUser(creds);
Result:=True;
end;
finally
//wipe the memory for security
FillChar(username,SizeOf(username),#0);
FillChar(password,SizeOf(username),#0);
FillChar(domain,SizeOf(username),#0);
end; //try-finally
end;
希望有人认为这是有用的。注释是的超过欢迎的
这篇关于视窗模拟Apache模块的拉撒路的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!