Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi LSALogonUser und Authentifikation (nichts komplexes!) (https://www.delphipraxis.net/97466-lsalogonuser-und-authentifikation-nichts-komplexes.html)

Dezipaitor 10. Aug 2007 22:36


LSALogonUser und Authentifikation (nichts komplexes!)
 
hi

Wer kenn die Funktion LsaLogonUser denn nicht - ok ein Scherz :D
Code:
NTSTATUS LsaLogonUser(
  HANDLE LsaHandle,
  PLSA_STRING OriginName,
  SECURITY_LOGON_TYPE LogonType,
  ULONG AuthenticationPackage,
  PVOID AuthenticationInformation,
  ULONG AuthenticationInformationLength,
  PTOKEN_GROUPS LocalGroups,
  PTOKEN_SOURCE SourceContext,
  PVOID* ProfileBuffer,
  PULONG ProfileBufferLength,
  PLUID LogonId,
  PHANDLE Token,
  PQUOTA_LIMITS Quotas,
  PNTSTATUS SubStatus
);
Es geht hier weniger um die Frage über die Funktion selbst, als mehr über das Problem, dass der Parameter AuthenticationInformation gut definiert werden muss.
Für den interaktiven standard login verwendet man dazu MSV1_0_INTERACTIVE_LOGON. D.h. ein Pointer auf eine Struktur von.
Die Struktur muss jedoch eine Besonderheit aufweißen. Und zwar müssen die Unicode Strings direkt unterhalb der Struktur im Speicher stehen.
Die Strings in der Struktur sind wiederum eine Struktur UNICODE_STRING. Der Member Buffer muss dabei eben auf den Speicher unterhalb der Struktur im Speicher zeigen. D.h. die Speicherplatz ist die Strukturgröße + die Stringlänge in Bytes (da Unicode).
Tja, das muss man erstmal wissen.


Wie man das macht? Ich habe mal Keith Brown gefragt. Und es stellte sich heraus, dass die Funktion LSALogonUser bereits als "Funktion aus der Hölle" bekannt ist.
Es gibt dazu einen Link : The function from the hell.
Den Quelltext habe ich nach Delphi übersetzt, allerdings ohne Erfolg.

Delphi-Quellcode:
procedure _initUnicodeString(target : PUNICODE_STRING; source : PWideChar; cbMax : USHORT );
begin
  target.Length := cbMax ;//-2;//- sizeof(source^); //bei -2 und cbMax = 0 gibts nen integer unterlauf (=große Zahl, also lassen wirs)
  target.MaximumLength := cbMax;
  target.Buffer := source;
end;


function Create_MSV1_0_INTERACTIVE_LOGON(
                MessageType : MSV1_0_LOGON_SUBMIT_TYPE;
                LogonDomainName,
                UserName,
                Password : WideString;
                out authLen : Cardinal) : PMSV1_0_INTERACTIVE_LOGON;
var iSize,i1 : Integer;
    uniStr : UNICODE_STRING;
    p : PWCHAR;

    cbHeader,
    cbDom,
    cbUser,
    cbPass : Integer;

    pDom, pUser, pPass : PWChar;


const iUSHORT = sizeof(USHORT);
      iWCHAR = sizeof(WideChar);
begin
  cbHeader := sizeof(MSV1_0_INTERACTIVE_LOGON);
  cbDom   := Length(LogonDomainName) * iWCHAR;
  cbUser  := Length(UserName) * iWCHAR;
  cbPass  := Length(Password) * iWCHAR;

  iSize := cbHeader + cbDom + cbUser + cbPass;

  authLen := iSize;

  result := PMSV1_0_INTERACTIVE_LOGON(LocalAlloc(LMEM_ZEROINIT or LMEM_FIXED, iSize));

  result.MessageType := MessageType;
  p := PWCHAR(result);
  Inc(Integer(p));
 
  pDom := p;
  pUser := PWChar(Integer(p) + cbDom);
  pPass := PWChar(Integer(p) + cbDom + cbUser);

  CopyMemory(pDom, @LogonDomainName[1], cbDom);
  CopyMemory(pUser, @UserName[1],  cbUser);
  CopyMemory(pPass, @Password[1],  cbPass);

  _initUnicodeString(@result.LogonDomainName, pDom, cbDom);
  _initUnicodeString(@result.UserName, pUser , cbUser);
  _initUnicodeString(@result.Password, pPass , cbPass);
end;
Es ist doch so richtig übersetzt oder? Erst bitte drüber nachdenken, dann weiterlesen.























Bin mir nicht sicher.
Jedoch habe ich eine Änderung machen müssen bei :

Delphi-Quellcode:
Inc(Integer(p));
durch
Delphi-Quellcode:
Inc(Integer(p),cbHeader);
Danach hat der Aufruf von LSALogon funktioniert.

Meiner Meinung nach, zeigt der erste Code mit Inc, nur an den Anfang der Struktur - jedoch nicht ans Ende.


Wer hat nun Recht?

Muetze1 10. Aug 2007 22:59

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Das sind doch zwei ganz unterschiedliche Anweisung. Einmal wird der Zeiger nur um 1 Byte weiter geschoben und beim anderen Inc() wird der Zeiger um cbHeader weiter geschoben. Somit zeigt der Zeiger beim zweiten Inc() auf das Byte nach der Struktur.

Bei dem C++ Quellcode erhöht er das ganze um eins, aber dabei ist der Pointer aber typisiert, somit erhöht er um 1*SizeOf(Typ). Gleiches mach auch Inc() in Delphi, dazu müsste aber der Pointer den entsprechenden Typ haben. Somit ist sein Code korrekt.

Dezipaitor 10. Aug 2007 23:44

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
boha, du hast tatsächlich Recht. Das ist mir garnicht aufgefallen. Es wird tatsächlich implizit die Zeigervariable ans Ende gesetzt.

Naja, wenn ich das Update der Security Library rausbringe, dann muss man sich damit nicht rumschlagen. Zumindest für den Login durch das Windows Auth Paket (kein Kerberos)

Remko 11. Aug 2007 23:11

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
I've put up some sample code for LsaLogonUser recently on DP: http://www.delphipraxis.net/internal...t=lsalogonuser

Dezipaitor 11. Aug 2007 23:27

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
You forgot to use CreateEnvironmentBlock to set the correct env. for the user.

The function LsaLogonUser is helpful for adding the user to a group for this session. But is it possible without using CreateToken to change privileges?

Its nearly the same code I produced some days ago without knowing yours :D
However I wrapped the LSA functions in my Security Library classes so its easier to use. I will publish it when the time is right.

Remko 12. Aug 2007 08:13

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
For my purpose I didn't need user environment :-) I put up the sample code because I struggled on it for a while. Essentially the sample is a translation of Gary Nebbet's sample. You do need to have the SeTcbPrivilege (Act as part of the operation system) and enable it. This corresponds with MSDN documentation. The purpose of Gary's example was (as I recall) to show the danger of this privilege.

I see LsaLogonUser as an easy (and Vista compatible way) of launching something on the user's desktop from a service (In general services run under the SYSTEM account which has SeTcpPrivilege). Because you obtain the user's LogonSid you need not worry about setting ACL's on the user's desktop etc. Other purposes could be to acquire admin permissions to the launched application.

Dezipaitor 12. Aug 2007 12:30

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Do I understand correctly? Your code will run not as a service but as a normal process that can be used by a logged on user?

I tested my program as a service :
1. I logged on the current logged on user - but I needed to add the new logged on user SID to the windows station+desktop DACL to run a graphic process.
2. the CreateEnv... parameter must be set to the users envir. - otherwise the env. was set to the local system env. which lead to terrible result - I killed the explorer on purpose and started it again in the command line I created by that service. The result was that this user could no more start explorer with winlogon. It always used the local system. I restarted windows and logged on - but only the command line was started. All I could do was to use an partition image I created.
Thus I test such a program in a VM.

Remko 12. Aug 2007 12:42

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Zitat:

Zitat von Dezipaitor
Do I understand correctly? Your code will run not as a service but as a normal process that can be used by a logged on user?

That's right, but the user who starts my code needs SeTcbPrivilege

Zitat:

Zitat von Dezipaitor
1. I logged on the current logged on user - but I needed to add the new logged on user SID to the windows station+desktop DACL to run a graphic process.

Not needed, add the users LogonSid to the PTOKEN_GROUPS parameter

Zitat:

Zitat von Dezipaitor
2. the CreateEnv... parameter must be set to the users envir. - otherwise the env. was set to the local system env. which lead to terrible result - I killed the explorer on purpose and started it again in the command line I created by that service. The result was that this user could no more start explorer with winlogon. It always used the local system. I restarted windows and logged on - but only the command line was started. All I could do was to use an partition image I created.
Thus I test such a program in a VM.

I think this has got to do with acl on the user desktop and not the environment. Remember that ACL's that you set on the desktop do not survive a reboot

If you simply want to start a process from a service and run this on the user's desktop (or even a specific terminal sessions desktop) I use this:
Delphi-Quellcode:
procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
var hToken: THandle;
  si: _STARTUPINFOA;
  pi: _PROCESS_INFORMATION;
begin
  ZeroMemory(@si, SizeOf(si));
  si.cb := SizeOf(si);
  si.lpDesktop := nil;
 
  if WTSQueryUserToken(3, hToken) then
  begin
    if CreateProcessAsUser(hToken, nil, 'cmd.exe', nil, nil, False,
      CREATE_NEW_CONSOLE or CREATE_NEW_PROCESS_GROUP, nil,
      nil, si, pi) then
    begin
      // Do some stuff
    end;
  end;
  Self.DoStop;
end;
This sample start a process in Terminal Session 3 but you can use WTSGetActiveConsoleSession for obtaining the logged on users session ID.

Dezipaitor 12. Aug 2007 12:55

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
All I wanted to do is :

1. Logon as a power user (without debug privileges)
2. Start a service that is already installed.
3. Connect to service and supply an admin password and process user token.
4. The process checks the password and logs on the user with debug privilege and add the user to the debug users for this session.
Then it starts a given program (e.g. delphi).
Maybe this should also work in a remote desktop session.

Now I can debug services or anything else. However MSVC++ 2003 does not run in debug mode without debug privileges - but MSVC++ 2005 does. Taskmanager is another example.

So I get rid of the powerful debug privilege which can be used by a virus for example.

Thats all.

Remko 12. Aug 2007 13:03

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Sure: Suppose you poweruser is called Joe.
Let your service find the LogonSid for Joe. Use LsaLogonUser to start your process (eg Delphi) (eg with the supplied credentials). Include in the PTOKEN_GROUPS both the (local) admin sid and Joe's LogonSid. The process has full access to Joe's desktop without the need to set ACL's because you "are" Joe. Because the process has also Admin's SID you also have his privilegs. If wanted replace admin by a special user with debug privileges.

Dezipaitor 12. Aug 2007 13:10

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Zitat:

Zitat von Remko
Sure: Suppose you poweruser is called Joe.
Let your service find the LogonSid for Joe. Use LsaLogonUser to start your process (eg Delphi) (eg with the supplied credentials). Include in the PTOKEN_GROUPS both the (local) admin sid and Joe's LogonSid. The process has full access to Joe's desktop without the need to set ACL's because you "are" Joe. Because the process has also Admin's SID you also have his privilegs. If wanted replace admin by a special user with debug privileges.

It is not good to use add Joe to the administrator groups. We have to create programs without such power :D
It also does not work to add the group debug users because the privilege is not added. I tested it.

I created successfully a new token with debug privilege (using NTCreateToken), but this needs the users LUID (which can be created by CreateLogonSession) - however I can find the users LUID by LsaGetLogonSessionData.

Delphi-Quellcode:

function GetUserNameLUID(const username : WideString) : TLuid;
var
    ws : WideString;
    res,
    i,
    lsCount : Cardinal;
    lsLUIDS : PLuid;
    LUIDarray : array of TLUID absolute lsLUIDS;
    pLogonSessionData : PSECURITY_LOGON_SESSION_DATA;
begin
  result.LowPart := 0;
  result.HighPart := 0;

  LsaEnumerateLogonSessions(@lsCount,lsLUIDS);
  try
    for i := 0 to lsCount-1 do
    begin
      res := LsaGetLogonSessionData(@LUIDarray[i], pLogonSessionData);

      if (res = 0) then
      begin

        if (CompareText(pLogonSessionData.UserName.Buffer, userName) = 0) and
           (CompareText(pLogonSessionData.AuthenticationPackage.Buffer, 'NTLM') = 0) then
        begin
          result := pLogonSessionData.LogonId;
          LsaFreeReturnBuffer(pLogonSessionData);
          LsaFreeReturnBuffer(lsLUIDS);
          exit;
        end;
        LsaFreeReturnBuffer(pLogonSessionData);
      end;
    end;
  finally
     LsaFreeReturnBuffer(lsLUIDS);
  end;
end;
By the way:
Did you see my post Security Library. I would appreciate it if you could make a comment (I also need reinforcement).

Remko 12. Aug 2007 13:20

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
I don't mean adding Joe the Administrators group rather the process you started with LsaLogonUser runs with admin permissions!

I thought that (zw)NTCreateToken was no longer possible in Vista :-(

Please look at my sample again, the way I understand your question it's does precisely what you want! Make it a service so you don't have to give a user SeTcbPrivilege...

Dezipaitor 12. Aug 2007 13:38

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Zitat:

Zitat von Remko
I don't mean adding Joe the Administrators group rather the process you started with LsaLogonUser runs with admin permissions!

I know! The newly created process has admin permission. That is not good. Debug privs is only neccessary.

Zitat:

I thought that (zw)NTCreateToken was no longer possible in Vista :-(
Dont know :D

Zitat:

Please look at my sample again, the way I understand your question it's does precisely what you want! Make it a service so you don't have to give a user SeTcbPrivilege...
It does not work as I want, because I need to add the debug privilege to the users token. The only way I know is to use CreateToken. I already tested it. It works, but it is complicated and dangerous.

Remko 12. Aug 2007 13:53

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
1. Define a special user with Debug privilege.
2. Joe is logged an and requests your service app a "debug session"
3. Service starts process with Joe's LogonSid and the Debug users SID
4. The process (and only the process) runs with debug privilege. Joe is the "owner" of this process.

What do we miss?

Dezipaitor 12. Aug 2007 14:57

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Okay that works - adding a special User (not group), add Debug privs to it (gpedit.msc) and add the user to the groups parameter.
Now she has debug privs.

By the way.
I cannot run a graphic programm (like regedit) without changing the window station DACL. I tried your suggestion and added the LogonSID (retrieved by your second sample code GetLogonSid) to the groups. However the started application cannot paint to desktop. The window is drawn transparent.

I retrieved the WinStation DACL in that newly started process - and always get an Access denied (5).

Ok its no use - I have to add the new user to the winstation DACL - however I cannot retrieve the DACL without admin privs.

Delphi-Quellcode:

(*
function GetLogonSID(const hWinStation : HWINSTA{TWindowStation}) : TSecurityID;
{TODO: Use TWindowStation (if implemented) instead}
var
   hAWinst : HWINSTA;
   logonSID : PSID;
   dwSize : Cardinal;
begin
  haWinst := hWinStation;
  if (hWinStation = 0) or (hWinStation = INVALID_HANDLE_VALUE) then
    hAWinst := OpenWindowStation(
               'winsta0',
               FALSE,
               READ_CONTROL,
               //READ_CONTROL or WRITE_DAC
              );
  result := nil;

  if not GetUserObjectInformation(hAWinst, UOI_USER_SID, nil,0, dwSize) then
  begin
      // GetUserObjectInformation returns required size
      GetMem(LogonSid, dwSize + 1);
      if not GetUserObjectInformation(hAWinst, UOI_USER_SID, LogonSid, dwSize, dwSize) then
      begin
        raise ESMWinCallFailedException.CreateFmtWinCall(
          'Call to GetUserObjectInformation failed. ',
           'GetLogonSID',
           '',
           'USM_KnownSID.pas',
           0,
           true,
           'GetUserObjectInformation',[]);
      end;
      if logonSID <> nil then
      begin
        result := TSecurityID.Create(logonSID);
        FreeMem(logonSID);
      end;
  end;

  if (hWinStation <> 0) and (hWinStation <> INVALID_HANDLE_VALUE) then
    CloseWindowStation(hAWinst);
end;
*)

function GetUserNameLUID(const username : WideString) : TLuid;
var
    ws : WideString;
    res,
    i,
    lsCount : Cardinal;
    lsLUIDS : PLuid;
    LUIDarray : array of TLUID absolute lsLUIDS;
    pLogonSessionData : PSECURITY_LOGON_SESSION_DATA;
begin
  result.LowPart := 0;
  result.HighPart := 0;

  LsaEnumerateLogonSessions(@lsCount,lsLUIDS);
  try
    for i := 0 to lsCount-1 do
    begin
      res := LsaGetLogonSessionData(@LUIDarray[i], pLogonSessionData);

      if (res = 0) then
      begin

        if (CompareText(pLogonSessionData.UserName.Buffer, userName) = 0) and
           (CompareText(pLogonSessionData.AuthenticationPackage.Buffer, 'NTLM') = 0) then
        begin
          result := pLogonSessionData.LogonId;
          LsaFreeReturnBuffer(pLogonSessionData);
          LsaFreeReturnBuffer(lsLUIDS);
          exit;
        end;
        LsaFreeReturnBuffer(pLogonSessionData);
      end;
    end;
  finally
     LsaFreeReturnBuffer(lsLUIDS);
  end;
end;

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  RunWithDebugService.Controller(CtrlCode);
end;

function TRunWithDebugService.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

procedure UpdateWindowStation(usertoken : TSecurityToken);
var
   aDACL : TDAccessControlList;
    s : String;
    lpEnv : Pointer;

    hwinstaold,
    hAWinst : HWINSTA;
    hADesk : HDESK;

    anOwner, aGroup : TSecurityID;
    desktopDACL : TDAccessControlList;
    aSACL : TSAccessControlList;
    aPriv : TPrivilege;
begin
  //http://msdn2.microsoft.com/en-us/library/aa379608.aspx
  hAWinst := OpenWindowStation(
             'winsta0',
             FALSE,
             GENERIC_ALL  //damit auch später ShowMessage noch funkz - nach SetProcessWindowStation
             //READ_CONTROL or WRITE_DAC
             );

   hwinstaold := GetProcessWindowStation();

   // To get the correct default desktop, set the caller's
// window station to the interactive window station.
   if not SetProcessWindowStation(hAWinst) then
     raise ESMWinCallFailedException.CreateFmtEx('SetProcessWindowStation ',
                        '','','USM_Token.pas', 0,true,[]);

   hADesk := OpenDesktop(
             'default',
             0,
             FALSE,
             READ_CONTROL or WRITE_DAC or
             DESKTOP_WRITEOBJECTS or DESKTOP_READOBJECTS);
   if hADesk = 0 then
      raise ESMWinCallFailedException.CreateFmtEx('OpenDesktop ',
                        '','','USM_Token.pas', 0,true,[]);

   TSecureGeneralObject.GetSecurityInfo(
        hAWinst,SE_WINDOW_OBJECT,[sif_DACL_SECURITY_INFORMATION],anOwner,aGroup,desktopDACL,aSACL);

   desktopDACL.Add(TDiscretionaryAccessControlEntryAllow.Create(nil,TAF_ThisFolderAndSubFoldersAndFiles,GENERIC_ALL,usertoken.GetTokenUser,false));

   TSecureGeneralObject.SetSecurityInfo(
        hAWinst,SE_WINDOW_OBJECT,[sif_DACL_SECURITY_INFORMATION],anOwner,aGroup,desktopDACL,aSACL);

   TSecureGeneralObject.GetSecurityInfo(
        hADesk,SE_WINDOW_OBJECT,[sif_DACL_SECURITY_INFORMATION],anOwner,aGroup,desktopDACL,aSACL);

   desktopDACL.Add(TDiscretionaryAccessControlEntryAllow.Create(nil,TAF_ThisFolderAndSubFoldersAndFiles,GENERIC_ALL,usertoken.GetTokenUser,false));

   TSecureGeneralObject.SetSecurityInfo(
        hADesk,SE_WINDOW_OBJECT,[sif_DACL_SECURITY_INFORMATION],anOwner,aGroup,desktopDACL,aSACL);

   // Restore the caller's window station.
   SetProcessWindowStation(hwinstaold);
end;

function DumpEnvironmentW(lpEnvironment: Pointer) : WideString;
var
  Env: PWideChar;
begin
  result := '';
  Env := lpEnvironment;
  while (lstrlenW(Env) > 0) do
  begin
    if WideString(Env)[1] <> '=' then
     result := result + #13#10 + WideString(Env);
    Env := PWideChar(DWORD(Env) + DWORD(lstrlenW(Env) + 1) * DWORD(sizeof(Env^)));
  end;
 // Delete(result,1,2)
end;




procedure TRunWithDebugService.ServiceExecute(Sender: TService);
var tokenSYSTEM : TSecurityToken;
    userLUID : TLUID;

    res : Cardinal;
    lsaHandle : THandle;
    lsaSecurityMode : LSA_OPERATIONAL_MODE;

    SecurityLSA : TSecurityLSA;

    logonData : MSV1_0_INTERACTIVE_LOGON;
    plogonData : PMSV1_0_INTERACTIVE_LOGON;
    aLocalGroups: TSecurityIDList;
    SourceContext: TTokenSource;

    aProfileBuffer: PMSV1_0_INTERACTIVE_PROFILE;//Pointer;
    afProfileBufferLength: Cardinal;
    aTokenLuid: TLUID;
    aToken: TSecurityToken;
    aQuotaLimits: QUOTA_LIMITS;
    aSubStatus: NTSTATUS;

    authLen : Cardinal;

    si: STARTUPINFOW;
    pif: PROCESS_INFORMATION;

    lpEnv : Pointer;
    ws : WideString;
    aLogonSid : TSecurityID;
begin
 // ShowMessage('Starting service');
  //EnablePrivilege(SE_TCB_NAME,pst_Enable);
  InitWellKnownSIDs;





  SecurityLSA := TSecurityLSA.Create('RunWithDebug');
  try



    logonData.MessageType := MsV1_0InteractiveLogon;

    plogonData := Create_MSV1_0_INTERACTIVE_LOGON(
                      logonData.MessageType,
                        '',
                        'DelphiTester',
                        'pass',authLen);

    aLocalGroups := TSecurityIDList.Create(true);
    aLogonSid := GetLogonSID;
    ShowMessage(aLogonSid.GetText(true));
    aLocalGroups.Add(aLogonSid);
    aLocalGroups.Add(TSecurityID.Create('','DebuggerUser'));
    aLocalGroups.Add(AdministratorsSID);

    SourceContext.SourceName := 'NTLM';
    SourceContext.SourceIdentifier := LOCALSERVICE_LUID;

    aProfileBuffer := nil;
    afProfileBufferLength := sizeof(aProfileBuffer);
    SecurityLSA.LsaLogonUser(
          'NTLM',//nOriginName: String;
          jwaWindows.Interactive,//aLogonType: SECURITY_LOGON_TYPE;
          MSV1_0_PACKAGE_NAME,//anAuthenticationPackageName : String;
          plogonData,//anAuthenticationInformation: Pointer;
          authLen,//anAuthenticationInformationLength: Cardinal;
          aLocalGroups,//aLocalGroups: TSecurityIDList;
          SourceContext,//aSourceContext: TTokenSource;
          Pointer(aProfileBuffer),//aProfileBuffer: Pointer;
          afProfileBufferLength,//out afProfileBufferLength: Cardinal;
          aTokenLuid,//out aTokenLuid: TLUID;
          aToken,//out aToken: TSecurityToken;
          aQuotaLimits,//out aQuotaLimits: QUOTA_LIMITS;
          aSubStatus//out SubStatus: NTSTATUS);
          );

    FillChar(si,sizeof(si),0);
    si.cb := SizeOf(startupinfo);
    si.dwFlags := STARTF_USESHOWWINDOW;
    si.wShowWindow := SW_SHOW;
    si.lpReserved := nil;

  //  UpdateWindowStation(aToken);

//    hier nun das Token nehmen um ein neues token zu erstellen mit dem debugrecht
   tokenSYSTEM := TSecurityToken.CreateTokenEffective(TOKEN_ALL_ACCESS);

   tokenSYSTEM.RevertToSelf;
   try

     ShowMessage(aToken.TokenUser.GetText(true));

   lpEnv := nil;
   if not CreateEnvironmentBlock(@lpEnv,aToken.TokenHandle,true) then
     raise ESMWinCallFailedException.CreateFmtEx('CreateEnvironmentBlock ',
                        'CreateEnvironmentBlock',ClassName,'USM_Token.pas', 0,true,[]);


   //ShowMessage(DumpEnvironmentW(lpEnv));

    if not createprocessasuserw(aToken.TokenHandle,('c:\windows\system32\cmd.exe'){'e:\whoami.exe'},nil,nil,nil,true,CREATE_UNICODE_ENVIRONMENT,lpEnv,nil,si,pif) then
      raise ESMWinCallFailedException.CreateFmtEx('createprocessasuser ',
                        'createprocessasuserw',ClassName,'USM_Token.pas', 0,true,[]);
   finally

    ShowMessage('OK');



    LsaFreeReturnBuffer(aProfileBuffer);
    CloseHandle(aToken.TokenHandle);


    //ShowMessage(tokenSYSTEM.GetTokenStatistics.GetText);
    userLUID := GetUserNameLUID('dezipaitor');

   end;
  except
   on E : Exception do
     ShowMessage(E.Message);

  end;
  SecurityLSA.Free;
end;

Remko 12. Aug 2007 15:35

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Can you show me your code?
Maybe the difference is that you run from a service, so if you get the Logged On User Sid you get the SID from the account that your service runs under. You need to get the Login Sid from the Interactive User.

Dezipaitor 12. Aug 2007 15:42

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
ok..but can you show me how to achieve that?

Remko 12. Aug 2007 16:38

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
The function GetLogonSid takes a hToken as first parameter. Why not pass it the token from some exe (say explorer.exe) that run's in the user's context?
Or maybe my alternative GetLogonSid, that should probably work in case of in interactive service (pre-vista). Or maybe it can be adapted to acquire the right hWinstation somehow...
Delphi-Quellcode:
//  Alternative way to get the Logon Sid
  procedure GetLogonSid(var LogonSid: pSid);
  var hWinstation: HWINSTA;
      dwSize    : Cardinal;
  begin
    // Open the WindowStation
    hWinstation := OpenWindowStation('winsta0', False, READ_CONTROL);
    if hWinstation = 0 then
    begin
      ShowMessageFmt('OpenWindowStation: %s', [SysErrorMessage(GetLastError)]);
      Exit;
    end;

    // GetUserObjectInformation returns required size in dwSizeNeeded
    if not GetUserObjectInformation(hWinStation, UOI_USER_SID, nil, 0, dwSize) then
    begin
      // GetUserObjectInformation returns required size
      GetMem(LogonSid, dwSize + 1);
      if not GetUserObjectInformation(hWinStation, UOI_USER_SID, LogonSid, dwSize, dwSize) then
      begin
        ShowMessageFmt('GetUserObjectInformation: %s', [SysErrorMessage(GetLastError)]);
        Exit;
      end;
    end;

    // Cleanup
    CloseWindowStation(hWinStation);
  end;
I got some other ideas but I have to look into that (will be continued)

Dezipaitor 12. Aug 2007 17:27

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
I'm using that already - to add this SID to the groups of the new logon token does not work. Believe me.

Remko 12. Aug 2007 19:33

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Try this:
Delphi-Quellcode:
  if WTSQueryUserToken(WtsGetActiveConsoleSessionID, hToken) then
  begin
    GetLogonSid(hToken, LogonSid);
Use this GetLogonSid:

Delphi-Quellcode:
procedure GetLogonSID(hToken: THandle; var ppsid: PSID);
var dwLength: DWORD;
    ptg    : ^TOKEN_GROUPS;
    i      : integer;
begin
  dwLength := 0;
  ptg := nil;

  try
    // Get required buffer size and allocate the TOKEN_GROUPS buffer.
    if not GetTokenInformation(hToken, TokenGroups, ptg, 0, dwLength) then
    begin
      if GetLastError <> ERROR_INSUFFICIENT_BUFFER then
      begin
        ShowMessage('GetTokenInformation failed');
        Exit;
      end;

      ptg := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwLength);
      if ptg = nil then
      begin
        Exit;
      end;

      // Get the token group information from the access token.
      if not GetTokenInformation(hToken, TokenGroups, ptg, dwLength, dwLength) then
      begin
        Exit;
      end;

      // Loop through the groups to find the logon SID.
      for i := 0 to ptg.GroupCount-1 do
      begin
       if ptg.Groups[i].Attributes and SE_GROUP_LOGON_ID = SE_GROUP_LOGON_ID then
       begin
         // Found the logon SID; make a copy of it.
         dwLength := GetLengthSid(ptg.Groups[i].Sid);
         ppsid := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwLength);
         if ppsid = nil then
         begin
           Exit;
         end;
         if not CopySid(dwLength, ppsid, ptg.Groups[i].Sid) then
         begin
//           raise exception.Create(Format('CopySid: %s', [SysErrorMessage(GetLastError)]));
           HeapFree(GetProcessHeap, 0, ppsid);
           Exit;
         end;

         Break;
        end;
      end;
    end;
  finally
    // Free the buffer for the token groups.
    if ptg <> nil then
    begin
      HeapFree(GetProcessHeap, 0, ptg);
    end;
  end;
end;
Continue on with LsaLogonUser

Dezipaitor 12. Aug 2007 20:46

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Actually I get this login SID, which is definitly a Session Logon ID :

(S-1-5-5-0-946199)

I translated your source :D- I hope it correct.

Delphi-Quellcode:
constructor TSecurityToken.CreateWTSQueryUserToken(SessionID : Cardinal {= INVALID_HANDLE_VALUE});
begin
  Self.Create;
  fShared := FALSE;

  if SessionID = INVALID_HANDLE_VALUE then
    SessionID := WtsGetActiveConsoleSessionID;

  fAccessMask := TOKEN_ALL_ACCESS;
  if not WTSQueryUserToken(SessionID, fTokenHandle) then
    raise ESMWinCallFailedException.CreateFmtEx('A call to WTSQueryUserToken failed. ',
                        'WTSQueryUserToken',ClassName,'USM_Token.pas', 0,true,[]);
end;

function GetLogonSID(aToken: TSecurityToken) : TSecurityID;
var i      : integer;
    ptg    : TSecurityIDList;
begin
  result := nil;
  ptg := aToken.GetTokenGroups;

  // Loop through the groups to find the logon SID.
  for i := 0 to ptg.Count-1 do
  begin
    if (ptg[i].Attributes and SE_GROUP_LOGON_ID) = SE_GROUP_LOGON_ID then
    begin
      // Found the logon SID; make a copy of it.
      result := TSecurityID.Create(ptg[i].CreateCopyOfSID);
      Break;
    end;
  end;
end;
And the code that replaces the old one :

Delphi-Quellcode:
var WTSToken := TSecurityToken;
    aLocalGroups : TSecurityID;
...  
    WTSToken := TSecurityToken.CreateWTSQueryUserToken();

    aLocalGroups := TSecurityIDList.Create(true);
    aLogonSid := GetLogonSID(WTSToken);
    ShowMessage('aLogonSid: '+ aLogonSid.GetText(true));


    aLocalGroups.Add(aLogonSid);
    aLocalGroups.Add(TSecurityID.Create('','DebuggerUser'));
    aLocalGroups.Add(AdministratorsSID);

Remko 12. Aug 2007 20:57

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
This works for me (adapt username and password to something of your liking):
It uses dialogs to give some debug info when something goes wrong (make service interactive to see the messageboxes), offcourse you can remove these in the final version.
Delphi-Quellcode:
unit uMain;

interface

uses
  Windows, SvcMgr, SysUtils,
  JwaWinType, JwaWinBase, JwaWinNT, JwaNtSecApi, JwaNTStatus,
  JwaNative, JwaUserEnv, JwaWinSta, JwaWtsApi32, Dialogs, uWinStation;
const
  DNLEN     = 15;
  UNLEN     = 256;

type
  TAuthInfo = record
    Header: MSV1_0_INTERACTIVE_LOGON;
    Domain: array[0..DNLEN] of WideChar;
    User: array[0..UNLEN] of WideChar;
    Password: array[0..UNLEN] of WideChar;
  end;

type
  TService1 = class(TService)
    procedure ServiceStart(Sender: TService; var Started: Boolean);
  private
    { Private declarations }
  public
    function GetServiceController: TServiceController; override;
    { Public declarations }
  end;

var
  Service1: TService1;

implementation

{$R *.DFM}

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
end;

function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

Procedure LsaInitUnicodeString(Var LsaString: TLsaUnicodeString; Const WS:
  WideString);
Begin
  FillChar(LsaString, SizeOf(LsaString), 0);
  If WS <> '' Then
  Begin
    LsaString.Length:=Length(WS) * SizeOf(WideChar);
    LsaString.MaximumLength:=LsaString.Length + SizeOf(WideChar);
    LsaString.Buffer:=PWideChar(WS);
  End;
End;

procedure GetLogonSID(hToken: THandle; var ppsid: PSID);
var dwLength: DWORD;
    ptg    : ^TOKEN_GROUPS;
    i      : integer;
begin
  dwLength := 0;
  ptg := nil;

  try
    // Get required buffer size and allocate the TOKEN_GROUPS buffer.
    if not GetTokenInformation(hToken, TokenGroups, ptg, 0, dwLength) then
    begin
      if GetLastError <> ERROR_INSUFFICIENT_BUFFER then
      begin
        ShowMessage('GetTokenInformation failed');
        Exit;
      end;

      ptg := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwLength);
      if ptg = nil then
      begin
        Exit;
      end;

      // Get the token group information from the access token.
      if not GetTokenInformation(hToken, TokenGroups, ptg, dwLength, dwLength) then
      begin
        Exit;
      end;

      // Loop through the groups to find the logon SID.
      for i := 0 to ptg.GroupCount-1 do
      begin
       if ptg.Groups[i].Attributes and SE_GROUP_LOGON_ID = SE_GROUP_LOGON_ID then
       begin
         // Found the logon SID; make a copy of it.
         dwLength := GetLengthSid(ptg.Groups[i].Sid);
         ppsid := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwLength);
         if ppsid = nil then
         begin
           Exit;
         end;
         if not CopySid(dwLength, ppsid, ptg.Groups[i].Sid) then
         begin
