Exemple #1
0
 def get_challenge(self):
     if self.salt is not None:
         log.error("challenge already sent!")
         return None
     self.salt = get_hex_uuid()+get_hex_uuid()
     #this authenticator can use the safer "hmac" digest:
     return self.salt, "hmac"
Exemple #2
0
 def get_challenge(self):
     if self.salt is not None:
         log.error("challenge already sent!")
         return None
     self.salt = get_hex_uuid()+get_hex_uuid()
     #we need the raw password, so tell the client to use "xor":
     return self.salt, "xor"
Exemple #3
0
 def _process_challenge(self, packet):
     authlog("processing challenge: %s", packet[1:])
     def warn_server_and_exit(code, message, server_message="authentication failed"):
         authlog.error("Error: authentication failed:")
         authlog.error(" %s", message)
         self.disconnect_and_quit(code, server_message)
     if not self.password_file and not os.environ.get('XPRA_PASSWORD'):
         warn_server_and_exit(EXIT_PASSWORD_REQUIRED, "this server requires authentication, please provide a password", "no password available")
         return
     password = self.load_password()
     if not password:
         warn_server_and_exit(EXIT_PASSWORD_FILE_ERROR, "failed to load password from file %s" % self.password_file, "no password available")
         return
     salt = packet[1]
     if self.encryption:
         assert len(packet)>=3, "challenge does not contain encryption details to use for the response"
         server_cipher = typedict(packet[2])
         key = self.get_encryption_key()
         if key is None:
             warn_server_and_exit(EXIT_ENCRYPTION, "the server does not use any encryption", "client requires encryption")
             return
         if not self.set_server_encryption(server_cipher, key):
             return
     #all server versions support a client salt,
     #they also tell us which digest to use:
     digest = packet[3]
     client_salt = get_hex_uuid()+get_hex_uuid()
     #TODO: use some key stretching algorigthm? (meh)
     try:
         from xpra.codecs.xor.cyxor import xor_str           #@UnresolvedImport
         salt = xor_str(salt, client_salt)
     except:
         salt = xor(salt, client_salt)
     if digest==b"hmac":
         import hmac, hashlib
         def s(v):
             try:
                 return v.encode()
             except:
                 return str(v)
         password = s(password)
         salt = s(salt)
         challenge_response = hmac.HMAC(password, salt, digestmod=hashlib.md5).hexdigest()
     elif digest==b"xor":
         #don't send XORed password unencrypted:
         if not self._protocol.cipher_out and not ALLOW_UNENCRYPTED_PASSWORDS:
             warn_server_and_exit(EXIT_ENCRYPTION, "server requested digest %s, cowardly refusing to use it without encryption" % digest, "invalid digest")
             return
         challenge_response = xor(password, salt)
     else:
         warn_server_and_exit(EXIT_PASSWORD_REQUIRED, "server requested an unsupported digest: %s" % digest, "invalid digest")
         return
     if digest:
         authlog("%s(%s, %s)=%s", digest, binascii.hexlify(password), binascii.hexlify(salt), challenge_response)
     self.password_sent = True
     self.remove_packet_handlers("challenge")
     self.send_hello(challenge_response, client_salt)
Exemple #4
0
def new_cipher_caps(proto, cipher, encryption_key):
    iv = get_hex_uuid()[:16]
    key_salt = get_hex_uuid()+get_hex_uuid()
    iterations = 1000
    proto.set_cipher_in(cipher, iv, encryption_key, key_salt, iterations)
    return {
                 "cipher"           : cipher,
                 "cipher.iv"        : iv,
                 "cipher.key_salt"  : key_salt,
                 "cipher.key_stretch_iterations" : iterations
                 }
