Example #1
0
 def get_challenge(self, mac="xor"):
     if self.salt is not None:
         log.error("challenge already sent!")
         return None
     self.salt = get_salt()
     #we need the raw password, so tell the client to use "xor":
     return self.salt, mac
Example #2
0
def main(argv):
    from xpra.util import xor
    from xpra.net.crypto import get_salt, get_digests, gendigest
    from xpra.platform import program_context
    with program_context("LDAP3-Password-Auth", "LDAP3-Password-Authentication"):
        for x in list(argv):
            if x=="-v" or x=="--verbose":
                enable_debug_for("auth")
                argv.remove(x)
        if len(argv) not in (3,4,5,6):
            sys.stderr.write("%s invalid arguments\n" % argv[0])
            sys.stderr.write("usage: %s username password [host] [port] [tls]\n" % argv[0])
            return 1
        username = argv[1]
        password = argv[2]
        kwargs = {}
        if len(argv)>=4:
            kwargs["host"] = argv[3]
        if len(argv)>=5:
            kwargs["port"] = argv[4]
        if len(argv)>=6:
            kwargs["tls"] = argv[5]
        a = Authenticator(username, **kwargs)
        server_salt, digest = a.get_challenge(["xor"])
        salt_digest = a.choose_salt_digest(get_digests())
        assert digest=="xor"
        client_salt = get_salt(len(server_salt))
        combined_salt = gendigest(salt_digest, client_salt, server_salt)
        response = xor(password, combined_salt)
        r = a.authenticate(response, client_salt)
        print("success: %s" % r)
        return int(not r)
Example #3
0
    def make_hello_base(self):
        capabilities = flatten_dict(get_network_caps())
        import struct

        bits = struct.calcsize("P") * 8
        capabilities.update(
            {
                "version": local_version,
                "encoding.generic": True,
                "namespace": True,
                "hostname": socket.gethostname(),
                "uuid": self.uuid,
                "username": self.username,
                "name": get_name(),
                "client_type": self.client_type(),
                "python.version": sys.version_info[:3],
                "python.bits": bits,
                "compression_level": self.compression_level,
                "argv": sys.argv,
            }
        )
        capabilities.update(self.get_file_transfer_features())
        if self.display:
            capabilities["display"] = self.display

        def up(prefix, d):
            updict(capabilities, prefix, d)

        up("build", self.get_version_info())
        mid = get_machine_id()
        if mid:
            capabilities["machine_id"] = mid

        if self.encryption:
            assert self.encryption in ENCRYPTION_CIPHERS
            iv = get_iv()
            key_salt = get_salt()
            iterations = get_iterations()
            padding = choose_padding(self.server_padding_options)
            up(
                "cipher",
                {
                    "": self.encryption,
                    "iv": iv,
                    "key_salt": key_salt,
                    "key_stretch_iterations": iterations,
                    "padding": padding,
                    "padding.options": PADDING_OPTIONS,
                },
            )
            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, padding)
            netlog(
                "encryption capabilities: %s", dict((k, v) for k, v in capabilities.items() if k.startswith("cipher"))
            )
        capabilities.update(self.hello_extra)
        return capabilities
Example #4
0
def main(argv):
    from xpra.platform import program_context
    with program_context("Kerberos-Password-Auth",
                         "Kerberos-Password-Authentication"):
        if len(argv) not in (3, 4, 5):
            sys.stderr.write("%s invalid arguments\n" % argv[0])
            sys.stderr.write(
                "usage: %s username password [service [realm]]\n" % argv[0])
            return 1
        username = argv[1]
        password = argv[2]
        kwargs = {}
        if len(argv) >= 4:
            kwargs["service"] = argv[3]
        if len(argv) == 5:
            kwargs["realm"] = argv[4]
        a = Authenticator(username, **kwargs)
        server_salt, digest = a.get_challenge(["xor"])
        salt_digest = a.choose_salt_digest(get_digests())
        assert digest == "xor"
        client_salt = get_salt(len(server_salt))
        combined_salt = gendigest(salt_digest, client_salt, server_salt)
        response = xor(password, combined_salt)
        a.authenticate(response, client_salt)
    return 0