//           raise exception.Create(Format('CopySid: %s', [SysErrorMessage(GetLastError)]));
           HeapFree(GetProcessHeap, 0, ppsid);
           Exit;
         end;

         Break;
        end;
      end;
    end;
  finally
    // Free the buffer for the token groups.
    if ptg <> nil then
    begin
      HeapFree(GetProcessHeap, 0, ptg);
    end;
  end;
end;


procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
var hToken: THandle;
  si: _STARTUPINFOA;
  pi: _PROCESS_INFORMATION;
  Res: NTSTATUS;
  hLSA: THandle;
  LSAString: _LSA_STRING;
  AuthenticationPackage: ULONG;
  AuthentificationInfo: TAuthInfo;
  pProfileBuffer: Pointer;
  LogonID: _LUID;
  hLsaToken: THandle;
  QuotaLimits: QUOTA_LIMITS;
  SubStatus: Integer;
  wsDomain: WideString;
  wsUser: WideString;
  wsPwd: WideString;
  TokenSource: TOKEN_SOURCE;
  dwReturnLength: DWORD;
  Mode: LSA_OPERATIONAL_MODE;
  pGroups: PTOKEN_GROUPS;
  AdminSid: PSid;
  dwSizeSid: Cardinal;
  dwSizeDomain: Cardinal;
  SidType: TSidNameUse;
  Domain: String;
  MaxGroups: Integer;
  bRes: Longbool;
