Xpra-org / xpra

Persistent remote applications for X11; screen sharing for X11, MacOS and MSWindows.
https://xpra.org/
GNU General Public License v2.0
1.91k stars 164 forks source link

win32 system service #1527

Closed totaam closed 4 years ago

totaam commented 7 years ago

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 #389 comment 23.

totaam commented 7 years ago

Blocked by #1528.

totaam commented 7 years ago

With the cx_freeze 5 workarounds added in #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.

totaam commented 7 years ago

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:

totaam commented 7 years ago

Difficulties in building the Complete Service Example with mingw:

totaam commented 7 years ago

2017-05-24 17:31:26: antoine uploaded file service-mingw.patch (2.7 KiB)

patch for building the service example with mingw

totaam commented 7 years ago

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

Still TODO:


Notes:

totaam commented 7 years ago

2017-05-25 15:21:27: antoine uploaded file start-shadow-from-proxy.patch (8.1 KiB)

ugly work in progress patch to use on top of r15971

totaam commented 7 years ago

2017-05-26 12:30:43: antoine uploaded file logon.patch (22.6 KiB)

work in progress logon patch

totaam commented 7 years ago

The ugly and incomplete patch above does:


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:

totaam commented 7 years ago

2017-05-27 10:12:19: antoine uploaded file logon-v2.patch (50.2 KiB)

logon succeeds but launching the new process does not..

totaam commented 7 years ago

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.

totaam commented 7 years ago

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

totaam commented 7 years ago

2017-05-28 07:15:54: antoine uploaded file logon.py (17.8 KiB)

this implementation does not work - but has some useful functions

totaam commented 6 years ago

See #389 comment 23 for LogonUser links.

totaam commented 6 years ago

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

totaam commented 5 years ago

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

totaam commented 5 years ago

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.

totaam commented 5 years ago

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

totaam commented 5 years ago

aUpdates:

Some related pointers:

totaam commented 5 years ago

aUpdates:

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

totaam commented 5 years ago

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.

totaam commented 5 years ago
totaam commented 5 years ago

2019-09-01 12:32:06: @totaam uploaded file service-test.patch (3.8 KiB)

tweaks to make it easier to test and debug the service

totaam commented 5 years ago

2019-09-01 12:33:09: @totaam uploaded file logon.c (18.9 KiB)

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

totaam commented 5 years ago

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:

totaam commented 4 years ago

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:

totaam commented 4 years ago

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.

totaam commented 4 years ago

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

totaam commented 4 years ago

Maybe the blank display problems (comment:17 IIRC), can be fixed by using a SYSTEM privilege, just like -s with psexec, see #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

totaam commented 4 years ago

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:

totaam commented 4 years ago

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), Creating a Remote Desktop Client Application without using Windows Forms (C#), How to use ActiveX component in ClassLibrary without Winforms.

To compile any of those C# based solutions, we need the AxMSTSCLib.dll. Where is the AxMSTSCLib library located? The answer (not found in that link) is actually here: 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. %%\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.

totaam commented 4 years ago

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

Options:

totaam commented 4 years ago

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)

totaam commented 4 years ago

2020-05-01 15:49:42: @totaam uploaded file win32-system-proxy.patch (3.8 KiB)

switch back to launching the proxy

totaam commented 4 years ago

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!

totaam commented 4 years ago

2020-05-02 06:53:59: @totaam uploaded file sessions.py (7.5 KiB)

various ill-fated attempts to get the list of sessions

totaam commented 4 years ago

Sort of working after:


TODO:


Other APIs and projects which may still be useful:

totaam commented 4 years ago

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:

totaam commented 4 years ago

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..