From 5e9271597e7d3834bdc11ff64ddffa0387028297 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Mon, 23 Jul 2018 21:49:46 +0000 Subject: [PATCH] #1920: support public key authentication using openssh's "~/.ssh/authorized_keys" file git-svn-id: https://xpra.org/svn/Xpra/trunk@19952 3bb7dfac-3a0b-4e04-842a-767bc560f471 --- src/xpra/server/server_core.py | 8 +++---- src/xpra/server/ssh.py | 38 ++++++++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/xpra/server/server_core.py b/src/xpra/server/server_core.py index 7caffaf2bd..c74c44f574 100644 --- a/src/xpra/server/server_core.py +++ b/src/xpra/server/server_core.py @@ -36,8 +36,7 @@ from xpra.net.digest import get_salt, gendigest, choose_digest from xpra.platform import set_name from xpra.os_util import load_binary_file, get_machine_id, get_user_uuid, platform_name, strtobytes, bytestostr, get_hex_uuid, \ - getuid, monotonic_time, get_peercred, hexstr, \ - SIGNAMES, WIN32, POSIX, PYTHON3, BITS, get_username_for_uid + getuid, monotonic_time, get_peercred, hexstr, SIGNAMES, WIN32, POSIX, PYTHON3, BITS from xpra.server.background_worker import stop_worker, get_worker from xpra.make_thread import start_thread from xpra.util import csv, merge_dicts, typedict, notypedict, flatten_dict, parse_simple_dict, repr_ellipsized, dump_all_frames, nonl, envint, envbool, envfloat, \ @@ -934,8 +933,9 @@ def handle_ssh_connection(self, conn): socktype = conn.socktype_wrapped sshlog("handle_ssh_connection(%s) socktype wrapped=%s", conn, socktype) def ssh_password_authenticate(username, password): - if POSIX and getuid()!=0: - sysusername = get_username_for_uid(os.getuid()) + if not POSIX or getuid()!=0: + import getpass + sysusername = getpass.getuser() if sysusername!=username: sshlog.warn("Warning: ssh password authentication failed") sshlog.warn(" username does not match: expected '%s', got '%s'", sysusername, username) diff --git a/src/xpra/server/ssh.py b/src/xpra/server/ssh.py index 1db8cd84c7..1ae81b1cce 100644 --- a/src/xpra/server/ssh.py +++ b/src/xpra/server/ssh.py @@ -16,11 +16,12 @@ from xpra.net.ssh import SSHSocketConnection from xpra.util import csv, envint -from xpra.os_util import osexpand, WIN32 +from xpra.os_util import osexpand, getuid, WIN32, POSIX SSH_KEY_DIRS = "/etc/ssh", "/usr/local/etc/ssh", "~/.ssh", "~/ssh/" SERVER_WAIT = envint("XPRA_SSH_SERVER_WAIT", 20) +AUTHORIZED_KEYS = "~/.ssh/authorized_keys" class SSHServer(paramiko.ServerInterface): @@ -65,7 +66,40 @@ def check_auth_publickey(self, username, key): log("check_auth_publickey(%s, %r) pubkey_auth=%s", username, key, self.pubkey_auth) if not self.pubkey_auth: return paramiko.AUTH_FAILED - #TODO: verify key + if not POSIX or getuid()!=0: + import getpass + sysusername = getpass.getuser() + if sysusername!=username: + log.warn("Warning: ssh password authentication failed") + log.warn(" username does not match: expected '%s', got '%s'", sysusername, username) + return paramiko.AUTH_FAILED + authorized_keys_filename = osexpand(AUTHORIZED_KEYS) + if not os.path.exists(authorized_keys_filename) or not os.path.isfile(authorized_keys_filename): + log("file '%s' does not exist", authorized_keys_filename) + return paramiko.AUTH_FAILED + import base64 + import binascii + fingerprint = key.get_fingerprint() + hex_fingerprint = binascii.hexlify(fingerprint) + log("looking for key fingerprint '%s' in '%s'", hex_fingerprint, authorized_keys_filename) + count = 0 + with open(authorized_keys_filename, "rb") as f: + for line in f: + if line.startswith("#"): + continue + line = line.strip("\n\r") + try: + key = base64.b64decode(line.strip().split()[1].encode('ascii')) + except Exception as e: + log("ignoring line '%s': %s", line, e) + continue + import hashlib + fp_plain = hashlib.md5(key).hexdigest() + log("key(%s)=%s", line, fp_plain) + if fp_plain==hex_fingerprint: + return paramiko.OPEN_SUCCEEDED + count += 1 + log("no match in %i keys from '%s'", count, authorized_keys_filename) return paramiko.AUTH_FAILED def check_auth_gssapi_keyex(self, username, gss_authenticated=paramiko.AUTH_FAILED, cc_file=None):