Xpra: Ticket #1527: win32 system service

Split from #389, similar to #1105 for Linux, we want to have a system wide service running even before the user logs in. Users can then trigger a login.

Some links already added in ticket:389#comment:23.



Tue, 23 May 2017 12:49:34 GMT - Antoine Martin: status changed

Blocked by #1528.


Tue, 23 May 2017 15:58:13 GMT - Antoine Martin:

With the cx_freeze 5 workarounds added in ticket:1528#comment:2 and the service stub added in r15933 +

r15934 (based on cx_Freeze samples: service), a "Xpra-Service.exe" is created - it just doesn't do anything when you run it...

The documentation is non-existent, and the executable doesn't give you any help at all. But I eventually found that we're supposed to install it by running:

Xpra-Service.exe --install NAME [configfile]

Problem is that this fails with:

Service not installed. See log file for details.

What log file you ask? Well "./.log" obviously, NOT. This file contains:

[04380] 2017/05/23 22:46:14.473 starting logging at level ERROR
[04380] 2017/05/23 22:46:14.520 Win32 error 0x5 encountered.
[04380] 2017/05/23 22:46:14.520     Context: cannot open service manager
[04380] 2017/05/23 22:46:14.520     Message: Access is denied.
[04380] 2017/05/23 22:46:14.520 ending logging

Fine, let's run it as administrator:

[02964] 2017/05/23 22:49:55.695 starting logging at level ERROR
[02964] 2017/05/23 22:49:55.757 Win32 error 0x57 encountered.
[02964] 2017/05/23 22:49:55.757     Context: cannot start service
[02964] 2017/05/23 22:49:55.757     Message: The parameter is incorrect.
[02964] 2017/05/23 22:49:55.773 ending logging

At least the service is registered and visible in the "Services" control tool, and the same error message is available in the "eventvwr". We'll need to get rid of "cx_Logging", it's unmaintained since 2014, undocumented (which filename is used from the service?) and generally not helpful.


Tue, 23 May 2017 17:24:37 GMT - Antoine Martin:

OK, the reason why this fails with the cryptic error 0x57 is because the path to the service binary is empty (for whatever reason). Assuming that the service has been installed using:

Xpra-Service.exe --install XPRA-TEST

The path can then be changed using regedit, or using Sc config. First query the service:

sc qc XpraXPRA-TEST

Change the path:

sc config XpraXPRA-TEST binPath= "C:\Program Files\Xpra\Xpra-Service.exe"

We can now also set it to auto-start:

sc config XpraXPRA-TEST start= auto

With these changes, starting the service takes longer to fail.. but fail it does with: Error 1053: The service did not respond to the start or control request in a timely fashion We can find the service PID with:

sc queryex XpraXPRA-TEST

Then kill it with:

taskkill /f /pid $PID

We could workaround the broken path installation by running the "sc config" commands after the service registration, still as part of the EXE / MSI

installation process.

But the fact that the process does not respond is more problematic... Ideas:

