Exemple #1
0
static char const *cap_info_2_str(struct proto_info const *info_)
{
    struct cap_proto_info const *info = DOWNCAST(info_, info, cap_proto_info);
    char *str = tempstr();
    snprintf(str, TEMPSTR_SIZE, "%s, dev_id=%u, tv=%s",
        proto_info_2_str(info_),
        info->dev_id,
        timeval_2_str(&info->tv));
    return str;
}
Exemple #2
0
// Decode the flow in src into flow. We already checked there are enough bytes to read in src.
static int nf_flow_decode(struct nf_flow *flow, struct nf_msg const *head, void const *src)
{
    struct nf_flow_ll const *flow_ll = src; // FIXME: won't work if not properly aligned

    CONV_IP(flow, addr[0]);   CONV_IP(flow, addr[1]);    CONV_IP(flow, next_hop);
    CONV_16(flow, port[0]);   CONV_16(flow, port[1]);
    CONV_16(flow, in_iface);  CONV_16(flow, out_iface);
    CONV_32(flow, packets);   CONV_32(flow, bytes);
    CONV_8(flow, tcp_flags);  CONV_8(flow, ip_proto);    CONV_8(flow, ip_tos);
    CONV_16(flow, as[0]);     CONV_16(flow, as[1]);
    CONV_8(flow, mask[0]);    CONV_8(flow, mask[1]);
    /* The first/last fields of the netflow are the uptime at the first/last pkt of the flow.
     * We find a timestamp more interesting, so we get it from sysuptime and localtime of the header.
     * But this imply trusting the netflow header localtime. */
    SLOG(LOG_DEBUG, "Decoding a flow which sys_uptime=%"PRIu32", now=%s, first=%u, last=%u",
        head->sys_uptime, timeval_2_str(&head->ts), ntohl(flow_ll->first), ntohl(flow_ll->last));
    flow->first = head->ts;
    timeval_sub_usec(&flow->first, (int64_t)(head->sys_uptime - ntohl(flow_ll->first)) * 1000);
    flow->last = head->ts;
    timeval_sub_usec(&flow->last, (int64_t)(head->sys_uptime - ntohl(flow_ll->last)) * 1000);
    SLOG(LOG_DEBUG, "...yielding: %s->%s", timeval_2_str(&flow->first), timeval_2_str(&flow->last));

    return 0;
}
Exemple #3
0
static enum proto_parse_status netbios_parse_frame(struct netbios_parser *netbios_parser,
        struct proto_info *parent, unsigned way, uint8_t const *packet, size_t cap_len, size_t wire_len,
        struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet, size_t *pos)
{
    if (cap_len == 0 && netbios_parser->sbuf.dir[way].cap_len == 0) {
        // Ignore pure gap start
        timeval_reset(netbios_parser->first_packet_tv + way);
        return PROTO_PARSE_ERR;
    }
    if (cap_len > 0 && !timeval_is_set(&netbios_parser->first_packet_tv[way])) {
        SLOG(LOG_DEBUG, "Set first packet ts for way %d to %s", way, timeval_2_str(now));
        netbios_parser->first_packet_tv[way] = *now;
    }
    if (wire_len < NETBIOS_HEADER_SIZE + SMB_FLAG_SIZE) {
        streambuf_set_restart(&netbios_parser->sbuf, way, packet, NETBIOS_HEADER_SIZE + SMB_FLAG_SIZE);
        return PROTO_OK;
    }
    if (cap_len < NETBIOS_HEADER_SIZE + SMB_FLAG_SIZE) {
        SLOG(LOG_DEBUG, "Got a gap on neccessary bytes");
        timeval_reset(netbios_parser->first_packet_tv + way);
        return PROTO_PARSE_ERR;
    }
    if (packet[0] != 0) {
        SLOG(LOG_DEBUG, "Expected Session message type 0x00, got 0x%"PRIx8, packet[0]);
        timeval_reset(netbios_parser->first_packet_tv + way);
        return PROTO_PARSE_ERR;
    }

    uint32_t smb_version = READ_U32N(packet + NETBIOS_HEADER_SIZE);
    if (smb_version != CIFS_SMB_HEADER && smb_version != CIFS_SMB2_HEADER) {
        static unsigned char smb_header[SMB_FLAG_SIZE] = {0xff, 0x53, 0x4d, 0x42};
        static unsigned char smb2_header[SMB_FLAG_SIZE] = {0xfe, 0x53, 0x4d, 0x42};
        void *res = memmem(packet + NETBIOS_HEADER_SIZE, cap_len - NETBIOS_HEADER_SIZE, &smb_header,
                SMB_FLAG_SIZE);
        if (!res) {
            res = memmem(packet + NETBIOS_HEADER_SIZE, cap_len - NETBIOS_HEADER_SIZE, &smb2_header,
                    SMB_FLAG_SIZE);
        }
        if (!res) {
            SLOG(LOG_DEBUG, "Netbios payload does not expected header (expected %"PRIx32" or %"PRIx32"),"
                    " got %"PRIx32, CIFS_SMB_HEADER, CIFS_SMB2_HEADER, smb_version);
            return PROTO_PARSE_ERR;
        }
        SLOG(LOG_DEBUG, "Found a SMB header in payload, restarting there");
        timeval_reset(netbios_parser->first_packet_tv + way);
        streambuf_set_restart(&netbios_parser->sbuf, way, res - NETBIOS_HEADER_SIZE,
                NETBIOS_HEADER_SIZE + SMB_FLAG_SIZE);
        return PROTO_OK;
    }

    uint32_t len = READ_U32N((uint32_t*) packet) & 0x00ffffff;
    *pos = len;
    size_t current_payload = wire_len - NETBIOS_HEADER_SIZE;
    SLOG(LOG_DEBUG, "Found netbios payload of %"PRIu32", current payload %zu",
            len, current_payload);
    if (len > current_payload) {
        streambuf_set_restart(&netbios_parser->sbuf, way, packet, len + NETBIOS_HEADER_SIZE);
        return PROTO_OK;
    }

    /* Parse */
    struct netbios_proto_info info;
    netbios_proto_info_ctor(&info, &netbios_parser->parser, parent,
            NETBIOS_HEADER_SIZE, wire_len - NETBIOS_HEADER_SIZE, len,
            netbios_parser->first_packet_tv + way);
    timeval_reset(netbios_parser->first_packet_tv + way);

    SLOG(LOG_DEBUG, "Parsing netbios content");
    uint8_t const *next_packet = packet + NETBIOS_HEADER_SIZE;
    if (!netbios_parser->msg_parser) {
        netbios_parser->msg_parser = proto_cifs->ops->parser_new(proto_cifs);
    }

    enum proto_parse_status status = PROTO_OK;
    if (netbios_parser->msg_parser) {
        status = proto_parse(netbios_parser->msg_parser, &info.info,
                way, next_packet,
                cap_len - NETBIOS_HEADER_SIZE, wire_len - NETBIOS_HEADER_SIZE,
                now, tot_cap_len, tot_packet);
        if (status == PROTO_OK) return PROTO_OK;
    }
    (void)proto_parse(NULL, &info.info, way, next_packet, cap_len - NETBIOS_HEADER_SIZE, wire_len - NETBIOS_HEADER_SIZE, now, tot_cap_len, tot_packet);
    return status;
}
Exemple #4
0
static enum proto_parse_status tns_sbuf_parse(struct parser *parser, struct proto_info *parent, unsigned way, uint8_t const *payload, size_t cap_len, size_t wire_len, struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet)
{
    struct tns_parser *tns_parser = DOWNCAST(parser, parser, tns_parser);

