Source code for pysap.SAPEnqueue

# encoding: utf-8
# pysap - Python library for crafting SAP's network protocols packets
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# Author:
#   Martin Gallo (@martingalloar)
#   Code contributed by SecureAuth to the OWASP CBAS project
#

# Standard imports
import struct
import logging
# External imports
from scapy.layers.inet import TCP
from scapy.packet import bind_layers
from scapy.fields import (IntField, IntEnumField, PacketListField,
                          StrFixedLenField, ConditionalField, ByteField,
                          FieldLenField, LenField, StrNullField,
                          ByteEnumKeysField)
# Custom imports
from pysap.SAPNI import SAPNI
from pysap.SAPRouter import SAPRoutedStreamSocket
from pysap.utils.fields import PacketNoPadded, StrNullFixedLenField


# Create a logger for the SAEnqueue layer
log_sapenqueue = logging.getLogger("pysap.sapenqueue")


# Enqueue Server Type values
enqueue_type_values = {
    0x00: "SYNC_REQUEST",
    0x01: "ASYNC_REQUEST",
    0x02: "RESPONSE",
}
"""Enqueue Server Type values"""


# Enqueue Server Destination values
enqueue_dest_values = {
    0x01: "SYNC_ENQUEUE",
    0x02: "ASYNC_ENQUEUE",
    0x03: "SERVER_ADMIN",
    0x05: "STAT_QUERY",
    0x06: "CONECTION_ADMIN",
    0x07: "ENQ_TO_REP",
    0x08: "REP_TO_ENQ",
}
"""Enqueue Server Destination values"""


# Enqueue Server Admin Opcode values
enqueue_server_admin_opcode_values = {
    0x01: "EnAdmDummyRequest",
    0x02: "EnAdmShutdownRequest",
    0x04: "EnAdmGetReplInfoRequest",
    0x06: "EnAdmTraceRequest",
}
"""Enqueue Server Admin Opcode values"""


# Enqueue Server Admin Trace Action values
enqueue_server_admin_trace_action_values = {
    0x01: "Raise level",
    0x02: "Lower level",
    0x03: "Get trace state",
    0x04: "Set trace status",
    0x05: "Reset trace files",
}
"""Enqueue Server Admin Trace Action values"""


# Enqueue Server Admin Trace Limit values
enqueue_server_admin_trace_limit_values = {
    0x00: "Globally",
    0x01: "Only in enserver",
    0x02: "Only in repserver",
    0x03: "Only in threads of type",
    0x04: "Only in one thread of type",
}
"""Enqueue Server Admin Trace Limit values"""


# Enqueue Server Admin Trace Thread values
enqueue_server_admin_trace_thread_values = {
    0x00: "All threads",
    0x01: "All I/O threads",
    0x02: "Enqueue Worker thread",
    0x03: "Replication thread",
    0x04: "ADM thread",
    0x05: "Signal thread",
    0x06: "Listener thread",
}
"""Enqueue Server Admin Trace Thread values"""


# Enqueue Server Connection Admin Opcode values
enqueue_conn_admin_opcode_values = {
    0x00: "Loopback packet",
    0x01: "Parameter Request",
    0x02: "Parameter Response",
    0x03: "Shutdown Read",
    0x04: "Shutdown Write",
    0x05: "Shutdown Both",
    0x06: "Keepalive",
}
"""Enqueue Server Opcode values"""


# Enqueue Server Connection Admin Parameter values
enqueue_param_values = {
    0x00: "ENCPARAM_RECV_LEN",
    0x01: "ENCPARAM_SEND_LEN",
    0x02: "ENCPARAM_MSG_TYPE",
    0x03: "ENCPARAM_SET_NAME",
    0x04: "ENCPARAM_SET_NOSUPP",
    0x05: "ENCPARAM_SET_VERSION",
    0x06: "ENCPARAM_SET_UCSUPPORT",
}
"""Enqueue Server Connection Admin Parameter values"""


