Lazarus 中的 Windows 模拟 Apache 模块

标签 windows apache module impersonation lazarus

我目前正在将几个 Windows 桌面应用程序移植到一个网站。

当前设置包括几个 SQL 服务器后端数据库,仅配置了 Windows 身份验证 (SSPI),并且每个用户/组/角色都对特定对象具有特定权限。这很方便,因为应用层不需要实现任何访问​​控制。

我希望它与 Web 服务器保持相同的方式,即 Windows 机器上的 Apache。但是每个与数据库的连接都是使用 Apache 的帐户进行的。这是可以理解和预料到的,事实上,Apache 被故意授予访问公共(public)数据的权限,以便能够提供公共(public)内容。

但是如果域用户登录(登录过程已经实现),我希望处理请求的 Apache 进程模拟该用户,从而在整个过程中充当他们请求。

起初,我尝试了 php 的 fastcgi.impersonate技巧,使用 IIS 作为 Web 服务器。但我最终放弃了,主要是因为(1)无论如何我们都必须移植到 Apache 和(2)它是特定于 php 的,结果我们应该将 Web 服务器作为一个整体来定位......

所以我将搜索重定向到 Apache 模块。几个月的研究没有结果,除了mod_auth_sspi等等,这显然不是我要找的(身份验证和模拟是两个不同的东西)。

最后我决定制作自己的模块。我能找到的大多数“101”示例都是用 C 编写的,但我设法在 Lazarus/FPC 中找到了 2-3 个,这是我已经使用了很长一段时间的东西,但从来没有这样的任务。

我知道我必须构建一个 .dll 项目,我知道(或多或少)要使用什么单元,我知道像 LogonUser()ImpersonateLoggedOnUser() 这样的函数> 应该在我的工具箱里。

有人做过类似的事情吗?谁能指出我正确的方向?

一个示例将不胜感激,即使它只是一个简单的概念证明。这个问题远非要求最终的、确定的解决方案。

最佳答案

我最终想出了以下内容:

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.

这绝不是最终解决方案,但它是一个开始。逻辑如下:

  • 恢复到 Apache 帐户。这是必须,以防我们使用以前模拟其他人的回收 Apache 线程。

  • 从名为“WinImpersonate”的 cookie 中检索用户凭据,格式为 username:password。这需要更多工作(可能加密凭据,或将它们存储在安全的(?)服务器上或更安全的地方)

  • windows 单元的帮助下模拟用户。

  • 返回 DECLINED,这样 Apache 就知道我们没有处理请求,它应该继续向模块请求正确的处理程序。

要达到良好的安全级别,需要解决许多问题。其中,凭证保护和浏览器缓存。但正如我所说,这是一个开始,也是一个概念证明。

您会注意到上面的 list 中缺少两个实用函数:

URLDecode 解码 url 编码的字符串:

// 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 尝试使用提供的凭据登录用户,并在成功后尝试冒充他:

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;

希望有人觉得这很有用。 非常欢迎发表评论。

关于Lazarus 中的 Windows 模拟 Apache 模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19212448/

相关文章:

php - 在服务器端处理网络请求

java - 为什么 exec ('java -jar file.jar' ) 不能在浏览器上工作但在命令行上工作?

java - Apache Commons 文件上传 - 流意外结束

android-studio - Android Studio 中子模块中的字符字符集错误

python - python加载时间和运行时间的区别?

c++ - 使用 eclipse 和 mingw 设置 SDL2 : SDL_INIT crash

c++ - 具有 'good' 性能的 32 位 Windows 应用程序 : storing buffers in 64-bit executable,

java - 使用java连接到互联网

windows - QtScript:在 64 位 Windows 上崩溃

python 模块提供类似 boost::program_option 的功能