Commit message: CVE-2017-13004/Juniper: Add a bounds check.

This fixes a buffer over-read discovered by Forcepoint's security
researchers Otto Airamo & Antti Levomäki.

Add tests using the capture files supplied by the reporter(s).
CVE: CVE-2017-13004
CWE: CWE-125
Vulnerability description: The Juniper protocols parser in tcpdump before 4.9.2 has a buffer over-read in print-juniper.c:juniper_parse_header().

Project: tcpdump
File: print-juniper.c

juniper_atm1_print(netdissect_options *ndo,
                   const struct pcap_pkthdr *h, register const u_char *p)
{
        int llc_hdrlen;

        struct juniper_l2info_t l2info;

        l2info.pictype = DLT_JUNIPER_ATM1;
        if (juniper_parse_header(ndo, p, h, &l2info) == 0)
            return l2info.header_len;

        p+=l2info.header_len;

        if (l2info.cookie[0] == 0x80) { /* OAM cell ? */
            oam_print(ndo, p, l2info.length, ATM_OAM_NOHEC);
            return l2info.header_len;
        }

        ND_TCHECK2(p[0], 3);
        if (EXTRACT_24BITS(p) == 0xfefe03 || /* NLPID encaps ? */
            EXTRACT_24BITS(p) == 0xaaaa03) { /* SNAP encaps ? */

            llc_hdrlen = llc_print(ndo, p, l2info.length, l2info.caplen, NULL, NULL);
            if (llc_hdrlen > 0)
                return l2info.header_len;
        }

        if (p[0] == 0x03) { /* Cisco style NLPID encaps ? */
            isoclns_print(ndo, p + 1, l2info.length - 1);
            /* FIXME check if frame was recognized */
            return l2info.header_len;
        }

        if (ip_heuristic_guess(ndo, p, l2info.length) != 0) /* last try - vcmux encaps ? */
            return l2info.header_len;

	return l2info.header_len;

trunc:
	ND_PRINT((ndo, "[|juniper_atm1]"));
	return l2info.header_len;
}

Project: tcpdump
File: print-juniper.c

juniper_atm2_print(netdissect_options *ndo,
                   const struct pcap_pkthdr *h, register const u_char *p)
{
        int llc_hdrlen;

        struct juniper_l2info_t l2info;

        l2info.pictype = DLT_JUNIPER_ATM2;
        if (juniper_parse_header(ndo, p, h, &l2info) == 0)
            return l2info.header_len;

        p+=l2info.header_len;

        if (l2info.cookie[7] & ATM2_PKT_TYPE_MASK) { /* OAM cell ? */
            oam_print(ndo, p, l2info.length, ATM_OAM_NOHEC);
            return l2info.header_len;
        }

        ND_TCHECK2(p[0], 3);
        if (EXTRACT_24BITS(p) == 0xfefe03 || /* NLPID encaps ? */
            EXTRACT_24BITS(p) == 0xaaaa03) { /* SNAP encaps ? */

            llc_hdrlen = llc_print(ndo, p, l2info.length, l2info.caplen, NULL, NULL);
            if (llc_hdrlen > 0)
                return l2info.header_len;
        }

        if (l2info.direction != JUNIPER_BPF_PKT_IN && /* ether-over-1483 encaps ? */
            (EXTRACT_32BITS(l2info.cookie) & ATM2_GAP_COUNT_MASK)) {
            ether_print(ndo, p, l2info.length, l2info.caplen, NULL, NULL);
            return l2info.header_len;
        }

        if (p[0] == 0x03) { /* Cisco style NLPID encaps ? */
            isoclns_print(ndo, p + 1, l2info.length - 1);
            /* FIXME check if frame was recognized */
            return l2info.header_len;
        }

        if(juniper_ppp_heuristic_guess(ndo, p, l2info.length) != 0) /* PPPoA vcmux encaps ? */
            return l2info.header_len;

        if (ip_heuristic_guess(ndo, p, l2info.length) != 0) /* last try - vcmux encaps ? */
            return l2info.header_len;

	return l2info.header_len;

trunc:
	ND_PRINT((ndo, "[|juniper_atm2]"));
	return l2info.header_len;
}

