Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations John Tel on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Impersonating a user during shutdown 2

Status
Not open for further replies.

Griffyn

Programmer
Jul 11, 2002
1,077
AU
Hi all,

I've been playing with the WinLogon Notification Package, which amounts to writing a DLL and adding some registry entries to the [tt]HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify[/tt] key, ie.
Code:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify\Backup]
"Asynchronous"=dword:00000000
"Impersonate"=dword:00000001
"DllName"="BackupSys.dll"
"Logoff"="RunBackup"

This tells Windows that when logging off, to run the [tt]RunBackup[/tt] procedure in the BackupSys.dll library I wrote. This all works, which should be cause for joy, except that the backup procedure I want to run is via a batch file (the batch file does get launched) and I'm getting access denied messages when attempting to access a network share.

This could be for two reasons that I can think of:
[ol][li]The network connections have already been closed by the time my batch file is running[/li]
[li]The batch file is being run not as the logged on user, and so doesn't have permission to access the network share[/li][/ol]

This is the relevant bit of my library code:
Code:
[b]type[/b]
  [blue]// this structure required in procedure header[/blue]
  PWLX_NOTIFICATION_INFO = ^TWLX_NOTIFICATION_INFO;
  TWLX_NOTIFICATION_INFO = [b]record[/b]
    Size: ULong;
    Flags: ULong;
    Username: PWideChar;
    WindowStation: PWideChar;
    hToken: THandle;
    hDesktop: HDesk;
    pStatusCallback: LongInt;
  [b]end[/b];

[b]procedure[/b] RunBackup(pInfo: PWLX_NOTIFICATION_INFO); [b]safecall[/b];
[b]var[/b]
  r : Integer;
[b]begin[/b]
  ImpersonateLoggedOnUser(pInfo^.hToken);
  WinExecAndWait32('c:\backuptest\go.bat', SW_SHOW, INFINITE, r);
[b]end[/b];
I added the [tt]ImpersonateLoggedOnUser[/tt] line after seeing it on another forum, but it hasn't helped. Just to reiterate, the batch file is getting called, but failing when trying to access the network share.

Can anyone give me some help in getting this working?

Thanks.
 
you need to have a user that is logged on BEFORE you can use impersonateloggedonuser. so try to add this line :

Code:
hToken = LogonUser(user,domain,password,LOGON32_LOGON_BATCH,LOGON32_PROVIDER_DEFAULT)
ImpersonateLoggedOnUser(hToken);

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
Hi whosrdaddy,

I can't get LogonUser to work. My code is now
Code:
[b]var[/b]
  hToken: THandle;
[b]begin[/b]
  [b]if[/b] LogonUser('myuser','mydomain','mypass',LOGON32_LOGON_BATCH,LOGON32_PROVIDER_DEFAULT, hToken) [b]then[/b]
  [b]begin[/b]
    ImpersonateLoggedOnUser(hToken);
    WinExecAsUserAndWait32('c:\backuptest\go.bat', SW_SHOW, INFINITE, hToken, r);
  [b]end[/b];
[b]end[/b];

But the inner block never gets called, because [tt]LogonUser[/tt] is failing. I'm using a WinXP VM machine, the user and password are correct, and the domain is the computer name.

But then, even if it did work, I don't think I can use this approach, because I need to deploy this library to many users, so I can't embed their username and password into the library. Plus - the Winlogon Notification Package is already providing a hToken handle which I was using - shouldn't this work?
 
Yeah it should. I think that the winexec function is not run in the right context. take a look at the CreateprocessWithLogonW function (full explanation on MSDN)

here's a torry example :


CreateprocessWithLogonW does NOT work under local system account. so make sure that is not the case.

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
CreateProcessWithLogonW also requires the username and password to be passed to it, so that's no good to me. I noticed on the MSDN site a function called CreateProcessWithTokenW which seems more suitable, but the documentation says it's only available Win2003 Server, and my library doesn't do anything when I try to link to it.

 
I think I found what I'm looking for, but haven't got it to work yet.

There's a Windows function called DuplicateTokenEx which, according to the help file for CreateProcessAsUser
Delphi Help said:
Alternatively, you can call the DuplicateTokenEx function to convert an impersonation token into a primary token. This allows a server application that is impersonating a client to create a process that has the security context of the client.

But I can't get DuplicateTokenEx to succeed (keeps returning False). I need to use it to create a PrimaryToken from the ImpersonationToken that I'm receiving from the Winlogon Notification Package.

My code for DuplicateTokenEx is
Code:
[b]var[/b]
  pToken: Cardinal;
  pSecAttrib: PSecurityAttributes;