Example #5
0
 def send_hello(self, challenge_response=None, client_salt=None):
     try:
         hello = self.make_hello_base()
         if self.has_password() and not challenge_response:
             #avoid sending the full hello: tell the server we want
             #a packet challenge first
             hello["challenge"] = True
         else:
             hello.update(self.make_hello())
     except InitExit as e:
         log.error("error preparing connection:")
         log.error(" %s", e)
         self.quit(EXIT_INTERNAL_ERROR)
         return
     except Exception as e:
         log.error("error preparing connection: %s", e, exc_info=True)
         self.quit(EXIT_INTERNAL_ERROR)
         return
     if challenge_response:
         hello["challenge_response"] = challenge_response
         #make it harder for a passive attacker to guess the password length
         #by observing packet sizes (only relevant for wss and ssl)
         hello["challenge_padding"] = get_salt(
             max(32, 512 - len(challenge_response)))
         if client_salt:
             hello["challenge_client_salt"] = client_salt
     log("send_hello(%s) packet=%s", hexstr(challenge_response or ""),
         hello)
     self.send("hello", hello)
Example #6
0
 def get_challenge(self, mac="xor"):
     if self.salt is not None:
         log.error("challenge already sent!")
         return None
     self.salt = get_salt()
     #we need the raw password, so tell the client to use "xor":
     return self.salt, mac