Project: tcpdump
File: print-juniper.c

juniper_es_print(netdissect_options *ndo,
                 const struct pcap_pkthdr *h, register const u_char *p)
{
        struct juniper_l2info_t l2info;
        struct juniper_ipsec_header {
            uint8_t sa_index[2];
            uint8_t ttl;
            uint8_t type;
            uint8_t spi[4];
            uint8_t src_ip[4];
            uint8_t dst_ip[4];
        };
        u_int rewrite_len,es_type_bundle;
        const struct juniper_ipsec_header *ih;

        l2info.pictype = DLT_JUNIPER_ES;
        if (juniper_parse_header(ndo, p, h, &l2info) == 0)
            return l2info.header_len;

        p+=l2info.header_len;
        ih = (const struct juniper_ipsec_header *)p;

        ND_TCHECK(*ih);
        switch (ih->type) {
        case JUNIPER_IPSEC_O_ESP_ENCRYPT_ESP_AUTHEN_TYPE:
        case JUNIPER_IPSEC_O_ESP_ENCRYPT_AH_AUTHEN_TYPE:
            rewrite_len = 0;
            es_type_bundle = 1;
            break;
        case JUNIPER_IPSEC_O_ESP_AUTHENTICATION_TYPE:
        case JUNIPER_IPSEC_O_AH_AUTHENTICATION_TYPE:
        case JUNIPER_IPSEC_O_ESP_ENCRYPTION_TYPE:
            rewrite_len = 16;
            es_type_bundle = 0;
            break;
        default:
            ND_PRINT((ndo, "ES Invalid type %u, length %u",
                   ih->type,
                   l2info.length));
            return l2info.header_len;
        }

        l2info.length-=rewrite_len;
        p+=rewrite_len;

        if (ndo->ndo_eflag) {
            if (!es_type_bundle) {
                ND_PRINT((ndo, "ES SA, index %u, ttl %u type %s (%u), spi %u, Tunnel %s > %s, length %u\n",
                       EXTRACT_16BITS(&ih->sa_index),
                       ih->ttl,
                       tok2str(juniper_ipsec_type_values,"Unknown",ih->type),
                       ih->type,
                       EXTRACT_32BITS(&ih->spi),
                       ipaddr_string(ndo, &ih->src_ip),
                       ipaddr_string(ndo, &ih->dst_ip),
                       l2info.length));
            } else {
                ND_PRINT((ndo, "ES SA, index %u, ttl %u type %s (%u), length %u\n",
                       EXTRACT_16BITS(&ih->sa_index),
                       ih->ttl,
                       tok2str(juniper_ipsec_type_values,"Unknown",ih->type),
                       ih->type,
                       l2info.length));
            }
        }

        ip_print(ndo, p, l2info.length);
        return l2info.header_len;

trunc:
	ND_PRINT((ndo, "[|juniper_services]"));
	return l2info.header_len;
}

Project: tcpdump
File: print-juniper.c

juniper_ggsn_print(netdissect_options *ndo,
                   const struct pcap_pkthdr *h, register const u_char *p)
{
        struct juniper_l2info_t l2info;
        struct juniper_ggsn_header {
            uint8_t svc_id;
            uint8_t flags_len;
            uint8_t proto;
            uint8_t flags;
            uint8_t vlan_id[2];
            uint8_t res[2];
        };
        const struct juniper_ggsn_header *gh;

        l2info.pictype = DLT_JUNIPER_GGSN;
        if (juniper_parse_header(ndo, p, h, &l2info) == 0)
            return l2info.header_len;

        p+=l2info.header_len;
        gh = (struct juniper_ggsn_header *)&l2info.cookie;

        ND_TCHECK(*gh);
        if (ndo->ndo_eflag) {
            ND_PRINT((ndo, "proto %s (%u), vlan %u: ",
                   tok2str(juniper_protocol_values,"Unknown",gh->proto),
                   gh->proto,
                   EXTRACT_16BITS(&gh->vlan_id[0])));
        }

        switch (gh->proto) {
        case JUNIPER_PROTO_IPV4:
            ip_print(ndo, p, l2info.length);
            break;
        case JUNIPER_PROTO_IPV6:
            ip6_print(ndo, p, l2info.length);
            break;
        default:
            if (!ndo->ndo_eflag)
                ND_PRINT((ndo, "unknown GGSN proto (%u)", gh->proto));
        }

        return l2info.header_len;

trunc:
	ND_PRINT((ndo, "[|juniper_services]"));
	return l2info.header_len;
}