begin
  ZeroMemory(@si, SizeOf(si));
  si.cb := SizeOf(si);
  si.lpDesktop := nil;

  if WTSQueryUserToken(WtsGetActiveConsoleSessionID, hToken) then
  begin
    RtlInitString(@LsaString, PCSZ(PChar('Winlogon')));
    Res := LsaRegisterLogonProcess(LsaString, hLSA, @Mode);
    if Failed(Res) then
    begin
      ShowMessageFmt('LsaRegisterLogonProcess: %s', [SysErrorMessage(LsaNtStatusToWinError(Res))]);
    end;

    RtlInitString(@LsaString, PCSZ(PChar(MSV1_0_PACKAGE_NAME)));

    Res := LsaLookupAuthenticationPackage(hLSA, LSAString, AuthenticationPackage);
    if Failed(Res) then
    begin
      ShowMessageFmt('LookupAuthPackage: %s', [SysErrorMessage(LsaNtStatusToWinError(Res))]);
    end;

    TokenSource.SourceName := '**ANON**';
    if not AllocateLocallyUniqueId(TokenSource.SourceIdentifier) then
    begin
      ShowMessageFmt('AllocLocUniqueId: %s', [SysErrorMessage(GetLastError)]);
    end;

    // The number of TOKEN_GROUPS we're going to insert
    MaxGroups := 2;

    // Reserve memory for MaxGroups numbur of PTOKEN_GROUPS
    pGroups := PTOKEN_GROUPS(GlobalAlloc(GPTR, sizeof(_SID_AND_ATTRIBUTES) * MaxGroups));
    pGroups^.GroupCount := MaxGroups;

    // Get the Logon Sid and it to the LocalGroups parameter of LsaLogonUser
    // The Logon Sid has the form S-1-5-5-XXXXXXXX-YYYYYYYY
    // We need it to obtain access to the user's desktop
    GetLogonSid(hToken, pGroups^.Groups[0].Sid);
    pGroups^.Groups[0].Attributes := SE_GROUP_MANDATORY or
                                     SE_GROUP_ENABLED or
                                     SE_GROUP_ENABLED_BY_DEFAULT or
                                     SE_GROUP_LOGON_ID;

    // Now get the Administrator's SID
    dwSizeSid := 0;
    dwSizeDomain := 0;
    bRes := LookupAccountName(nil, 'Administrator', nil, dwSizeSid, nil, dwSizeDomain, SidType);

    if (not bRes) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
    begin
      // Reserve memory
      AdminSid := AllocMem(dwSizeSid);
      SetLength(Domain, dwSizeDomain);

      // Lookup Sid from Accountname
      // Assuming that the Admin account has not been renamed!
      bRes := LookUpAccountName(nil, 'Administrator', AdminSid, dwSizeSid, PChar(Domain), dwSizeDomain, SidType);
      if not bRes then
      begin
        // Cleanup
        FreeMem(AdminSid);
        AdminSid := nil;
      end;
    end
    else begin
      RaiseLastOSError;
    end;

    ShowMessageFmt('Administrator Sid: %s, Domain: %s', [SidToStr(AdminSid), Domain]);

    // Add the Administrator's sid to pGroups
    pGroups^.Groups[MaxGroups -1].Sid := AdminSid;
    pGroups^.Groups[MaxGroups -1].Attributes := SE_GROUP_MANDATORY or
                                                SE_GROUP_ENABLED or
                                                SE_GROUP_ENABLED_BY_DEFAULT;

    // Fill the AuthentificationInfo structure
    // First convert the EDITs to WideString
    wsDomain:= '';
    wsUser:= 'username';
    wsPwd:= 'password';

    // Fill with zeros
    RtlZeroMemory(@AuthentificationInfo, sizeof(AuthentificationInfo));
    AuthentificationInfo.Header.MessageType := MsV1_0InteractiveLogon;
    //  AuthentificationInfo.Header.MessageType := MsV1_0NetworkLogon;

    // Copy the strings into a buffer.
    RtlCopyMemory(@AuthentificationInfo.Domain, @wsDomain[1], sizeof(WideChar) * Length(wsDomain));
    RtlCopyMemory(@AuthentificationInfo.User, @wsUser[1], sizeof(WideChar) * Length(wsUser));
    RtlCopyMemory(@AuthentificationInfo.Password, @wsPwd[1], sizeof(WideChar) * Length(wsPwd));

    // Now set which buffer we want to use (the arrays of WideChar from the struct)
    RtlInitUnicodeString(@AuthentificationInfo.Header.LogonDomainName, AuthentificationInfo.Domain);
    RtlInitUnicodeString(@AuthentificationInfo.Header.UserName, AuthentificationInfo.User);
    RtlInitUnicodeString(@AuthentificationInfo.Header.Password, AuthentificationInfo.Password);

    Res := LsaLogonUser(hLSA, LsaString, RemoteInteractive,
                        AuthenticationPackage, @AuthentificationInfo,
                        SizeOf(AuthentificationInfo), pGroups, @TokenSource,
                        pProfileBuffer, dwReturnLength, LogonID, hLSAToken,
                        QuotaLimits, SubStatus);

    if Failed(Res) then
    begin
      ShowMessageFmt('LsaLogonUser: %s', [SysErrorMessage(LsaNtStatusToWinError(Res))]);
    end;

    ZeroMemory(@si, SizeOf(si));
    si.cb := SizeOf(si);
    si.lpReserved := nil;
    si.lpDesktop := nil;
    si.dwFlags := STARTF_USESHOWWINDOW;;
    si.wShowWindow := SW_SHOWNORMAL;

    if not CreateProcessAsUser(hLsaToken, nil, PChar('notepad.exe'), nil, nil, False,
                               NORMAL_PRIORITY_CLASS or CREATE_NEW_PROCESS_GROUP,
                               nil, nil, &si, &pi) then
    begin
      ShowMessageFmt('CreateProcessAsUser: %s', [SysErrorMessage(GetLastError)]);   end
    else
    begin
    end;

    // Cleanup
    CloseHandle(hToken);
    FreeMem(AdminSid);
    LsaDeregisterLogonProcess(hLSA);
    LsaFreeReturnBuffer(pProfileBuffer);
  end;
  Self.DoStop;