Example #7
0
    def send_challenge_reply(self, packet, password):
        if not password:
            if self.password_file:
                self.auth_error(
                    EXIT_PASSWORD_FILE_ERROR,
                    "failed to load password from file%s %s" %
                    (engs(self.password_file), csv(self.password_file)),
                    "no password available")
            else:
                self.auth_error(
                    EXIT_PASSWORD_REQUIRED,
                    "this server requires authentication and no password is available"
                )
            return
        server_salt = bytestostr(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:
                self.auth_error(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 = bytestostr(packet[3])
        actual_digest = digest.split(":", 1)[0]
        l = len(server_salt)
        salt_digest = "xor"
        if len(packet) >= 5:
            salt_digest = bytestostr(packet[4])
        if salt_digest == "xor":
            #with xor, we have to match the size
            assert l >= 16, "server salt is too short: only %i bytes, minimum is 16" % l
            assert l <= 256, "server salt is too long: %i bytes, maximum is 256" % l
        else:
            #other digest, 32 random bytes is enough:
            l = 32
        client_salt = get_salt(l)
        salt = gendigest(salt_digest, client_salt, server_salt)
        authlog("combined %s salt(%s, %s)=%s", salt_digest,
                hexstr(server_salt), hexstr(client_salt), hexstr(salt))

        challenge_response = gendigest(actual_digest, password, salt)
        if not challenge_response:
            log("invalid digest module '%s': %s", actual_digest)
            self.auth_error(
                EXIT_UNSUPPORTED,
                "server requested '%s' digest but it is not supported" %
                actual_digest, "invalid digest")
            return
        authlog("%s(%s, %s)=%s", actual_digest, repr(password), repr(salt),
                repr(challenge_response))
        self.do_send_challenge_reply(challenge_response, client_salt)
Example #8
0
 def get_challenge(self, digests):
     if self.salt is not None:
         log.error("Error: authentication challenge already sent!")
         return None
     self.salt = get_salt()
     self.digest = choose_digest(digests)
     self.challenge_sent = True
     return self.salt, self.digest
Example #9
0
 def get_challenge(self, digests):
     if self.salt is not None:
         log.error("Error: authentication challenge already sent!")
         return None
     self.salt = get_salt()
     self.digest = choose_digest(digests)
     #we need the raw password, so tell the client to use "xor":
     return self.salt, self.digest
Example #10
0
 def get_challenge(self, digests):
     assert not self.challenge_sent
     if "gss" not in digests:
         log.error("Error: client does not support gss authentication")
         return None
     self.salt = get_salt()
     self.challenge_sent = True
     return self.salt, "gss:%s" % self.service
Example #11
0
    def make_hello_base(self):
        capabilities = flatten_dict(get_network_caps())
        import struct
        bits = struct.calcsize("P") * 8
        capabilities.update({
            "version": XPRA_VERSION,
            "encoding.generic": True,
            "namespace": True,
            "hostname": socket.gethostname(),
            "uuid": self.uuid,
            "username": self.username,
            "name": get_name(),
            "client_type": self.client_type(),
            "python.version": sys.version_info[:3],
            "python.bits": bits,
            "compression_level": self.compression_level,
            "argv": sys.argv,
        })
        capabilities.update(self.get_file_transfer_features())
        if self.display:
            capabilities["display"] = self.display

        def up(prefix, d):
            updict(capabilities, prefix, d)

        up("build", self.get_version_info())
        mid = get_machine_id()
        if mid:
            capabilities["machine_id"] = mid

        if self.encryption:
            assert self.encryption in ENCRYPTION_CIPHERS
            iv = get_iv()
            key_salt = get_salt()
            iterations = get_iterations()
            padding = choose_padding(self.server_padding_options)
            up(
                "cipher", {
                    "": self.encryption,
                    "iv": iv,
                    "key_salt": key_salt,
                    "key_stretch_iterations": iterations,
                    "padding": padding,
                    "padding.options": PADDING_OPTIONS,
                })
            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, padding)
            netlog(
                "encryption capabilities: %s",
                dict((k, v) for k, v in capabilities.items()
                     if k.startswith("cipher")))
        capabilities.update(self.hello_extra)
        return capabilities
Example #12
0
 def get_challenge(self):
     if self.salt is not None:
         log.error("challenge already sent!")
         if self.salt is not False:
             self.salt = False
         return None
     self.salt = get_salt()
     #this authenticator can use the safer "hmac" digest:
     return self.salt, "hmac"
Example #13
0
 def get_challenge(self, digests):
     if self.salt is not None:
         log.error("challenge already sent!")
         if self.salt is not False:
             self.salt = False
         return None
     self.salt = get_salt()
     self.digest = choose_digest(digests)
     if not self.digest:
         return None
     return self.salt, self.digest
Example #14
0
def main(argv):
    from xpra.platform import program_context
    with program_context("GSS-Auth", "GSS-Authentication"):
        if len(argv) != 3:
            sys.stderr.write("%s invalid arguments\n" % argv[0])
            sys.stderr.write("usage: %s username token\n" % argv[0])
            return 1
        username = argv[1]
        token = argv[2]
        kwargs = {}
        a = Authenticator(username, **kwargs)
        server_salt, digest = a.get_challenge(["xor"])
        salt_digest = a.choose_salt_digest(get_digests())
        assert digest == "xor"
        client_salt = get_salt(len(server_salt))
        combined_salt = gendigest(salt_digest, client_salt, server_salt)
        response = xor(token, combined_salt)
        a.authenticate(response, client_salt)
    return 0
Example #15
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.has_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_salt(len(salt))
        # TODO: use some key stretching algorigthm? (meh)
        salt = xor(salt, client_salt)
        if digest == b"hmac":
            import hmac, hashlib

            password = strtobytes(password)
            salt = strtobytes(salt)
            challenge_response = hmac.HMAC(password, salt, digestmod=hashlib.md5).hexdigest()
        elif digest == b"xor":
            # don't send XORed password unencrypted:
            encrypted = self._protocol.cipher_out or self._protocol.get_info().get("type") == "ssl"
            local = self.display_desc.get("local", False)
            authlog("xor challenge, encrypted=%s, local=%s", encrypted, local)
            if local and ALLOW_LOCALHOST_PASSWORDS:
                pass
            elif not encrypted 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
            salt = salt[: len(password)]
            challenge_response = strtobytes(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)
Example #16
0
 def get_challenge(self):
     return get_salt(), "hmac"
Example #17
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.has_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_salt(len(salt))
        #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
            password = strtobytes(password)
            salt = strtobytes(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
            salt = salt[:len(password)]
            challenge_response = strtobytes(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)
Example #18
0
 def get_challenge(self, digests):
     return get_salt(), choose_digest(digests)
Example #19
0
 def get_challenge(self, digests):
     self.challenge_sent = True
     return get_salt(), choose_digest(digests)
 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)
Example #21
0
    def make_hello_base(self):
        capabilities = flatten_dict(get_network_caps())
        import struct
        bits = struct.calcsize("P") * 8
        capabilities.update({
                "version"               : XPRA_VERSION,
                "encoding.generic"      : True,
                "namespace"             : True,
                "hostname"              : socket.gethostname(),
                "uuid"                  : self.uuid,
                "username"              : self.username,
                "name"                  : get_name(),
                "client_type"           : self.client_type(),
                "python.version"        : sys.version_info[:3],
                "python.bits"           : bits,
                "compression_level"     : self.compression_level,
                "argv"                  : sys.argv,
                })
        capabilities.update(self.get_file_transfer_features())
        if self.display:
            capabilities["display"] = self.display
        def up(prefix, d):
            updict(capabilities, prefix, d)
        up("build",     self.get_version_info())
        mid = get_machine_id()
        if mid:
            capabilities["machine_id"] = mid
        #get socket speed if we have it:
        pinfo = self._protocol.get_info()
        netlog("protocol info=%s", pinfo)
        socket_speed = pinfo.get("socket", {}).get("speed")
        if socket_speed:
            capabilities["connection-data"] = {"speed" : socket_speed}
        bandwidth_limit = self.bandwidth_limit
        log("bandwidth-limit=%s, socket-speed=%s", self.bandwidth_limit, socket_speed)
        if bandwidth_limit is None:
            if socket_speed:
                #auto: use 80% of socket speed if we have it:
                bandwidth_limit = socket_speed*AUTO_BANDWIDTH_PCT//100 or 0
            else:
                bandwidth_limit = 0
        if bandwidth_limit>0:
            capabilities["bandwidth-limit"] = bandwidth_limit

        if self.encryption:
            assert self.encryption in ENCRYPTION_CIPHERS
            iv = get_iv()
            key_salt = get_salt()
            iterations = get_iterations()
            padding = choose_padding(self.server_padding_options)
            up("cipher", {
                    ""                      : self.encryption,
                    "iv"                    : iv,
                    "key_salt"              : key_salt,
                    "key_stretch_iterations": iterations,
                    "padding"               : padding,
                    "padding.options"       : PADDING_OPTIONS,
                    })
            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, padding)
            netlog("encryption capabilities: %s", dict((k,v) for k,v in capabilities.items() if k.startswith("cipher")))
        capabilities.update(self.hello_extra)
        return capabilities