Project: tcpdump
File: print-juniper.c

juniper_monitor_print(netdissect_options *ndo,
                      const struct pcap_pkthdr *h, register const u_char *p)
{
        struct juniper_l2info_t l2info;
        struct juniper_monitor_header {
            uint8_t pkt_type;
            uint8_t padding;
            uint8_t iif[2];
            uint8_t service_id[4];
        };
        const struct juniper_monitor_header *mh;

        l2info.pictype = DLT_JUNIPER_MONITOR;
        if (juniper_parse_header(ndo, p, h, &l2info) == 0)
            return l2info.header_len;

        p+=l2info.header_len;
        mh = (const struct juniper_monitor_header *)p;

        ND_TCHECK(*mh);
        if (ndo->ndo_eflag)
            ND_PRINT((ndo, "service-id %u, iif %u, pkt-type %u: ",
                   EXTRACT_32BITS(&mh->service_id),
                   EXTRACT_16BITS(&mh->iif),
                   mh->pkt_type));

        /* no proto field - lets guess by first byte of IP header*/
        ip_heuristic_guess (ndo, p, l2info.length);

        return l2info.header_len;

trunc:
	ND_PRINT((ndo, "[|juniper_services]"));
	return l2info.header_len;
}

Project: tcpdump
File: print-juniper.c

juniper_pppoe_atm_print(netdissect_options *ndo,
                        const struct pcap_pkthdr *h, register const u_char *p)
{
        struct juniper_l2info_t l2info;
	uint16_t extracted_ethertype;

        l2info.pictype = DLT_JUNIPER_PPPOE_ATM;
        if (juniper_parse_header(ndo, p, h, &l2info) == 0)
            return l2info.header_len;

        p+=l2info.header_len;

        ND_TCHECK2(p[0], 2);
        extracted_ethertype = EXTRACT_16BITS(p);
        /* this DLT contains nothing but raw PPPoE frames,
         * prepended with a type field*/
        if (ethertype_print(ndo, extracted_ethertype,
                              p+ETHERTYPE_LEN,
                              l2info.length-ETHERTYPE_LEN,
                              l2info.caplen-ETHERTYPE_LEN,
                              NULL, NULL) == 0)
            /* ether_type not known, probably it wasn't one */
            ND_PRINT((ndo, "unknown ethertype 0x%04x", extracted_ethertype));

        return l2info.header_len;

trunc:
	ND_PRINT((ndo, "[|juniper_pppoe_atm]"));
	return l2info.header_len;
}

Project: tcpdump
File: print-juniper.c

juniper_services_print(netdissect_options *ndo,
                       const struct pcap_pkthdr *h, register const u_char *p)
{
        struct juniper_l2info_t l2info;
        struct juniper_services_header {
            uint8_t svc_id;
            uint8_t flags_len;
            uint8_t svc_set_id[2];
            uint8_t dir_iif[4];
        };
        const struct juniper_services_header *sh;

        l2info.pictype = DLT_JUNIPER_SERVICES;
        if (juniper_parse_header(ndo, p, h, &l2info) == 0)
            return l2info.header_len;

        p+=l2info.header_len;
        sh = (const struct juniper_services_header *)p;

        ND_TCHECK(*sh);
        if (ndo->ndo_eflag)
            ND_PRINT((ndo, "service-id %u flags 0x%02x service-set-id 0x%04x iif %u: ",
                   sh->svc_id,
                   sh->flags_len,
                   EXTRACT_16BITS(&sh->svc_set_id),
                   EXTRACT_24BITS(&sh->dir_iif[1])));

        /* no proto field - lets guess by first byte of IP header*/
        ip_heuristic_guess (ndo, p, l2info.length);

        return l2info.header_len;

trunc:
	ND_PRINT((ndo, "[|juniper_services]"));
	return l2info.header_len;
}