end;

end.
Maybe you'll need unit uWinStation to compile:
Delphi-Quellcode:
unit uWinStation;

interface

uses SysUtils, Dialogs,
     JwaWinType, JwaWinBase, JwaWinNT, JwaNtSecApi, JwaNTStatus, JwaNative,
     JwaWinUser, JwaWinError;

function StartInteractiveClientProcess(const lpszUsername: PAnsiChar; const lpszDomain: PAnsiChar; const lpszPassword: PAnsiChar; const lpCommandLine: PChar): Boolean;
function GetLogonSID(hToken: THandle; var ppsid: PSID): Boolean;
function AddAceToWindowStation(hwinsta: HWINSTA; psid: PSID): Boolean;
function AddAceToDesktop(hdesktop: HDESK; ps: PSID): Boolean;
function SIDToStr (sid : PSID) : string;
procedure StrToSid (const sidName : string; sid : PSid; sidLen : DWORD);

implementation

const
DESKTOP_ALL = DESKTOP_READOBJECTS or DESKTOP_CREATEWINDOW or DESKTOP_CREATEMENU or DESKTOP_HOOKCONTROL or
DESKTOP_JOURNALRECORD or DESKTOP_JOURNALPLAYBACK or DESKTOP_ENUMERATE or DESKTOP_WRITEOBJECTS or
DESKTOP_SWITCHDESKTOP or STANDARD_RIGHTS_REQUIRED;

