Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/server/auth/exec_auth.py : 62%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of Xpra.
2# Copyright (C) 2017-2020 Antoine Martin <antoine@xpra.org>
3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
4# later version. See the file COPYING for details.
6import os
7from subprocess import Popen
8from gi.repository import GLib
10from xpra.util import envint, typedict
11from xpra.os_util import OSX
12from xpra.child_reaper import getChildReaper
13from xpra.server.auth.sys_auth_base import SysAuthenticator, log
14from xpra.platform.features import EXECUTABLE_EXTENSION
16TIMEOUT = envint("XPRA_EXEC_AUTH_TIMEOUT", 600)
19class Authenticator(SysAuthenticator):
21 def __init__(self, username, **kwargs):
22 log("exec.Authenticator(%s, %s)", username, kwargs)
23 self.command = kwargs.pop("command", "")
24 self.timeout = kwargs.pop("timeout", TIMEOUT)
25 self.timer = None
26 self.proc = None
27 self.timeout_event = False
28 if not self.command:
29 #try to find the default auth_dialog executable:
30 from xpra.platform.paths import get_libexec_dir
31 libexec = get_libexec_dir()
32 xpralibexec = os.path.join(libexec, "xpra")
33 log("libexec=%s, xpralibexec=%s", libexec, xpralibexec)
34 if os.path.exists(xpralibexec) and os.path.isdir(xpralibexec):
35 libexec = xpralibexec
36 auth_dialog = os.path.join(libexec, "auth_dialog")
37 if EXECUTABLE_EXTENSION:
38 #ie: add ".exe" on MS Windows
39 auth_dialog += ".%s" % EXECUTABLE_EXTENSION
40 log("auth_dialog=%s", auth_dialog)
41 if os.path.exists(auth_dialog):
42 self.command = auth_dialog
43 assert self.command, "exec authentication module is not configured correctly: no command specified"
44 connection = kwargs.get("connection")
45 log("exec connection info: %s", connection)
46 assert connection, "connection object is missing"
47 self.connection_str = str(connection)
48 super().__init__(username, **kwargs)
50 def requires_challenge(self) -> bool:
51 return False
53 def authenticate(self, caps : typedict) -> bool:
54 info = "Connection request from %s" % self.connection_str
55 cmd = [self.command, info, str(self.timeout)]
56 proc = Popen(cmd)
57 self.proc = proc
58 log("authenticate(..) Popen(%s)=%s", cmd, proc)
59 #if required, make sure we kill the command when it times out:
60 if self.timeout>0:
61 self.timer = GLib.timeout_add(self.timeout*1000, self.command_timedout)
62 if not OSX:
63 #python on macos may set a 0 returncode when we use poll()
64 #so we cannot use the ChildReaper on macos,
65 #and we can't cancel the timer
66 getChildReaper().add_process(proc, "exec auth", cmd, True, True, self.command_ended)
67 v = proc.wait()
68 log("authenticate(..) returncode(%s)=%s", cmd, v)
69 if self.timeout_event:
70 return False
71 return v==0
73 def command_ended(self, *args):
74 t = self.timer
75 log("exec auth.command_ended%s timer=%s", args, t)
76 if t:
77 self.timer = None
78 GLib.source_remove(t)
80 def command_timedout(self):
81 proc = self.proc
82 log("exec auth.command_timedout() proc=%s", proc)
83 self.timeout_event = True
84 self.timer = None
85 if proc:
86 try:
87 proc.terminate()
88 except:
89 log("error trying to terminate exec auth process %s", proc, exc_info=True)
91 def __repr__(self):
92 return "exec"