/* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef lint static const char rcsid[] _U_ = "@(#) $Header: /tcpdump/master/tcpdump/print-isakmp.c,v 1.36.2.11 2004/03/24 01:32:42 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "isakmp.h" #include "ipsec_doi.h" #include "oakley.h" #include "interface.h" #include "addrtoname.h" #include "extract.h" /* must come after interface.h */ #include "ip.h" #ifdef INET6 #include "ip6.h" #endif #ifndef HAVE_SOCKADDR_STORAGE #define sockaddr_storage sockaddr #endif static const u_char *isakmp_sa_print(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_p_print(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_t_print(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_ke_print(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_id_print(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_cert_print(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_cr_print(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_sig_print(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_hash_print(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_nonce_print(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_n_print(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_d_print(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_vid_print(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_sub0_print(u_char, const struct isakmp_gen *, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static const u_char *isakmp_sub_print(u_char, const struct isakmp_gen *, const u_char *, u_int32_t, u_int32_t, u_int32_t, int); static char *numstr(int); static void safememcpy(void *, const void *, size_t); #define MAXINITIATORS 20 int ninitiator = 0; struct { cookie_t initiator; struct sockaddr_storage iaddr; struct sockaddr_storage raddr; } cookiecache[MAXINITIATORS]; /* protocol id */ static const char *protoidstr[] = { NULL, "isakmp", "ipsec-ah", "ipsec-esp", "ipcomp", }; /* isakmp->np */ static const char *npstr[] = { "none", "sa", "p", "t", "ke", "id", "cert", "cr", "hash", "sig", "nonce", "n", "d", "vid" }; /* isakmp->np */ static const u_char *(*npfunc[])(const struct isakmp_gen *, u_int, const u_char *, u_int32_t, u_int32_t, u_int32_t, int) = { NULL, isakmp_sa_print, isakmp_p_print, isakmp_t_print, isakmp_ke_print, isakmp_id_print, isakmp_cert_print, isakmp_cr_print, isakmp_hash_print, isakmp_sig_print, isakmp_nonce_print, isakmp_n_print, isakmp_d_print, isakmp_vid_print, }; /* isakmp->etype */ static const char *etypestr[] = { "none", "base", "ident", "auth", "agg", "inf", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "oakley-quick", "oakley-newgroup", }; #define STR_OR_ID(x, tab) \ (((x) < sizeof(tab)/sizeof(tab[0]) && tab[(x)]) ? tab[(x)] : numstr(x)) #define PROTOIDSTR(x) STR_OR_ID(x, protoidstr) #define NPSTR(x) STR_OR_ID(x, npstr) #define ETYPESTR(x) STR_OR_ID(x, etypestr) #define NPFUNC(x) \ (((x) < sizeof(npfunc)/sizeof(npfunc[0]) && npfunc[(x)]) \ ? npfunc[(x)] : NULL) static int iszero(u_char *p, size_t l) { while (l--) { if (*p++) return 0; } return 1; } /* find cookie from initiator cache */ static int cookie_find(cookie_t *in) { int i; for (i = 0; i < MAXINITIATORS; i++) { if (memcmp(in, &cookiecache[i].initiator, sizeof(*in)) == 0) return i; } return -1; } /* record initiator */ static void cookie_record(cookie_t *in, const u_char *bp2) { int i; struct ip *ip; struct sockaddr_in *sin; #ifdef INET6 struct ip6_hdr *ip6; struct sockaddr_in6 *sin6; #endif i = cookie_find(in); if (0 <= i) { ninitiator = (i + 1) % MAXINITIATORS; return; } ip = (struct ip *)bp2; switch (IP_V(ip)) { case 4: memset(&cookiecache[ninitiator].iaddr, 0, sizeof(cookiecache[ninitiator].iaddr)); memset(&cookiecache[ninitiator].raddr, 0, sizeof(cookiecache[ninitiator].raddr)); sin = (struct sockaddr_in *)&cookiecache[ninitiator].iaddr; #ifdef HAVE_SOCKADDR_SA_LEN sin->sin_len = sizeof(struct sockaddr_in); #endif sin->sin_family = AF_INET; memcpy(&sin->sin_addr, &ip->ip_src, sizeof(ip->ip_src)); sin = (struct sockaddr_in *)&cookiecache[ninitiator].raddr; #ifdef HAVE_SOCKADDR_SA_LEN sin->sin_len = sizeof(struct sockaddr_in); #endif sin->sin_family = AF_INET; memcpy(&sin->sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)); break; #ifdef INET6 case 6: memset(&cookiecache[ninitiator].iaddr, 0, sizeof(cookiecache[ninitiator].iaddr)); memset(&cookiecache[ninitiator].raddr, 0, sizeof(cookiecache[ninitiator].raddr)); ip6 = (struct ip6_hdr *)bp2; sin6 = (struct sockaddr_in6 *)&cookiecache[ninitiator].iaddr; #ifdef HAVE_SOCKADDR_SA_LEN sin6->sin6_len = sizeof(struct sockaddr_in6); #endif sin6->sin6_family = AF_INET6; memcpy(&sin6->sin6_addr, &ip6->ip6_src, sizeof(ip6->ip6_src)); sin6 = (struct sockaddr_in6 *)&cookiecache[ninitiator].raddr; #ifdef HAVE_SOCKADDR_SA_LEN sin6->sin6_len = sizeof(struct sockaddr_in6); #endif sin6->sin6_family = AF_INET6; memcpy(&sin6->sin6_addr, &ip6->ip6_dst, sizeof(ip6->ip6_dst)); break; #endif default: return; } memcpy(&cookiecache[ninitiator].initiator, in, sizeof(*in)); ninitiator = (ninitiator + 1) % MAXINITIATORS; } #define cookie_isinitiator(x, y) cookie_sidecheck((x), (y), 1) #define cookie_isresponder(x, y) cookie_sidecheck((x), (y), 0) static int cookie_sidecheck(int i, const u_char *bp2, int initiator) { struct sockaddr_storage ss; struct sockaddr *sa; struct ip *ip; struct sockaddr_in *sin; #ifdef INET6 struct ip6_hdr *ip6; struct sockaddr_in6 *sin6; #endif int salen; memset(&ss, 0, sizeof(ss)); ip = (struct ip *)bp2; switch (IP_V(ip)) { case 4: sin = (struct sockaddr_in *)&ss; #ifdef HAVE_SOCKADDR_SA_LEN sin->sin_len = sizeof(struct sockaddr_in); #endif sin->sin_family = AF_INET; memcpy(&sin->sin_addr, &ip->ip_src, sizeof(ip->ip_src)); break; #ifdef INET6 case 6: ip6 = (struct ip6_hdr *)bp2; sin6 = (struct sockaddr_in6 *)&ss; #ifdef HAVE_SOCKADDR_SA_LEN sin6->sin6_len = sizeof(struct sockaddr_in6); #endif sin6->sin6_family = AF_INET6; memcpy(&sin6->sin6_addr, &ip6->ip6_src, sizeof(ip6->ip6_src)); break; #endif default: return 0; } sa = (struct sockaddr *)&ss; if (initiator) { if (sa->sa_family != ((struct sockaddr *)&cookiecache[i].iaddr)->sa_family) return 0; #ifdef HAVE_SOCKADDR_SA_LEN salen = sa->sa_len; #else #ifdef INET6 if (sa->sa_family == AF_INET6) salen = sizeof(struct sockaddr_in6); else salen = sizeof(struct sockaddr); #else salen = sizeof(struct sockaddr); #endif #endif if (memcmp(&ss, &cookiecache[i].iaddr, salen) == 0) return 1; } else { if (sa->sa_family != ((struct sockaddr *)&cookiecache[i].raddr)->sa_family) return 0; #ifdef HAVE_SOCKADDR_SA_LEN salen = sa->sa_len; #else #ifdef INET6 if (sa->sa_family == AF_INET6) salen = sizeof(struct sockaddr_in6); else salen = sizeof(struct sockaddr); #else salen = sizeof(struct sockaddr); #endif #endif if (memcmp(&ss, &cookiecache[i].raddr, salen) == 0) return 1; } return 0; } static int rawprint(caddr_t loc, size_t len) { static u_char *p; size_t i; TCHECK2(*loc, len); p = (u_char *)loc; for (i = 0; i < len; i++) printf("%02x", p[i] & 0xff); return 1; trunc: return 0; } struct attrmap { const char *type; u_int nvalue; const char *value[30]; /*XXX*/ }; static const u_char * isakmp_attrmap_print(const u_char *p, const u_char *ep, const struct attrmap *map, size_t nmap) { u_int16_t *q; int totlen; u_int32_t t, v; q = (u_int16_t *)p; if (p[0] & 0x80) totlen = 4; else totlen = 4 + EXTRACT_16BITS(&q[1]); if (ep < p + totlen) { printf("[|attr]"); return ep + 1; } printf("("); t = EXTRACT_16BITS(&q[0]) & 0x7fff; if (map && t < nmap && map[t].type) printf("type=%s ", map[t].type); else printf("type=#%d ", t); if (p[0] & 0x80) { printf("value="); v = EXTRACT_16BITS(&q[1]); if (map && t < nmap && v < map[t].nvalue && map[t].value[v]) printf("%s", map[t].value[v]); else rawprint((caddr_t)&q[1], 2); } else { printf("len=%d value=", EXTRACT_16BITS(&q[1])); rawprint((caddr_t)&p[4], EXTRACT_16BITS(&q[1])); } printf(")"); return p + totlen; } static const u_char * isakmp_attr_print(const u_char *p, const u_char *ep) { u_int16_t *q; int totlen; u_int32_t t; q = (u_int16_t *)p; if (p[0] & 0x80) totlen = 4; else totlen = 4 + EXTRACT_16BITS(&q[1]); if (ep < p + totlen) { printf("[|attr]"); return ep + 1; } printf("("); t = EXTRACT_16BITS(&q[0]) & 0x7fff; printf("type=#%d ", t); if (p[0] & 0x80) { printf("value="); t = q[1]; rawprint((caddr_t)&q[1], 2); } else { printf("len=%d value=", EXTRACT_16BITS(&q[1])); rawprint((caddr_t)&p[2], EXTRACT_16BITS(&q[1])); } printf(")"); return p + totlen; } static const u_char * isakmp_sa_print(const struct isakmp_gen *ext, u_int item_len, const u_char *ep, u_int32_t phase, u_int32_t doi0 _U_, u_int32_t proto0, int depth) { const struct isakmp_pl_sa *p; struct isakmp_pl_sa sa; const u_int32_t *q; u_int32_t doi, sit, ident; const u_char *cp, *np; int t; printf("%s:", NPSTR(ISAKMP_NPTYPE_SA)); p = (struct isakmp_pl_sa *)ext; TCHECK(*p); safememcpy(&sa, ext, sizeof(sa)); doi = ntohl(sa.doi); sit = ntohl(sa.sit); if (doi != 1) { printf(" doi=%d", doi); printf(" situation=%u", (u_int32_t)ntohl(sa.sit)); return (u_char *)(p + 1); } printf(" doi=ipsec"); q = (u_int32_t *)&sa.sit; printf(" situation="); t = 0; if (sit & 0x01) { printf("identity"); t++; } if (sit & 0x02) { printf("%ssecrecy", t ? "+" : ""); t++; } if (sit & 0x04) printf("%sintegrity", t ? "+" : ""); np = (u_char *)ext + sizeof(sa); if (sit != 0x01) { TCHECK2(*(ext + 1), sizeof(ident)); safememcpy(&ident, ext + 1, sizeof(ident)); printf(" ident=%u", (u_int32_t)ntohl(ident)); np += sizeof(ident); } ext = (struct isakmp_gen *)np; TCHECK(*ext); cp = isakmp_sub_print(ISAKMP_NPTYPE_P, ext, ep, phase, doi, proto0, depth); return cp; trunc: printf(" [|%s]", NPSTR(ISAKMP_NPTYPE_SA)); return NULL; } static const u_char * isakmp_p_print(const struct isakmp_gen *ext, u_int item_len, const u_char *ep, u_int32_t phase, u_int32_t doi0, u_int32_t proto0 _U_, int depth) { const struct isakmp_pl_p *p; struct isakmp_pl_p prop; const u_char *cp; printf("%s:", NPSTR(ISAKMP_NPTYPE_P)); p = (struct isakmp_pl_p *)ext; TCHECK(*p); safememcpy(&prop, ext, sizeof(prop)); printf(" #%d protoid=%s transform=%d", prop.p_no, PROTOIDSTR(prop.prot_id), prop.num_t); if (prop.spi_size) { printf(" spi="); if (!rawprint((caddr_t)(p + 1), prop.spi_size)) goto trunc; } ext = (struct isakmp_gen *)((u_char *)(p + 1) + prop.spi_size); TCHECK(*ext); cp = isakmp_sub_print(ISAKMP_NPTYPE_T, ext, ep, phase, doi0, prop.prot_id, depth); return cp; trunc: printf(" [|%s]", NPSTR(ISAKMP_NPTYPE_P)); return NULL; } static const char *isakmp_p_map[] = { NULL, "ike", }; static const char *ah_p_map[] = { NULL, "(reserved)", "md5", "sha", "1des", "sha2-256", "sha2-384", "sha2-512", }; static const char *esp_p_map[] = { NULL, "1des-iv64", "1des", "3des", "rc5", "idea", "cast", "blowfish", "3idea", "1des-iv32", "rc4", "null", "aes" }; static const char *ipcomp_p_map[] = { NULL, "oui", "deflate", "lzs", }; const struct attrmap ipsec_t_map[] = { { NULL, 0, { NULL } }, { "lifetype", 3, { NULL, "sec", "kb", }, }, { "life", 0, { NULL } }, { "group desc", 5, { NULL, "modp768", "modp1024", "EC2N 2^155", "EC2N 2^185", }, }, { "enc mode", 3, { NULL, "tunnel", "transport", }, }, { "auth", 5, { NULL, "hmac-md5", "hmac-sha1", "1des-mac", "keyed", }, }, { "keylen", 0, { NULL } }, { "rounds", 0, { NULL } }, { "dictsize", 0, { NULL } }, { "privalg", 0, { NULL } }, }; const struct attrmap oakley_t_map[] = { { NULL, 0, { NULL } }, { "enc", 8, { NULL, "1des", "idea", "blowfish", "rc5", "3des", "cast", "aes", }, }, { "hash", 7, { NULL, "md5", "sha1", "tiger", "sha2-256", "sha2-384", "sha2-512", }, }, { "auth", 6, { NULL, "preshared", "dss", "rsa sig", "rsa enc", "rsa enc revised", }, }, { "group desc", 5, { NULL, "modp768", "modp1024", "EC2N 2^155", "EC2N 2^185", }, }, { "group type", 4, { NULL, "MODP", "ECP", "EC2N", }, }, { "group prime", 0, { NULL } }, { "group gen1", 0, { NULL } }, { "group gen2", 0, { NULL } }, { "group curve A", 0, { NULL } }, { "group curve B", 0, { NULL } }, { "lifetype", 3, { NULL, "sec", "kb", }, }, { "lifeduration", 0, { NULL } }, { "prf", 0, { NULL } }, { "keylen", 0, { NULL } }, { "field", 0, { NULL } }, { "order", 0, { NULL } }, }; static const u_char * isakmp_t_print(const struct isakmp_gen *ext, u_int item_len, const u_char *ep, u_int32_t phase _U_, u_int32_t doi _U_, u_int32_t proto, int depth _U_) { const struct isakmp_pl_t *p; struct isakmp_pl_t t; const u_char *cp; const char *idstr; const struct attrmap *map; size_t nmap; const u_char *ep2; printf("%s:", NPSTR(ISAKMP_NPTYPE_T)); p = (struct isakmp_pl_t *)ext; TCHECK(*p); safememcpy(&t, ext, sizeof(t)); switch (proto) { case 1: idstr = STR_OR_ID(t.t_id, isakmp_p_map); map = oakley_t_map; nmap = sizeof(oakley_t_map)/sizeof(oakley_t_map[0]); break; case 2: idstr = STR_OR_ID(t.t_id, ah_p_map); map = ipsec_t_map; nmap = sizeof(ipsec_t_map)/sizeof(ipsec_t_map[0]); break; case 3: idstr = STR_OR_ID(t.t_id, esp_p_map); map = ipsec_t_map; nmap = sizeof(ipsec_t_map)/sizeof(ipsec_t_map[0]); break; case 4: idstr = STR_OR_ID(t.t_id, ipcomp_p_map); map = ipsec_t_map; nmap = sizeof(ipsec_t_map)/sizeof(ipsec_t_map[0]); break; default: idstr = NULL; map = NULL; nmap = 0; break; } if (idstr) printf(" #%d id=%s ", t.t_no, idstr); else printf(" #%d id=%d ", t.t_no, t.t_id); cp = (u_char *)(p + 1); ep2 = (u_char *)p + item_len; while (cp < ep && cp < ep2) { if (map && nmap) { cp = isakmp_attrmap_print(cp, (ep < ep2) ? ep : ep2, map, nmap); } else cp = isakmp_attr_print(cp, (ep < ep2) ? ep : ep2); } if (ep < ep2) printf("..."); return cp; trunc: printf(" [|%s]", NPSTR(ISAKMP_NPTYPE_T)); return NULL; } static const u_char * isakmp_ke_print(const struct isakmp_gen *ext, u_int item_len, const u_char *ep, u_int32_t phase _U_, u_int32_t doi _U_, u_int32_t proto _U_, int depth _U_) { struct isakmp_gen e; printf("%s:", NPSTR(ISAKMP_NPTYPE_KE)); TCHECK(*ext); safememcpy(&e, ext, sizeof(e)); printf(" key len=%d", ntohs(e.len) - 4); if (2 < vflag && 4 < ntohs(e.len)) { printf(" "); if (!rawprint((caddr_t)(ext + 1), ntohs(e.len) - 4)) goto trunc; } return (u_char *)ext + ntohs(e.len); trunc: printf(" [|%s]", NPSTR(ISAKMP_NPTYPE_KE)); return NULL; } static const u_char * isakmp_id_print(const struct isakmp_gen *ext, u_int item_len, const u_char *ep, u_int32_t phase, u_int32_t doi _U_, u_int32_t proto _U_, int depth _U_) { #define USE_IPSECDOI_IN_PHASE1 1 const struct isakmp_pl_id *p; struct isakmp_pl_id id; static const char *idtypestr[] = { "IPv4", "IPv4net", "IPv6", "IPv6net", }; static const char *ipsecidtypestr[] = { NULL, "IPv4", "FQDN", "user FQDN", "IPv4net", "IPv6", "IPv6net", "IPv4range", "IPv6range", "ASN1 DN", "ASN1 GN", "keyid", }; int len; const u_char *data; printf("%s:", NPSTR(ISAKMP_NPTYPE_ID)); p = (struct isakmp_pl_id *)ext; TCHECK(*p); safememcpy(&id, ext, sizeof(id)); if (sizeof(*p) < item_len) { data = (u_char *)(p + 1); len = item_len - sizeof(*p); } else { data = NULL; len = 0; } #if 0 /*debug*/ printf(" [phase=%d doi=%d proto=%d]", phase, doi, proto); #endif switch (phase) { #ifndef USE_IPSECDOI_IN_PHASE1 case 1: #endif default: printf(" idtype=%s", STR_OR_ID(id.d.id_type, idtypestr)); printf(" doi_data=%u", (u_int32_t)(ntohl(id.d.doi_data) & 0xffffff)); break; #ifdef USE_IPSECDOI_IN_PHASE1 case 1: #endif case 2: { const struct ipsecdoi_id *p; struct ipsecdoi_id id; struct protoent *pe; p = (struct ipsecdoi_id *)ext; TCHECK(*p); safememcpy(&id, ext, sizeof(id)); printf(" idtype=%s", STR_OR_ID(id.type, ipsecidtypestr)); if (id.proto_id) { #ifndef WIN32 setprotoent(1); #endif /* WIN32 */ pe = getprotobynumber(id.proto_id); if (pe) printf(" protoid=%s", pe->p_name); #ifndef WIN32 endprotoent(); #endif /* WIN32 */ } else { /* it DOES NOT mean IPPROTO_IP! */ printf(" protoid=%s", "0"); } printf(" port=%d", ntohs(id.port)); if (!len) break; if (data == NULL) goto trunc; TCHECK2(*data, len); switch (id.type) { case IPSECDOI_ID_IPV4_ADDR: if (len < 4) printf(" len=%d [bad: < 4]", len); else printf(" len=%d %s", len, ipaddr_string(data)); len = 0; break; case IPSECDOI_ID_FQDN: case IPSECDOI_ID_USER_FQDN: { int i; printf(" len=%d ", len); for (i = 0; i < len; i++) safeputchar(data[i]); len = 0; break; } case IPSECDOI_ID_IPV4_ADDR_SUBNET: { const u_char *mask; if (len < 8) printf(" len=%d [bad: < 8]", len); else { mask = data + sizeof(struct in_addr); printf(" len=%d %s/%u.%u.%u.%u", len, ipaddr_string(data), mask[0], mask[1], mask[2], mask[3]); } len = 0; break; } #ifdef INET6 case IPSECDOI_ID_IPV6_ADDR: if (len < 16) printf(" len=%d [bad: < 16]", len); else printf(" len=%d %s", len, ip6addr_string(data)); len = 0; break; case IPSECDOI_ID_IPV6_ADDR_SUBNET: { const u_int32_t *mask; if (len < 20) printf(" len=%d [bad: < 20]", len); else { mask = (u_int32_t *)(data + sizeof(struct in6_addr)); /*XXX*/ printf(" len=%d %s/0x%08x%08x%08x%08x", len, ip6addr_string(data), mask[0], mask[1], mask[2], mask[3]); } len = 0; break; } #endif /*INET6*/ case IPSECDOI_ID_IPV4_ADDR_RANGE: if (len < 8) printf(" len=%d [bad: < 8]", len); else { printf(" len=%d %s-%s", len, ipaddr_string(data), ipaddr_string(data + sizeof(struct in_addr))); } len = 0; break; #ifdef INET6 case IPSECDOI_ID_IPV6_ADDR_RANGE: if (len < 32) printf(" len=%d [bad: < 32]", len); else { printf(" len=%d %s-%s", len, ip6addr_string(data), ip6addr_string(data + sizeof(struct in6_addr))); } len = 0; break; #endif /*INET6*/ case IPSECDOI_ID_DER_ASN1_DN: case IPSECDOI_ID_DER_ASN1_GN: case IPSECDOI_ID_KEY_ID: break; } break; } } if (data && len) { printf(" len=%d", len); if (2 < vflag) { printf(" "); if (!rawprint((caddr_t)data, len)) goto trunc; } } return (u_char *)ext + item_len; trunc: printf(" [|%s]", NPSTR(ISAKMP_NPTYPE_ID)); return NULL; } static const u_char * isakmp_cert_print(const struct isakmp_gen *ext, u_int item_len, const u_char *ep, u_int32_t phase _U_, u_int32_t doi0 _U_, u_int32_t proto0 _U_, int depth _U_) { const struct isakmp_pl_cert *p; struct isakmp_pl_cert cert; static const char *certstr[] = { "none", "pkcs7", "pgp", "dns", "x509sign", "x509ke", "kerberos", "crl", "arl", "spki", "x509attr", }; printf("%s:", NPSTR(ISAKMP_NPTYPE_CERT)); p = (struct isakmp_pl_cert *)ext; TCHECK(*p); safememcpy(&cert, ext, sizeof(cert)); printf(" len=%d", item_len - 4); printf(" type=%s", STR_OR_ID((cert.encode), certstr)); if (2 < vflag && 4 < item_len) { printf(" "); if (!rawprint((caddr_t)(ext + 1), item_len - 4)) goto trunc; } return (u_char *)ext + item_len; trunc: printf(" [|%s]", NPSTR(ISAKMP_NPTYPE_CERT)); return NULL; } static const u_char * isakmp_cr_print(const struct isakmp_gen *ext, u_int item_len, const u_char *ep, u_int32_t phase _U_, u_int32_t doi0 _U_, u_int32_t proto0 _U_, int depth _U_) { const struct isakmp_pl_cert *p; struct isakmp_pl_cert cert; static const char *certstr[] = { "none", "pkcs7", "pgp", "dns", "x509sign", "x509ke", "kerberos", "crl", "arl", "spki", "x509attr", }; printf("%s:", NPSTR(ISAKMP_NPTYPE_CR)); p = (struct isakmp_pl_cert *)ext; TCHECK(*p); safememcpy(&cert, ext, sizeof(cert)); printf(" len=%d", item_len - 4); printf(" type=%s", STR_OR_ID((cert.encode), certstr)); if (2 < vflag && 4 < item_len) { printf(" "); if (!rawprint((caddr_t)(ext + 1), item_len - 4)) goto trunc; } return (u_char *)ext + item_len; trunc: printf(" [|%s]", NPSTR(ISAKMP_NPTYPE_CR)); return NULL; } static const u_char * isakmp_hash_print(const struct isakmp_gen *ext, u_int item_len, const u_char *ep, u_int32_t phase _U_, u_int32_t doi _U_, u_int32_t proto _U_, int depth _U_) { struct isakmp_gen e; printf("%s:", NPSTR(ISAKMP_NPTYPE_HASH)); TCHECK(*ext); safememcpy(&e, ext, sizeof(e)); printf(" len=%d", ntohs(e.len) - 4); if (2 < vflag && 4 < ntohs(e.len)) { printf(" "); if (!rawprint((caddr_t)(ext + 1), ntohs(e.len) - 4)) goto trunc; } return (u_char *)ext + ntohs(e.len); trunc: printf(" [|%s]", NPSTR(ISAKMP_NPTYPE_HASH)); return NULL; } static const u_char * isakmp_sig_print(const struct isakmp_gen *ext, u_int item_len, const u_char *ep, u_int32_t phase _U_, u_int32_t doi _U_, u_int32_t proto _U_, int depth _U_) { struct isakmp_gen e; printf("%s:", NPSTR(ISAKMP_NPTYPE_SIG)); TCHECK(*ext); safememcpy(&e, ext, sizeof(e)); printf(" len=%d", ntohs(e.len) - 4); if (2 < vflag && 4 < ntohs(e.len)) { printf(" "); if (!rawprint((caddr_t)(ext + 1), ntohs(e.len) - 4)) goto trunc; } return (u_char *)ext + ntohs(e.len); trunc: printf(" [|%s]", NPSTR(ISAKMP_NPTYPE_SIG)); return NULL; } static const u_char * isakmp_nonce_print(const struct isakmp_gen *ext, u_int item_len, const u_char *ep, u_int32_t phase _U_, u_int32_t doi _U_, u_int32_t proto _U_, int depth _U_) { struct isakmp_gen e; printf("%s:", NPSTR(ISAKMP_NPTYPE_NONCE)); TCHECK(*ext); safememcpy(&e, ext, sizeof(e)); printf(" n len=%d", ntohs(e.len) - 4); if (2 < vflag && 4 < ntohs(e.len)) { printf(" "); if (!rawprint((caddr_t)(ext + 1), ntohs(e.len) - 4)) goto trunc; } return (u_char *)ext + ntohs(e.len); trunc: printf(" [|%s]", NPSTR(ISAKMP_NPTYPE_NONCE)); return NULL; } static const u_char * isakmp_n_print(const struct isakmp_gen *ext, u_int item_len, const u_char *ep, u_int32_t phase, u_int32_t doi0 _U_, u_int32_t proto0 _U_, int depth) { struct isakmp_pl_n *p, n; const u_char *cp; u_char *ep2; u_int32_t doi; u_int32_t proto; static const char *notify_error_str[] = { NULL, "INVALID-PAYLOAD-TYPE", "DOI-NOT-SUPPORTED", "SITUATION-NOT-SUPPORTED", "INVALID-COOKIE", "INVALID-MAJOR-VERSION", "INVALID-MINOR-VERSION", "INVALID-EXCHANGE-TYPE", "INVALID-FLAGS", "INVALID-MESSAGE-ID", "INVALID-PROTOCOL-ID", "INVALID-SPI", "INVALID-TRANSFORM-ID", "ATTRIBUTES-NOT-SUPPORTED", "NO-PROPOSAL-CHOSEN", "BAD-PROPOSAL-SYNTAX", "PAYLOAD-MALFORMED", "INVALID-KEY-INFORMATION", "INVALID-ID-INFORMATION", "INVALID-CERT-ENCODING", "INVALID-CERTIFICATE", "CERT-TYPE-UNSUPPORTED", "INVALID-CERT-AUTHORITY", "INVALID-HASH-INFORMATION", "AUTHENTICATION-FAILED", "INVALID-SIGNATURE", "ADDRESS-NOTIFICATION", "NOTIFY-SA-LIFETIME", "CERTIFICATE-UNAVAILABLE", "UNSUPPORTED-EXCHANGE-TYPE", "UNEQUAL-PAYLOAD-LENGTHS", }; static const char *ipsec_notify_error_str[] = { "RESERVED", }; static const char *notify_status_str[] = { "CONNECTED", }; static const char *ipsec_notify_status_str[] = { "RESPONDER-LIFETIME", "REPLAY-STATUS", "INITIAL-CONTACT", }; /* NOTE: these macro must be called with x in proper range */ /* 0 - 8191 */ #define NOTIFY_ERROR_STR(x) \ STR_OR_ID((x), notify_error_str) /* 8192 - 16383 */ #define IPSEC_NOTIFY_ERROR_STR(x) \ STR_OR_ID((u_int)((x) - 8192), ipsec_notify_error_str) /* 16384 - 24575 */ #define NOTIFY_STATUS_STR(x) \ STR_OR_ID((u_int)((x) - 16384), notify_status_str) /* 24576 - 32767 */ #define IPSEC_NOTIFY_STATUS_STR(x) \ STR_OR_ID((u_int)((x) - 24576), ipsec_notify_status_str) printf("%s:", NPSTR(ISAKMP_NPTYPE_N)); p = (struct isakmp_pl_n *)ext; TCHECK(*p); safememcpy(&n, ext, sizeof(n)); doi = ntohl(n.doi); proto = n.prot_id; if (doi != 1) { printf(" doi=%d", doi); printf(" proto=%d", proto); if (ntohs(n.type) < 8192) printf(" type=%s", NOTIFY_ERROR_STR(ntohs(n.type))); else if (ntohs(n.type) < 16384) printf(" type=%s", numstr(ntohs(n.type))); else if (ntohs(n.type) < 24576) printf(" type=%s", NOTIFY_STATUS_STR(ntohs(n.type))); else printf(" type=%s", numstr(ntohs(n.type))); if (n.spi_size) { printf(" spi="); if (!rawprint((caddr_t)(p + 1), n.spi_size)) goto trunc; } return (u_char *)(p + 1) + n.spi_size; } printf(" doi=ipsec"); printf(" proto=%s", PROTOIDSTR(proto)); if (ntohs(n.type) < 8192) printf(" type=%s", NOTIFY_ERROR_STR(ntohs(n.type))); else if (ntohs(n.type) < 16384) printf(" type=%s", IPSEC_NOTIFY_ERROR_STR(ntohs(n.type))); else if (ntohs(n.type) < 24576) printf(" type=%s", NOTIFY_STATUS_STR(ntohs(n.type))); else if (ntohs(n.type) < 32768) printf(" type=%s", IPSEC_NOTIFY_STATUS_STR(ntohs(n.type))); else printf(" type=%s", numstr(ntohs(n.type))); if (n.spi_size) { printf(" spi="); if (!rawprint((caddr_t)(p + 1), n.spi_size)) goto trunc; } cp = (u_char *)(p + 1) + n.spi_size; ep2 = (u_char *)p + item_len; if (cp < ep) { printf(" orig=("); switch (ntohs(n.type)) { case IPSECDOI_NTYPE_RESPONDER_LIFETIME: { const struct attrmap *map = oakley_t_map; size_t nmap = sizeof(oakley_t_map)/sizeof(oakley_t_map[0]); while (cp < ep && cp < ep2) { cp = isakmp_attrmap_print(cp, (ep < ep2) ? ep : ep2, map, nmap); } break; } case IPSECDOI_NTYPE_REPLAY_STATUS: printf("replay detection %sabled", (*(u_int32_t *)cp) ? "en" : "dis"); break; case ISAKMP_NTYPE_NO_PROPOSAL_CHOSEN: if (isakmp_sub_print(ISAKMP_NPTYPE_SA, (struct isakmp_gen *)cp, ep, phase, doi, proto, depth) == NULL) return NULL; break; default: /* NULL is dummy */ isakmp_print(cp, item_len - sizeof(*p) - n.spi_size, NULL); } printf(")"); } return (u_char *)ext + item_len; trunc: printf(" [|%s]", NPSTR(ISAKMP_NPTYPE_N)); return NULL; } static const u_char * isakmp_d_print(const struct isakmp_gen *ext, u_int item_len, const u_char *ep, u_int32_t phase _U_, u_int32_t doi0 _U_, u_int32_t proto0 _U_, int depth _U_) { const struct isakmp_pl_d *p; struct isakmp_pl_d d; const u_int8_t *q; u_int32_t doi; u_int32_t proto; int i; printf("%s:", NPSTR(ISAKMP_NPTYPE_D)); p = (struct isakmp_pl_d *)ext; TCHECK(*p); safememcpy(&d, ext, sizeof(d)); doi = ntohl(d.doi); proto = d.prot_id; if (doi != 1) { printf(" doi=%u", doi); printf(" proto=%u", proto); } else { printf(" doi=ipsec"); printf(" proto=%s", PROTOIDSTR(proto)); } printf(" spilen=%u", d.spi_size); printf(" nspi=%u", ntohs(d.num_spi)); printf(" spi="); q = (u_int8_t *)(p + 1); for (i = 0; i < ntohs(d.num_spi); i++) { if (i != 0) printf(","); if (!rawprint((caddr_t)q, d.spi_size)) goto trunc; q += d.spi_size; } return q; trunc: printf(" [|%s]", NPSTR(ISAKMP_NPTYPE_D)); return NULL; } static const u_char * isakmp_vid_print(const struct isakmp_gen *ext, u_int item_len, const u_char *ep, u_int32_t phase _U_, u_int32_t doi _U_, u_int32_t proto _U_, int depth _U_) { struct isakmp_gen e; printf("%s:", NPSTR(ISAKMP_NPTYPE_VID)); TCHECK(*ext); safememcpy(&e, ext, sizeof(e)); printf(" len=%d", ntohs(e.len) - 4); if (2 < vflag && 4 < ntohs(e.len)) { printf(" "); if (!rawprint((caddr_t)(ext + 1), ntohs(e.len) - 4)) goto trunc; } return (u_char *)ext + ntohs(e.len); trunc: printf(" [|%s]", NPSTR(ISAKMP_NPTYPE_VID)); return NULL; } static const u_char * isakmp_sub0_print(u_char np, const struct isakmp_gen *ext, const u_char *ep, u_int32_t phase, u_int32_t doi, u_int32_t proto, int depth) { const u_char *cp; struct isakmp_gen e; u_int item_len; cp = (u_char *)ext; TCHECK(*ext); safememcpy(&e, ext, sizeof(e)); /* * Since we can't have a payload length of less than 4 bytes, * we need to bail out here if the generic header is nonsensical * or truncated, otherwise we could loop forever processing * zero-length items or otherwise misdissect the packet. */ item_len = ntohs(e.len); if (item_len <= 4) return NULL; if (NPFUNC(np)) { /* * XXX - what if item_len is too short, or too long, * for this payload type? */ cp = (*NPFUNC(np))(ext, item_len, ep, phase, doi, proto, depth); } else { printf("%s", NPSTR(np)); cp += item_len; } return cp; trunc: printf(" [|isakmp]"); return NULL; } static const u_char * isakmp_sub_print(u_char np, const struct isakmp_gen *ext, const u_char *ep, u_int32_t phase, u_int32_t doi, u_int32_t proto, int depth) { const u_char *cp; int i; struct isakmp_gen e; cp = (const u_char *)ext; while (np) { TCHECK(*ext); safememcpy(&e, ext, sizeof(e)); TCHECK2(*ext, ntohs(e.len)); depth++; printf("\n"); for (i = 0; i < depth; i++) printf(" "); printf("("); cp = isakmp_sub0_print(np, ext, ep, phase, doi, proto, depth); printf(")"); depth--; if (cp == NULL) { /* Zero-length subitem */ return NULL; } np = e.np; ext = (struct isakmp_gen *)cp; } return cp; trunc: printf(" [|%s]", NPSTR(np)); return NULL; } static char * numstr(int x) { static char buf[20]; snprintf(buf, sizeof(buf), "#%d", x); return buf; } /* * some compiler tries to optimize memcpy(), using the alignment constraint * on the argument pointer type. by using this function, we try to avoid the * optimization. */ static void safememcpy(void *p, const void *q, size_t l) { memcpy(p, q, l); } void isakmp_print(const u_char *bp, u_int length, const u_char *bp2) { const struct isakmp *p; struct isakmp base; const u_char *ep; u_char np; int i; int phase; int major, minor; p = (const struct isakmp *)bp; ep = snapend; if ((struct isakmp *)ep < p + 1) { printf("[|isakmp]"); return; } safememcpy(&base, p, sizeof(base)); printf("isakmp"); if (vflag) { major = (base.vers & ISAKMP_VERS_MAJOR) >> ISAKMP_VERS_MAJOR_SHIFT; minor = (base.vers & ISAKMP_VERS_MINOR) >> ISAKMP_VERS_MINOR_SHIFT; printf(" %d.%d", major, minor); } if (vflag) { printf(" msgid "); rawprint((caddr_t)&base.msgid, sizeof(base.msgid)); } if (1 < vflag) { printf(" cookie "); rawprint((caddr_t)&base.i_ck, sizeof(base.i_ck)); printf("->"); rawprint((caddr_t)&base.r_ck, sizeof(base.r_ck)); } printf(":"); phase = (*(u_int32_t *)base.msgid == 0) ? 1 : 2; if (phase == 1) printf(" phase %d", phase); else printf(" phase %d/others", phase); i = cookie_find(&base.i_ck); if (i < 0) { if (iszero((u_char *)&base.r_ck, sizeof(base.r_ck))) { /* the first packet */ printf(" I"); if (bp2) cookie_record(&base.i_ck, bp2); } else printf(" ?"); } else { if (bp2 && cookie_isinitiator(i, bp2)) printf(" I"); else if (bp2 && cookie_isresponder(i, bp2)) printf(" R"); else printf(" ?"); } printf(" %s", ETYPESTR(base.etype)); if (base.flags) { printf("[%s%s]", base.flags & ISAKMP_FLAG_E ? "E" : "", base.flags & ISAKMP_FLAG_C ? "C" : ""); } if (vflag) { const struct isakmp_gen *ext; int nparen; #define CHECKLEN(p, np) \ if (ep < (u_char *)(p)) { \ printf(" [|%s]", NPSTR(np)); \ goto done; \ } printf(":"); /* regardless of phase... */ if (base.flags & ISAKMP_FLAG_E) { /* * encrypted, nothing we can do right now. * we hope to decrypt the packet in the future... */ printf(" [encrypted %s]", NPSTR(base.np)); goto done; } nparen = 0; CHECKLEN(p + 1, base.np) np = base.np; ext = (struct isakmp_gen *)(p + 1); isakmp_sub_print(np, ext, ep, phase, 0, 0, 0); } done: if (vflag) { if (ntohl(base.len) != length) { printf(" (len mismatch: isakmp %u/ip %u)", (u_int32_t)ntohl(base.len), length); } } }