WINSTA_ALL = WINSTA_ENUMDESKTOPS or WINSTA_READATTRIBUTES or WINSTA_ACCESSCLIPBOARD or WINSTA_CREATEDESKTOP or
WINSTA_WRITEATTRIBUTES or WINSTA_ACCESSGLOBALATOMS or WINSTA_EXITWINDOWS or WINSTA_ENUMERATE or
WINSTA_READSCREEN or STANDARD_RIGHTS_REQUIRED;

GENERIC_ACCESS = GENERIC_READ or GENERIC_WRITE or GENERIC_EXECUTE or GENERIC_ALL;

HEAP_ZERO_MEMORY = 8;
ACL_REVISION = 2;
ACCESS_ALLOWED_ACE_TYPE = 0;
CONTAINER_INHERIT_ACE = 2;
INHERIT_ONLY_ACE = 8;
OBJECT_INHERIT_ACE = 1;
NO_PROPAGATE_INHERIT_ACE = 4;
SE_GROUP_LOGON_ID = $C0000000;

type
ACL_SIZE_INFORMATION = record
AceCount: DWORD;
AclBytesInUse: DWORD;
AclBytesFree: DWORD;
end;

ACE_HEADER = record
AceType: BYTE;
AceFlags: BYTE;
AceSize: WORD;
end;
PACE_HEADER = ^ACE_HEADER;

