def _client_handshake(self): """ Perform handshake/authentication with a connecting client Outline: 1. Client connects 2. We fake RFB 3.8 protocol and require VNC authentication [processing also supports RFB 3.3] 3. Client accepts authentication method 4. We send an authentication challenge 5. Client sends the authentication response 6. We check the authentication Upon return, self.client socket is connected to the client. """ self.ws.send(rfb.RFB_VERSION_3_8 + "\n") client_version_str = self.ws.recv(1024) client_version = rfb.check_version(client_version_str) if not client_version: self.error("Invalid version: %s", client_version_str) raise gevent.GreenletExit # Both for RFB 3.3 and 3.8 self.debug("Requesting authentication") auth_request = rfb.make_auth_request(rfb.RFB_AUTHTYPE_VNC, version=client_version) self.ws.send(auth_request) # The client gets to propose an authtype only for RFB 3.8 if client_version == rfb.RFB_VERSION_3_8: res = self.ws.recv(1024) type = rfb.parse_client_authtype(res) if type == rfb.RFB_AUTHTYPE_ERROR: self.warn("Client refused authentication: %s", res[1:]) else: self.debug("Client requested authtype %x", type) if type != rfb.RFB_AUTHTYPE_VNC: self.error("Wrong auth type: %d", type) self.ws.send(rfb.to_u32(rfb.RFB_AUTH_ERROR)) raise gevent.GreenletExit # Generate the challenge challenge = os.urandom(16) self.ws.send(challenge) response = self.ws.recv(1024) if len(response) != 16: self.error("Wrong response length %d, should be 16", len(response)) raise gevent.GreenletExit if rfb.check_password(challenge, response, self.password): self.debug("Authentication successful") else: self.warn("Authentication failed") self.ws.send(rfb.to_u32(rfb.RFB_AUTH_ERROR)) raise gevent.GreenletExit # Accept the authentication self.ws.send(rfb.to_u32(rfb.RFB_AUTH_SUCCESS))
def _perform_server_handshake(self): """ Initiate a connection with the backend server and perform basic RFB 3.8 handshake with it. Return a socket connected to the backend server. """ server = None tries = VncAuthProxy.connect_retries while tries: tries -= 1 # Initiate server connection for res in socket.getaddrinfo(self.daddr, self.dport, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): af, socktype, proto, canonname, sa = res try: server = socket.socket(af, socktype, proto) except socket.error: server = None continue # Set socket timeout for the initial handshake server.settimeout(VncAuthProxy.server_timeout) try: self.debug("Connecting to %s:%s", *sa[:2]) server.connect(sa) self.debug("Connection to %s:%s successful", *sa[:2]) except socket.error as err: self.debug("Failed to perform sever hanshake, retrying...") server.close() server = None continue # We succesfully connected to the server tries = 0 break # Wait and retry gevent.sleep(VncAuthProxy.retry_wait) if server is None: raise InternalError("Failed to connect to server") version = server.recv(1024) if not rfb.check_version(version): raise InternalError("Unsupported RFB version: %s" % version.strip()) server.send(rfb.RFB_VERSION_3_8 + "\n") res = server.recv(1024) types = rfb.parse_auth_request(res) if not types: raise InternalError("Error handshaking with the server") else: self.debug("Supported authentication types: %s", " ".join([str(x) for x in types])) if rfb.RFB_AUTHTYPE_NONE not in types: raise InternalError("Error, server demands authentication") server.send(rfb.to_u8(rfb.RFB_AUTHTYPE_NONE)) # Check authentication response res = server.recv(4) res = rfb.from_u32(res) if res != 0: raise InternalError("Authentication error") # Reset the timeout for the rest of the session server.settimeout(None) self.server = server
def _handshake(self): """ Perform handshake/authentication with a connecting client Outline: 1. Client connects 2. We fake RFB 3.8 protocol and require VNC authentication [also supports RFB 3.3] 3. Client accepts authentication method 4. We send an authentication challenge 5. Client sends the authentication response 6. We check the authentication 7. We initiate a connection with the backend server and perform basic RFB 3.8 handshake with it. Upon return, self.client and self.server are sockets connected to the client and the backend server, respectively. """ self.client.send(rfb.RFB_VERSION_3_8 + "\n") client_version_str = self.client.recv(1024) client_version = rfb.check_version(client_version_str) if not client_version: self.error("Invalid version: %s" % client_version_str) raise gevent.GreenletExit # Both for RFB 3.3 and 3.8 self.debug("Requesting authentication") auth_request = rfb.make_auth_request(rfb.RFB_AUTHTYPE_VNC, version=client_version) self.client.send(auth_request) # The client gets to propose an authtype only for RFB 3.8 if client_version == rfb.RFB_VERSION_3_8: res = self.client.recv(1024) type = rfb.parse_client_authtype(res) if type == rfb.RFB_AUTHTYPE_ERROR: self.warn("Client refused authentication: %s" % res[1:]) else: self.debug("Client requested authtype %x" % type) if type != rfb.RFB_AUTHTYPE_VNC: self.error("Wrong auth type: %d" % type) self.client.send(rfb.to_u32(rfb.RFB_AUTH_ERROR)) raise gevent.GreenletExit # Generate the challenge challenge = os.urandom(16) self.client.send(challenge) response = self.client.recv(1024) if len(response) != 16: self.error("Wrong response length %d, should be 16" % len(response)) raise gevent.GreenletExit if rfb.check_password(challenge, response, self.password): self.debug("Authentication successful!") else: self.warn("Authentication failed") self.client.send(rfb.to_u32(rfb.RFB_AUTH_ERROR)) raise gevent.GreenletExit # Accept the authentication self.client.send(rfb.to_u32(rfb.RFB_AUTH_SUCCESS)) # Try to connect to the server tries = 50 while tries: tries -= 1 # Initiate server connection for res in socket.getaddrinfo(self.daddr, self.dport, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): af, socktype, proto, canonname, sa = res try: self.server = socket.socket(af, socktype, proto) except socket.error, msg: self.server = None continue try: self.debug("Connecting to %s:%s" % sa[:2]) self.server.connect(sa) self.debug("Connection to %s:%s successful" % sa[:2]) except socket.error, msg: self.server.close() self.server = None continue # We succesfully connected to the server tries = 0 break
self.server = None continue # We succesfully connected to the server tries = 0 break # Wait and retry gevent.sleep(0.2) if self.server is None: self.error("Failed to connect to server") raise gevent.GreenletExit version = self.server.recv(1024) if not rfb.check_version(version): self.error("Unsupported RFB version: %s" % version.strip()) raise gevent.GreenletExit self.server.send(rfb.RFB_VERSION_3_8 + "\n") res = self.server.recv(1024) types = rfb.parse_auth_request(res) if not types: self.error("Error handshaking with the server") raise gevent.GreenletExit else: self.debug("Supported authentication types: %s" % " ".join([str(x) for x in types]))