[docs]class SAPEnqueueTracePattern(PacketNoPadded): """SAP Enqueue Server Admin Trace Pattern """ name = "SAP Enqueue Server Admin Trace Pattern" fields_desc = [ FieldLenField("len", None, length_of="pattern", fmt="B"), StrNullFixedLenField("pattern", "", length_from=lambda pkt:pkt.len, max_length=0xff), ]
[docs]class SAPEnqueueParam(PacketNoPadded): """SAP Enqueue Server Connection Admin Parameter packet """ name = "SAP Enqueue Connection Admin Parameter" fields_desc = [ IntEnumField("param", 0, enqueue_param_values), ConditionalField(IntField("len", 0), lambda pkt:pkt.param in [0x06]), ConditionalField(IntField("value", 0), lambda pkt:pkt.param not in [0x03, 0x04]), ConditionalField(StrNullField("set_name", ""), lambda pkt:pkt.param in [0x03]), ]
[docs]class SAPEnqueue(PacketNoPadded): """SAP Enqueue Server packet This packet is used for general Enqueue packets. """ name = "SAP Enqueue" fields_desc = [ StrFixedLenField("magic_bytes", "\xab\xcd\xe1\x23", 4), IntField("id", 0), LenField("len", None, fmt="!I"), LenField("len_frag", None, fmt="!I"), ByteEnumKeysField("dest", 0x00, enqueue_dest_values), ByteEnumKeysField("opcode", 0x00, enqueue_conn_admin_opcode_values), ByteField("more_frags", 0), ByteEnumKeysField("type", 0x00, enqueue_type_values), # Server Admin fields ConditionalField(StrNullFixedLenField("adm_eyecatcher1", "ENC", 3), lambda pkt:pkt.dest == 3), ConditionalField(ByteField("adm_version", 1), lambda pkt:pkt.dest == 3), ConditionalField(ByteField("adm_padd1", 0), lambda pkt:pkt.dest == 3), ConditionalField(ByteField("adm_padd2", 0), lambda pkt:pkt.dest == 3), ConditionalField(ByteField("adm_padd3", 0), lambda pkt:pkt.dest == 3), ConditionalField(StrFixedLenField("adm_eyecatcher2", "#EAA", 4), lambda pkt:pkt.dest == 3), ConditionalField(ByteField("adm_1", 1), lambda pkt:pkt.dest == 3), ConditionalField(IntField("adm_len", 0), lambda pkt:pkt.dest == 3), ConditionalField(ByteEnumKeysField("adm_opcode", 0, enqueue_server_admin_opcode_values), lambda pkt:pkt.dest == 3), ConditionalField(ByteField("adm_flags", 0), lambda pkt:pkt.dest == 3), ConditionalField(IntField("adm_rc", 0), lambda pkt:pkt.dest == 3), ConditionalField(StrFixedLenField("adm_eyecatcher3", "#EAE", 4), lambda pkt:pkt.dest == 3), # Server Admin Trace fields ConditionalField(ByteField("adm_trace_protocol_version", 1), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(ByteEnumKeysField("adm_trace_action", 3, enqueue_server_admin_trace_action_values), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(ByteEnumKeysField("adm_trace_limit", 0, enqueue_server_admin_trace_limit_values), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(ByteEnumKeysField("adm_trace_thread", 0, enqueue_server_admin_trace_thread_values), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(IntField("adm_trace_unknown1", 0), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(IntField("adm_trace_level", 1), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(IntField("adm_trace_level1", 1), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(ByteField("adm_trace_logging", 0), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(IntField("adm_trace_max_file_size", 20 * 1024 * 1024), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(FieldLenField("adm_trace_nopatterns", 0, count_of="adm_trace_patterns", fmt="!I"), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(FieldLenField("adm_trace_nopatterns1", 0, count_of="adm_trace_patterns", fmt="!I"), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(IntField("adm_trace_unknown3", 37), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(StrFixedLenField("adm_trace_eyecatcher4", "#EAH", 4), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(PacketListField("adm_trace_patterns", None, SAPEnqueueTracePattern, count_from=lambda pkt:pkt.adm_trace_nopatterns), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField(StrFixedLenField("adm_trace_eyecatcher5", "#EAD", 4), lambda pkt:pkt.dest == 3 and pkt.adm_opcode in [0x06]), # Connection Admin fields ConditionalField(FieldLenField("params_count", None, count_of="params", fmt="!I"), lambda pkt:pkt.dest == 6 and pkt.opcode in [1, 2]), ConditionalField(PacketListField("params", None, SAPEnqueueParam, count_from=lambda pkt:pkt.params_count), lambda pkt:pkt.dest == 6 and pkt.opcode in [1, 2]), ]
[docs] def post_build(self, pkt, pay): """Adjust the len and len_frags fields after the build of the whole packet. """ length = struct.pack("!I", len(pkt) + len(pay)) pkt = pkt[:8] + length + length + pkt[16:] return pkt + pay
[docs]class SAPEnqueueStreamSocket(SAPRoutedStreamSocket): """Stream socket implementation of the Enqueue Server protocol. It performs reassemble of received fragmented packets to ease use in upper layers. """ # TODO: Add support for sending fragmented packets. # TODO: Add recv/send max length values as parameters. desc = "Enqueue Stream socket" def __init__(self, sock, *args, **kwargs): """Initialization defaults to SAPEnqueue as base class""" if "base_cls" not in kwargs: kwargs["base_cls"] = SAPEnqueue SAPRoutedStreamSocket.__init__(self, sock, *args, **kwargs)
[docs] def recv(self): """Receive a packet at the Enqueue layer, performing reassemble of fragmented packets if necessary. :return: received :class:`SAPEnqueue` packet :rtype: :class:`SAPEnqueue` :raise socket.error: if the connection was close """ # Receive the NI packet packet = SAPRoutedStreamSocket.recv(self) if SAPEnqueue in packet and packet[SAPEnqueue].more_frags: log_sapenqueue.debug("Received Enqueue fragmented packet") head = str(packet[SAPEnqueue])[:20] data = str(packet[SAPEnqueue])[20:] total_length = packet[SAPEnqueue].len - 20 recvd_length = len(packet[SAPEnqueue]) - 20 log_sapenqueue.debug("Received %d up to %d bytes", recvd_length, total_length) while recvd_length < total_length and packet[SAPEnqueue].more_frags == 1: response = SAPRoutedStreamSocket.recv(self)[SAPEnqueue] data += str(response)[20:] recvd_length += len(response) - 20 log_sapenqueue.debug("Received %d up to %d bytes", recvd_length, total_length) packet = SAPEnqueue(head + data) return packet
# Bind SAP NI with the SAP Enqueue port bind_layers(TCP, SAPNI, dport=3200)