    // If this is the first time we are called, init c2s_way
    if (tns_parser->c2s_way == UNSET) {
        ASSIGN_INFO_OPT(tcp, parent);
        if (tcp) tns_parser->c2s_way = tcp->to_srv ? way : !way;
        else tns_parser->c2s_way = way;
        SLOG(LOG_DEBUG, "First packet, init c2s_way to %u", tns_parser->c2s_way);
    }

    if (!timeval_is_set(&tns_parser->first_ts)) {
        SLOG(LOG_DEBUG, "Setting first ts to %s", timeval_2_str(now));
        tns_parser->first_ts = *now;
    }

    // Now build the proto_info
    struct sql_proto_info info;
    SLOG(LOG_DEBUG, "Constructing with %zu", wire_len);
    proto_info_ctor(&info.info, parser, parent, wire_len, 0);
    info.is_query = way == tns_parser->c2s_way;
    info.set_values = 0;
    info.msg_type = SQL_UNKNOWN;
    info.first_ts = tns_parser->first_ts;

    // and try to read a TNS PDN
    struct cursor cursor;
    cursor_ctor(&cursor, payload, cap_len);

    uint8_t const *const msg_start = cursor.head;
    size_t pdu_len = 0;
    enum tns_type pdu_type = 0;
    enum proto_parse_status status = cursor_read_tns_hdr(&cursor, &pdu_len, &pdu_type, wire_len);
    if (status == PROTO_PARSE_ERR) {
        SLOG(LOG_DEBUG, "Error while parsing tns header");
        timeval_reset(&tns_parser->first_ts);
        return status;
    }