Example #22
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):
            authlog.error("Error: authentication failed")
            authlog.error(" %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:
            authlog("creating authenticator %s", proto.auth_class)
            try:
                auth, aclass, options = proto.auth_class
                ainstance = aclass(username, **options)
                proto.authenticator = ainstance
                authlog("%s=%s", auth, ainstance)
            except Exception as e:
                authlog.error("Error instantiating %s:", proto.auth_class)
                authlog.error(" %s", 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")
        padding = c.strget("cipher.padding", DEFAULT_PADDING)
        padding_options = c.strlistget("cipher.padding.options",
                                       [DEFAULT_PADDING])
        auth_caps = {}
        if cipher and cipher_iv:
            if cipher not in ENCRYPTION_CIPHERS:
                authlog.warn("unsupported cipher: %s", cipher)
                auth_failed("unsupported cipher")
                return False
            encryption_key = self.get_encryption_key(proto.authenticator,
                                                     proto.keyfile)
            if encryption_key is None:
                auth_failed("encryption key is missing")
                return False
            if padding not in ALL_PADDING_OPTIONS:
                auth_failed("unsupported padding: %s" % padding)
                return False
            authlog("set output cipher using encryption key '%s'",
                    repr_ellipsized(encryption_key))
            proto.set_cipher_out(cipher, cipher_iv, encryption_key, key_salt,
                                 iterations, padding)
            #use the same cipher as used by the client:
            auth_caps = new_cipher_caps(proto, cipher, encryption_key,
                                        padding_options)
            authlog("server cipher=%s", auth_caps)
        else:
            if proto.encryption:
                authlog("client does not provide encryption tokens")
                auth_failed("missing encryption tokens")
                return False
            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")
            authlog(
                "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
                    authlog("challenge: %s", challenge)
                    salt, digest = challenge
                    authlog.info(
                        "Authentication required by %s authenticator module",
                        proto.authenticator)
                    authlog.info(" sending challenge for '%s' using %s digest",
                                 username, digest)
                    if digest not in self.digest_modes:
                        auth_failed(
                            "cannot proceed without %s digest support" %
                            digest)
                        return False
                else:
                    authlog.warn(
                        "Warning: client expects a challenge but this connection is unauthenticated"
                    )
                    #fake challenge so the client will send the real hello:
                    salt = get_salt()
                    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
            authlog("authentication challenge passed")
        else:
            #did the client expect a challenge?
            if c.boolget("challenge"):
                authlog.warn(
                    "this server does not require authentication (client supplied a challenge)"
                )
        return auth_caps
Example #23
0
 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 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":
         password = self.session_options.get("password")
         if not password:
             self.stop(
                 "authentication requested by the server, but no password available for this session"
             )
             return
         from xpra.net.crypto import get_salt, gendigest
         #client may have already responded to the challenge,
         #so we have to handle authentication from this end
         server_salt = bytestostr(packet[1])
         l = len(server_salt)
         digest = bytestostr(packet[3])
         salt_digest = "xor"
         if len(packet) >= 5:
             salt_digest = bytestostr(packet[4])
         if salt_digest in ("xor", "des"):
             if not LEGACY_SALT_DIGEST:
                 self.stop("server uses legacy salt digest '%s'" %
                           salt_digest)
                 return
             log.warn(
                 "Warning: server using legacy support for '%s' salt digest",
                 salt_digest)
         if salt_digest == "xor":
             #with xor, we have to match the size
             assert l >= 16, "server salt is too short: only %i bytes, minimum is 16" % l
             assert l <= 256, "server salt is too long: %i bytes, maximum is 256" % l
         else:
             #other digest, 32 random bytes is enough:
             l = 32
         client_salt = get_salt(l)
         salt = gendigest(salt_digest, client_salt, server_salt)
         challenge_response = gendigest(digest, password, salt)
         if not challenge_response:
             log("invalid digest module '%s': %s", digest)
             self.stop(
                 "server requested '%s' digest but it is not supported" %
                 digest)
             return
         log.info("sending %s challenge response", digest)
         self.send_hello(challenge_response, client_salt)
         return
     self.queue_client_packet(packet)
Example #25
0
 def get_challenge(self):
     return get_salt(), "hmac"
Example #26
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.has_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
        server_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 = bytestostr(packet[3])
        l = len(server_salt)
        salt_digest = "xor"
        if len(packet) >= 5:
            salt_digest = bytestostr(packet[4])
        if salt_digest == "xor":
            #with xor, we have to match the size
            assert l >= 16, "server salt is too short: only %i bytes, minimum is 16" % l
            assert l <= 256, "server salt is too long: %i bytes, maximum is 256" % l
        else:
            #other digest, 32 random bytes is enough:
            l = 32
        client_salt = get_salt(l)
        if salt_digest in ("xor", "des"):
            if not LEGACY_SALT_DIGEST:
                warn_server_and_exit(
                    EXIT_INCOMPATIBLE_VERSION,
                    "server uses legacy salt digest '%s'" % salt_digest,
                    "unsupported digest %s" % salt_digest)
                return
            log.warn(
                "Warning: server using legacy support for '%s' salt digest",
                salt_digest)
        salt = gendigest(salt_digest, client_salt, server_salt)
        authlog("combined %s salt(%s, %s)=%s", salt_digest,
                binascii.hexlify(server_salt), binascii.hexlify(client_salt),
                binascii.hexlify(salt))
        if digest.startswith(b"hmac"):
            import hmac
            digestmod = get_digest_module(digest)
            if not digestmod:
                log("invalid digest module '%s': %s", digest)
                warn_server_and_exit(
                    EXIT_UNSUPPORTED,
                    "server requested digest '%s' but it is not supported" %
                    digest, "invalid digest")
                return
            password = strtobytes(password)
            salt = memoryview_to_bytes(salt)
            challenge_response = hmac.HMAC(password, salt,
                                           digestmod=digestmod).hexdigest()
            authlog("hmac.HMAC(%s, %s)=%s", binascii.hexlify(password),
                    binascii.hexlify(salt), challenge_response)
        elif digest == b"xor":
            #don't send XORed password unencrypted:
            encrypted = self._protocol.cipher_out or self._protocol.get_info(
            ).get("type") == "ssl"
            local = self.display_desc.get("local", False)
            authlog("xor challenge, encrypted=%s, local=%s", encrypted, local)
            if local and ALLOW_LOCALHOST_PASSWORDS:
                pass
            elif not encrypted 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
            salt = salt[:len(password)]
            challenge_response = memoryview_to_bytes(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),
                    binascii.hexlify(challenge_response))
        self.password_sent = True
        self.remove_packet_handlers("challenge")
        self.send_hello(challenge_response, client_salt)
Example #27
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):
            authlog.error("Error: authentication failed")
            authlog.error(" %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:
            authlog("creating authenticator %s", proto.auth_class)
            try:
                auth, aclass, options = proto.auth_class
                ainstance = aclass(username, **options)
                proto.authenticator = ainstance
                authlog("%s=%s", auth, ainstance)
            except Exception as e:
                authlog.error("Error instantiating %s:", proto.auth_class)
                authlog.error(" %s", e)
                auth_failed("authentication failed")
                return False

        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")
        padding = c.strget("cipher.padding", DEFAULT_PADDING)
        padding_options = c.strlistget("cipher.padding.options", [DEFAULT_PADDING])
        auth_caps = {}
        if cipher and cipher_iv:
            if cipher not in ENCRYPTION_CIPHERS:
                authlog.warn("unsupported cipher: %s", cipher)
                auth_failed("unsupported cipher")
                return False
            encryption_key = self.get_encryption_key(proto.authenticator, proto.keyfile)
            if encryption_key is None:
                auth_failed("encryption key is missing")
                return False
            if padding not in ALL_PADDING_OPTIONS:
                auth_failed("unsupported padding: %s" % padding)
                return False
            authlog("set output cipher using encryption key '%s'", repr_ellipsized(encryption_key))
            proto.set_cipher_out(cipher, cipher_iv, encryption_key, key_salt, iterations, padding)
            #use the same cipher as used by the client:
            auth_caps = new_cipher_caps(proto, cipher, encryption_key, padding_options)
            authlog("server cipher=%s", auth_caps)
        else:
            if proto.encryption:
                authlog("client does not provide encryption tokens")
                auth_failed("missing encryption tokens")
                return False
            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")
            authlog("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
                    authlog("challenge: %s", challenge)
                    salt, digest = challenge
                    authlog.info("Authentication required by %s authenticator module", proto.authenticator)
                    authlog.info(" sending challenge for '%s' using %s digest", username, digest)
                    if digest not in digest_modes:
                        auth_failed("cannot proceed without %s digest support" % digest)
                        return False
                else:
                    authlog.warn("Warning: client expects a challenge but this connection is unauthenticated")
                    #fake challenge so the client will send the real hello:
                    salt = get_salt()
                    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
            authlog("authentication challenge passed")
        else:
            #did the client expect a challenge?
            if c.boolget("challenge"):
                authlog.warn("this server does not require authentication (client supplied a challenge)")
        return auth_caps