Commit message: CVE-2017-12993/Juniper: Add more bounds checks.

This fixes a buffer over-read discovered by Kamil Frankowicz.

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

Project: tcpdump
File: print-juniper.c

ip_heuristic_guess(netdissect_options *ndo,
                   register const u_char *p, u_int length)
{
    switch(p[0]) {
    case 0x45:
    case 0x46:
    case 0x47:
    case 0x48:
    case 0x49:
    case 0x4a:
    case 0x4b:
    case 0x4c:
    case 0x4d:
    case 0x4e:
    case 0x4f:
	    ip_print(ndo, p, length);
	    break;
    case 0x60:
    case 0x61:
    case 0x62:
    case 0x63:
    case 0x64:
    case 0x65:
    case 0x66:
    case 0x67:
    case 0x68:
    case 0x69:
    case 0x6a:
    case 0x6b:
    case 0x6c:
    case 0x6d:
    case 0x6e:
    case 0x6f:
        ip6_print(ndo, p, length);
        break;
    default:
        return 0; /* did not find a ip header */
        break;
    }
    return 1; /* we printed an v4/v6 packet */
}

Project: tcpdump
File: print-juniper.c

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

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

        p+=l2info.header_len;
        /* this DLT contains nothing but raw c-hdlc frames */
        chdlc_print(ndo, p, l2info.length);
        return l2info.header_len;
}

Project: tcpdump
File: print-juniper.c

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

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

        p+=l2info.header_len;
        /* this DLT contains nothing but raw Ethernet frames */
        ether_print(ndo, p, l2info.length, l2info.caplen, NULL, NULL);
        return l2info.header_len;
}

Project: tcpdump
File: print-juniper.c

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

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

        p+=l2info.header_len;
        /* this DLT contains nothing but raw frame-relay frames */
        fr_print(ndo, p, l2info.length);
        return l2info.header_len;
}

Project: tcpdump
File: print-juniper.c

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

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

        p+=l2info.header_len;

        /* child-link ? */
        if (l2info.cookie_len == 0) {
            mfr_print(ndo, p, l2info.length);
            return l2info.header_len;
        }

        /* first try the LSQ protos */
        if (l2info.cookie_len == AS_PIC_COOKIE_LEN) {
            switch(l2info.proto) {
            case JUNIPER_LSQ_L3_PROTO_IPV4:
                ip_print(ndo, p, l2info.length);
                return l2info.header_len;
            case JUNIPER_LSQ_L3_PROTO_IPV6:
                ip6_print(ndo, p,l2info.length);
                return l2info.header_len;
            case JUNIPER_LSQ_L3_PROTO_MPLS:
                mpls_print(ndo, p, l2info.length);
                return l2info.header_len;
            case JUNIPER_LSQ_L3_PROTO_ISO:
                isoclns_print(ndo, p, l2info.length);
                return l2info.header_len;
            default:
                break;
            }
            return l2info.header_len;
        }

        /* suppress Bundle-ID if frame was captured on a child-link */
        if (ndo->ndo_eflag && EXTRACT_32BITS(l2info.cookie) != 1)
            ND_PRINT((ndo, "Bundle-ID %u, ", l2info.bundle));
        switch (l2info.proto) {
        case (LLCSAP_ISONS<<8 | LLCSAP_ISONS):
            isoclns_print(ndo, p + 1, l2info.length - 1);
            break;
        case (LLC_UI<<8 | NLPID_Q933):
        case (LLC_UI<<8 | NLPID_IP):
        case (LLC_UI<<8 | NLPID_IP6):
            /* pass IP{4,6} to the OSI layer for proper link-layer printing */
            isoclns_print(ndo, p - 1, l2info.length + 1);
            break;
        default:
            ND_PRINT((ndo, "unknown protocol 0x%04x, length %u", l2info.proto, l2info.length));
        }

        return l2info.header_len;
}

Project: tcpdump
File: print-juniper.c

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

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

        p+=l2info.header_len;

        /* suppress Bundle-ID if frame was captured on a child-link */
        if (ndo->ndo_eflag && EXTRACT_32BITS(l2info.cookie) != 1)
            ND_PRINT((ndo, "Bundle-ID %u, ", l2info.bundle));
        switch (l2info.proto) {
        case (LLC_UI):
        case (LLC_UI<<8):
            isoclns_print(ndo, p, l2info.length);
            break;
        case (LLC_UI<<8 | NLPID_Q933):
        case (LLC_UI<<8 | NLPID_IP):
        case (LLC_UI<<8 | NLPID_IP6):
            /* pass IP{4,6} to the OSI layer for proper link-layer printing */
            isoclns_print(ndo, p - 1, l2info.length + 1);
            break;
        default:
            ND_PRINT((ndo, "unknown protocol 0x%04x, length %u", l2info.proto, l2info.length));
        }

        return l2info.header_len;
}

