def process_login_request(self, data, client): if self.key is None: return b"" stream = streams_nex.StreamIn(data, self.settings) ticket_data = stream.buffer() request_data = stream.buffer() ticket = kerberos.ServerTicket.decrypt(ticket_data, self.key, self.settings) if ticket.timestamp.timestamp() < time.time() - 120: raise ValueError("Ticket has expired") kerb = kerberos.KerberosEncryption(ticket.session_key) decrypted = kerb.decrypt(request_data) if len(decrypted) != self.settings["nex.pid_size"] + 8: raise ValueError("Invalid ticket size") stream = streams_nex.StreamIn(decrypted, self.settings) if stream.pid() != ticket.source: raise ValueError("Invalid pid in kerberos ticket") client.login(ticket.source, stream.u32(), ticket.session_key) check_value = stream.u32() return struct.pack("<II", 4, (check_value + 1) & 0xFFFFFFFF)
def login(self, username, password, auth_info=None, login_data=None): if auth_info: ticket = self.auth_client.login_ex(username, auth_info) else: ticket = self.auth_client.login(username) kerberos_key = self.key_derivation.derive_key( password.encode("ascii"), self.auth_client.pid ) kerberos_encryption = kerberos.KerberosEncryption(kerberos_key) ticket.decrypt(kerberos_encryption, self.settings) if ticket.pid != self.auth_client.secure_station["PID"]: ticket = self.auth_client.request_ticket( self.auth_client.pid, self.auth_client.secure_station["PID"] ) ticket.decrypt(kerberos_encryption, self.settings) host = self.auth_client.secure_station["address"] port = self.auth_client.secure_station["port"] if host == "0.0.0.1": host, port = self.auth_client.server_address() self.secure_client.set_ticket(ticket) self.secure_client.connect(host, port) if login_data: urls = self.secure_client.register_urls(login_data) else: urls = self.secure_client.register_urls() self.local_station, self.public_station = urls
def handle_connection_request(self, packet): logger.debug("Received connection request: %i bytes", len(packet.payload)) if not self.server_key: logger.debug("No validation needed, accepting connection") return b"" if not packet.payload: logger.error("Received empty connection request for secure server") self.cleanup() return stream = streams.StreamIn(packet.payload, self.settings) ticket = stream.buffer() request = stream.buffer() server_ticket = kerberos.ServerTicket() try: server_ticket.decrypt(ticket, self.server_key, self.settings) except ValueError: logger.error("Server ticket decryption failed") self.cleanup() return if server_ticket.expiration.timestamp() < time.time(): logger.error("Ticket has expired") self.cleanup() return kerb = kerberos.KerberosEncryption(server_ticket.session_key) try: decrypted = kerb.decrypt(request) except ValueError: logger.error("Ticket decryption failed") self.cleanup() return if len(decrypted) != self.settings.get("common.pid_size") + 8: logger.error("Invalid ticket size") self.cleanup() return stream = streams.StreamIn(decrypted, self.settings) if stream.pid() != server_ticket.source_pid: logger.error("Invalid pid in kerberos ticket") self.cleanup() return self.pid = server_ticket.source_pid stream.u32() #Don't care about cid check_value = stream.u32() logger.debug("Connection request was validated successfully") self.set_session_key(server_ticket.session_key) return struct.pack("<II", 4, (check_value + 1) & 0xFFFFFFFF)
def __init__(self, backend, ticket): super().__init__(backend, service.ServiceClient.SECURE) self.ticket = ticket self.auth_client = backend.auth_client self.kerberos_encryption = kerberos.KerberosEncryption(self.ticket.key) station_url = self.auth_client.secure_station self.connection_id = station_url["CID"] self.principal_id = station_url["PID"]
def test_kerberos_encryption(): kerb = kerberos.KerberosEncryption(b"key") data = kerb.encrypt(b"test message") assert data == bytes.fromhex( "7f09479904e21e393b2a6f1ed5b96acc69a869dce66679d0cedb242d") assert kerb.check(data) assert not kerb.check(b"\x7e" + data[1:]) assert kerb.decrypt(data) == b"test message"
def build_connection_request(self, ticket, check_value): stream = streams.StreamOut(self.settings) stream.buffer(ticket.internal) substream = streams.StreamOut(self.settings) substream.pid(ticket.source_pid) substream.u32(ticket.target_cid) substream.u32(check_value) kerb = kerberos.KerberosEncryption(ticket.session_key) stream.buffer(kerb.encrypt(substream.get())) return stream.get()
def build_connection_request(self): if self.credentials is None: return b"" stream = streams_nex.StreamOut(self.settings) stream.buffer(self.credentials.ticket.internal) substream = streams_nex.StreamOut(self.settings) substream.pid(self.credentials.pid) substream.u32(self.credentials.cid) substream.u32(self.connection_check) kerb = kerberos.KerberosEncryption(self.credentials.ticket.session_key) stream.buffer(kerb.encrypt(substream.get())) return stream.get()
def build_connection_request(self): if not self.client_ticket: return b"" self.check_value = random.randint(0, 0xFFFFFFFF) stream = streams.StreamOut(self.settings) stream.buffer(self.client_ticket.internal) substream = streams.StreamOut(self.settings) substream.pid(self.client_ticket.source_pid) substream.u32(self.client_ticket.target_cid) substream.u32(self.check_value) kerb = kerberos.KerberosEncryption(self.client_ticket.session_key) stream.buffer(kerb.encrypt(substream.get())) return stream.get()
def handle_login_result(self, call_id, password): stream = self.get_response(call_id) result = stream.u32() if result & 0x80000000: raise AuthenticationError("Login failed (%s)" %errors.error_names.get(result, "unknown error")) self.pid = stream.uint() kerberos_data = stream.buffer() self.secure_station = stream.extract(RVConnectionData).main_station server_name = stream.string() kerberos_key = self.key_derivation.derive_key(password.encode("ascii"), self.pid) self.kerberos_encryption = kerberos.KerberosEncryption(kerberos_key) if not self.kerberos_encryption.check_hmac(kerberos_data): raise AuthenticationError("Kerberos key validation failed (incorrect password)") logger.info("Authentication.login(_ex) -> (%08X, %s, %s)", self.pid, self.secure_station, server_name)
def validate_connection_request(self, payload): if not self.server_key or not payload: return True stream = streams.StreamIn(payload, self.settings) ticket = stream.buffer() request = stream.buffer() self.server_ticket = kerberos.ServerTicket() try: self.server_ticket.decrypt(ticket, self.server_key, self.settings) except ValueError: logger.error("Server ticket decryption failed") return False if self.server_ticket.expiration.timestamp() < time.time(): logger.error("Ticket has expired") return False kerb = kerberos.KerberosEncryption(self.server_ticket.session_key) try: decrypted = kerb.decrypt(request) except ValueError: logger.error("Ticket decryption failed") return False self.set_session_key(self.server_ticket.session_key) if len(decrypted) != self.settings.get("common.pid_size") + 8: logger.error("Invalid ticket size") return False stream = streams.StreamIn(decrypted, self.settings) if stream.pid() != self.server_ticket.source_pid: logger.error("Invalid pid in kerberos ticket") return False stream.u32() #Don't care about cid self.check_value = stream.u32() return True
def connect(self, host, port): encryption = kerberos.KerberosEncryption(self.ticket.key) stream = streams.StreamOut(self.backend.settings) stream.buffer(self.ticket.data) check_value = random.randint(0, 0xFFFFFFFF) substream = streams.StreamOut(self.backend.settings) substream.uint(self.auth_client.pid) substream.u32(self.auth_client.secure_station["CID"]) substream.u32(check_value) #Used to check connection response stream.buffer(encryption.encrypt(substream.get())) response = super().connect(host, port, stream.get()) stream = streams.StreamIn(response, self.backend.settings) if stream.u32() != 4: raise ConnectionError("Invalid connection response size") if stream.u32() != (check_value + 1) & 0xFFFFFFFF: raise ConnectionError("Connection response check failed") self.client.set_secure_key(self.ticket.key)
def handle_connection_request(self, data): logger.debug("Received connection request: %i bytes", len(data)) if not self.server_key: logger.debug("No validation needed, accepting connection") return b"" if not data: raise ValueError("Received empty connection request on secure server") stream = streams_nex.StreamIn(data, self.settings) ticket_data = stream.buffer() request_data = stream.buffer() ticket = kerberos.ServerTicket.decrypt(ticket_data, self.server_key, self.settings) if ticket.timestamp.timestamp() < time.time() - 120: raise ValueError("Ticket has expired") kerb = kerberos.KerberosEncryption(ticket.session_key) decrypted = kerb.decrypt(request_data) if len(decrypted) != self.settings["nex.pid_size"] + 8: raise ValueError("Invalid ticket size") stream = streams_nex.StreamIn(decrypted, self.settings) if stream.pid() != ticket.source: raise ValueError("Invalid pid in kerberos ticket") self.user_pid = ticket.source self.user_cid = stream.u32() check_value = stream.u32() logger.debug("Connection request was validated successfully") self.set_session_key(ticket.session_key) return struct.pack("<II", 4, (check_value + 1) & 0xFFFFFFFF)