Skip to content

Commit

Permalink
#1620 move digest checking to crypto module so we can more easily add…
Browse files Browse the repository at this point in the history
… des for rfb generically and re-use the auth modules challenge functions

git-svn-id: https://xpra.org/svn/Xpra/trunk@16710 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Aug 27, 2017
1 parent 4c63548 commit fd2c2c2
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 52 deletions.
49 changes: 40 additions & 9 deletions src/xpra/net/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
# later version. See the file COPYING for details.

import os
from xpra.util import envint, envbool, csv
import hmac
import hashlib
import binascii

from xpra.util import envint, envbool, csv, xor
from xpra.log import Logger
from xpra.os_util import strtobytes, memoryview_to_bytes

log = Logger("network", "crypto")

ENABLE_CRYPTO = envbool("XPRA_ENABLE_CRYPTO", True)
Expand All @@ -33,6 +39,13 @@
PADDING_OPTIONS.append(x)


try:
from xpra.codecs.xor.cyxor import xor_str #@UnresolvedImport
xor = xor_str
except Exception as e:
log("no accelerated xor: %s", e)


ENCRYPTION_CIPHERS = []
backend = False
def crypto_backend_init():
Expand All @@ -55,8 +68,6 @@ def crypto_backend_init():
backend = None

def validate_backend(try_backend):
import binascii
from xpra.os_util import strtobytes
try_backend.init()
message = b"some message1234"
password = "this is our secret"
Expand All @@ -82,14 +93,12 @@ def validate_backend(try_backend):


def get_digests():
import hashlib
return ["hmac", "xor"] + ["hmac+%s" % x for x in list(reversed(sorted(hashlib.algorithms_available)))]

def get_digest_module(digest):
log("get_digest_module(%s)", digest)
if not digest or not digest.startswith("hmac"):
return None
import hashlib
try:
digest_module = digest.split("+")[1] #ie: "hmac+sha512" -> "sha512"
except:
Expand All @@ -114,6 +123,31 @@ def choose_digest(options):
return "xor"
raise Exception("no known digest options found in '%s'" % csv(options))

def get_hexdigest(digest, password, salt):
assert digest and password and salt
if digest=="xor":
salt = salt.ljust(16, "\x00")[:len(password)]
v = memoryview_to_bytes(xor(password, salt))
return binascii.hexlify(v)
digestmod = get_digest_module(digest)
if not digestmod:
log("invalid digest module '%s': %s", digest)
return None
#warn_server_and_exit(EXIT_UNSUPPORTED, "server requested digest '%s' but it is not supported" % digest, "invalid digest")
password = strtobytes(password)
salt = memoryview_to_bytes(salt)
v = hmac.HMAC(password, salt, digestmod=digestmod).hexdigest()
return v

def verify_digest(digest, password, salt, challenge_response):
if not password or not salt or not challenge_response:
return False
verify = get_hexdigest(digest, password, salt)
if not hmac.compare_digest(verify, challenge_response):
log("expected '%s' but got '%s'", verify, challenge_response)
return False
return True


def pad(padding, size):
if padding==PADDING_LEGACY:
Expand Down Expand Up @@ -146,10 +180,7 @@ def get_rand_str(l):
assert l<256, "salt is too long: %i bytes" % l
#all server versions support a client salt,
#they also tell us which digest to use:
salt = get_hex_uuid()
while len(salt)<l:
salt += get_hex_uuid()
return salt[:l]
return os.urandom(l)

def get_salt(l=64):
return get_rand_str(l)
Expand Down
2 changes: 1 addition & 1 deletion src/xpra/server/auth/allow_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ def __repr__(self):
def get_password(self):
return None

def authenticate(self, challenge_response, client_salt):
def authenticate(self, _challenge_response, _client_salt=None):
return True
20 changes: 4 additions & 16 deletions src/xpra/server/auth/file_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@

#authentication from a file containing just the password

import binascii
import hmac

from xpra.os_util import strtobytes
from xpra.net.crypto import get_digest_module
from xpra.net.crypto import verify_digest
from xpra.server.auth.file_auth_base import FileAuthenticatorBase, init, log
from xpra.util import xor, nonl
from xpra.util import xor


#will be called when we init the module
Expand All @@ -20,7 +16,7 @@

class Authenticator(FileAuthenticatorBase):

def authenticate_hmac(self, challenge_response, client_salt):
def authenticate_hmac(self, challenge_response, client_salt=None):
if not self.salt:
log.error("Error: illegal challenge response received - salt cleared or unset")
return None
Expand All @@ -35,15 +31,7 @@ def authenticate_hmac(self, challenge_response, client_salt):
log.error("Error: authentication failed")
log.error(" no password for '%s' in '%s'", self.username, self.password_filename)
return False
digestmod = get_digest_module(self.digest)
if not digestmod:
log.error("Error: %s authentication failed", self)
log.error(" digest module '%s' is invalid", self.digest)
return False
verify = hmac.HMAC(strtobytes(password), strtobytes(salt), digestmod=digestmod).hexdigest()
log("file authenticate(%s) password='%s', salt=%s, hash=%s", nonl(challenge_response), nonl(password), binascii.hexlify(strtobytes(salt)), verify)
if not hmac.compare_digest(verify, challenge_response):
log("expected '%s' but got '%s'", verify, challenge_response)
if not verify_digest(self.digest, password, salt, challenge_response):
log.error("Error: hmac password challenge for '%s' does not match", self.username)
return False
return True
Expand Down
10 changes: 3 additions & 7 deletions src/xpra/server/auth/multifile_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@

import os
import binascii
import hmac

from xpra.server.auth.file_auth_base import log, FileAuthenticatorBase, init as file_init
from xpra.os_util import strtobytes, POSIX
from xpra.util import xor, parse_simple_dict
from xpra.net.crypto import get_digest_module
from xpra.net.crypto import verify_digest


socket_dir = None
Expand Down Expand Up @@ -126,7 +125,7 @@ def get_password(self):
return None
return entry[0]

def authenticate_hmac(self, challenge_response, client_salt):
def authenticate_hmac(self, challenge_response, client_salt=None):
log("authenticate_hmac(%s, %s)", challenge_response, client_salt)
self.sessions = None
if not self.salt:
Expand All @@ -145,11 +144,8 @@ def authenticate_hmac(self, challenge_response, client_salt):
return None
log("authenticate: auth-info(%s)=%s", self.username, entry)
fpassword, uid, gid, displays, env_options, session_options = entry
digestmod = get_digest_module(self.digest)
verify = hmac.HMAC(strtobytes(fpassword), strtobytes(salt), digestmod=digestmod).hexdigest()
log("multifile authenticate_hmac(%s) password='%s', hex(salt)=%s, hash=%s", challenge_response, fpassword, binascii.hexlify(strtobytes(salt)), verify)
if not hmac.compare_digest(verify, challenge_response):
log("expected '%s' but got '%s'", verify, challenge_response)
if not verify_digest(self.digest, fpassword, salt, challenge_response):
log.error("Error: hmac password challenge for '%s' does not match", self.username)
return False
self.sessions = uid, gid, displays, env_options, session_options
Expand Down
4 changes: 2 additions & 2 deletions src/xpra/server/auth/none_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ def __init__(self, username, **kwargs):
def requires_challenge(self):
return False

def get_challenge(self, digests):
def get_challenge(self, _digests):
return None

def get_password(self):
return None

def authenticate(self, challenge_response, client_salt):
def authenticate(self, _challenge_response, _client_salt=None):
return True

def __repr__(self):
Expand Down
2 changes: 1 addition & 1 deletion src/xpra/server/auth/peercred_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def requires_challenge(self):
#pretend to require a challenge and fail it:
return self.uid<0

def authenticate(self, challenge_response, client_salt):
def authenticate(self, _challenge_response, _client_salt=None):
return False

def __repr__(self):
Expand Down
4 changes: 2 additions & 2 deletions src/xpra/server/auth/reject_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def init(opts):


class Authenticator(object):
def __init__(self, username, **kwargs):
def __init__(self, username, **_kwargs):
self.username = username

def requires_challenge(self):
Expand All @@ -29,7 +29,7 @@ def get_gid(self):
def get_password(self):
return None

def authenticate(self, challenge_response, client_salt):
def authenticate(self, _challenge_response, _client_salt=None):
return False

def get_sessions(self):
Expand Down
20 changes: 6 additions & 14 deletions src/xpra/server/auth/sys_auth_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
# 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 hmac, binascii
import binascii

from xpra.platform.dotxpra import DotXpra
from xpra.util import xor
from xpra.net.crypto import get_salt, choose_digest, get_digest_module
from xpra.net.crypto import get_salt, choose_digest, verify_digest
from xpra.os_util import strtobytes
from xpra.log import Logger
log = Logger("auth")
Expand Down Expand Up @@ -65,11 +65,11 @@ def get_password(self):
def check(self, password):
raise NotImplementedError()

def authenticate(self, challenge_response, client_salt):
def authenticate(self, challenge_response, client_salt=None):
#this will call check(password)
return self.authenticate_check(challenge_response, client_salt)

def authenticate_check(self, challenge_response, client_salt):
def authenticate_check(self, challenge_response, client_salt=None):
if self.salt is None:
log.error("Error: illegal challenge response received - salt cleared or unset")
return False
Expand All @@ -91,7 +91,7 @@ def authenticate_check(self, challenge_response, client_salt):
return False
return ret

def authenticate_hmac(self, challenge_response, client_salt):
def authenticate_hmac(self, challenge_response, client_salt=None):
if not self.salt:
log.error("Error: illegal challenge response received - salt cleared or unset")
return None
Expand All @@ -107,15 +107,7 @@ def authenticate_hmac(self, challenge_response, client_salt):
log.error("Error: %s authentication failed", self)
log.error(" no password defined for '%s'", self.username)
return False
digestmod = get_digest_module(self.digest)
if not digestmod:
log.error("Error: %s authentication failed", self)
log.error(" digest module '%s' is invalid", self.digest)
return False
verify = hmac.HMAC(strtobytes(password), strtobytes(salt), digestmod=digestmod).hexdigest()
log("%s auth: authenticate(%s) password=%s, hex(salt)=%s, hash=%s", self, challenge_response, password, binascii.hexlify(strtobytes(salt)), verify)
if not hmac.compare_digest(verify, challenge_response):
log("expected '%s' but got '%s'", verify, challenge_response)
if not verify_digest(self.digest, password, salt, challenge_response):
log.error("Error: hmac password challenge for '%s' does not match", self.username)
return False
return True
Expand Down

0 comments on commit fd2c2c2

Please sign in to comment.