[b]begin[/b]
  New(pSecAttrib);
  [b]try[/b]
    [blue]//not really sure what I'm doing with [b]pSecAttrib[/b][/blue]
    pSecAttrib^.nLength := SizeOf(pSecAttrib^);
    pSecAttrib^.bInheritHandle := True;
    [b]if[/b] DuplicateTokenEx(pInfo^.hToken, 0, pSecAttrib, SecurityImpersonation, TokenPrimary, pToken) [b]then[/b]
        writeln('allocated duplicate token')
      [b]else[/b]
        writeln('denied duplicate token');
    [blue]//call routine that calls CreateProcessAsUser using [b]PHandle(pToken)^[/b] as the token[/blue]
  [b]finally[/b]
    Dispose(pSecAttrib);
  [b]end[/b];
[b]end[/b];

I've tried just passing pToken straight to CreateProcessAsUser (process doesn't get called), but then I saw that it returns a pointer to the token, so I dereferenced it as per the comment in the code. But still no workie.
 
mmm, it is still unclear for me under what user context you dll is run. still looking for a solution here...

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
this is the best what I could find, but it is written in .NET and is used to spawn a process from server-side APS.NET. so I don't know this will help you:


-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
ah HA!

[bigsmile] [sunshine] [2thumbsup]

got it! yes!

Code:
[b]type[/b]
  PFNMSGECALLBACK = [b]function[/b] (bVerbose: BOOL; lpMessage: PWideChar): DWORD; [b]stdcall[/b];
  [navy]{$EXTERNALSYM PFNMSGECALLBACK}[/navy]
  TFnMsgeCallback = PFNMSGECALLBACK;

  PWLX_NOTIFICATION_INFO = ^WLX_NOTIFICATION_INFO;
  [navy]{$EXTERNALSYM PWLX_NOTIFICATION_INFO}[/navy]
  _WLX_NOTIFICATION_INFO = [b]record[/b]
    Size: ULONG;
    Flags: ULONG;
    UserName: PWideChar;
    Domain: PWideChar;
    WindowStation: PWideChar;
    hToken: THandle;
    hDesktop: HDESK;
    pStatusCallback: PFNMSGECALLBACK;
  [b]end;[/b]
  [navy]{$EXTERNALSYM _WLX_NOTIFICATION_INFO}[/navy]
  WLX_NOTIFICATION_INFO = _WLX_NOTIFICATION_INFO;
  [navy]{$EXTERNALSYM WLX_NOTIFICATION_INFO}[/navy]
  TWlxNotificationInfo = WLX_NOTIFICATION_INFO;
  PWlxNotificationInfo = PWLX_NOTIFICATION_INFO;

[b]procedure[/b] RunBackup(pInfo: PWLX_NOTIFICATION_INFO); [b]safecall[/b];
[b]var[/b]
  r : Integer;
  pToken : Cardinal;
  pSecAttrib: PSecurityAttributes;
[b]begin[/b]
  New(pSecAttrib);
  [b]try[/b]
    pSecAttrib^.nLength := SizeOf(pSecAttrib^);
    pSecAttrib^.lpSecurityDescriptor := [b]nil[/b];
    pSecAttrib^.bInheritHandle := False;
    [b]if[/b] DuplicateTokenEx(pInfo^.hToken, 0, pSecAttrib, SecurityIdentification, TokenPrimary, pToken) [b]then[/b]
      WinExecAsUserAndWait32([teal]'c:\backuptest\go.bat'[/teal], SW_SHOW, INFINITE, pToken, r);
[navy]// The above WinExec... function calls CreateProcessAsUser, and passing pToken[/navy]
  [b]finally[/b]
    Dispose(pSecAttrib);
  [b]end[/b];
[b]end[/b];

[b]exports[/b]
  RunBackup;

[b]begin[/b]
  DisableThreadLibraryCalls(GetModuleHandle([teal]'BackupSys.dll'[/teal]));
[b]end[/b].

Finally got it cobbled together with your help, plus a french and a russian forum. It seems every example had a slightly different implementation - a different WLX... structure, or variables being used.

And despite DuplicateTokenEx returning a pointer to the new token, dereferencing the pointer doesn't work, and passing the cardinal value straight to CreateProcessAsUser works fine.

But all works great now. What a relief.

Thanks again whosrdaddy!
 
a star for you too, because this is no easy stuff.
glad you found the answer!


[2thumbsup]


-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
After all that, it appears Windows only waits 60 seconds before killing any process launched via this method - way to short for most backups.

I tried discarding everything a having a service application with an OnShutdown event handler that did the same thing, but Windows only waits 5 seconds(!) before killing it. Considering some computers are taking 5-10 hours to do their backup, this approach is entirely inappropriate for this task.

So, after all that, I'm going to do things the other way around. Train the users to double-click their Backup icon (I may just name it Shutdown to keep things simple), and the last program to run during the batch process will shutdown windows.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top