Windows Impersonation Apache Module in Lazarus -
i in process of porting several windows desktop applications single web site.
the current setup includes several sql server backend databases, configured windows authentication (sspi) only, , every user/group/role has specific rights on specific objects. convenient, because application layer doesn't have implement access control.
i'd keep same way web server, apache on windows machine. every connection databases being made using apache's account. that's understandable , expected, in fact apache deliberately given access public data, able deliver public content.
but in case domain user logs in (the login process implemented) i'd apache process handles request impersonate user, , act them during whole request.
at first, tried php's fastcgi.impersonate trick, using iis web server. gave on that, because (1) had port apache anyway , (2) php-specific, , turned out should targeting web server whole...
so redirected search apache modules. months of research gave no fruits, other mod_auth_sspi , like, apparently isn't i'm looking (authentication , impersonation 2 different things).
finally decided make own module. of "101" examples find written in c, managed find 2-3 ones in lazarus/fpc, i've been using quite while now, never such task.
i know have build .dll project, know (more or less) units use , know functions logonuser()
, impersonateloggedonuser()
should in toolbox.
has done similar? can point me right direction?
an example appreciated, if it's simple proof of concept. question far asking final, definitive solution.
i came 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 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); themodule begin name := 'mod_winimpersonate.dll'; register_hooks := @registerhooks; end; end.
this no means final solution, it's start. logic following:
revert apache account. a must, in case using recycled apache thread impersonate else.
retrieve user's credentials cookie named 'winimpersonate', in form of
username:password
. needs more work (maybe encrypt credentials, or store them in safe(?) place on server or more secure)impersonate user, of
windows
unit.return
declined
, apache knows didn't handle request, , should continue asking modules right handler.
there many concerns addressed, in order achieve decent security level. among others, protection of credentials, , browser cache. said, it's start, proof of concept.
you'll notice there 2 utility functions missing above listing:
urldecode
decodes url-encoded string:
// convert urlencoded string 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)); := 1; len := 1; while (i <= cardinal(length(sansi))) begin if (sansi[i] <> '%') begin if (sansi[i] = '+') 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
tries login user using credentials supplied, , upon success tries 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 ) begin impersonateloggedonuser(creds); result:=true; end; //wipe memory security fillchar(username,sizeof(username),#0); fillchar(password,sizeof(username),#0); fillchar(domain,sizeof(username),#0); end; //try-finally end;
hope finds useful. comments more welcome.
Comments
Post a Comment