Exemple #5
0
 def _process_challenge(self, packet):
     log("processing challenge: %s", packet[1:])
     if not self.password_file and not os.environ.get('XPRA_PASSWORD'):
         self.warn_and_quit(EXIT_PASSWORD_REQUIRED, "server requires authentication, please provide a password")
         return
     password = self.load_password()
     if not password:
         self.warn_and_quit(EXIT_PASSWORD_FILE_ERROR, "failed to load password from file %s" % self.password_file)
         return
     salt = packet[1]
     if self.encryption:
         assert len(packet)>=3, "challenge does not contain encryption details to use for the response"
         server_cipher = packet[2]
         key = self.get_encryption_key()
         if key is None:
             self.warn_and_quit(EXIT_ENCRYPTION, "encryption key is missing")
             return
         if not self.set_server_encryption(server_cipher, key):
             return
     digest = "hmac"
     client_can_salt = len(packet)>=4
     client_salt = None
     if client_can_salt:
         #server supports client salt, and tells us which digest to use:
         digest = packet[3]
         client_salt = get_hex_uuid()+get_hex_uuid()
         #TODO: use some key stretching algorigthm? (meh)
         salt = xor(salt, client_salt)
     if digest=="hmac":
         import hmac
         challenge_response = hmac.HMAC(password, salt).hexdigest()
     elif digest=="xor":
         #don't send XORed password unencrypted:
         if not self._protocol.cipher_out and not ALLOW_UNENCRYPTED_PASSWORDS:
             self.warn_and_quit(EXIT_ENCRYPTION, "server requested digest %s, cowardly refusing to use it without encryption" % digest)
             return
         challenge_response = xor(password, salt)
     else:
         self.warn_and_quit(EXIT_PASSWORD_REQUIRED, "server requested an unsupported digest: %s" % digest)
         return
     if digest:
         log("%s(%s, %s)=%s", digest, password, salt, challenge_response)
     self.password_sent = True
     for d in (self._packet_handlers, self._ui_packet_handlers):
         try:
             del d["challenge"]
         except:
             pass
     self.send_hello(challenge_response, client_salt)
Exemple #6
0
    def make_hello_base(self):
        capabilities = get_network_caps()
        capabilities.update({
                "version"               : local_version,
                "encoding.generic"      : True,
                "namespace"             : True,
                "file-transfer"         : self.file_transfer,
                "file-size-limit"       : self.file_size_limit,
                "printing"              : self.printing,
                "hostname"              : socket.gethostname(),
                "uuid"                  : self.uuid,
                "username"              : self.username,
                "name"                  : get_name(),
                "client_type"           : self.client_type(),
                "python.version"        : sys.version_info[:3],
                "compression_level"     : self.compression_level,
                })
        if self.display:
            capabilities["display"] = self.display
        def up(prefix, d):
            updict(capabilities, prefix, d)
        up("platform",  get_platform_info())
        up("build",     get_version_info())
        mid = get_machine_id()
        if mid:
            capabilities["machine_id"] = mid

        if self.encryption:
            assert self.encryption in ENCRYPTION_CIPHERS
            iv = get_hex_uuid()[:16]
            key_salt = get_hex_uuid()+get_hex_uuid()
            iterations = 1000
            capabilities.update({
                        "cipher"                       : self.encryption,
                        "cipher.iv"                    : iv,
                        "cipher.key_salt"              : key_salt,
                        "cipher.key_stretch_iterations": iterations,
                        })
            key = self.get_encryption_key()
            if key is None:
                self.warn_and_quit(EXIT_ENCRYPTION, "encryption key is missing")
                return
            self._protocol.set_cipher_in(self.encryption, iv, key, key_salt, iterations)
            log("encryption capabilities: %s", [(k,v) for k,v in capabilities.items() if k.startswith("cipher")])
        return capabilities
Exemple #7
0
	def test_file(self):
		from xpra.os_util import get_hex_uuid
		password = get_hex_uuid()
		f = self._temp_file(password)
		self._test_auth("file", "", EXIT_PASSWORD_REQUIRED)
		self._test_auth("file:filename=%s" % f.name, "", EXIT_PASSWORD_REQUIRED)
		self._test_auth("file:filename=%s" % f.name, "", EXIT_OK, password)
		self._test_auth("file:filename=%s" % f.name, "", EXIT_FAILURE, password+"A")
		f.close()