ACCESS_ALLOWED_ACE = record
Header: ACE_HEADER;
Mask: ACCESS_MASK;
SidStart: DWORD;
end;

function SIDToStr (sid : PSID) : string;
var
  psia : PSIDIdentifierAuthority;
  dwSubAuthorities : DWORD;
  dwSidRev : DWORD;
  dwCounter : DWORD;
begin
  dwSidRev := SID_REVISION;


  if IsValidSid (sid) then
  begin
    psia := GetSidIdentifierAuthority (sid);
    dwSubAuthorities := GetSidSubAuthorityCount (sid)^;


    result := Format ('S-%u-', [dwSidRev]);


    if (psia^.Value[0] <> 0) or (psia^.Value[1] <> 0) then
      result := result + format ('0x%02x%02x%02x%02x%02x%02x',[ 
                                   psia^.Value [0],
                                   psia^.Value [1],
                                   psia^.Value [2],
                                   psia^.Value [3],
                                   psia^.Value [4],
                                   psia^.Value [5]])
    else
      result := result + format ('%u',
                                 [DWORD (psia^.Value [5]) + 
                                  DWORD (psia^.Value [4] shl 8) + 
                                  DWORD (psia^.Value [3] shl 16) + 
                                  DWORD (psia^.Value [2] shl 24)]);


    for dwCounter := 0 to dwSubAuthorities - 1 do
      result := result + Format ('-%u', [GetSidSubAuthority (sid,
dwCounter)^])
  end
  else
    raise Exception.Create ('Invalid SID');
end;


procedure StrToSid (const sidName : string; sid : PSid; sidLen : DWORD);
var
  ps : PChar;
  pn : PChar;
  p : PChar;
  pa : PChar;
  valueStr : string;
  psia : PSIDIdentifierAuthority;
  i : DWORD;
  d : DWORD;
  authorityCount : DWORD;
begin
(* 
typedef struct _SID { 
   BYTE Revision;
   BYTE SubAuthorityCount;
   SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
#ifdef MIDL_PASS
   [size_is(SubAuthorityCount)] DWORD SubAuthority[*];
#else // MIDL_PASS
   DWORD SubAuthority[ANYSIZE_ARRAY];
*)
  if not ((Length (sidName) > 3) and (sidName [1] = 'S') and (sidName [2] =
'-')) then
    raise Exception.Create ('Bad SID');


  if sidLen < sizeof (_SID_IDENTIFIER_AUTHORITY) + 2 then
    raise Exception.Create ('Bad SID');


  ps := PChar (sid);
  pn := PChar (sidName);
  Inc (pn, 2);


  p := StrScan (pn, '-');
  if not Assigned (p) then
    raise Exception.Create ('Bad SID');


  p^ := #0;


  ps^ := char (StrToInt (pn)); // Revision
  Inc (ps);
  pa := ps;            // Save authority count position
  Inc (ps);


  pn := p + 1;
  p := StrScan (pn, '-');
  if not Assigned (p) then
    raise Exception.Create ('Bad SID');


  p^ := #0;


  valueStr := pn;


  if Length (valueStr) < 1 then
    raise Exception.Create ('Bad SID');


  psia := PSIDIdentifierAuthority (ps);
  Inc (ps, sizeof (_SID_IDENTIFIER_AUTHORITY));
  Dec (sidLen, 2 + sizeof (_SID_IDENTIFIER_AUTHORITY));
  if valueStr [1] = 'x' then
  begin
    if Length (valueStr) <> 14 then
      raise Exception.Create ('Bad SID');


    psia^.value [0] := StrToInt ('$' + Copy (valueStr, 3, 2));
    psia^.value [1] := StrToInt ('$' + Copy (valueStr, 5, 2));
    psia^.value [2] := StrToInt ('$' + Copy (valueStr, 7, 2));
    psia^.value [3] := StrToInt ('$' + Copy (valueStr, 9, 2));
    psia^.value [4] := StrToInt ('$' + Copy (valueStr, 11, 2));
    psia^.value [5] := StrToInt ('$' + Copy (valueStr, 13, 2))
  end
  else
  begin
    psia^.value [0] := 0;
    psia^.value [1] := 0;
    i := StrToInt (valueStr);


    d := i shl 24;
    psia^.value [2] := d and $ff;


    d := i shl 16;
    psia^.value [3] := d and $ff;


    d := i shl 8;
    psia^.value [4] := d and $ff;
    psia^.value [5] := i and $ff;
  end;


  pn := p + 1;
  authorityCount := 0;
  while lstrlen (pn) > 0 do
  begin
    p := StrScan (pn, '-');
    if Assigned (p) then
    begin
      p^ := #0;
      i := StrToInt (pn);
      pn := p + 1 
    end
    else
    begin
      i := StrToInt (pn);
      pn := pn + lstrlen (pn)
    end;


    if sidLen < sizeof (DWORD) then
      raise Exception.Create ('Bad SID');


    PDWORD (ps)^ := i;
    Inc (ps, sizeof (DWORD));
    Dec (sidLen, sizeof (DWORD));
    Inc (authorityCount);
  end;


  pa^ := char (authorityCount);
  if not IsValidSID (sid) then
    raise Exception.Create ('Bad SID');
end;


function AddAceToWindowStation(hwinsta: HWINSTA; psid: PSID): Boolean;
var
si: SECURITY_INFORMATION;
psd, psdNew: PSECURITY_DESCRIPTOR;
dwSidSize, dwSdSizeNeeded, dwNewAclSize: DWORD;
bDaclPresent, bDaclExist: LongBool;
pdacl, pNewAcl: PACL;
aclSizeInfo: ACL_SIZE_INFORMATION;
i: integer;
pTempAce: PACE_HEADER;
pace: ^ACCESS_ALLOWED_ACE;
begin
Result := False;
si := DACL_SECURITY_INFORMATION;
pace := nil;
psd := nil;
dwSidSize := 0;
pNewAcl := nil;
psdNew := nil;
// Obtain the DACL for the window station.

try
if not GetUserObjectSecurity(hwinsta, si, psd, dwSidSize, dwSdSizeNeeded) then begin
if GetLastError = ERROR_INSUFFICIENT_BUFFER then begin
psd := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwSdSizeNeeded);
if psd = nil then
Exit;

psdNew := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwSdSizeNeeded);
if psdNew = nil then
Exit;

dwSidSize := dwSdSizeNeeded;

if not GetUserObjectSecurity(hwinsta, si, psd, dwSidSize, dwSdSizeNeeded) then
Exit;
end
else begin
Exit;
end;
end;

// Create a new DACL.

if not InitializeSecurityDescriptor(psdNew, SECURITY_DESCRIPTOR_REVISION) then
Exit;

// Get the DACL from the security descriptor.

if not GetSecurityDescriptorDacl(psd, bDaclPresent, pdacl, bDaclExist) then
Exit;

// Initialize the ACL.

ZeroMemory(@aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION));
aclSizeInfo.AclBytesInUse := SizeOf(ACL);

// Call only if the DACL is not NULL.

if pdacl <> nil then begin
// get the file ACL size info
if not GetAclInformation(pdacl, @aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION), AclSizeInformation) then
Exit;
end;

// Compute the size of the new ACL.

dwNewAclSize := aclSizeInfo.AclBytesInUse + (2 * SizeOf(ACCESS_ALLOWED_ACE)) + (2 * GetLengthSid(psid)) - (2 * SizeOf(DWORD));