    bool has_gap = cap_len < wire_len;
    if (status == PROTO_TOO_SHORT && !has_gap) {
        proto_parse(NULL, parent, way, NULL, 0, 0, now, tot_cap_len, tot_packet);
        streambuf_set_restart(&tns_parser->sbuf, way, msg_start, pdu_len > 0 ? pdu_len : 8);
        SLOG(LOG_DEBUG, "Payload too short for parsing message, will restart @ %zu", tns_parser->sbuf.dir->restart_offset);
        return PROTO_OK;
    }
    switch (pdu_type) {
        case TNS_CONNECT:
            info.msg_type = SQL_STARTUP;
            status = tns_parse_connect(tns_parser, &info, &cursor);
            break;
        case TNS_ACCEPT:
            info.msg_type = SQL_STARTUP;
            status = tns_parse_accept(tns_parser, &info, &cursor);
            break;
        case TNS_DATA:
            status = tns_parse_data(tns_parser, &info, &cursor, way);
            break;
        case TNS_RESEND:
            SLOG(LOG_DEBUG, "Got a tns resend");
            break;
        case TNS_REFUSE:
        case TNS_REDIRECT:
        case TNS_ABORT:
        case TNS_MARKER:
        case TNS_ATTENTION:
        case TNS_CONTROL:
        default:    // A type we do not handle, skip the PDU
            break;
    }

    // We advertize the tns pdu even if we don't know how to parse it
    if (status != PROTO_OK) SLOG(LOG_DEBUG, "Error parsing tns packet");
    timeval_reset(&tns_parser->first_ts);
    return proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet);
}
Exemple #5
0
static enum proto_parse_status tds_sbuf_parse(struct parser *parser, struct proto_info *parent, unsigned way, uint8_t const *payload, size_t cap_len, size_t wire_len, struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet)
{
    if (cap_len == 0 && wire_len > 0) return PROTO_TOO_SHORT;   // We do not know how to handle pure gaps

    struct tds_parser *tds_parser = DOWNCAST(parser, parser, tds_parser);
    SLOG(LOG_DEBUG, "Got tds packet, data_left %zu, way %d", tds_parser->data_left, way);
    bool has_gap = wire_len > cap_len;

    if (tds_parser->had_gap && tds_parser->data_left > 0) {
        tds_parser->data_left = wire_len > tds_parser->data_left ? 0 : tds_parser->data_left - wire_len;
        SLOG(LOG_DEBUG, "Discard tds with gap, data_left %zu...", tds_parser->data_left);
        timeval_reset(&tds_parser->first_ts);
        return PROTO_OK;
    }
    tds_parser->had_gap = has_gap;

    struct tds_header tds_header;
    bool unknown_token = false;
    struct cursor cursor;
    enum proto_parse_status status;
    cursor_ctor(&cursor, payload, cap_len);

    struct smp_header smp_header = {.length = 0};
    if (PROTO_OK != (status = tds_parse_smp_header(&cursor, &smp_header))) return status;
    if (smp_header.length) {
        SLOG(LOG_DEBUG, "Smp header: %s", smp_header_2_str(&smp_header));
        wire_len -= SMP_PKT_HDR_LEN;
        cap_len -= SMP_PKT_HDR_LEN;
        if (smp_header.flags & SMP_ACK) return PROTO_OK;
    }

    status = tds_parse_header(&cursor, &tds_header, &unknown_token);
    if (status != PROTO_OK) {
        // We have an unknown token if the payload is encrypted after a ssl handshake
        // It is valid but we don't know how to parse it yet
        // TODO It would be better if we knew the values of the encryption options exchanged in prelogin messages
        timeval_reset(&tds_parser->first_ts);
        if (unknown_token) return PROTO_OK;
        return status;
    }

    // Sanity check on pkt number
    if (tds_header.pkt_number > 1 && ((tds_parser->pkt_number + 1) != tds_header.pkt_number)) {
        SLOG(LOG_DEBUG, "Expected pkt number %"PRIu8", got %"PRIu8"",
                tds_parser->pkt_number + 1, tds_header.pkt_number);
        tds_parser->pkt_number = 1;
        timeval_reset(&tds_parser->first_ts);
        return PROTO_PARSE_ERR;
    } else if (tds_header.pkt_number <= 1) {
        SLOG(LOG_DEBUG, "Reset pkt number from %"PRIu8"", tds_parser->pkt_number);
        tds_parser->pkt_number = 1;
    }

    // Sanity check on channels
    if (tds_parser->channels[way] && (tds_parser->channels[way] != tds_header.channel)) {
        SLOG(LOG_DEBUG, "Expected channel %"PRIu16", got channel %"PRIu16"",
                tds_parser->channels[way], tds_header.channel);
        timeval_reset(&tds_parser->first_ts);
        return PROTO_PARSE_ERR;
    }