The shim would allow us to continue to use cx_freeze 4.x (no need for #1528). Some links for that:


Wed, 24 May 2017 16:31:00 GMT - Antoine Martin:

Difficulties in building the Complete Service Example with mingw:


Wed, 24 May 2017 16:31:26 GMT - Antoine Martin: attachment set

patch for building the service example with mingw


Thu, 25 May 2017 07:50:12 GMT - Antoine Martin:

Stub cx_freeze5 service removed in r15960 (#1528 re-scheduled), replaced in r15962 by a shim implemented in C.

Still TODO:


Notes:


Thu, 25 May 2017 14:21:26 GMT - Antoine Martin: attachment set

ugly work in progress patch to use on top of r15971


Fri, 26 May 2017 11:30:42 GMT - Antoine Martin: attachment set

work in progress logon patch


Fri, 26 May 2017 11:34:48 GMT - Antoine Martin:

The ugly and incomplete patch above does:

And adds a small Login-Test.exe utility. Problem is that this works when running from the service context, but not when running directly from the utility - even when running from an administrator shell. This is going to make development and testing tedious.

Some useful links:


Sat, 27 May 2017 09:12:18 GMT - Antoine Martin: attachment set

logon succeeds but launching the new process does not..


Sat, 27 May 2017 10:15:16 GMT - Antoine Martin:

With the patch above, I could logon but when trying to start the server, or even a test application like whoami.exe with all of its dlls installed, the event log would show:

Application popup: Xpra_cmd.exe - Application Error : \
The application was unable to start correctly (0xc0000142). Click OK to close the application.

The environment looked suspicious, but since we use the LOGON_WITH_PROFILE flag, it should be OK. (will need to re-check that) See What is up with "The application failed to initialize properly (0xc0000142)" error?, which has lots of relevant information. (and some dead links too.. sigh) Also some pointers here: The Perils and Pitfalls of Launching a Process Under New Credentials. It was just missing the desktop name in the STARTUPINFO.... (not obvious) Now maybe we also need permission to access that desktop? As per CreateProcessAsUser() windowstations and desktops, because the resulting screen is empty.


Sun, 28 May 2017 06:14:43 GMT - Antoine Martin:

r15980 adds all the hooks for starting the shadow process from the service. Still TODO:


Sun, 28 May 2017 06:15:54 GMT - Antoine Martin: attachment set

this implementation does not work - but has some useful functions


Mon, 07 May 2018 05:38:19 GMT - Antoine Martin: milestone changed

See ticket:389#comment:23 for LogonUser links.


Sat, 04 Aug 2018 19:07:29 GMT - Antoine Martin:

See Another Windows 10 SKU is on its way, this time for remote desktops: With the new SKU, the multi-session capability is now a part of desktop Windows


Thu, 14 Mar 2019 14:19:29 GMT - Antoine Martin: milestone changed


Tue, 19 Mar 2019 05:29:55 GMT - Antoine Martin:

See also: python-win32: runas analog: Starting a subprocess in Windows is somewhat of a black art, I fear.


Mon, 26 Aug 2019 17:18:55 GMT - Antoine Martin:

Fixes and updates:

The service somehow fails to find the glib typelib. Debugging this is tedious, will add yet another shim to make it easier to debug.


Tue, 27 Aug 2019 06:04:51 GMT - totaamwin32:

The gi bindings error is an unrelated packaging blocker bug: #2393.


Tue, 27 Aug 2019 09:09:43 GMT - totaamwin32:

Updates:

Some related pointers:


Wed, 28 Aug 2019 11:32:50 GMT - totaamwin32:

Updates:

The shadow we start creates a named-pipe, but the proxy server running as "system" cannot connect to it. Some pointers:


Thu, 29 Aug 2019 16:46:22 GMT - totaamwin32:

Updates:

With these changes, the proxy manages to start a shadow subprocess, but it doesn't seem to reach a usable state. Redirecting its output to a log file shows that it goes through server initialization, it stops after loading a few icons.


Fri, 30 Aug 2019 10:13:29 GMT - totaamwin32:

Updates:


Sun, 01 Sep 2019 11:32:05 GMT - totaamwin32: attachment set

tweaks to make it easier to test and debug the service


Sun, 01 Sep 2019 11:33:09 GMT - totaamwin32: attachment set

logon.c example from "Starting an Interactive Client Process in C++"


Sun, 01 Sep 2019 11:41:08 GMT - totaamwin32:

We need to either shadow the console (so remote users can login from the login screen) or find a way to create a session programmatically.

More pointers:


Sat, 14 Sep 2019 04:24:21 GMT - Antoine Martin: milestone changed

Too late for v3.


Wed, 12 Feb 2020 05:16:10 GMT - Antoine Martin: milestone changed


Sun, 05 Apr 2020 14:26:20 GMT - Antoine Martin:

Progress made using the openssh server (#2711): we can shadow the Winlogon secure desktop using https://docs.microsoft.com/en-us/sysinternals/downloads/psexec.

More pointers:

Also: can bitvise ssh server overcome the session 0 isolation problem? No, nothing can get around it. What Bitvise does is properly associate a Windows Station (something which exists within a Desktop, which exists within a Session) with programs so that they will manage to run when invoked headlessly when the server is running within an interactive session.


Sun, 05 Apr 2020 15:12:42 GMT - Antoine Martin: milestone changed

r25977 starts the shadow server on the Winlogon secure desktop, it's more useful than the proxy it replaces since we can actually see the desktop.

To make more progress, it would be useful to:

As for the change of desktop, that's going to be harder. GTK probably can't be taught to re-initialize against the new display, so we would need to spawn a new process and either give it the existing socket connection, or proxy it.


Sun, 05 Apr 2020 17:43:22 GMT - Antoine Martin:

r25980: we can now easily start new commands when connected to a shadow server


Tue, 07 Apr 2020 16:27:44 GMT - Antoine Martin:

Maybe the blank display problems (comment:17 IIRC), can be fixed by using a SYSTEM privilege, just like -s with psexec, see ticket:2711#comment:1.

Also: CreateProcessAsUser creates blank/black window: The call to LogonUser generates a new session (and associated logon SID) rather than reusing the existing one, so your process does not have access to the desktop, and only has minimal access to the window station


Mon, 27 Apr 2020 14:56:39 GMT - Antoine Martin:

r26188 uses paexec (as per #2711) to start the shadow server.

Tested by reverting r25977 and then connecting to the proxy server using:

xpra shadow "ssl://user:password@server:14500/" --ssl-server-verify=none

Progress! This now allows the shadow server to access the GUI session, even though it is started from a SYSTEM account. But only if the user is already logged in.


If the user is not already logged in, the shadow command will just fail:

New ssl connection received
 from 'CLIENTIP:44224'
 on '0.0.0.0:14500'
Authentication required by win32 authenticator module 1
sending challenge for username 'windows 10 test' using xor digest
proxy_auth win32.get_sessions()=(0, 0, [], {}, {})
proxy_auth(Protocol(..), {..}, None) found sessions: (0, 0, [], {}, {})
proxy_session: displays=[], start_sessions=True, start-new-session={b'mode': b'shadow', b'display': b''}
start_new_session('windows 10 test', 0, 0, {b'mode': b'shadow', b'display': b''}, [])
start_win32_shadow('windows 10 test', {b'mode': b'shadow', b'display': b''})
exec_command('windows 10 test', ['paexec.exe', '-i', '1', '-s', 'C:\\Program Files\\Xpra\\Xpra-Shadow.exe', '--bind=windows_10_test', '-d', 'proxy,win32'], {..})
lsa_logon_user(..)
logon_msv1_s4u(windows 10 test)=..
creation_info=<xpra.platform.win32.create_process_lib.CREATIONINFO object at 0x0000000000c9ca40>
Popen(['paexec.exe', '-i', '1', '-s', 'C:\\Program Files\\Xpra\\Xpra-Shadow.exe', '--bind=windows_10_test', '-d', 'proxy,win32'])=<xpra.platform.win32.create_process_lib.Popen object at 0x0000000000ceb070>
poll()=4294967287
stdout=b''
stderr=b''
start_server_subprocess failed
Traceback (most recent call last):
  File "E:\Xpra\trunk\src/xpra/server/proxy/proxy_server.py", line 349, in proxy_session
    proc, socket_path, display = self.start_new_session(username, uid, gid, sns, displays)
  File "C:\Program Files\Xpra\lib\xpra\platform\win32\proxy_server.py", line 49, in start_new_session
    return self.start_win32_shadow(username, new_session_dict)
  File "C:\Program Files\Xpra\lib\xpra\platform\win32\proxy_server.py", line 94, in start_win32_shadow
    raise Exception("shadow subprocess failed with exit code %s" % r)
Exception: shadow subprocess failed with exit code 4294967287
Error: failed to start server subprocess:
 shadow subprocess failed with exit code 4294967287
disconnect(server error, ('failed to start a new session',))

So we need some other call to create the GUI session. No idea which one.

Pointers:


Tue, 28 Apr 2020 13:16:08 GMT - totaamwin32:

creating window station and windows desktop using c#

Programmatically create and launch and RDP session (without gui)

All of those solutions use windows forms (GUI based), How to use ActiveX component in ClassLibrary without Winforms.

To compile any of those C# based solutions, we need the AxMSTSCLib.dll. SharpRDP: If you do not want to use the provided DLLs you will need to .NET SDK to create the AxMSTSCLib.dll DLL. To create it you'll need to run aximp from the SDK on mstscax.dll. %<SDK dir>%\aximp.exe %windir%\system32\mstscax.dll. (aximp.exe)

To logoff once we're done: WTSLogoffSession function

Eventually, I would like to compile this DLL from the command line (Command-line build with csc.exe), as part of the build process.


Tue, 28 Apr 2020 16:17:05 GMT - totaamwin32:

Next difficulty: calling this dotnet code from the python server without using pythonnet since that's not supported under mingw.

Options:


Fri, 01 May 2020 02:03:18 GMT - Antoine Martin:

Updates in r26196 + r26197 including the VS project to create the DesktopLogon.dll. Reverting r25977 should work, but paexec is now failing (was working fine before..)

The server log files can be located using xpra info | grep log-file. On my win10 test system, they end up in: C:\Windows\System32\config\systemprofile\AppData\Local\Xpra. (both the Xpra-Proxy.log and Xpra-Shadow.log)


Fri, 01 May 2020 14:49:41 GMT - totaamwin32: attachment set

switch back to launching the proxy


Fri, 01 May 2020 16:59:48 GMT - Antoine Martin:

With the patch above, the proxy tries to run:

paexec.exe -i 1 -s "C:\Program Files\Xpra\Xpra-Shadow.exe" --bind=Windows_10_Test -d win32,proxy

When trying this command from an ssh session, paexec now fails (used to work?!):

Remote app failed to start.  Returned error:
  Failed to start "C:\Program Files\Xpra\Xpra-Shadow.exe" --bind=Windows_10_Test -d win32,proxy. Access is denied. [Err=0x5, 5]
PAExec returning exit code -9

Then I also got File not found - that one was completely misleading, nothing to do with the path, just the -i argument needed a different session id!


Sat, 02 May 2020 05:53:58 GMT - totaamwin32: attachment set

various ill-fated attempts to get the list of sessions


Sat, 02 May 2020 15:29:39 GMT - totaamwin32:

Sort of working after:


TODO:


Other APIs and projects which may still be useful:


Sun, 03 May 2020 15:14:19 GMT - Antoine Martin:

Updates:

New weird bug: the service responds on boot (if auto-start is on), responds if started using the service manager when logged in, but it seems to die when a user logs out? (only if it had started a shadow server? even if that process is now gone.. maybe a SIGPIPE?)

So the DesktopSession.dll hack still doesn't work, but using rdesktop -i $USER -p $PASSWORD $IP does what we need ... Just need to emulate it? Without actually showing anything via RDP, and without terminating the session... because disconnecting RDP stops our server receiving anything - the station / desktop are suspended? (logging in resumes the session!)

New links:


Wed, 06 May 2020 06:27:17 GMT - Antoine Martin: status, summary changed; resolution set

This will have to do for this release, follow up in #2756.

The proxy server does work and can start a shadow server for any user, but it is unable to create the station / desktop if one does not exist already..


Sat, 23 Jan 2021 05:26:45 GMT - migration script:

this ticket has been moved to: https://github.com/Xpra-org/xpra/issues/1527