// Allocate memory for the new ACL.

pNewAcl := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwNewAclSize);

if pNewAcl = nil then
Exit;

// Initialize the new DACL.

if not InitializeAcl(pNewAcl, dwNewAclSize, ACL_REVISION) then
Exit;

// If DACL is present, copy it to a new DACL.

if bDaclPresent then begin
// Copy the ACEs to the new ACL.
if aclSizeInfo.AceCount > 0 then begin
for i := 0 to aclSizeInfo.AceCount - 1 do begin
// Get an ACE.
if not GetAce(pdacl, i, Pointer(pTempAce)) then
Exit;

// Add the ACE to the new ACL.
if not AddAce(pNewAcl, ACL_REVISION, MAXDWORD, pTempAce, pTempAce.AceSize) then
Exit;
end;
end;
end;

// Add the first ACE to the window station.

pace := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - SizeOf(DWORD));

if pace = nil then
Exit;

pace.Header.AceType := ACCESS_ALLOWED_ACE_TYPE;
pace.Header.AceFlags := CONTAINER_INHERIT_ACE or INHERIT_ONLY_ACE or OBJECT_INHERIT_ACE;
pace.Header.AceSize := SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - SizeOf(DWORD);
pace.Mask := GENERIC_ACCESS;

if not CopySid(GetLengthSid(psid), @pace.SidStart, psid) then
Exit;

if not AddAce(pNewAcl, ACL_REVISION, MAXDWORD, pace, pace.Header.AceSize) then
Exit;

// Add the second ACE to the window station.

pace.Header.AceFlags := NO_PROPAGATE_INHERIT_ACE;
pace.Mask := WINSTA_ALL;

if not AddAce(pNewAcl, ACL_REVISION, MAXDWORD, pace, pace.Header.AceSize) then
Exit;

// Set a new DACL for the security descriptor.

if not SetSecurityDescriptorDacl(psdNew, True, pNewAcl, False) then
Exit;

// Set the new security descriptor for the window station.

if not SetUserObjectSecurity(hwinsta, si, psdNew) then
Exit;

// Indicate success.

Result := True;
finally
// Free the allocated buffers.

if pace <> nil then
HeapFree(GetProcessHeap, 0, pace);

if pNewAcl <> nil then
HeapFree(GetProcessHeap, 0, pNewAcl);

if psd <> nil then
HeapFree(GetProcessHeap, 0, psd);

if psdNew <> nil then
HeapFree(GetProcessHeap, 0, psdNew);
end;
end;

function GetLogonSID(hToken: THandle; var ppsid: PSID): Boolean;
var dwLength: DWORD;
  ptg: ^TOKEN_GROUPS;
  i: integer;
begin
  Result := False;
  dwLength := 0;
  ptg := nil;

  try
    // Verify the parameter passed in is not NULL.
    // if ppsid = nil then
    // Exit;

    // Get required buffer size and allocate the TOKEN_GROUPS buffer.
    if not GetTokenInformation(hToken, TokenGroups, ptg, 0, dwLength) then
    begin
      if GetLastError <> ERROR_INSUFFICIENT_BUFFER then
      begin
        ShowMessage('GetTokenInformation failed');
        Exit;
      end;

      ptg := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwLength);
      if ptg = nil then
      begin
        Exit;
      end;

      // Get the token group information from the access token.

      if not GetTokenInformation(hToken, TokenGroups, ptg, dwLength, dwLength) then
      begin
        Exit;
      end;

      // Loop through the groups to find the logon SID.

      for i := 0 to ptg.GroupCount-1 do
      begin
        if ptg.Groups[i].Attributes and SE_GROUP_LOGON_ID = SE_GROUP_LOGON_ID then
        begin
          // Found the logon SID; make a copy of it.
          dwLength := GetLengthSid(ptg.Groups[i].Sid);
          ppsid := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwLength);
          if ppsid = nil then
          begin
            Exit;
          end;
          if not CopySid(dwLength, ppsid, ptg.Groups[i].Sid) then
          begin
            raise exception.Create(Format('CopySid: %s', [SysErrorMessage(GetLastError)]));
            HeapFree(GetProcessHeap, 0, ppsid);
            Exit;
          end;
          Break;
        end;
      end;
      Result := True;
    end;
  finally
    // Free the buffer for the token groups.
    if ptg <> nil then
    begin
      HeapFree(GetProcessHeap, 0, ptg);
    end;
  end;
end;

function AddAceToDesktop(hdesktop: HDESK; ps: PSID): Boolean;
var
aclSizeInfo: ACL_SIZE_INFORMATION;
bDaclExist, bDaclPresent: LongBool;
dwNewAclSize, dwSidSize, dwSdSizeNeeded: DWORD;
pdacl, pNewAcl: PACL;
psd, psdNew: PSECURITY_DESCRIPTOR;
pTempAce: PACE_HEADER;
si: SECURITY_INFORMATION;
i: integer;
begin
Result := False;
psd := nil;
psdNew := nil;
pNewAcl := nil;
si := DACL_SECURITY_INFORMATION;
dwSidSize := 0;
try
// Obtain the security descriptor for the desktop object.

if not GetUserObjectSecurity(hdesktop, si, psd, dwSidSize, dwSdSizeNeeded) then begin
if GetLastError = ERROR_INSUFFICIENT_BUFFER then begin
psd := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwSdSizeNeeded);
if psd = nil then
Exit;

psdNew := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwSdSizeNeeded);
if psdNew = nil then
Exit;

dwSidSize := dwSdSizeNeeded;

if not GetUserObjectSecurity(hdesktop, si, psd, dwSidSize, dwSdSizeNeeded) then
Exit;
end
else begin
Exit;
end;
end;

// Create a new security descriptor.

if not InitializeSecurityDescriptor(psdNew, SECURITY_DESCRIPTOR_REVISION) then
Exit;

// Obtain the DACL from the security descriptor.

if not GetSecurityDescriptorDacl(psd, bDaclPresent, pdacl, bDaclExist) then
Exit;

// Initialize.

ZeroMemory(@aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION));
aclSizeInfo.AclBytesInUse := SizeOf(ACL);

// Call only if NULL DACL.

if pdacl <> nil then begin
// Determine the size of the ACL information.

if not GetAclInformation(pdacl, @aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION), AclSizeInformation) then
Exit;
end;

// Compute the size of the new ACL.

dwNewAclSize := aclSizeInfo.AclBytesInUse + SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(ps) - SizeOf(DWORD);

// Allocate buffer for the new ACL.

pNewAcl := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwNewAclSize);

if pNewAcl = nil then
Exit;

// Initialize the new ACL.

if not InitializeAcl(pNewAcl, dwNewAclSize, ACL_REVISION) then
Exit;

// If DACL is present, copy it to a new DACL.

if bDaclPresent then begin
// Copy the ACEs to the new ACL.
if aclSizeInfo.AceCount > 0 then begin
for i := 0 to aclSizeInfo.AceCount - 1 do begin
// Get an ACE.
if not GetAce(pdacl, i, Pointer(pTempAce)) then
Exit;

// Add the ACE to the new ACL.
if not AddAce(pNewAcl, ACL_REVISION, MAXDWORD, pTempAce, pTempAce.AceSize) then
Exit;
end;
end;
end;

// Add ACE to the DACL.

if not AddAccessAllowedAce(pNewAcl, ACL_REVISION, DESKTOP_ALL, ps) then
Exit;

// Set new DACL to the new security descriptor.

if not SetSecurityDescriptorDacl(psdNew, True, pNewAcl, False) then
Exit;

// Set the new security descriptor for the desktop object.

if not SetUserObjectSecurity(hdesktop, si, psdNew) then
Exit;

// Indicate success.

Result := True;
finally
// Free buffers.

if pNewAcl <> nil then
HeapFree(GetProcessHeap, 0, pNewAcl);

if psd <> nil then
HeapFree(GetProcessHeap(), 0, psd);

if psdNew <> nil then
HeapFree(GetProcessHeap(), 0, psdNew);
end;
end;

function StartInteractiveClientProcess(const lpszUsername: PAnsiChar; const lpszDomain: PAnsiChar; const lpszPassword: PAnsiChar; const lpCommandLine: PChar): Boolean;
var hToken   : THandle;
    hdesktop : HDESK;
    hwinst   : HWINSTA;
    hwinstSave: HWINSTA;
    pi       : PROCESS_INFORMATION;
    pS       : PSID;
    si       : STARTUPINFO;
begin
  Result := False;
  hdesktop := 0;
  hwinst := 0;
  hwinstSave := 0;
  pS := nil;

//  try
    // Log the client on to the local computer.

