def do_set_keymap(xkbmap_layout, xkbmap_variant, xkbmap_print, xkbmap_query): """ xkbmap_layout is the generic layout name (used on non posix platforms) xkbmap_variant is the layout variant (may not be set) xkbmap_print is the output of "setxkbmap -print" on the client xkbmap_query is the output of "setxkbmap -query" on the client Use those to try to setup the correct keyboard map for the client so that all the keycodes sent will be mapped """ #First we try to use data from setxkbmap -query if xkbmap_query: log.debug("do_set_keymap using xkbmap_query") """ The xkbmap_query data will look something like this: rules: evdev model: evdev layout: gb options: grp:shift_caps_toggle And we want to call something like: setxkbmap -rules evdev -model evdev -layout gb setxkbmap -option "" -option grp:shift_caps_toggle (we execute the options separately in case that fails..) """ #parse the data into a dict: settings = {} opt_re = re.compile("(\w*):\s*(.*)") for line in xkbmap_query.splitlines(): m = opt_re.match(line) if m: settings[m.group(1)] = m.group(2).strip() #construct the command line arguments for setxkbmap: args = ["setxkbmap"] used_settings = {} for setting in ["rules", "model", "layout"]: if setting in settings: value = settings.get(setting) args += ["-%s" % setting, value] used_settings[setting] = value if len(args)==1: log.warn("do_set_keymap could not find rules, model or layout in the xkbmap query string..") else: log.info("setting keymap: %s", ", ".join(["%s=%s" % (std(k), std(v)) for k,v in used_settings.items()])) exec_keymap_command(args) #try to set the options: if "options" in settings: log.info("setting keymap options: %s", std(str(settings.get("options")))) exec_keymap_command(["setxkbmap", "-option", "", "-option", settings.get("options")]) elif xkbmap_print: debug("do_set_keymap using xkbmap_print") #try to guess the layout by parsing "setxkbmap -print" try: sym_re = re.compile("\s*xkb_symbols\s*{\s*include\s*\"([\w\+]*)") for line in xkbmap_print.splitlines(): m = sym_re.match(line) if m: layout = std(m.group(1)) log.info("guessing keyboard layout='%s'" % layout) exec_keymap_command(["setxkbmap", layout]) break except Exception, e: log.info("error setting keymap: %s" % e)
def test_std(self): self.assertEqual(std(""), "") self.assertEqual(std("abcd"), "abcd") self.assertEqual(std("r1"), "r1") for invalid in ("*", "\n", "\r"): self.assertEqual(std(invalid), "") self.assertEqual(std("a" + invalid + "b"), "ab")
def parse_server_capabilities(self): for c in CLIENT_BASES: if not c.parse_server_capabilities(self): log.info("failed to parse server capabilities in %s", c) return False c = self.server_capabilities self.server_session_name = strtobytes(c.rawget("session_name", b"")).decode("utf-8") set_name("Xpra", self.session_name or self.server_session_name or "Xpra") self.server_platform = c.strget("platform") self.server_sharing = c.boolget("sharing") self.server_sharing_toggle = c.boolget("sharing-toggle") self.server_lock = c.boolget("lock") self.server_lock_toggle = c.boolget("lock-toggle") self.server_keyboard = c.boolget("keyboard", True) self.server_toggle_keyboard_sync = self.server_keyboard and c.boolget("toggle_keyboard_sync", True) self.server_pointer = c.boolget("pointer", True) self.server_start_new_commands = c.boolget("start-new-commands") if self.server_start_new_commands: self.xdg_menu = c.dictget("xdg-menu") if self.start_new_commands or self.start_child_new_commands: if self.server_start_new_commands: self.after_handshake(self.send_start_new_commands) else: log.warn("Warning: cannot start new commands") log.warn(" the feature is currently disabled on the server") self.server_commands_info = c.boolget("server-commands-info") self.server_commands_signals = c.strlistget("server-commands-signals") self.server_readonly = c.boolget("readonly") if self.server_readonly and not self.readonly: log.info("server is read only") self.readonly = True if not self.server_keyboard and self.keyboard_helper: #swallow packets: def nosend(*args): pass self.keyboard_helper.send = nosend i = platform_name(self._remote_platform, c.strlistget("platform.linux_distribution") or c.strget("platform.release", "")) r = self._remote_version if self._remote_revision: r += "-r%s" % self._remote_revision mode = c.strget("server.mode", "server") bits = c.intget("python.bits", 32) log.info("Xpra %s server version %s %i-bit", mode, std(r), bits) if i: log.info(" running on %s", std(i)) if c.boolget("proxy"): proxy_hostname = c.strget("proxy.hostname") proxy_platform = c.strget("proxy.platform") proxy_release = c.strget("proxy.platform.release") proxy_version = c.strget("proxy.version") proxy_version = c.strget("proxy.build.version", proxy_version) proxy_distro = c.strget("linux_distribution") msg = "via: %s proxy version %s" % (platform_name(proxy_platform, proxy_distro or proxy_release), std(proxy_version or "unknown")) if proxy_hostname: msg += " on '%s'" % std(proxy_hostname) log.info(msg) return True
def do_set_keymap(xkbmap_layout, xkbmap_variant, xkbmap_print, xkbmap_query): """ xkbmap_layout is the generic layout name (used on non posix platforms) xkbmap_variant is the layout variant (may not be set) xkbmap_print is the output of "setxkbmap -print" on the client xkbmap_query is the output of "setxkbmap -query" on the client Use those to try to setup the correct keyboard map for the client so that all the keycodes sent will be mapped """ #First we try to use data from setxkbmap -query if xkbmap_query: log("do_set_keymap using xkbmap_query") """ The xkbmap_query data will look something like this: rules: evdev model: evdev layout: gb options: grp:shift_caps_toggle (we execute the options separately in case that fails..) """ #parse the data into a dict: settings = parse_xkbmap_query(xkbmap_query) rules = settings.get("rules") model = settings.get("model") layout = settings.get("layout") variant = settings.get("variant") options = settings.get("options") if layout: log.info("setting keymap: %s", ", ".join(["%s=%s" % (std(k), std(v)) for k,v in settings.items() if k in ["rules", "model", "layout"] and v])) try: X11Keyboard.setxkbmap(rules, model, layout, variant, options) return except: log.warn("failed to set exact keymap using %s", settings) if options: #try again with no options: try: X11Keyboard.setxkbmap(rules, model, layout, variant, "") except: log.error("failed to set exact keymap even without applying options") if xkbmap_print: log("do_set_keymap using xkbmap_print") #try to guess the layout by parsing "setxkbmap -print" try: sym_re = re.compile("\s*xkb_symbols\s*{\s*include\s*\"([\w\+]*)") for line in xkbmap_print.splitlines(): m = sym_re.match(line) if m: layout = std(m.group(1)) log.info("guessing keyboard layout='%s'" % layout) X11Keyboard.setxkbmap("", "pc104", layout, "", "") return except Exception as e: log.info("error setting keymap: %s" % e) #fallback: layout = xkbmap_layout or "us" log.info("setting keyboard layout to '%s'", std(layout)) X11Keyboard.setxkbmap("", "", layout, xkbmap_variant, "")
def do_set_keymap(xkbmap_layout, xkbmap_variant, xkbmap_options, xkbmap_print, xkbmap_query, xkbmap_query_struct={}): """ xkbmap_layout is the generic layout name (used on non posix platforms) xkbmap_variant is the layout variant (may not be set) xkbmap_print is the output of "setxkbmap -print" on the client xkbmap_query is the output of "setxkbmap -query" on the client xkbmap_query_struct is xkbmap_query parsed into a dictionary Use those to try to setup the correct keyboard map for the client so that all the keycodes sent will be mapped """ #First we try to use data from setxkbmap -query, #preferably as structured data: if xkbmap_query and not xkbmap_query_struct: from xpra.keyboard.layouts import parse_xkbmap_query xkbmap_query_struct = parse_xkbmap_query(xkbmap_query) if xkbmap_query_struct: log("do_set_keymap using xkbmap_query struct=%s", xkbmap_query_struct) """ The xkbmap_query_struct data will look something like this: { b"rules" : b"evdev", b"model" : b"pc105", b"layout" : b"gb", b"options" : b"grp:shift_caps_toggle", } """ #parse the data into a dict: rules = xkbmap_query_struct.get(b"rules") model = xkbmap_query_struct.get(b"model") layout = xkbmap_query_struct.get(b"layout") variant = xkbmap_query_struct.get(b"variant") options = xkbmap_query_struct.get(b"options") if layout: log.info("setting keymap: %s", csv("%s=%s" % (std(k), std(v)) for k,v in xkbmap_query_struct.items() if k in ("rules", "model", "layout") and v)) safe_setxkbmap(rules, model, layout, variant, options) else: safe_setxkbmap(rules, model, "", "", "") if xkbmap_print: #TODO: this is no longer used with any client, remove it log("do_set_keymap using xkbmap_print") #try to guess the layout by parsing "setxkbmap -print" try: sym_re = re.compile(r"\s*xkb_symbols\s*{\s*include\s*\"([\w\+]*)") for line in xkbmap_print.splitlines(): m = sym_re.match(line) if m: layout = std(m.group(1)) log.info("guessing keyboard layout='%s'" % layout) safe_setxkbmap("evdev", "pc105", layout, "", xkbmap_options) return except Exception as e: log.info("error setting keymap: %s" % e) #fallback for non X11 clients: layout = xkbmap_layout or "us" log.info("setting keyboard layout to '%s'", std(layout)) safe_setxkbmap("evdev", "pc105", layout, xkbmap_variant, xkbmap_options)
def do_set_keymap(xkbmap_layout, xkbmap_variant, xkbmap_print, xkbmap_query): """ xkbmap_layout is the generic layout name (used on non posix platforms) xkbmap_variant is the layout variant (may not be set) xkbmap_print is the output of "setxkbmap -print" on the client xkbmap_query is the output of "setxkbmap -query" on the client Use those to try to setup the correct keyboard map for the client so that all the keycodes sent will be mapped """ #First we try to use data from setxkbmap -query if xkbmap_query: log("do_set_keymap using xkbmap_query") """ The xkbmap_query data will look something like this: rules: evdev model: evdev layout: gb options: grp:shift_caps_toggle And we want to call something like: setxkbmap -rules evdev -model evdev -layout gb setxkbmap -option "" -option grp:shift_caps_toggle (we execute the options separately in case that fails..) """ #parse the data into a dict: settings = parse_xkbmap_query(xkbmap_query) #construct the command line arguments for setxkbmap: args = ["setxkbmap"] used_settings = {} for setting in ["rules", "model", "layout"]: if setting in settings: value = settings.get(setting) args += ["-%s" % setting, value] used_settings[setting] = value if len(args)==1: log.warn("do_set_keymap could not find rules, model or layout in the xkbmap query string..") else: log.info("setting keymap: %s", ", ".join(["%s=%s" % (std(k), std(v)) for k,v in used_settings.items()])) exec_keymap_command(args) #try to set the options: if "options" in settings: log.info("setting keymap options: %s", std(str(settings.get("options")))) exec_keymap_command(["setxkbmap", "-option", "", "-option", settings.get("options")]) elif xkbmap_print: log("do_set_keymap using xkbmap_print") #try to guess the layout by parsing "setxkbmap -print" try: sym_re = re.compile("\s*xkb_symbols\s*{\s*include\s*\"([\w\+]*)") for line in xkbmap_print.splitlines(): m = sym_re.match(line) if m: layout = std(m.group(1)) log.info("guessing keyboard layout='%s'" % layout) exec_keymap_command(["setxkbmap", layout]) break except Exception, e: log.info("error setting keymap: %s" % e)
def do_set_keymap(xkbmap_layout, xkbmap_variant, xkbmap_print, xkbmap_query): """ xkbmap_layout is the generic layout name (used on non posix platforms) xkbmap_variant is the layout variant (may not be set) xkbmap_print is the output of "setxkbmap -print" on the client xkbmap_query is the output of "setxkbmap -query" on the client Use those to try to setup the correct keyboard map for the client so that all the keycodes sent will be mapped """ #First we try to use data from setxkbmap -query if xkbmap_query: log("do_set_keymap using xkbmap_query") """ The xkbmap_query data will look something like this: rules: evdev model: evdev layout: gb options: grp:shift_caps_toggle (we execute the options separately in case that fails..) """ #parse the data into a dict: settings = parse_xkbmap_query(xkbmap_query) rules = settings.get("rules") model = settings.get("model") layout = settings.get("layout") variant = settings.get("variant") options = settings.get("options") log.info("setting keymap: %s", ", ".join(["%s=%s" % (std(k), std(v)) for k,v in settings.items() if k in ["rules", "model", "layout"] and v])) try: X11Keyboard.setxkbmap(rules, model, layout, variant, options) except: log.warn("failed to set exact keymap using %s", settings) #try again with no options: try: X11Keyboard.setxkbmap(rules, model, layout, variant, "") except: log.error("failed to set exact keymap even without applying options") elif xkbmap_print: log("do_set_keymap using xkbmap_print") #try to guess the layout by parsing "setxkbmap -print" try: sym_re = re.compile("\s*xkb_symbols\s*{\s*include\s*\"([\w\+]*)") for line in xkbmap_print.splitlines(): m = sym_re.match(line) if m: layout = std(m.group(1)) log.info("guessing keyboard layout='%s'" % layout) X11Keyboard.setxkbmap("", "pc104", layout, "", "") except Exception as e: log.info("error setting keymap: %s" % e) else: layout = xkbmap_layout or "us" log.info("setting keyboard layout to '%s'", std(layout)) X11Keyboard.setxkbmap("", "", layout, xkbmap_variant, "")
def get_connect_info(self): cinfo = [] pinfo = "" if self.client_platform: pinfo = " %s" % platform_name( self.client_platform, self.client_linux_distribution or self.client_release) if self.client_session_type: pinfo += " %s" % self.client_session_type revinfo = "" if self.client_revision: revinfo = "-r%s" % self.client_revision bitsstr = "" if self.client_bits: bitsstr = " %i-bit" % self.client_bits cinfo.append("%s%s client version %s%s%s" % (std(self.client_type), pinfo, std( self.client_version), std(revinfo), bitsstr)) msg = "" if self.hostname: msg += "connected from '%s'" % std(self.hostname) if self.username: msg += " as '%s'" % std(self.username) if self.name and self.name != self.username: msg += " - '%s'" % std(self.name) if msg: cinfo.append(msg) if self.client_proxy: msg = "via %s proxy version %s" % (platform_name( self.proxy_platform, self.proxy_release), std(self.proxy_version or "unknown")) if self.proxy_hostname: msg += " on '%s'" % std(self.proxy_hostname) cinfo.append(msg) return cinfo
def parse_server_capabilities(self): for c in UIXpraClient.__bases__: if not c.parse_server_capabilities(self): log.info("failed to parse server capabilities in %s", c) return False c = self.server_capabilities self.server_session_name = strtobytes(c.rawget("session_name", b"")).decode("utf-8") set_name("Xpra", self.session_name or self.server_session_name or "Xpra") self.server_platform = c.strget("platform") self.server_sharing = c.boolget("sharing") self.server_sharing_toggle = c.boolget("sharing-toggle") self.server_lock = c.boolget("lock") self.server_lock_toggle = c.boolget("lock-toggle") self.server_start_new_commands = c.boolget("start-new-commands") self.server_commands_info = c.boolget("server-commands-info") self.server_commands_signals = c.strlistget("server-commands-signals") self.server_readonly = c.boolget("readonly") if self.server_readonly and not self.readonly: log.info("server is read only") self.readonly = True i = platform_name( self._remote_platform, c.strlistget("platform.linux_distribution") or c.strget("platform.release", "")) r = self._remote_version if self._remote_revision: r += "-r%s" % self._remote_revision mode = c.strget("server.mode", "server") bits = c.intget("python.bits", 32) log.info("Xpra %s server version %s %i-bit", mode, std(r), bits) if i: log.info(" running on %s", std(i)) if c.boolget("proxy"): proxy_hostname = c.strget("proxy.hostname") proxy_platform = c.strget("proxy.platform") proxy_release = c.strget("proxy.platform.release") proxy_version = c.strget("proxy.version") proxy_version = c.strget("proxy.build.version", proxy_version) proxy_distro = c.strget("linux_distribution") msg = "via: %s proxy version %s" % (platform_name( proxy_platform, proxy_distro or proxy_release), std(proxy_version or "unknown")) if proxy_hostname: msg += " on '%s'" % std(proxy_hostname) log.info(msg) return True
def handle(self, packet): prompt = "password" digest = bytestostr(packet[3]) if digest.startswith("gss:") or digest.startswith("kerberos:"): prompt = "%s token" % (digest.split(":", 1)[0]) if len(packet) >= 6: prompt = std(bytestostr(packet[5])) return self.client.do_process_challenge_prompt(packet, prompt)
def process_challenge_prompt(self, packet): prompt = "password" digest = packet[3] if digest.startswith(b"gss:") or digest.startswith(b"kerberos:"): prompt = "%s token" % (digest.split(b":", 1)[0]) if len(packet)>=6: prompt = std(packet[5]) return self.do_process_challenge_prompt(packet, prompt)
def do_set_keymap(xkbmap_layout, xkbmap_variant, xkbmap_options, xkbmap_query, xkbmap_query_struct): """ xkbmap_layout is the generic layout name (used on non posix platforms) xkbmap_variant is the layout variant (may not be set) xkbmap_print is the output of "setxkbmap -print" on the client xkbmap_query is the output of "setxkbmap -query" on the client xkbmap_query_struct is xkbmap_query parsed into a dictionary Use those to try to setup the correct keyboard map for the client so that all the keycodes sent will be mapped """ #First we try to use data from setxkbmap -query, #preferably as structured data: if xkbmap_query and not xkbmap_query_struct: xkbmap_query_struct = parse_xkbmap_query(xkbmap_query) xkbmap_query_struct = typedict(xkbmap_query_struct) if xkbmap_query_struct: log("do_set_keymap using xkbmap_query struct=%s", xkbmap_query_struct) #The xkbmap_query_struct data will look something like this: # { # b"rules" : b"evdev", # b"model" : b"pc105", # b"layout" : b"gb", # b"options" : b"grp:shift_caps_toggle", # } #parse the data into a dict: rules = xkbmap_query_struct.strget("rules") model = xkbmap_query_struct.strget("model") layout = xkbmap_query_struct.strget("layout") variant = xkbmap_query_struct.strget("variant") options = xkbmap_query_struct.strget("options") if layout: log.info( "setting keymap: %s", csv("%s=%s" % (std(k), std(v)) for k, v in xkbmap_query_struct.items() if k in ("rules", "model", "layout", "variant", "options") and v)) safe_setxkbmap(rules, model, layout, variant, options) else: safe_setxkbmap(rules, model, "", "", "") #fallback for non X11 clients: layout = xkbmap_layout or "us" log.info("setting keyboard layout to '%s'", std(layout)) safe_setxkbmap("evdev", "pc105", layout, xkbmap_variant, xkbmap_options)
def log_keyboard_info(self): #show the user a summary of what we have detected: kb_info = {} if self.xkbmap_query_struct or self.xkbmap_query: xkbqs = self.xkbmap_query_struct if xkbqs: #parse query into a dict from xpra.keyboard.layouts import parse_xkbmap_query xkbqs = parse_xkbmap_query(self.xkbmap_query) for x in ["rules", "model", "layout"]: v = xkbqs.get(x) if v: kb_info[x] = v if self.xkbmap_layout: kb_info["layout"] = self.xkbmap_layout if len(kb_info)==0: log.info(" using default keyboard settings") else: log.info(" keyboard settings: %s", ", ".join(["%s=%s" % (std(k), std(v)) for k,v in kb_info.items()]))
def end_headers(self): #magic for querying request header values: path = getattr(self, "path", "") if path.endswith("?echo-headers"): #ie: "en-GB,en-US;q=0.8,en;q=0.6" accept = self.headers.get("Accept-Language") if accept: self.send_header("Echo-Accept-Language", std(accept, extras="-,./:;=")) for k,v in self.get_headers().items(): self.send_header(k, v) BaseHTTPRequestHandler.end_headers(self)
def end_headers(self): #magic for querying request header values: path = getattr(self, "path", "") if path.endswith("?echo-headers"): #ie: "en-GB,en-US;q=0.8,en;q=0.6" accept = self.headers.get("Accept-Language") if accept: self.send_header("Echo-Accept-Language", std(accept, extras="-,./:;=")) if HTTP_NOCACHE: self.send_nocache_headers() WebSocketRequestHandler.end_headers(self)
def get_connect_info(self) -> list: cinfo = [] #client platform / version info: pinfo = "" if self.client_platform: pinfo = " %s" % platform_name( self.client_platform, self.client_linux_distribution or self.client_release) if self.client_session_type: pinfo += " %s" % self.client_session_type revinfo = "" if self.client_revision: revinfo = "-r%s" % self.client_revision bitsstr = "" if self.client_bits: bitsstr = " %i-bit" % self.client_bits cinfo.append("%s%s client version %s%s%s" % (std(self.client_type), pinfo, std( self.client_version), std(revinfo), bitsstr)) #opengl info: if self.client_opengl: msg = "OpenGL is " if not self.client_opengl.boolget("enabled"): msg += "disabled" else: msg += "enabled" driver_info = self.client_opengl.strget( "renderer") or self.client_opengl.strget("vendor") if driver_info: msg += " with %s" % driver_info cinfo.append(msg) #connection info: msg = "" if self.hostname: msg += "connected from '%s'" % std(self.hostname) if self.username: msg += " as '%s'" % std(self.username) if self.name and self.name != self.username: msg += " - '%s'" % std(self.name) if msg: cinfo.append(msg) #proxy info if self.client_proxy: msg = "via %s proxy version %s" % (platform_name( self.proxy_platform, self.proxy_release), std(self.proxy_version or "unknown")) if self.proxy_hostname: msg += " on '%s'" % std(self.proxy_hostname) cinfo.append(msg) return cinfo
def end_headers(self): #magic for querying request header values: path = getattr(self, "path", "") if path.endswith("?echo-headers"): #ie: "en-GB,en-US;q=0.8,en;q=0.6" accept = self.headers.get("Accept-Language") if accept: self.extra_headers["Echo-Accept-Language"] = std(accept, extras="-,./:;=") headers = self.get_headers() if self.extra_headers: headers.update(self.extra_headers) if headers: for k,v in headers.items(): self.send_header(k, v) super().end_headers()
def __init__(self, **kwargs): self.username = kwargs.get("username", get_username()) if str(kwargs.get("client-username", self.CLIENT_USERNAME)).lower() in TRUE_OPTIONS: #allow the client to specify the username to authenticate with: self.username = kwargs.get("remote", {}).get("username", self.username) self.salt = None self.digest = None self.salt_digest = None prompt_attr = {"username": std(self.username)} self.prompt = kwargs.pop("prompt", self.DEFAULT_PROMPT).format(**prompt_attr) self.socket_dirs = kwargs.pop("socket-dirs", get_socket_dirs()) self.challenge_sent = False self.passed = False self.password_used = None #we can't warn about unused options #because the options are shared with other socket options (nodelay, cork, etc) #unused = dict((k,v) for k,v in kwargs.items() if k not in ("connection", "exec_cwd", "username")) #if unused: # log.warn("Warning: unused keyword arguments for %s authentication:", self) # log.warn(" %s", unused) log("auth prompt=%s, socket_dirs=%s", self.prompt, self.socket_dirs)
def process_server_packet(self, proto, packet): packet_type = packet[0] log("process_server_packet: %s", packet_type) if packet_type==Protocol.CONNECTION_LOST: self.stop("server connection lost", proto) return elif packet_type=="disconnect": log("got disconnect from server: %s", packet[1]) if self.exit: self.server_protocol.close() else: self.stop("disconnect from server: %s" % packet[1]) elif packet_type=="hello": c = typedict(packet[1]) maxw, maxh = c.intpair("max_desktop_size", (4096, 4096)) caps = self.filter_server_caps(c) #add new encryption caps: if self.cipher: from xpra.net.crypto import crypto_backend_init, new_cipher_caps, DEFAULT_PADDING crypto_backend_init() padding_options = self.caps.strlistget("cipher.padding.options", [DEFAULT_PADDING]) auth_caps = new_cipher_caps(self.client_protocol, self.cipher, self.encryption_key, padding_options) caps.update(auth_caps) #may need to bump packet size: proto.max_packet_size = maxw*maxh*4*4 file_transfer = self.caps.boolget("file-transfer") and c.boolget("file-transfer") file_size_limit = max(self.caps.intget("file-size-limit"), c.intget("file-size-limit")) file_max_packet_size = int(file_transfer) * (1024 + file_size_limit*1024*1024) self.client_protocol.max_packet_size = max(self.client_protocol.max_packet_size, file_max_packet_size) self.server_protocol.max_packet_size = max(self.server_protocol.max_packet_size, file_max_packet_size) packet = ("hello", caps) elif packet_type=="info-response": #adds proxy info: #note: this is only seen by the client application #"xpra info" is a new connection, which talks to the proxy server... info = packet[1] info.update(self.get_proxy_info(proto)) elif packet_type=="lost-window": wid = packet[1] #mark it as lost so we can drop any current/pending frames self.lost_windows.add(wid) #queue it so it gets cleaned safely (for video encoders mostly): self.encode_queue.put(packet) #and fall through so tell the client immediately elif packet_type=="draw": #use encoder thread: self.encode_queue.put(packet) #which will queue the packet itself when done: return #we do want to reformat cursor packets... #as they will have been uncompressed by the network layer already: elif packet_type=="cursor": #packet = ["cursor", x, y, width, height, xhot, yhot, serial, pixels, name] #or: #packet = ["cursor", "png", x, y, width, height, xhot, yhot, serial, pixels, name] #or: #packet = ["cursor", ""] if len(packet)>=8: #hard to distinguish png cursors from normal cursors... try: int(packet[1]) self._packet_recompress(packet, 8, "cursor") except: self._packet_recompress(packet, 9, "cursor") elif packet_type=="window-icon": self._packet_recompress(packet, 5, "icon") elif packet_type=="send-file": if packet[6]: packet[6] = Compressed("file-data", packet[6]) elif packet_type=="send-file-chunk": if packet[3]: packet[3] = Compressed("file-chunk-data", packet[3]) elif packet_type=="challenge": from xpra.net.crypto import get_salt #client may have already responded to the challenge, #so we have to handle authentication from this end salt = packet[1] digest = packet[3] client_salt = get_salt(len(salt)) salt = xor_str(salt, client_salt) if digest!=b"hmac": self.stop("digest mode '%s' not supported", std(digest)) return password = self.session_options.get("password") if not password: self.stop("authentication requested by the server, but no password available for this session") return import hmac, hashlib password = strtobytes(password) salt = strtobytes(salt) challenge_response = hmac.HMAC(password, salt, digestmod=hashlib.md5).hexdigest() log.info("sending %s challenge response", digest) self.send_hello(challenge_response, client_salt) return self.queue_client_packet(packet)
def parse_server_capabilities(self, c : typedict) -> bool: for cb in CLIENT_BASES: if not cb.parse_server_capabilities(self, c): log.info("failed to parse server capabilities in %s", cb) return False self.server_session_name = c.uget("session_name") set_name("Xpra", self.session_name or self.server_session_name or "Xpra") self.server_platform = c.strget("platform") self.server_sharing = c.boolget("sharing") self.server_sharing_toggle = c.boolget("sharing-toggle") self.server_lock = c.boolget("lock") self.server_lock_toggle = c.boolget("lock-toggle") self.server_keyboard = c.boolget("keyboard", True) self.server_pointer = c.boolget("pointer", True) self.server_start_new_commands = c.boolget("start-new-commands") if self.server_start_new_commands: self.server_xdg_menu = c.dictget("xdg-menu", None) if self.start_new_commands or self.start_child_new_commands: if self.server_start_new_commands: self.after_handshake(self.send_start_new_commands) else: log.warn("Warning: cannot start new commands") log.warn(" the feature is currently disabled on the server") self.server_commands_info = c.boolget("server-commands-info") self.server_commands_signals = c.strtupleget("server-commands-signals") self.server_readonly = c.boolget("readonly") if self.server_readonly and not self.readonly: log.info("server is read only") self.readonly = True if not self.server_keyboard and self.keyboard_helper: #swallow packets: def nosend(*_args): pass self.keyboard_helper.send = nosend i = platform_name(self._remote_platform, c.strtupleget("platform.linux_distribution") or c.strget("platform.release", "")) r = self._remote_version if self._remote_revision: r += "-r%s" % self._remote_revision mode = c.strget("server.mode", "server") bits = c.intget("python.bits", 32) log.info("Xpra %s server version %s %i-bit", mode, std(r), bits) if i: log.info(" running on %s", std(i)) if c.boolget("desktop") or c.boolget("shadow"): v = c.intpair("actual_desktop_size") if v: w, h = v ss = c.tupleget("screen_sizes") if ss: log.info(" remote desktop size is %sx%s with %s screen%s:", w, h, len(ss), engs(ss)) log_screen_sizes(w, h, ss) else: log.info(" remote desktop size is %sx%s", w, h) if c.boolget("proxy"): proxy_hostname = c.strget("proxy.hostname") proxy_platform = c.strget("proxy.platform") proxy_release = c.strget("proxy.platform.release") proxy_version = c.strget("proxy.version") proxy_version = c.strget("proxy.build.version", proxy_version) proxy_distro = c.strget("proxy.linux_distribution") msg = "via: %s proxy version %s" % ( platform_name(proxy_platform, proxy_distro or proxy_release), std(proxy_version or "unknown") ) if proxy_hostname: msg += " on '%s'" % std(proxy_hostname) log.info(msg) return True
log("do_set_keymap using xkbmap_print") #try to guess the layout by parsing "setxkbmap -print" try: sym_re = re.compile("\s*xkb_symbols\s*{\s*include\s*\"([\w\+]*)") for line in xkbmap_print.splitlines(): m = sym_re.match(line) if m: layout = std(m.group(1)) log.info("guessing keyboard layout='%s'" % layout) exec_keymap_command(["setxkbmap", layout]) break except Exception, e: log.info("error setting keymap: %s" % e) else: layout = xkbmap_layout or "us" log.info("setting keyboard layout to '%s'", std(layout)) set_layout = ["setxkbmap", "-layout", layout] if xkbmap_variant: set_layout += ["-variant", xkbmap_variant] if not exec_keymap_command(set_layout) and xkbmap_variant: log.info("error setting keymap with variant %s, retrying with just layout %s", std(xkbmap_variant), std(layout)) set_layout = ["setxkbmap", "-layout", layout] exec_keymap_command(set_layout) display = os.environ.get("DISPLAY") if xkbmap_print: #there may be a junk header, if so remove it: pos = xkbmap_print.find("xkb_keymap {") if pos>0: xkbmap_print = xkbmap_print[pos:] log.info("setting full keymap definition from client via xkbcomp")
def do_update_screen(self): self.log("do_update_screen()") #c = self.stdscr.getch() #if c==curses.KEY_RESIZE: height, width = self.stdscr.getmaxyx() #log.info("update_screen() %ix%i", height, width) title = get_title() sli = self.server_last_info def _addstr(pad, y, x, s, *args): if len(s) + x >= width - pad: s = s[:max(0, width - x - 2 - pad)] + ".." self.stdscr.addstr(y, x, s, *args) def addstr_main(y, x, s, *args): _addstr(0, y, x, s, *args) def addstr_box(y, x, s, *args): _addstr(2, y, x, s, *args) try: x = max(0, width // 2 - len(title) // 2) addstr_main(0, x, title, curses.A_BOLD) if height <= 1: return server_info = self.slidictget("server") build = self.slidictget("server", "build") vstr = caps_to_version(build) mode = server_info.strget("mode", "server") python_info = typedict(server_info.dictget("python", {})) bits = python_info.intget("bits", 32) server_str = "Xpra %s server version %s %i-bit" % (mode, vstr, bits) proxy_info = self.slidictget("proxy") if proxy_info: proxy_platform_info = typedict( proxy_info.dictget("platform", {})) proxy_platform = proxy_platform_info.strget("") proxy_release = proxy_platform_info.strget("release") proxy_build_info = typedict(proxy_info.dictget("build", {})) proxy_version = proxy_build_info.strget("version") proxy_distro = proxy_info.strget("linux_distribution") server_str += " via: %s proxy version %s" % (platform_name( proxy_platform, proxy_distro or proxy_release), std(proxy_version or "unknown")) addstr_main(1, 0, server_str) if height <= 2: return #load and uptime: now = datetime.now() uptime = "" elapsed_time = server_info.intget("elapsed_time") if elapsed_time: td = timedelta(seconds=elapsed_time) uptime = " up %s" % str(td).lstrip("0:") clients_info = self.slidictget("clients") nclients = clients_info.intget("") load_average = "" load = sli.inttupleget("load") if load and len(load) == 3: float_load = tuple(v / 1000.0 for v in load) load_average = ", load average: %1.2f, %1.2f, %1.2f" % float_load addstr_main( 2, 0, "xpra top - %s%s, %2i users%s" % (now.strftime("%H:%M:%S"), uptime, nclients, load_average)) if height <= 3: return thread_info = self.slidictget("threads") rinfo = "%i threads" % thread_info.intget("count") server_pid = server_info.intget("pid", 0) if server_pid: rinfo += ", pid %i" % server_pid machine_id = server_info.get("machine-id") if machine_id is None or machine_id == get_machine_id(): try: process = self.psprocess.get(server_pid) if not process: import psutil process = psutil.Process(server_pid) self.psprocess[server_pid] = process else: cpu = process.cpu_percent() rinfo += ", %i%% CPU" % (cpu) except Exception: pass cpuinfo = self.slidictget("cpuinfo") if cpuinfo: rinfo += ", %s" % cpuinfo.strget("hz_actual") elapsed = monotonic_time() - self.server_last_info_time color = WHITE if self.server_last_info_time == 0: rinfo += " - no server data" elif elapsed > 2: rinfo += " - last updated %i seconds ago" % elapsed color = RED addstr_main(3, 0, rinfo, curses.color_pair(color)) if height <= 4: return #display: dinfo = [] server = self.slidictget("server") rws = server.intpair("root_window_size", None) if rws: sinfo = "%ix%i display" % (rws[0], rws[1]) mds = server.intpair("max_desktop_size") if mds: sinfo += " (max %ix%i)" % (mds[0], mds[1]) dinfo.append(sinfo) cursor_info = self.slidictget("cursor") if cursor_info: cx, cy = cursor_info.inttupleget("position", (0, 0)) dinfo.append("cursor at %ix%i" % (cx, cy)) display_info = self.slidictget("display") pid = display_info.intget("pid") if pid: dinfo.append("pid %i" % pid) addstr_main(4, 0, csv(dinfo)) if height <= 5: return hpos = 5 gl_info = self.get_gl_info(display_info.dictget("opengl")) if gl_info: addstr_main(5, 0, gl_info) hpos += 1 if hpos < height - 3: hpos += 1 if nclients == 0: addstr_main(hpos, 0, "no clients connected") else: addstr_main( hpos, 0, "%i client%s connected:" % (nclients, engs(nclients))) hpos += 1 client_info = self.slidictget("client") client_no = 0 while True: ci = client_info.dictget(client_no) if not ci: break client_no += 1 ci = typedict(ci) session_id = ci.strget("session-id") if session_id: #don't show ourselves: if session_id == self.session_id: continue elif not ci.boolget("windows", True): #for older servers, hide any client that doesn't display windows: continue ci = self.get_client_info(ci) l = len(ci) if hpos + 2 + l > height: if hpos < height: more = nclients - client_no addstr_box( hpos, 0, "%i client%s not shown" % (more, engs(more)), curses.A_BOLD) break self.box(1, hpos, width - 2, 2 + l) for i, info in enumerate(ci): info_text, color = info cpair = curses.color_pair(color) addstr_box(hpos + i + 1, 2, info_text, cpair) hpos += 2 + l windows = self.slidictget("windows") if hpos < height - 3: hpos += 1 addstr_main(hpos, 0, "%i window%s:" % (len(windows), engs(windows))) hpos += 1 wins = tuple(windows.values()) nwindows = len(wins) for win_no, win in enumerate(wins): wi = self.get_window_info(typedict(win)) l = len(wi) if hpos + 2 + l > height: if hpos < height: more = nwindows - win_no addstr_main(hpos, 0, "terminal window is too small: %i window%s not shown" % \ (more, engs(more)), curses.A_BOLD) break self.box(1, hpos, width - 2, 2 + l) for i, info in enumerate(wi): info_text, color = info cpair = curses.color_pair(color) addstr_box(hpos + i + 1, 2, info_text, cpair) hpos += 2 + l except Exception as e: self.err(e)
def process_server_packet(self, proto, packet): packet_type = packet[0] log("process_server_packet: %s", packet_type) if packet_type==Protocol.CONNECTION_LOST: self.stop("server connection lost", proto) return elif packet_type=="disconnect": log("got disconnect from server: %s", packet[1]) if self.exit: self.server_protocol.close() else: self.stop("disconnect from server: %s" % packet[1]) elif packet_type=="hello": c = typedict(packet[1]) maxw, maxh = c.intpair("max_desktop_size", (4096, 4096)) caps = self.filter_server_caps(c) #add new encryption caps: if self.cipher: from xpra.net.crypto import crypto_backend_init, new_cipher_caps, DEFAULT_PADDING crypto_backend_init() padding_options = self.caps.strlistget("cipher.padding.options", [DEFAULT_PADDING]) auth_caps = new_cipher_caps(self.client_protocol, self.cipher, self.encryption_key, padding_options) caps.update(auth_caps) #may need to bump packet size: proto.max_packet_size = maxw*maxh*4*4 file_transfer = self.caps.boolget("file-transfer") and c.boolget("file-transfer") file_size_limit = max(self.caps.intget("file-size-limit"), c.intget("file-size-limit")) file_max_packet_size = int(file_transfer) * (1024 + file_size_limit*1024*1024) self.client_protocol.max_packet_size = max(self.client_protocol.max_packet_size, file_max_packet_size) self.server_protocol.max_packet_size = max(self.server_protocol.max_packet_size, file_max_packet_size) packet = ("hello", caps) elif packet_type=="info-response": #adds proxy info: #note: this is only seen by the client application #"xpra info" is a new connection, which talks to the proxy server... info = packet[1] info.update(self.get_proxy_info(proto)) elif packet_type=="lost-window": wid = packet[1] #mark it as lost so we can drop any current/pending frames self.lost_windows.add(wid) #queue it so it gets cleaned safely (for video encoders mostly): self.encode_queue.put(packet) #and fall through so tell the client immediately elif packet_type=="draw": #use encoder thread: self.encode_queue.put(packet) #which will queue the packet itself when done: return #we do want to reformat cursor packets... #as they will have been uncompressed by the network layer already: elif packet_type=="cursor": #packet = ["cursor", x, y, width, height, xhot, yhot, serial, pixels, name] #or: #packet = ["cursor", "png", x, y, width, height, xhot, yhot, serial, pixels, name] #or: #packet = ["cursor", ""] if len(packet)>=8: #hard to distinguish png cursors from normal cursors... try: int(packet[1]) self._packet_recompress(packet, 8, "cursor") except: self._packet_recompress(packet, 9, "cursor") elif packet_type=="window-icon": self._packet_recompress(packet, 5, "icon") elif packet_type=="send-file": if packet[6]: packet[6] = Compressed("file-data", packet[6]) elif packet_type=="send-file-chunk": if packet[3]: packet[3] = Compressed("file-chunk-data", packet[3]) elif packet_type=="challenge": from xpra.net.crypto import get_salt #client may have already responded to the challenge, #so we have to handle authentication from this end salt = packet[1] digest = packet[3] client_salt = get_salt(len(salt)) salt = xor_str(salt, client_salt) if digest!=b"hmac": self.stop("digest mode '%s' not supported", std(digest)) return password = self.disp_desc.get("password", self.session_options.get("password")) log("password from %s / %s = %s", self.disp_desc, self.session_options, password) if not password: self.stop("authentication requested by the server, but no password available for this session") return import hmac, hashlib password = strtobytes(password) salt = strtobytes(salt) challenge_response = hmac.HMAC(password, salt, digestmod=hashlib.md5).hexdigest() log.info("sending %s challenge response", digest) self.send_hello(challenge_response, client_salt) return self.queue_client_packet(packet)
def do_set_keymap(xkbmap_layout, xkbmap_variant, xkbmap_print, xkbmap_query): """ xkbmap_layout is the generic layout name (used on non posix platforms) xkbmap_variant is the layout variant (may not be set) xkbmap_print is the output of "setxkbmap -print" on the client xkbmap_query is the output of "setxkbmap -query" on the client Use those to try to setup the correct keyboard map for the client so that all the keycodes sent will be mapped """ #First we try to use data from setxkbmap -query if xkbmap_query: log("do_set_keymap using xkbmap_query") """ The xkbmap_query data will look something like this: rules: evdev model: evdev layout: gb options: grp:shift_caps_toggle And we want to call something like: setxkbmap -rules evdev -model evdev -layout gb setxkbmap -option "" -option grp:shift_caps_toggle (we execute the options separately in case that fails..) """ #parse the data into a dict: settings = parse_xkbmap_query(xkbmap_query) #construct the command line arguments for setxkbmap: args = ["setxkbmap"] used_settings = {} for setting in ["rules", "model", "layout"]: if setting in settings: value = settings.get(setting) args += ["-%s" % setting, value] used_settings[setting] = value if len(args)==1: log.warn("do_set_keymap could not find rules, model or layout in the xkbmap query string..") else: log.info("setting keymap: %s", ", ".join(["%s=%s" % (std(k), std(v)) for k,v in used_settings.items()])) exec_keymap_command(args) #try to set the options: if "options" in settings: log.info("setting keymap options: %s", std(str(settings.get("options")))) exec_keymap_command(["setxkbmap", "-option", "", "-option", settings.get("options")]) elif xkbmap_print: log("do_set_keymap using xkbmap_print") #try to guess the layout by parsing "setxkbmap -print" try: sym_re = re.compile("\s*xkb_symbols\s*{\s*include\s*\"([\w\+]*)") for line in xkbmap_print.splitlines(): m = sym_re.match(line) if m: layout = std(m.group(1)) log.info("guessing keyboard layout='%s'" % layout) exec_keymap_command(["setxkbmap", layout]) break except Exception as e: log.info("error setting keymap: %s" % e) else: layout = xkbmap_layout or "us" log.info("setting keyboard layout to '%s'", std(layout)) set_layout = ["setxkbmap", "-layout", layout] if xkbmap_variant: set_layout += ["-variant", xkbmap_variant] if not exec_keymap_command(set_layout) and xkbmap_variant: log.info("error setting keymap with variant %s, retrying with just layout %s", std(xkbmap_variant), std(layout)) set_layout = ["setxkbmap", "-layout", layout] exec_keymap_command(set_layout) display = os.environ.get("DISPLAY") if xkbmap_print: #there may be a junk header, if so remove it: pos = xkbmap_print.find("xkb_keymap {") if pos>0: xkbmap_print = xkbmap_print[pos:] log.info("setting full keymap definition from client via xkbcomp") exec_keymap_command(["xkbcomp", "-", display], xkbmap_print)
def do_update_screen(self): #c = self.stdscr.getch() #if c==curses.KEY_RESIZE: height, width = self.stdscr.getmaxyx() #log.info("update_screen() %ix%i", height, width) title = "Xpra top %s" % __version__ try: from xpra.src_info import REVISION, LOCAL_MODIFICATIONS title += "-r%s" % REVISION if LOCAL_MODIFICATIONS: title += "M" except ImportError: pass x = max(0, width // 2 - len(title) // 2) sli = self.server_last_info try: self.stdscr.addstr(0, x, title, curses.A_BOLD) if height <= 1: return server_info = self.dictget("server") build = self.dictget("build") v = build.strget("version") revision = build.strget("revision") if v and revision: v = " version %s-r%s" % (v, revision) mode = server_info.strget("mode", "server") python_info = typedict(server_info.dictget("python", {})) bits = python_info.intget("bits", 32) server_str = "Xpra %s server%s %i-bit" % (mode, std(v), bits) proxy_info = self.dictget("proxy") if proxy_info: proxy_platform_info = typedict( proxy_info.dictget("platform", {})) proxy_platform = proxy_platform_info.strget("") proxy_release = proxy_platform_info.strget("release") proxy_build_info = typedict(proxy_info.dictget("build", {})) proxy_version = proxy_build_info.strget("version") proxy_distro = proxy_info.strget("linux_distribution") server_str += " via: %s proxy version %s" % (platform_name( proxy_platform, proxy_distro or proxy_release), std(proxy_version or "unknown")) self.stdscr.addstr(1, 0, server_str) if height <= 2: return #load and uptime: now = datetime.now() uptime = "" elapsed_time = server_info.intget("elapsed_time") if elapsed_time: td = timedelta(seconds=elapsed_time) uptime = " up %s" % str(td).lstrip("0:") clients_info = self.dictget("clients") nclients = clients_info.intget("") load_average = "" load = sli.intlistget("load") if load and len(load) == 3: float_load = tuple(v / 1000.0 for v in load) load_average = ", load average: %1.2f, %1.2f, %1.2f" % float_load self.stdscr.addstr( 2, 0, "xpra top - %s%s, %2i users%s" % (now.strftime("%H:%M:%S"), uptime, nclients, load_average)) if height <= 3: return thread_info = self.dictget("threads") self.stdscr.addstr(3, 0, "%i threads" % thread_info.intget("count")) if height <= 4: return #cursor: cursor_info = self.dictget("cursor") cx, cy = cursor_info.intlistget("position", (0, 0)) self.stdscr.addstr(4, 0, "cursor at %ix%i" % (cx, cy)) if height <= 5: return hpos = 6 client_info = self.dictget("client") client_no = 0 while True: ci = client_info.dictget(client_no) if not ci: break client_no += 1 ci = typedict(ci) if not ci.boolget("windows", True): continue ci = self.get_client_info(ci) l = len(ci) if hpos + 2 + l > height: if hpos < height: more = nclients - client_no self.stdscr.addstr( hpos, 0, "%i client%s not shown" % (more, engs(more)), curses.A_BOLD) break self.box(self.stdscr, 1, hpos, width - 2, 2 + l) for i, info in enumerate(ci): info_text, color = info cpair = curses.color_pair(color) self.stdscr.addstr(hpos + i + 1, 2, info_text, cpair) hpos += 2 + l windows = self.dictget("windows") if hpos < height - 3: hpos += 1 self.stdscr.addstr(hpos, 0, "%i windows" % len(windows)) hpos += 1 wins = tuple(windows.values()) nwindows = len(wins) for win_no, win in enumerate(wins): wi = self.get_window_info(typedict(win)) l = len(wi) if hpos + 2 + l > height: if hpos < height: more = nwindows - win_no self.stdscr.addstr( hpos, 0, "terminal window is too small: %i window%s not shown" % (more, engs(more)), curses.A_BOLD) break self.box(self.stdscr, 1, hpos, width - 2, 2 + l) for i, info in enumerate(wi): info_text, color = info cpair = curses.color_pair(color) self.stdscr.addstr(hpos + i + 1, 2, info_text, cpair) hpos += 2 + l except Exception as e: import traceback self.stdscr.addstr(0, 0, str(e)) self.stdscr.addstr(0, 1, traceback.format_exc())
debug("do_set_keymap using xkbmap_print") #try to guess the layout by parsing "setxkbmap -print" try: sym_re = re.compile("\s*xkb_symbols\s*{\s*include\s*\"([\w\+]*)") for line in xkbmap_print.splitlines(): m = sym_re.match(line) if m: layout = std(m.group(1)) log.info("guessing keyboard layout='%s'" % layout) exec_keymap_command(["setxkbmap", layout]) break except Exception, e: log.info("error setting keymap: %s" % e) else: layout = xkbmap_layout or "us" log.info("setting keyboard layout to '%s'", std(layout)) set_layout = ["setxkbmap", "-layout", layout] if xkbmap_variant: set_layout += ["-variant", xkbmap_variant] if not exec_keymap_command(set_layout) and xkbmap_variant: log.info( "error setting keymap with variant %s, retrying with just layout %s", std(xkbmap_variant), std(layout)) set_layout = ["setxkbmap", "-layout", layout] exec_keymap_command(set_layout) display = os.environ.get("DISPLAY") if xkbmap_print: #there may be a junk header, if so remove it: pos = xkbmap_print.find("xkb_keymap {") if pos > 0:
def ssl_retry(e, ssl_ca_certs): SSL_RETRY = envbool("XPRA_SSL_RETRY", True) if not SSL_RETRY: return None if not isinstance(e, SSLVerifyFailure): return None #we may be able to ask the user if we wants to accept this certificate from xpra.log import Logger ssllog = Logger("ssl") verify_code = e.verify_code ssl_sock = e.ssl_sock msg = str(e) del e addr = ssl_sock.getpeername()[:2] port = addr[-1] server_hostname = ssl_sock.server_hostname ssllog("ssl_retry: peername=%s, server_hostname=%s", addr, server_hostname) if verify_code not in ( SSL_VERIFY_SELF_SIGNED, SSL_VERIFY_WRONG_HOST, SSL_VERIFY_IP_MISMATCH, SSL_VERIFY_HOSTNAME_MISMATCH, ): ssllog("ssl_retry: %s not handled here", SSL_VERIFY_CODES.get(verify_code, verify_code)) return None if not server_hostname: ssllog("ssl_retry: not server hostname") return None ssllog("ssl_retry: server_hostname=%s, ssl verify_code=%s (%i)", server_hostname, SSL_VERIFY_CODES.get(verify_code, verify_code), verify_code) from xpra.platform.paths import get_ssl_hosts_config_dirs from xpra.scripts.pinentry_wrapper import confirm host_dirname = std(server_hostname, extras="-.:#_")+"_%i" % port #self-signed cert: if verify_code==SSL_VERIFY_SELF_SIGNED: if ssl_ca_certs not in ("", "default"): ssllog("self-signed cert does not match %r", ssl_ca_certs) return None #perhaps we already have the certificate for this hostname dirs = get_ssl_hosts_config_dirs() host_dirs = [os.path.join(osexpand(d), host_dirname) for d in dirs] cert_filename = "cert.pem" ssllog("looking for %s in %s", cert_filename, host_dirs) for d in host_dirs: f = os.path.join(d, cert_filename) if os.path.exists(f): ssllog("found certificate for %s: %s", server_hostname, f) ssllog("retrying") return {"ssl_ca_certs" : f} #download the certificate data import ssl try: cert_data = ssl.get_server_certificate(addr) except ssl.SSLError: cert_data = None if not cert_data: ssllog("failed to get server certificate from %s", addr) return None ssllog("downloaded ssl cert data for %s: %s", addr, ellipsizer(cert_data)) #ask the user if he wants to accept this certificate: title = "SSL Certificate Verification Failure" prompt = "Do you want to accept this certificate?" r = confirm((msg, ), title, prompt) ssllog("run_pinentry_confirm(..) returned %r", r) if not r: return None #if there is an existing host config dir, try to use it: for d in [x for x in host_dirs if os.path.exists(x)]: try: filename = os.path.join(d, cert_filename) with open(filename, "wb") as f: f.write(cert_data.encode("latin1")) ssllog.info("saved SSL certificate to %s", filename) return {"ssl_ca_certs" : filename} except OSError: ssllog("failed to save cert data to %r", filename, exc_info=True) #try to create a host config dir: for d in host_dirs: folders = os.path.normpath(d).split(os.sep) #we have to be careful and create the 'ssl' dir with 0o700 permissions #but any directory above that can use 0o755 try: ssl_dir_index = len(folders)-1 while ssl_dir_index>0 and folders[ssl_dir_index]!="ssl": ssl_dir_index -= 1 if ssl_dir_index>1: parent = os.path.join(*folders[:ssl_dir_index-1]) ssl_dir = os.path.join(*folders[:ssl_dir_index]) os.makedirs(parent, exist_ok=True) os.makedirs(ssl_dir, mode=0o700, exist_ok=True) os.makedirs(d, mode=0o700) filename = os.path.join(d, cert_filename) with open(filename, "wb") as f: f.write(cert_data.encode("latin1")) ssllog.info("saved SSL certificate to %s", filename) return {"ssl_ca_certs" : filename} except OSError: ssllog("failed to save cert data to %r", d, exc_info=True) ssllog.warn("Warning: failed to save certificate data") return None if verify_code in (SSL_VERIFY_WRONG_HOST, SSL_VERIFY_IP_MISMATCH, SSL_VERIFY_HOSTNAME_MISMATCH): #ask the user if he wants to skip verifying the host title = "SSL Certificate Verification Failure" prompt = "Do you want to connect anyway?" r = confirm((msg,), title, prompt) ssllog("run_pinentry_confirm(..) returned %r", r) if r: ssllog.info("retrying without checking the hostname") return {"ssl_check_hostname" : False} return None