    if (wire_len > tds_header.len) {
        SLOG(LOG_DEBUG, "Wire len %zu unexpected (> %zu), considering a gap", wire_len, tds_header.len);
        has_gap = true;
    }
    tds_parser->data_left = wire_len >= tds_header.len ? 0 : tds_header.len - wire_len;
    SLOG(LOG_DEBUG, "Data left after wire %zu", tds_parser->data_left);
    if (tds_parser->data_left > 0 && !has_gap) {
        SLOG(LOG_DEBUG, "Incomplete tds packet, buffering it");
        if (!timeval_is_set(&tds_parser->first_ts)) {
            SLOG(LOG_DEBUG, "Setting first ts to %s for way %d", timeval_2_str(now), way);
            tds_parser->first_ts = *now;
        }
        proto_parse(NULL, parent, way, NULL, 0, 0, now, tot_cap_len, tot_packet);
        streambuf_set_restart(&tds_parser->sbuf, way, payload, tds_header.len);
        return PROTO_OK;
    }

    // We have a buffered tds packet at this point
    if (!timeval_is_set(&tds_parser->first_ts)) {
        SLOG(LOG_DEBUG, "Setting first ts to %s for way %d since it is not setted", timeval_2_str(now), way);
        tds_parser->first_ts = *now;
    }

    struct tds_proto_info info;
    proto_info_ctor(&info.info, parser, parent, TDS_PKT_HDR_LEN, tds_header.len - TDS_PKT_HDR_LEN);
    info.type = tds_header.type;
    info.status = tds_header.status;
    info.length = tds_header.len;
    info.first_ts = tds_parser->first_ts;
    info.has_gap = has_gap;
    if (tds_header.channel > 0) {
        SLOG(LOG_DEBUG, "Saving channel %"PRIu16"", tds_header.channel);
        tds_parser->channels[way] = tds_header.channel;
    }
    SLOG(LOG_DEBUG, "Saving pkt number %"PRIu8"", tds_header.pkt_number);
    tds_parser->pkt_number = tds_header.pkt_number;

    SLOG(LOG_DEBUG, "Parsing %s", tds_header_2_str(&tds_header));
    if (! tds_parser->msg_parser) {
        SLOG(LOG_DEBUG, "Building new tds_msg_parser");
        tds_parser->msg_parser = proto_tds_msg->ops->parser_new(proto_tds_msg);
        if (!tds_parser->msg_parser) {
            SLOG(LOG_INFO, "Could not build tds msg parser");
            return PROTO_PARSE_ERR;
        }
    }
    if (tds_header.status & TDS_EOM) {
        SLOG(LOG_DEBUG, "Reset pkt number from %"PRIu8" since we parsed an EOM", tds_parser->pkt_number);
        tds_parser->pkt_number = 1;
    }
    status = proto_parse(tds_parser->msg_parser, &info.info, way,
            cursor.head, cursor.cap_len, wire_len - TDS_PKT_HDR_LEN, now, tot_cap_len, tot_packet);
    if (status != PROTO_OK) {
        SLOG(LOG_INFO, "Tds msg parse failed, returning %s", proto_parse_status_2_str(status));
        return status;
    }
    timeval_reset(&tds_parser->first_ts);
    // Advertise this packet if it was not done already
    return proto_parse(NULL, &info.info, way, payload, cap_len, wire_len, now, tot_cap_len, tot_packet);
}

static enum proto_parse_status tds_parse(struct parser *parser, struct proto_info *parent, unsigned way, uint8_t const *payload, size_t cap_len, size_t wire_len, struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet)
{
    struct tds_parser *tds_parser = DOWNCAST(parser, parser, tds_parser);

    enum proto_parse_status const status = streambuf_add(&tds_parser->sbuf, parser, parent, way, payload, cap_len, wire_len, now, tot_cap_len, tot_packet);

    return status;
}


/*
 * Construction/Destruction
 */

static struct proto proto_tds_;
struct proto *proto_tds = &proto_tds_;
static struct port_muxer tds_tcp_muxer;

void tds_init(void)
{
    log_category_proto_tds_init();

    static struct proto_ops const ops = {
        .parse       = tds_parse,
        .parser_new  = tds_parser_new,
        .parser_del  = tds_parser_del,
        .info_2_str  = tds_info_2_str,
        .info_addr   = tds_info_addr
    };
    proto_ctor(&proto_tds_, &ops, "TDS", PROTO_CODE_TDS);
    port_muxer_ctor(&tds_tcp_muxer, &tcp_port_muxers, 1433, 1433, proto_tds);
}