Exemple #8
0
 def make_hello(self, challenge_response=None):
     capabilities = {}
     add_version_info(capabilities)
     capabilities["python.version"] = sys.version_info[:3]
     if challenge_response:
         assert self.password
         capabilities["challenge_response"] = challenge_response
     if self.encryption:
         assert self.encryption in ENCRYPTION_CIPHERS
         capabilities["cipher"] = self.encryption
         iv = get_hex_uuid()[:16]
         capabilities["cipher.iv"] = iv
         key_salt = get_hex_uuid()
         capabilities["cipher.key_salt"] = key_salt
         iterations = 1000
         capabilities["cipher.key_stretch_iterations"] = iterations
         self._protocol.set_cipher_in(self.encryption, iv, self.get_password(), key_salt, iterations)
         log("encryption capabilities: %s", [(k,v) for k,v in capabilities.items() if k.startswith("cipher")])
     capabilities["platform"] = sys.platform
     capabilities["platform.release"] = python_platform.release()
     capabilities["platform.machine"] = python_platform.machine()
     capabilities["platform.processor"] = python_platform.processor()
     capabilities["client_type"] = self.client_type()
     capabilities["namespace"] = True
     capabilities["raw_packets"] = True
     capabilities["chunked_compression"] = True
     capabilities["bencode"] = True
     capabilities["rencode"] = has_rencode
     if has_rencode:
         capabilities["rencode.version"] = rencode_version
     capabilities["hostname"] = socket.gethostname()
     capabilities["uuid"] = self.uuid
     try:
         from xpra.platform.info import get_username, get_name
         capabilities["username"] = get_username()
         capabilities["name"] = get_name()
     except:
         log.error("failed to get username/name", exc_info=True)
     capabilities["randr_notify"] = False    #only client.py cares about this
     capabilities["windows"] = False         #only client.py cares about this
     if self._reverse_aliases:
         capabilities["aliases"] = self._reverse_aliases
     return capabilities
Exemple #9
0
	def test_multifile(self):
		from xpra.platform.info import get_username
		username = get_username()
		from xpra.os_util import get_hex_uuid
		password = get_hex_uuid()
		displays = ""
		data = "%s|%s|%i|%i|%s||" % (username, password, os.getuid(), os.getgid(), displays)
		f = self._temp_file(data)
		self._test_auth("multifile", "", EXIT_PASSWORD_REQUIRED)
		self._test_auth("multifile:filename=%s" % f.name, "", EXIT_PASSWORD_REQUIRED)
		self._test_auth("multifile:filename=%s" % f.name, "", EXIT_OK, password)
		self._test_auth("multifile:filename=%s" % f.name, "", EXIT_FAILURE, password+"A")
		f.close()
	def copy_and_verify(self, display1, display2, synced=True, wait=1, selection="clipboard"):
		log("copy_and_verify%s", (display1, display2, synced, wait, selection))
		value = get_hex_uuid()
		self.set_clipboard_value(display1, value)
		#wait for synchronization to occur:
		time.sleep(wait)
		new_value = self.get_clipboard_value(display2)
		if synced:
			assert new_value==value, "clipboard contents do not match, expected '%s' but got '%s'" % (value, new_value)
		else:
			assert new_value!=value, "clipboard contents match but synchronization was not expected: value='%s'" % value
		if SANITY_CHECKS and display2!=display1:
			#verify that the value has not changed on the original display:
			new_value = self.get_clipboard_value(display1)
		return value
Exemple #11
0
 def get_challenge(self):
     return get_hex_uuid(), "hmac"
Exemple #12
0
            "HOME"          : os.environ.get("HOME", os.getcwd()),
            "DISPLAY"       : display_name}
    for var,value in subs.items():
        xvfb_str = xvfb_str.replace("$%s" % var, value)
        xvfb_str = xvfb_str.replace("${%s}" % var, value)
    xvfb_cmd = xvfb_str.split()+[display_name]
    xvfb_executable = xvfb_cmd[0]
    xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)
    def setsid():
        #run in a new session
        if os.name=="posix":
            os.setsid()
    xvfb = subprocess.Popen(xvfb_cmd, executable=xvfb_executable, close_fds=True,
                                stdin=subprocess.PIPE, preexec_fn=setsid)
    from xpra.os_util import get_hex_uuid
    xauth_cmd = ["xauth", "add", display_name, "MIT-MAGIC-COOKIE-1", get_hex_uuid()]
    try:
        code = subprocess.call(xauth_cmd)
        if code != 0:
            raise OSError("non-zero exit code: %s" % code)
    except OSError, e:
        #trying to continue anyway!
        sys.stderr.write("Error running \"%s\": %s\n" % (" ".join(xauth_cmd), e))
    return xvfb

def check_xvfb_process(xvfb=None):
    if xvfb is None:
        #we don't have a process to check
        return True
    if xvfb.poll() is None:
        #process is running
