Skip to content

Commit

Permalink
#1796: modularize client authentication handlers
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@22711 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed May 15, 2019
1 parent ab8b755 commit d085ab6
Show file tree
Hide file tree
Showing 11 changed files with 394 additions and 225 deletions.
4 changes: 4 additions & 0 deletions src/xpra/client/auth/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# This file is part of Xpra.
# Copyright (C) 2019 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.
26 changes: 26 additions & 0 deletions src/xpra/client/auth/env_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# This file is part of Xpra.
# Copyright (C) 2019 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os


class Handler(object):

def __init__(self, client, **kwargs):
self.client = client
self.var_name = kwargs.pop("name", "XPRA_PASSWORD")

def __repr__(self):
return "env"

def get_digest(self):
return None

def handle(self, packet):
password = os.environ.get(self.var_name)
if not password:
return False
self.client.send_challenge_reply(packet, password)
return True
34 changes: 34 additions & 0 deletions src/xpra/client/auth/file_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This file is part of Xpra.
# Copyright (C) 2019 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os

from xpra.os_util import load_binary_file


class Handler(object):

def __init__(self, client, **kwargs):
self.client = client
self.password_file = kwargs.get("filename", None)
if not self.password_file and client.password_file:
self.password_file = client.password_file[0]
client.password_file = client.password_file[1:]

def __repr__(self):
return "file"

def get_digest(self):
return None

def handle(self, packet):
if not self.password_file:
return False
filename = os.path.expanduser(self.password_file)
data = load_binary_file(filename)
if not data:
return False
self.client.send_challenge_reply(packet, data)
return True
59 changes: 59 additions & 0 deletions src/xpra/client/auth/gss_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# This file is part of Xpra.
# Copyright (C) 2019 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os

from xpra.util import csv
from xpra.os_util import bytestostr, OSX
from xpra.log import Logger

log = Logger("auth")


class Handler(object):

def __init__(self, client, **_kwargs):
self.client = client
self.services = os.environ.get("XPRA_GSS_SERVICES", "*").split(",")
import gssapi #@UnresolvedImport
self.gssapi = gssapi
if OSX and False:
from gssapi.raw import (cython_converters, cython_types, oids) # @UnresolvedImport
assert cython_converters and cython_types and oids

def __repr__(self):
return "gss"

def get_digest(self):
return "gss"

def handle(self, packet):
digest = bytestostr(packet[3])
if not digest.startswith("gss:"):
#not a gss challenge
log("%s is not a gss challenge", digest)
return False
service = bytestostr(digest.split(b":", 1)[1])
if service not in self.services and "*" not in self.services:
log.warn("Warning: invalid GSS request for service '%s'", service)
log.warn(" services supported: %s", csv(self.services))
return False
log("gss service=%s", service)
service_name = self.gssapi.Name(service)
try:
ctx = self.gssapi.SecurityContext(name=service_name, usage="initiate")
token = ctx.step()
except Exception as e:
log("gssapi failure", exc_info=True)
log.error("Error: gssapi client authentication failure:")
try:
for x in str(e).split(":", 2):
log.error(" %s", x.lstrip(" "))
except Exception:
log.error(" %s", e)
return False
log("gss token=%s", repr(token))
self.client.send_challenge_reply(packet, token)
return True
76 changes: 76 additions & 0 deletions src/xpra/client/auth/kerberos_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# This file is part of Xpra.
# Copyright (C) 2019 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os

from xpra.util import csv
from xpra.os_util import bytestostr, WIN32
from xpra.log import Logger

log = Logger("auth")


def log_kerberos_exception(e):
try:
for x in e.args:
if isinstance(x, (list, tuple)):
try:
log.error(" %s", csv(x))
continue
except Exception:
pass
log.error(" %s", x)
except Exception:
log.error(" %s", e)

class Handler(object):

def __init__(self, client, **_kwargs):
self.client = client
self.services = os.environ.get("XPRA_KERBEROS_SERVICES", "*").split(",")
if WIN32:
import winkerberos #@UnresolvedImport
self.kerberos = winkerberos
else:
import kerberos #@UnresolvedImport
self.kerberos = kerberos

def __repr__(self):
return "kerberos"

def get_digest(self):
return "kerberos"