Project: tcpdump
File: print-juniper.c

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

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

        /* suppress Bundle-ID if frame was captured on a child-link
         * best indicator if the cookie looks like a proto */
        if (ndo->ndo_eflag &&
            EXTRACT_16BITS(&l2info.cookie) != PPP_OSI &&
            EXTRACT_16BITS(&l2info.cookie) !=  (PPP_ADDRESS << 8 | PPP_CONTROL))
            ND_PRINT((ndo, "Bundle-ID %u: ", l2info.bundle));

        p+=l2info.header_len;

        /* first try the LSQ protos */
        switch(l2info.proto) {
        case JUNIPER_LSQ_L3_PROTO_IPV4:
            /* IP traffic going to the RE would not have a cookie
             * -> this must be incoming IS-IS over PPP
             */
            if (l2info.cookie[4] == (JUNIPER_LSQ_COOKIE_RE|JUNIPER_LSQ_COOKIE_DIR))
                ppp_print(ndo, p, l2info.length);
            else
                ip_print(ndo, p, l2info.length);
            return l2info.header_len;
        case JUNIPER_LSQ_L3_PROTO_IPV6:
            ip6_print(ndo, p,l2info.length);
            return l2info.header_len;
        case JUNIPER_LSQ_L3_PROTO_MPLS:
            mpls_print(ndo, p, l2info.length);
            return l2info.header_len;
        case JUNIPER_LSQ_L3_PROTO_ISO:
            isoclns_print(ndo, p, l2info.length);
            return l2info.header_len;
        default:
            break;
        }

        /* zero length cookie ? */
        switch (EXTRACT_16BITS(&l2info.cookie)) {
        case PPP_OSI:
            ppp_print(ndo, p - 2, l2info.length + 2);
            break;
        case (PPP_ADDRESS << 8 | PPP_CONTROL): /* fall through */
        default:
            ppp_print(ndo, p, l2info.length);
            break;
        }

        return l2info.header_len;
}

Project: tcpdump
File: print-juniper.c

juniper_ppp_heuristic_guess(netdissect_options *ndo,
                            register const u_char *p, u_int length)
{
    switch(EXTRACT_16BITS(p)) {
    case PPP_IP :
    case PPP_OSI :
    case PPP_MPLS_UCAST :
    case PPP_MPLS_MCAST :
    case PPP_IPCP :
    case PPP_OSICP :
    case PPP_MPLSCP :
    case PPP_LCP :
    case PPP_PAP :
    case PPP_CHAP :
    case PPP_ML :
    case PPP_IPV6 :
    case PPP_IPV6CP :
        ppp_print(ndo, p, length);
        break;

    default:
        return 0; /* did not find a ppp header */
        break;
    }
    return 1; /* we printed a ppp packet */
}

Project: tcpdump
File: print-juniper.c

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

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

        p+=l2info.header_len;
        /* this DLT contains nothing but raw ethernet frames */
        ether_print(ndo, p, l2info.length, l2info.caplen, NULL, NULL);
        return l2info.header_len;
}

Project: tcpdump
File: print-juniper.c

juniper_read_tlv_value(const u_char *p, u_int tlv_type, u_int tlv_len)
{
   int tlv_value;

   /* TLVs < 128 are little endian encoded */
   if (tlv_type < 128) {
       switch (tlv_len) {
       case 1:
           tlv_value = *p;
           break;
       case 2:
           tlv_value = EXTRACT_LE_16BITS(p);
           break;
       case 3:
           tlv_value = EXTRACT_LE_24BITS(p);
           break;
       case 4:
           tlv_value = EXTRACT_LE_32BITS(p);
           break;
       default:
           tlv_value = -1;
           break;
       }
   } else {
       /* TLVs >= 128 are big endian encoded */
       switch (tlv_len) {
       case 1:
           tlv_value = *p;
           break;
       case 2:
           tlv_value = EXTRACT_16BITS(p);
           break;
       case 3:
           tlv_value = EXTRACT_24BITS(p);
           break;
       case 4:
           tlv_value = EXTRACT_32BITS(p);
           break;
       default:
           tlv_value = -1;
           break;
       }
   }
   return tlv_value;
}