Exemple #13
0
    def verify_hello(self, proto, c):
        remote_version = c.strget("version")
        verr = version_compat_check(remote_version)
        if verr is not None:
            self.disconnect_client(proto, VERSION_ERROR, "incompatible version: %s" % verr)
            proto.close()
            return False

        def auth_failed(msg):
            log.warn("Warning: authentication failed: %s", msg)
            self.timeout_add(1000, self.disconnect_client, proto, msg)

        # authenticator:
        username = c.strget("username")
        if proto.authenticator is None and proto.auth_class:
            try:
                proto.authenticator = proto.auth_class(username)
            except Exception as e:
                log.warn("error instantiating %s: %s", proto.auth_class, e)
                auth_failed("authentication failed")
                return False
        self.digest_modes = c.get("digest", ("hmac",))

        # client may have requested encryption:
        cipher = c.strget("cipher")
        cipher_iv = c.strget("cipher.iv")
        key_salt = c.strget("cipher.key_salt")
        iterations = c.intget("cipher.key_stretch_iterations")
        auth_caps = {}
        if cipher and cipher_iv:
            if cipher not in ENCRYPTION_CIPHERS:
                log.warn("unsupported cipher: %s", cipher)
                auth_failed("unsupported cipher")
                return False
            encryption_key = self.get_encryption_key(proto.authenticator)
            if encryption_key is None:
                auth_failed("encryption key is missing")
                return False
            proto.set_cipher_out(cipher, cipher_iv, encryption_key, key_salt, iterations)
            # use the same cipher as used by the client:
            auth_caps = new_cipher_caps(proto, cipher, encryption_key)
            log("server cipher=%s", auth_caps)
        else:
            auth_caps = None

        # verify authentication if required:
        if (proto.authenticator and proto.authenticator.requires_challenge()) or c.get("challenge") is not None:
            challenge_response = c.strget("challenge_response")
            client_salt = c.strget("challenge_client_salt")
            log(
                "processing authentication with %s, response=%s, client_salt=%s, challenge_sent=%s",
                proto.authenticator,
                challenge_response,
                binascii.hexlify(client_salt or ""),
                proto.challenge_sent,
            )
            # send challenge if this is not a response:
            if not challenge_response:
                if proto.challenge_sent:
                    auth_failed("invalid state, challenge already sent - no response!")
                    return False
                if proto.authenticator:
                    challenge = proto.authenticator.get_challenge()
                    if challenge is None:
                        auth_failed("invalid state: unexpected challenge response")
                        return False
                    salt, digest = challenge
                    log.info(
                        "Authentication required, %s sending challenge for '%s' using digest %s",
                        proto.authenticator,
                        username,
                        digest,
                    )
                    if digest not in self.digest_modes:
                        auth_failed("cannot proceed without %s digest support" % digest)
                        return False
                else:
                    log.warn("Warning: client expects a challenge but this connection is unauthenticated")
                    # fake challenge so the client will send the real hello:
                    from xpra.os_util import get_hex_uuid

                    salt = get_hex_uuid() + get_hex_uuid()
                    digest = "hmac"
                proto.challenge_sent = True
                proto.send_now(("challenge", salt, auth_caps or "", digest))
                return False

            if not proto.authenticator.authenticate(challenge_response, client_salt):
                auth_failed("invalid challenge response")
                return False
            log("authentication challenge passed")
        else:
            # did the client expect a challenge?
            if c.boolget("challenge"):
                log.warn("this server does not require authentication (client supplied a challenge)")
        return auth_caps
Exemple #14
0
                    return False                
                if proto.authenticator:
                    challenge = proto.authenticator.get_challenge()
                    if challenge is None:
                        auth_failed("invalid state: unexpected challenge response")
                        return False
                    salt, digest = challenge
                    log.info("Authentication required, %s sending challenge for '%s' using digest %s", proto.authenticator, username, digest)
                    if digest not in self.digest_modes:
                        auth_failed("cannot proceed without %s digest support" % digest)
                        return False
                else:
                    log.warn("Warning: client expects a challenge but this connection is unauthenticated")
                    #fake challenge so the client will send the real hello:
                    from xpra.os_util import get_hex_uuid
                    salt = get_hex_uuid()+get_hex_uuid()
                    digest = "hmac"
                proto.challenge_sent = True
                proto.send_now(("challenge", salt, auth_caps or "", digest))
                return False

            if not proto.authenticator.authenticate(challenge_response, client_salt):
                auth_failed("invalid challenge response")
                return False
            log("authentication challenge passed")
        else:
            #did the client expect a challenge?
            if c.boolget("challenge"):
                log.warn("this server does not require authentication (client supplied a challenge)")
        return auth_caps