//    if not LogonUser(lpszUsername, lpszDomain, lpszPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hToken) then
    if not LogonUser(lpszUsername, lpszDomain, lpszPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, hToken) then
    begin
    ShowMessageFmt('Logonuser failed: %s', [SysErrorMessage(GetLastError)]);
      Result := False;
    //      raise exception.create(Format('LogonUser: %s', [SysErrorMessage(GetLastError)]));
      Exit;
    end;
    OutputDebugString(PChar(Format('LogonUser: %s', [SysErrorMessage(GetLastError)])));

    // Save a handle to the caller's current window station.
    hwinstSave := GetProcessWindowStation;
    if hwinstSave = 0 then
    begin
      raise exception.create(Format('GetProcessWindowStation: %s', [SysErrorMessage(GetLastError)]));
      Exit;
    end;
    OutputDebugString(PChar(Format('GetProcessWindowStation: %s', [SysErrorMessage(GetLastError)])));
//\Sessions\2\Windows\WindowStations\WinSta0\Default
    // Get a handle to the interactive window station.
    hwinst := OpenWindowStation('WinSta0', False, READ_CONTROL or WRITE_DAC);
    if hwinst = 0 then
    begin
      raise exception.create(Format('OpenWindowStation: %s', [SysErrorMessage(GetLastError)]));
      Exit;
    end;
    OutputDebugString(PChar(Format('OpenWindowStation: %s', [SysErrorMessage(GetLastError)])));

    // To get the correct default desktop, set the caller's
    // window station to the interactive window station.

    if not SetProcessWindowStation(hwinst) then
    begin
      raise exception.create(Format('SetProcessWindowStation(hwinst): %s', [SysErrorMessage(GetLastError)]));
      Exit;
    end;
    OutputDebugString(PChar(Format('SetProcessWindowStation(hwinst): %s', [SysErrorMessage(GetLastError)])));

    // Get a handle to the interactive desktop.
    hdesktop := OpenDesktop('default', 0, False, READ_CONTROL or WRITE_DAC or DESKTOP_WRITEOBJECTS or DESKTOP_READOBJECTS);
    if hdesktop = 0 then
    begin
      raise exception.create(Format('OpenDesktop: %s', [SysErrorMessage(GetLastError)]));
      Exit;
    end;
    OutputDebugString(PChar(Format('OpenDesktop: %s', [SysErrorMessage(GetLastError)])));

    // Restore the caller's window station.

    if not SetProcessWindowStation(hwinstSave) then
    begin
      raise exception.create(Format('SetProcessWindowStation(hwinstSave): %s', [SysErrorMessage(GetLastError)]));
      Exit;
    end;


    // Get the SID for the client's logon session.
    if not GetLogonSID(hToken, pS) then
    begin
      raise exception.create(Format('GetLogonSID: %s', [SysErrorMessage(GetLastError)]));
      Exit;
    end;

    // Allow logon SID full access to interactive window station.

    if not AddAceToWindowStation(hwinst, pS) then
    begin
      raise exception.create(Format('AddAceToWindowStation: %s', [SysErrorMessage(GetLastError)]));
      Exit;
    end;

    // Allow logon SID full access to interactive desktop.
    if not AddAceToDesktop(hdesktop, pS) then
    begin
      raise exception.create(Format('AddAceToDesktop: %s', [SysErrorMessage(GetLastError)]));
      Exit;
    end;

    // Impersonate client to ensure access to executable file.
    if not ImpersonateLoggedOnUser(hToken) then
    begin
      raise exception.create(Format('ImpersonateLoggedOnUser: %s', [SysErrorMessage(GetLastError)]));
      Exit;
    end;

    // Initialize the STARTUPINFO structure.
    // Specify that the process runs in the interactive desktop.
    ZeroMemory(@si, SizeOf(STARTUPINFO));
    si.cb := SizeOf(STARTUPINFO);
    si.lpDesktop := PChar('Sessions\3\Windows\WindowStations\WinSta0\Default');

    // Launch the process in the client's logon session.
    Result := CreateProcessAsUser(hToken, nil, lpCommandLine, nil, nil, False, // handles are not inheritable
                                  NORMAL_PRIORITY_CLASS or CREATE_NEW_CONSOLE,
                                  nil, nil, si, pi);

    // End impersonation of client.
    RevertToSelf();

    if Result and (pi.hProcess <> INVALID_HANDLE_VALUE) then
    begin
      WaitForSingleObject(pi.hProcess, INFINITE);
      CloseHandle(pi.hProcess);
    end;

    if pi.hThread <> INVALID_HANDLE_VALUE then
    begin
      CloseHandle(pi.hThread);
    end;

    Result := True;

//  finally

    if hwinstSave <> 0 then
    begin
      SetProcessWindowStation(hwinstSave);
    end;

    // Free the buffer for the logon SID.

    if pS <> nil then
    begin
      HeapFree(GetProcessHeap, 0, pS);
    end;

    // Close the handles to the interactive window station and desktop.

    if hwinst <> 0 then
    begin
      CloseWindowStation(hwinst);
    end;

    if hdesktop <> 0 then
    begin
      CloseDesktop(hdesktop);
    end;

    // Close the handle to the client's access token.

    if hToken <> INVALID_HANDLE_VALUE then
    begin
      CloseHandle(hToken);
    end;
  end;
//end;

end.

Dezipaitor 12. Aug 2007 21:42

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Okay thats great.

I reread your code and found the problem :

I forgot to add the attributes :D
Delphi-Quellcode:
pGroups^.Groups[0].Attributes := SE_GROUP_MANDATORY or
                                     SE_GROUP_ENABLED or
                                     SE_GROUP_ENABLED_BY_DEFAULT or
                                     SE_GROUP_LOGON_ID;

Delphi-Quellcode:
    aLocalGroups := TSecurityIDList.Create(true);
    aLogonSid := GetLogonSID(WTSToken);
[b]OR[/b]
    aLogonSid := GetLogonSID();
   
    aLocalGroups.Add(aLogonSid);
    aLogonSid.Attributes := SE_GROUP_MANDATORY or
                                     SE_GROUP_ENABLED or
                                     SE_GROUP_ENABLED_BY_DEFAULT or
                                     SE_GROUP_LOGON_ID;

You see both versions of GetLogonSID are working.

Question:
Do I have to set the attributes for the my special debugger user ?
Delphi-Quellcode:
aLocalGroups.Add(TSecurityID.Create('','DebuggerUser'));
I get debug privileges but cannot bring the taskmanager to enable "show processes of all users" .
('C:\WINDOWS\system32\taskmgr.exe')


Only this works:
Delphi-Quellcode:
    aSID := AdministratorsSID;
    aLocalGroups.Add(aSID);
    aSID.Attributes := SE_GROUP_MANDATORY or
                                     SE_GROUP_ENABLED or
                                     SE_GROUP_ENABLED_BY_DEFAULT or
                                     SE_GROUP_LOGON_ID;

Remko 12. Aug 2007 21:49

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Just remembered something: GetWtsActiveConsoleSessionID is for XP/2003 and higher. On lower versions the functions doesn't exist and the active console session is always session id 0. If you want to be safe wrap the function and do an OS check and return 0 if NT or 2000.

Dezipaitor 13. Aug 2007 15:44

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
The WTA function family is one more class to be implemented.
OMG the security library grows and grows and grows and did I mention "grows" ?

Remko 13. Aug 2007 16:10

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
LOL
BTW Don't forget to look at my JwaWinSta in that case, it has got some undocumented Terminal Server stuff (shadow sessions, connect to other sessions and obtaining login and idle time for terminal server sessions).

Dezipaitor 13. Aug 2007 17:18

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
I know your unit - its part of the jedi api lib - I converted it :D

I developed a desktop class named UDesktop.
I did this also for winstation - but without implementation - only interface section is available (if I could even find it).

Olli 13. Aug 2007 23:12

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Guys, you are notorious! Get a life! :mrgreen: :zwinker:

Dezipaitor 13. Aug 2007 23:21

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Zitat:

Zitat von Olli
Guys, you are notorious! Get a life! :mrgreen: :zwinker:

And that is saying you who works 25 hours a day ? :mrgreen: :zwinker:

Olli 13. Aug 2007 23:22

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Zitat:

Zitat von Dezipaitor
Zitat:

Zitat von Olli
Guys, you are notorious! Get a life! :mrgreen: :zwinker:

And that is saying you who works 25 hours a day ? :mrgreen: :zwinker:

Argh, you got me :zwinker:

Dezipaitor 13. Aug 2007 23:34

Re: LSALogonUser und Authentifikation (nichts komplexes!)
 
Zitat:

Zitat von Olli
Zitat:

Zitat von Dezipaitor
Zitat:

Zitat von Olli
Guys, you are notorious! Get a life! :mrgreen: :zwinker:

And that is saying you who works 25 hours a day ? :mrgreen: :zwinker:

Argh, you got me :zwinker:

Ah come on - post some useful security code herein. But let it be easy to understand like the title say - nix complex


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:31 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz