import struct import logging from heralding.capabilities.handlerbase import HandlerBase logger = logging.getLogger(__name__) class PostgreSQL(HandlerBase): async def execute_capability(self, reader, writer, session): try: await self._handle_session(session, reader, writer) except struct.error as exc: logger.debug('PostgreSQL connection error: %s', exc) session.end_session() async def _handle_session(self, session, reader, writer): # Read (we blindly assume) SSL request and deny it await read_msg(reader, writer) writer.write(b'N') session.activity() # Read login details data = await read_msg(reader, writer) login_dict = parse_dict(data) # Request plain text password login password_request = [b'R', 8, 3] writer.write(struct.pack('>c I I', *password_request)) await writer.drain() # Read password data = await read_msg(reader, writer) password = parse_str(data) username = login_dict['user'] session.add_auth_attempt('plaintext', username=username, password=password) # Report login failure writer.write(b'E') fail = [ b'SFATAL\x00C28P01\x00', 'Mpassword authentication failed for user "{}"'.format(username).encode( 'utf-8'), b'\x00Fauth.c\x00L288\x00Rauth_failed\x00\x00', ] length = sum([len(f) for f in fail]) writer.write(struct.pack('>I', length + 4)) for f in fail: writer.write(f) await writer.drain() session.end_session() async def read_msg(reader, writer): i = await reader.read(4) length = struct.unpack('>I', i)[0] data = await reader.read(length) return data def parse_dict(data): dct = {} mode = 'pad' key = [] value = [] for c in struct.iter_unpack('c', data): c = c[0] if mode == 'pad': if c in (bytes([0]), bytes([3])): continue else: mode = 'key' if mode == 'key': if c == bytes([0]): mode = 'value' else: key.append(c.decode()) elif mode == 'value': if c == bytes([0]): dct[''.join(key)] = ''.join(value) key = [] value = [] mode = 'pad' else: value.append(c.decode()) return dct def parse_str(data): data_array = bytearray(data) return data_array[1:-1].decode('utf-8')