Exemple #15
0
def start_Xvfb(xvfb_str, display_name):
    # We need to set up a new server environment
    xauthority = os.environ.get("XAUTHORITY", os.path.expanduser("~/.Xauthority"))
    if not os.path.exists(xauthority):
        try:
            open(xauthority, 'wa').close()
        except Exception as e:
            #trying to continue anyway!
            sys.stderr.write("Error trying to create XAUTHORITY file %s: %s\n" % (xauthority, e))

    if not xvfb_str:
        raise InitException("the 'xvfb' command is not defined")
    #identify logfile argument if it exists and if we may have to rename it:
    xvfb_cmd = xvfb_str.split()
    if '-logfile' in xvfb_cmd and display_name[0]=='S':
        xvfb_cmd = xvfb_str.split()
        logfile_argindex = xvfb_cmd.index('-logfile') + 1
        assert logfile_argindex<len(xvfb_cmd), "invalid xvfb command string: -logfile should not be last"
        xorg_log_file = xvfb_cmd[logfile_argindex]
    else:
        xorg_log_file = None

    #apply string substitutions:
    subs = {"XAUTHORITY"    : xauthority,
            "USER"          : os.environ.get("USER", "unknown-user"),
            "HOME"          : os.environ.get("HOME", os.getcwd()),
            "DISPLAY"       : display_name}
    xvfb_str = shellsub(xvfb_str, subs)

    def setsid():
        #run in a new session
        if os.name=="posix":
            os.setsid()

    xvfb_cmd = xvfb_str.split()
    if not xvfb_cmd:
        raise Exception("cannot start Xvfb, command definition is missing!")
    xvfb_executable = xvfb_cmd[0]
    if display_name[0]=='S':
        # 'S' means that we allocate the display automatically
        r_pipe, w_pipe = os.pipe()
        xvfb_cmd += ["-displayfd", str(w_pipe)]
        xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)
        def preexec():
            setsid()
            #duplicate python's _close_fds() function
            #(taking care to exclude the pipe)
            try:
                MAXFD = os.sysconf("SC_OPEN_MAX")
            except:
                MAXFD = 256
            for i in range(3, MAXFD):
                if i not in (r_pipe, w_pipe):
                    try:
                        os.close(i)
                    except:
                        pass
        xvfb = subprocess.Popen(xvfb_cmd, executable=xvfb_executable, close_fds=False,
                                stdin=subprocess.PIPE, preexec_fn=preexec)
        # Read the display number from the pipe we gave to Xvfb
        # waiting up to 10 seconds for it to show up
        limit = time.time()+10
        buf = ""
        while time.time()<limit and len(buf)<8:
            r, _, _ = select.select([r_pipe], [], [], max(0, limit-time.time()))
            if r_pipe in r:
                buf += os.read(r_pipe, 8)
                if buf[-1] == '\n':
                    break
        os.close(r_pipe)
        os.close(w_pipe)
        if len(buf) == 0:
            raise OSError("%s did not provide a display number using -displayfd" % xvfb_executable)
        if buf[-1] != '\n':
            raise OSError("%s output not terminated by newline: %s" % (xvfb_executable, buf))
        try:
            n = int(buf[:-1])
        except:
            raise OSError("%s display number is not a valid number: %s" % (xvfb_executable, buf[:-1]))
        if n<0 or n>=2**16:
            raise OSError("%s provided an invalid display number: %s" % (xvfb_executable, n))
        new_display_name = ":%s" % n
        sys.stdout.write("Using display number provided by %s: %s\n" % (xvfb_executable, new_display_name))
        if xorg_log_file != None:
            #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.S14700.log
            f0 = shellsub(xorg_log_file, subs)
            subs["DISPLAY"] = new_display_name
            #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.:1.log
            f1 = shellsub(xorg_log_file, subs)
            if f0 != f1:
                try:
                    os.rename(f0, f1)
                except Exception as e:
                    sys.stderr.write("failed to rename Xorg log file from '%s' to '%s'\n" % (f0, f1))
                    sys.stderr.write(" %s\n" % e)
        display_name = new_display_name
    else:
        # use display specified
        xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)
        xvfb_cmd.append(display_name)
        xvfb = subprocess.Popen(xvfb_cmd, executable=xvfb_executable, close_fds=True,
                                stdin=subprocess.PIPE, preexec_fn=setsid)

    from xpra.os_util import get_hex_uuid
    xauth_cmd = ["xauth", "add", display_name, "MIT-MAGIC-COOKIE-1", get_hex_uuid()]
    try:
        code = subprocess.call(xauth_cmd)
        if code != 0:
            raise OSError("non-zero exit code: %s" % code)
    except OSError as e:
        #trying to continue anyway!
        sys.stderr.write("Error running \"%s\": %s\n" % (" ".join(xauth_cmd), e))
    return xvfb, display_name