def handle(self, packet):
digest = bytestostr(packet[3])
if not digest.startswith("kerberos:"):
log("%s is not a kerberos challenge", digest)
#not a kerberos challenge
return False
service = digest.split(":", 1)[1]
if service not in self.services and "*" not in self.services:
log.warn("Warning: invalid kerberos request for service '%s'", service)
log.warn(" services supported: %s", csv(self.services))
return False
log("kerberos service=%s", service)
try:
r, ctx = self.kerberos.authGSSClientInit(service)
assert r==1, "return code %s" % r
except Exception as e:
log("kerberos.authGSSClientInit(%s)", service, exc_info=True)
log.error("Error: cannot initialize kerberos client:")
log_kerberos_exception(e)
return False
try:
self.kerberos.authGSSClientStep(ctx, "")
except Exception as e:
log("kerberos.authGSSClientStep", exc_info=True)
log.error("Error: kerberos client authentication failure:")
log_kerberos_exception(e)
return False
token = self.kerberos.authGSSClientResponse(ctx)
log("kerberos token=%s", token)
self.client.send_challenge_reply(packet, token)
return True
28 changes: 28 additions & 0 deletions src/xpra/client/auth/prompt_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This file is part of Xpra.
# Copyright (C) 2019 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

from xpra.util import std
from xpra.os_util import bytestostr


class Handler(object):

def __init__(self, client, **_kwargs):
self.client = client

def __repr__(self):
return "prompt"

def get_digest(self):
return None

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)
71 changes: 71 additions & 0 deletions src/xpra/client/auth/u2f_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# This file is part of Xpra.
# Copyright (C) 2019 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os
import logging
import binascii

from xpra.os_util import bytestostr, load_binary_file, osexpand
from xpra.log import Logger, is_debug_enabled

log = Logger("auth")


class Handler(object):

def __init__(self, client, **_kwargs):
self.client = client
import pyu2f #@UnresolvedImport @UnusedImport

def __repr__(self):
return "u2f"

def get_digest(self):
return "u2f"

def handle(self, packet):
digest = bytestostr(packet[3])
if not digest.startswith("u2f:"):
log("%s is not a u2f challenge", digest)
return False
if not is_debug_enabled("auth"):
logging.getLogger("pyu2f.hardware").setLevel(logging.INFO)
logging.getLogger("pyu2f.hidtransport").setLevel(logging.INFO)
from pyu2f import model #@UnresolvedImport
from pyu2f.u2f import GetLocalU2FInterface #@UnresolvedImport
dev = GetLocalU2FInterface()
APP_ID = os.environ.get("XPRA_U2F_APP_ID", "Xpra")
key_handle_str = os.environ.get("XPRA_U2F_KEY_HANDLE")
log("process_challenge_u2f XPRA_U2F_KEY_HANDLE=%s", key_handle_str)
if not key_handle_str:
#try to load the key handle from the user conf dir(s):
from xpra.platform.paths import get_user_conf_dirs
info = self.client._protocol.get_info(False)
key_handle_filenames = []
for hostinfo in ("-%s" % info.get("host", ""), ""):
for d in get_user_conf_dirs():
key_handle_filenames.append(os.path.join(d, "u2f-keyhandle%s.hex" % hostinfo))
for filename in key_handle_filenames:
p = osexpand(filename)
key_handle_str = load_binary_file(p)
log("key_handle_str(%s)=%s", p, key_handle_str)
if key_handle_str:
key_handle_str = key_handle_str.rstrip(b" \n\r")
break
if not key_handle_str:
log.warn("Warning: no U2F key handle found")
return False
log("process_challenge_u2f key_handle=%s", key_handle_str)
key_handle = binascii.unhexlify(key_handle_str)
key = model.RegisteredKey(key_handle)
#use server salt as challenge directly
challenge = packet[1]
log.info("activate your U2F device for authentication")
response = dev.Authenticate(APP_ID, challenge, [key])
sig = response.signature_data
client_data = response.client_data
log("process_challenge_u2f client data=%s, signature=%s", client_data, binascii.hexlify(sig))
self.client.do_send_challenge_reply(bytes(sig), client_data.origin)
return True
22 changes: 22 additions & 0 deletions src/xpra/client/auth/uri_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This file is part of Xpra.
# Copyright (C) 2019 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.


class Handler(object):

def __init__(self, client, **_kwargs):
self.client = client

def __repr__(self):
return "uri"

def get_digest(self):
return None

def handle(self, packet):
if not self.client.password:
return False
self.client.send_challenge_reply(packet, self.client.password)
return True
Loading

0 comments on commit d085ab6

Please sign in to comment.