4

I'm trying to implement rebooting of a remote computer with InitiateShutdown API using the following code, but it fails with RPC_S_SERVER_UNAVAILABLE or 1722 error code:

//Process is running as administrator

//Select a remote machine to reboot:
//INFO: Tried it with and w/o two opening slashes.
LPCTSTR pServerName = L"192.168.42.105";
//Or use 127.0.0.1 if you don't have access to another machine on your network.
//This will attempt to reboot your local machine.
//In that case make sure to call shutdown /a /m \\127.0.0.1 to cancel it.

if(AdjustPrivilege(NULL, L"SeShutdownPrivilege", TRUE) &&
    AdjustPrivilege(pServerName, L"SeRemoteShutdownPrivilege", TRUE))
{
    int nErrorCode = ::InitiateShutdown(pServerName, NULL, 30,
                                        SHUTDOWN_INSTALL_UPDATES | SHUTDOWN_RESTART, 0);

    //Receive nErrorCode == 1722, or RPC_S_SERVER_UNAVAILABLE
}



BOOL AdjustPrivilege(LPCTSTR pStrMachine, LPCTSTR pPrivilegeName, BOOL bEnable)
{
    HANDLE hToken; 
    TOKEN_PRIVILEGES tkp;
    BOOL bRes = FALSE;

    if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
        return FALSE; 

    if(LookupPrivilegeValue(pStrMachine, pPrivilegeName, &tkp.Privileges[0].Luid))
    {
        tkp.PrivilegeCount = 1;  
        tkp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : SE_PRIVILEGE_REMOVED; 

        bRes = AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
        int nOSError = GetLastError();
        if(bRes)
        {
            if(nOSError != ERROR_SUCCESS)
                bRes = FALSE;
        }
    }

    CloseHandle(hToken);

    return bRes;
}

So to prepare for this code to run I do the following on this computer, which is Windows 7 Pro (as I would do for the Microsoft's shutdown tool):

  • Run the following "as administrator" to allow SMB access to the logged in user D1 on the 192.168.42.105 computer (per this answer):
 NET USE \\192.168.42.105\IPC$ 1234 /USER:D1
  • Run the process with my code above "as administrator".

And then do the following on remote computer, or 192.168.42.105, that has Windows 7 Pro (per answer here with most upvotes):

  • Control Panel, Network and Sharing Center, Change Advanced Sharing settings "Private" enable "Turn on File and Printer sharing"

  • Set the following key:

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System

    LocalAccountTokenFilterPolicy=dword:1

  • RUN secpol.msc, then go to Local Security Policy, Security Settings, Local Policies, User Rights Assignment. Add "Everyone" to "Force shutdown from a remote system". (Just remember to remove it after you're done testing!)

Note that the following shutdown command seems to work just fine to reboot the remote computer:

shutdown /r /m \\192.168.42.105 /t 30

What am I missing with my code?

EDIT:

OK. I will admit that I was merely interested in why InitiateShutdown doesn't seem to "want" to work with a remote server connection, while InitiateSystemShutdownEx or InitiateSystemShutdown had no issues at all. (Unfortunately the latter two did not have the dwShutdownFlags parameter, which I needed to pass the SHUTDOWN_INSTALL_UPDATES flag to, which caused my persistence...)

At this point I had no other way of finding out than dusting out a copy of WinDbg... I'm still trying to dig into it, but so far this is what I found...

(A) It turns out that InitiateSystemShutdownEx internally uses a totally different RPC call. W/o too many details, it initiates RPC binding with RpcStringBindingComposeW using the following parameters:

   ObjUuid = NULL
   ProtSeq = ncacn_np
   NetworkAddr = \\192.168.42.105
   EndPoint = \\PIPE\\InitShutdown
   Options = NULL

or the following binding string:

ncacn_np:\\\\192.168.42.105[\\PIPE\\InitShutdown]

(B) While InitiateShutdown on the other hand uses the following binding parameters:

   ObjUuid = 765294ba-60bc-48b8-92e9-89fd77769d91
   ProtSeq = ncacn_ip_tcp
   NetworkAddr = 192.168.42.105
   EndPoint = NULL
   Options = NULL

which it later translates into the following binding string:

ncacn_np:\\\\192.168.42.105[\\PIPE\\lsarpc]

that it uses to obtain the RPC handle that it passes to WsdrInitiateShutdown (that seems to have its own specification):

enter image description here

So as you see, the InitiateShutdown call is technically treated as Unknown RPC service (for the UUID {765294ba-60bc-48b8-92e9-89fd77769d91}), which later causes a whole bunch of credential checks between the server and the client:

enter image description here

which, honestly, I'm not sure I want to step into with a low-level debugger :)

At this stage I will say that I am not very well versed on "Local Security Authority" interface (or the \PIPE\lsarpc named pipe configuration.) So if anyone knows what configuration is missing on the server side to allow this RPC call to go through, I would appreciate if you could post your take on it?

Jonathon
  • 778
  • 8
  • 21
c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • Comments are not for extended discussion; this conversation has been [moved to chat](http://chat.stackoverflow.com/rooms/101312/discussion-on-question-by-c00000fd-initiateshutdown-fails-with-rpc-s-server-unav). – George Stocker Jan 21 '16 at 14:47
  • 2
    I can reproduce the OPs problem; InitiateShutdown does not work for remote machines, even within a domain. Looks like a Windows bug. – Harry Johnston Jan 21 '16 at 23:06
  • @HarryJohnston: I'm curious if you were able to get anywhere with it? It seems like it's still not fixed on my Windows 10. – c00000fd Feb 09 '18 at 18:32
  • Nothing to report. – Harry Johnston Feb 10 '18 at 00:05

0 Answers0