print-esp.c revision 147900
137446Srnordier/* $NetBSD: print-ah.c,v 1.4 1996/05/20 00:41:16 fvdl Exp $ */ 237446Srnordier 337446Srnordier/* 437446Srnordier * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994 537446Srnordier * The Regents of the University of California. All rights reserved. 637446Srnordier * 737446Srnordier * Redistribution and use in source and binary forms, with or without 837446Srnordier * modification, are permitted provided that: (1) source code distributions 937446Srnordier * retain the above copyright notice and this paragraph in its entirety, (2) 1037446Srnordier * distributions including binary code include the above copyright notice and 1137446Srnordier * this paragraph in its entirety in the documentation or other materials 1237446Srnordier * provided with the distribution, and (3) all advertising materials mentioning 1337446Srnordier * features or use of this software display the following acknowledgement: 1437446Srnordier * ``This product includes software developed by the University of California, 1537446Srnordier * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 1637446Srnordier * the University nor the names of its contributors may be used to endorse 1737446Srnordier * or promote products derived from this software without specific prior 1837446Srnordier * written permission. 1937446Srnordier * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 2037446Srnordier * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 2137446Srnordier * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 2237446Srnordier */ 2337446Srnordier 2437446Srnordier#ifndef lint 2537446Srnordierstatic const char rcsid[] _U_ = 2637446Srnordier "@(#) $Header: /tcpdump/master/tcpdump/print-esp.c,v 1.55.2.1 2005/04/21 06:44:57 guy Exp $ (LBL)"; 2737446Srnordier#endif 2837446Srnordier 2937446Srnordier#ifdef HAVE_CONFIG_H 3050476Speter#include "config.h" 3137446Srnordier#endif 3237446Srnordier 3337446Srnordier#include <string.h> 34106372Sscottl 35106372Sscottl#include <tcpdump-stdinc.h> 3637446Srnordier 3737446Srnordier#include <stdlib.h> 3866907Swollman 3966907Swollman#ifdef HAVE_LIBCRYPTO 4037446Srnordier#ifdef HAVE_OPENSSL_EVP_H 4137446Srnordier#include <openssl/evp.h> 4237446Srnordier#endif 4337446Srnordier#endif 4437446Srnordier 45185594Smlaier#include <stdio.h> 4637446Srnordier 47223945Sae#include "ip.h" 4837446Srnordier#include "esp.h" 4937446Srnordier#ifdef INET6 5037446Srnordier#include "ip6.h" 5166907Swollman#endif 5237446Srnordier 5337446Srnordier#include "netdissect.h" 5437446Srnordier#include "addrtoname.h" 5537446Srnordier#include "extract.h" 5637446Srnordier 5737446Srnordier#ifndef HAVE_SOCKADDR_STORAGE 5837446Srnordier#ifdef INET6 59170166Strhodesstruct sockaddr_storage { 6037446Srnordier union { 6137446Srnordier struct sockaddr_in sin; 6237446Srnordier struct sockaddr_in6 sin6; 6337446Srnordier } un; 6437446Srnordier}; 6537446Srnordier#else 66203868Skib#define sockaddr_storage sockaddr 67203868Skib#endif 68203868Skib#endif /* HAVE_SOCKADDR_STORAGE */ 69229550Spfg 70203868Skib#ifdef HAVE_LIBCRYPTO 71203868Skibstruct sa_list { 7237446Srnordier struct sa_list *next; 7337446Srnordier struct sockaddr_storage daddr; 7437446Srnordier u_int32_t spi; 7537446Srnordier const EVP_CIPHER *evp; 7637446Srnordier int ivlen; 7737446Srnordier int authlen; 7837446Srnordier u_char secret[256]; /* is that big enough for all secrets? */ 7937446Srnordier int secretlen; 8037446Srnordier}; 8137446Srnordier 8237446Srnordierstatic void esp_print_addsa(netdissect_options *ndo, 8337446Srnordier struct sa_list *sa, int sa_def) 8437446Srnordier{ 8537446Srnordier /* copy the "sa" */ 8637446Srnordier 8737446Srnordier struct sa_list *nsa; 8837446Srnordier 8937446Srnordier nsa = (struct sa_list *)malloc(sizeof(struct sa_list)); 9037446Srnordier if (nsa == NULL) 9137446Srnordier (*ndo->ndo_error)(ndo, "ran out of memory to allocate sa structure"); 9237446Srnordier 9337446Srnordier *nsa = *sa; 9437446Srnordier 9537446Srnordier if (sa_def) 9637446Srnordier ndo->ndo_sa_default = nsa; 9737446Srnordier 9837446Srnordier nsa->next = ndo->ndo_sa_list_head; 9937446Srnordier ndo->ndo_sa_list_head = nsa; 100229550Spfg} 101203869Skib 102231162Sdelphij 10337446Srnordierstatic u_int hexdigit(netdissect_options *ndo, char hex) 10437446Srnordier{ 105203869Skib if (hex >= '0' && hex <= '9') 106203869Skib return (hex - '0'); 107203869Skib else if (hex >= 'A' && hex <= 'F') 108203869Skib return (hex - 'A' + 10); 109203869Skib else if (hex >= 'a' && hex <= 'f') 110203869Skib return (hex - 'a' + 10); 111203869Skib else { 112203869Skib (*ndo->ndo_error)(ndo, "invalid hex digit %c in espsecret\n", hex); 113203869Skib return 0; 114203869Skib } 115203869Skib} 116203869Skib 117231162Sdelphijstatic u_int hex2byte(netdissect_options *ndo, char *hexstring) 11837446Srnordier{ 11937446Srnordier u_int byte; 120203869Skib 121203869Skib byte = (hexdigit(ndo, hexstring[0]) << 4) + hexdigit(ndo, hexstring[1]); 122203869Skib return byte; 123203869Skib} 124203869Skib 125203869Skib/* 126229550Spfg * decode the form: SPINUM@IP <tab> ALGONAME:0xsecret 127231162Sdelphij * 12837446Srnordier * special form: file /name 12937446Srnordier * causes us to go read from this file instead. 130203869Skib * 131203869Skib */ 132203869Skibstatic void esp_print_decode_onesecret(netdissect_options *ndo, char *line) 133203869Skib{ 134203869Skib struct sa_list sa1; 135203869Skib int sa_def; 136231162Sdelphij 13737446Srnordier char *spikey; 13837446Srnordier char *decode; 139229550Spfg 140229550Spfg spikey = strsep(&line, " \t"); 141229550Spfg sa_def = 0; 142229550Spfg memset(&sa1, 0, sizeof(struct sa_list)); 143229550Spfg 144229550Spfg /* if there is only one token, then it is an algo:key token */ 145229550Spfg if (line == NULL) { 146231162Sdelphij decode = spikey; 14737446Srnordier spikey = NULL; 14837446Srnordier /* memset(&sa1.daddr, 0, sizeof(sa1.daddr)); */ 149203869Skib /* sa1.spi = 0; */ 150203869Skib sa_def = 1; 151203869Skib } else 152203869Skib decode = line; 153203869Skib 154203869Skib if (spikey && strcasecmp(spikey, "file") == 0) { 155203869Skib /* open file and read it */ 156203869Skib FILE *secretfile; 157203869Skib char fileline[1024]; 158203869Skib char *nl; 159203869Skib 160203869Skib secretfile = fopen(line, FOPEN_READ_TXT); 161203869Skib if (secretfile == NULL) { 162203869Skib perror(line); 163229550Spfg exit(3); 164229550Spfg } 16537446Srnordier 16637446Srnordier while (fgets(fileline, sizeof(fileline)-1, secretfile) != NULL) { 167140384Sdelphij /* remove newline from the line */ 168140384Sdelphij nl = strchr(fileline, '\n'); 16937446Srnordier if (nl) 17037446Srnordier *nl = '\0'; 17137446Srnordier if (fileline[0] == '#') continue; 172190927Sed if (fileline[0] == '\0') continue; 173229550Spfg 174229550Spfg esp_print_decode_onesecret(ndo, fileline); 175229550Spfg } 176229550Spfg fclose(secretfile); 177229550Spfg 178229550Spfg return; 179229550Spfg } 180229550Spfg 181229550Spfg if (spikey) { 182229550Spfg char *spistr, *foo; 18337446Srnordier u_int32_t spino; 18437446Srnordier struct sockaddr_in *sin; 185190927Sed#ifdef INET6 18637446Srnordier struct sockaddr_in6 *sin6; 18737446Srnordier#endif 18837446Srnordier 18937446Srnordier spistr = strsep(&spikey, "@"); 19037446Srnordier 19137446Srnordier spino = strtoul(spistr, &foo, 0); 19237446Srnordier if (spistr == foo || !spikey) { 19337446Srnordier (*ndo->ndo_warning)(ndo, "print_esp: failed to decode spi# %s\n", foo); 19437446Srnordier return; 19537446Srnordier } 19637446Srnordier 19737446Srnordier sa1.spi = spino; 19837446Srnordier 19937446Srnordier sin = (struct sockaddr_in *)&sa1.daddr; 20037446Srnordier#ifdef INET6 20137446Srnordier sin6 = (struct sockaddr_in6 *)&sa1.daddr; 20237446Srnordier if (inet_pton(AF_INET6, spikey, &sin6->sin6_addr) == 1) { 20337446Srnordier#ifdef HAVE_SOCKADDR_SA_LEN 20437446Srnordier sin6->sin6_len = sizeof(struct sockaddr_in6); 20537446Srnordier#endif 20637446Srnordier sin6->sin6_family = AF_INET6; 20737446Srnordier } else 20837446Srnordier#endif 20937446Srnordier if (inet_pton(AF_INET, spikey, &sin->sin_addr) == 1) { 21037446Srnordier#ifdef HAVE_SOCKADDR_SA_LEN 21137446Srnordier sin->sin_len = sizeof(struct sockaddr_in); 21237446Srnordier#endif 21337446Srnordier sin->sin_family = AF_INET; 21437446Srnordier } else { 21537446Srnordier (*ndo->ndo_warning)(ndo, "print_esp: can not decode IP# %s\n", spikey); 21637446Srnordier return; 217223945Sae } 218223945Sae } 219223945Sae 22037446Srnordier if (decode) { 22137446Srnordier char *colon, *p; 22237446Srnordier u_char espsecret_key[256]; 22337446Srnordier int len; 22437446Srnordier size_t i; 22537446Srnordier const EVP_CIPHER *evp; 22637446Srnordier int authlen = 0; 227185587Sluigi 22837446Srnordier /* skip any blank spaces */ 22937446Srnordier while (isspace((unsigned char)*decode)) 23037446Srnordier decode++; 23137446Srnordier 23237446Srnordier colon = strchr(decode, ':'); 23337446Srnordier if (colon == NULL) { 234102231Strhodes (*ndo->ndo_warning)(ndo, "failed to decode espsecret: %s\n", decode); 23537446Srnordier return; 23637446Srnordier } 23737446Srnordier *colon = '\0'; 23837446Srnordier 239190927Sed len = colon - decode; 240190927Sed if (strlen(decode) > strlen("-hmac96") && 241190927Sed !strcmp(decode + strlen(decode) - strlen("-hmac96"), 242190927Sed "-hmac96")) { 243190927Sed p = strstr(decode, "-hmac96"); 244190927Sed *p = '\0'; 245190927Sed authlen = 12; 24637446Srnordier } 247223945Sae if (strlen(decode) > strlen("-cbc") && 24837446Srnordier !strcmp(decode + strlen(decode) - strlen("-cbc"), "-cbc")) { 24937446Srnordier p = strstr(decode, "-cbc"); 25037446Srnordier *p = '\0'; 25137446Srnordier } 25237446Srnordier evp = EVP_get_cipherbyname(decode); 25337446Srnordier if (!evp) { 25437446Srnordier (*ndo->ndo_warning)(ndo, "failed to find cipher algo %s\n", decode); 25537446Srnordier sa1.evp = NULL; 25637446Srnordier sa1.authlen = 0; 25737446Srnordier sa1.ivlen = 0; 25837446Srnordier return; 25937446Srnordier } 26040487Sbde 26137446Srnordier sa1.evp = evp; 26237446Srnordier sa1.authlen = authlen; 263190927Sed sa1.ivlen = EVP_CIPHER_iv_length(evp); 26437446Srnordier 26537446Srnordier colon++; 26637446Srnordier if (colon[0] == '0' && colon[1] == 'x') { 267185587Sluigi /* decode some hex! */ 268185587Sluigi colon += 2; 269185587Sluigi len = strlen(colon) / 2; 27037446Srnordier 27137446Srnordier if (len > 256) { 27237446Srnordier (*ndo->ndo_warning)(ndo, "secret is too big: %d\n", len); 27337446Srnordier return; 27437446Srnordier } 27537446Srnordier 276185587Sluigi i = 0; 277185587Sluigi while (colon[0] != '\0' && colon[1]!='\0') { 278185587Sluigi espsecret_key[i] = hex2byte(ndo, colon); 27937446Srnordier colon += 2; 28037446Srnordier i++; 28137446Srnordier } 28237446Srnordier 28337446Srnordier memcpy(sa1.secret, espsecret_key, i); 28437446Srnordier sa1.secretlen = i; 28537446Srnordier } else { 28637446Srnordier i = strlen(colon); 28737446Srnordier 28837446Srnordier if (i < sizeof(sa1.secret)) { 28937446Srnordier memcpy(sa1.secret, colon, i); 29037446Srnordier sa1.secretlen = i; 29137446Srnordier } else { 29237446Srnordier memcpy(sa1.secret, colon, sizeof(sa1.secret)); 29337446Srnordier sa1.secretlen = sizeof(sa1.secret); 29437446Srnordier } 29537446Srnordier } 29637446Srnordier } 29737446Srnordier 29837446Srnordier esp_print_addsa(ndo, &sa1, sa_def); 29937446Srnordier} 30037446Srnordier 30137446Srnordierstatic void esp_print_decodesecret(netdissect_options *ndo) 30237446Srnordier{ 30337446Srnordier char *line; 30437446Srnordier char *p; 30537446Srnordier 30637446Srnordier p = ndo->ndo_espsecret; 30737446Srnordier 30837446Srnordier while (ndo->ndo_espsecret && ndo->ndo_espsecret[0] != '\0') { 30937446Srnordier /* pick out the first line or first thing until a comma */ 31037446Srnordier if ((line = strsep(&ndo->ndo_espsecret, "\n,")) == NULL) { 31137446Srnordier line = ndo->ndo_espsecret; 31237446Srnordier ndo->ndo_espsecret = NULL; 31337446Srnordier } 31437446Srnordier 31537446Srnordier esp_print_decode_onesecret(ndo, line); 31637446Srnordier } 31737446Srnordier} 31837446Srnordier 31937446Srnordierstatic void esp_init(netdissect_options *ndo _U_) 32037446Srnordier{ 32137446Srnordier 32237446Srnordier OpenSSL_add_all_algorithms(); 32337446Srnordier EVP_add_cipher_alias(SN_des_ede3_cbc, "3des"); 32437446Srnordier} 32537446Srnordier#endif 32637446Srnordier 32737446Srnordierint 32837446Srnordieresp_print(netdissect_options *ndo, 32937446Srnordier const u_char *bp, const int length, const u_char *bp2 33037446Srnordier#ifndef HAVE_LIBCRYPTO 33137446Srnordier _U_ 33237446Srnordier#endif 33337446Srnordier , 33437446Srnordier int *nhdr 33537446Srnordier#ifndef HAVE_LIBCRYPTO 33637446Srnordier _U_ 33737446Srnordier#endif 33837446Srnordier , 33937446Srnordier int *padlen 34037446Srnordier#ifndef HAVE_LIBCRYPTO 34137446Srnordier _U_ 34237446Srnordier#endif 34337446Srnordier ) 344102231Strhodes{ 34537446Srnordier register const struct newesp *esp; 34637446Srnordier register const u_char *ep; 34737446Srnordier#ifdef HAVE_LIBCRYPTO 34837446Srnordier struct ip *ip; 34937446Srnordier struct sa_list *sa = NULL; 35037446Srnordier int espsecret_keylen; 35137446Srnordier#ifdef INET6 35237446Srnordier struct ip6_hdr *ip6 = NULL; 35337446Srnordier#endif 35437446Srnordier int advance; 35537446Srnordier int len; 35637446Srnordier u_char *secret; 357190932Sed int ivlen = 0; 35863892Sasmodai u_char *ivoff; 35937446Srnordier u_char *p; 36037446Srnordier EVP_CIPHER_CTX ctx; 36137446Srnordier int blocksz; 36237446Srnordier static int initialized = 0; 363185587Sluigi#endif 364185587Sluigi 365185587Sluigi esp = (struct newesp *)bp; 366185587Sluigi 367185587Sluigi#ifdef HAVE_LIBCRYPTO 368185587Sluigi secret = NULL; 369190929Sed advance = 0; 370185594Smlaier 371190930Sed if (!initialized) { 37237446Srnordier esp_init(ndo); 373190930Sed initialized = 1; 374190930Sed } 375190931Sed#endif 376190931Sed 377190931Sed#if 0 378190931Sed /* keep secret out of a register */ 379190931Sed p = (u_char *)&secret; 380190931Sed#endif 381190931Sed 38237446Srnordier /* 'ep' points to the end of available data. */ 38337446Srnordier ep = ndo->ndo_snapend; 384185587Sluigi 385185594Smlaier if ((u_char *)(esp + 1) >= ep) { 38637446Srnordier fputs("[|ESP]", stdout); 38737446Srnordier goto fail; 38837446Srnordier } 389203869Skib (*ndo->ndo_printf)(ndo, "ESP(spi=0x%08x", EXTRACT_32BITS(&esp->esp_spi)); 390203869Skib (*ndo->ndo_printf)(ndo, ",seq=0x%x)", EXTRACT_32BITS(&esp->esp_seq)); 391203869Skib (*ndo->ndo_printf)(ndo, ", length %u", length); 392203869Skib 39337446Srnordier#ifndef HAVE_LIBCRYPTO 39437446Srnordier goto fail; 395203869Skib#else 39637446Srnordier /* initiailize SAs */ 397203869Skib if (ndo->ndo_sa_list_head == NULL) { 39837446Srnordier if (!ndo->ndo_espsecret) 399203869Skib goto fail; 40037446Srnordier 401203869Skib esp_print_decodesecret(ndo); 40237446Srnordier } 403203869Skib 404185587Sluigi if (ndo->ndo_sa_list_head == NULL) 405185587Sluigi goto fail; 40637446Srnordier 407203869Skib ip = (struct ip *)bp2; 408203869Skib switch (IP_V(ip)) { 409185587Sluigi#ifdef INET6 410185587Sluigi case 6: 411203869Skib ip6 = (struct ip6_hdr *)bp2; 412203869Skib /* we do not attempt to decrypt jumbograms */ 413185587Sluigi if (!EXTRACT_16BITS(&ip6->ip6_plen)) 414203869Skib goto fail; 415203869Skib /* if we can't get nexthdr, we do not need to decrypt it */ 416203869Skib len = sizeof(struct ip6_hdr) + EXTRACT_16BITS(&ip6->ip6_plen); 417203869Skib 418203869Skib /* see if we can find the SA, and if so, decode it */ 419203869Skib for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) { 420203869Skib struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa->daddr; 421203869Skib if (sa->spi == ntohl(esp->esp_spi) && 422203869Skib sin6->sin6_family == AF_INET6 && 423185587Sluigi memcmp(&sin6->sin6_addr, &ip6->ip6_dst, 424203869Skib sizeof(struct in6_addr)) == 0) { 425185587Sluigi break; 426185587Sluigi } 427203869Skib } 428203869Skib break; 429203869Skib#endif /*INET6*/ 43037446Srnordier case 4: 431203869Skib /* nexthdr & padding are in the last fragment */ 43248954Sbillf if (EXTRACT_16BITS(&ip->ip_off) & IP_MF) 43337446Srnordier goto fail; 43437446Srnordier len = EXTRACT_16BITS(&ip->ip_len); 43537446Srnordier 43637446Srnordier /* see if we can find the SA, and if so, decode it */ 43748954Sbillf for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) { 43837446Srnordier struct sockaddr_in *sin = (struct sockaddr_in *)&sa->daddr; 43937446Srnordier if (sa->spi == ntohl(esp->esp_spi) && 44037446Srnordier sin->sin_family == AF_INET && 44137446Srnordier sin->sin_addr.s_addr == ip->ip_dst.s_addr) { 44237446Srnordier break; 443203869Skib } 44437446Srnordier } 44537446Srnordier break; 44637446Srnordier default: 447203869Skib goto fail; 44837446Srnordier } 449203869Skib 450203869Skib /* if we didn't find the specific one, then look for 45137446Srnordier * an unspecified one. 452203869Skib */ 453203869Skib if (sa == NULL) 45437446Srnordier sa = ndo->ndo_sa_default; 45537446Srnordier 45637446Srnordier /* if not found fail */ 45737446Srnordier if (sa == NULL) 458203869Skib goto fail; 45937446Srnordier 46037446Srnordier /* if we can't get nexthdr, we do not need to decrypt it */ 461203869Skib if (ep - bp2 < len) 46237446Srnordier goto fail; 46337446Srnordier if (ep - bp2 > len) { 46437446Srnordier /* FCS included at end of frame (NetBSD 1.6 or later) */ 46537446Srnordier ep = bp2 + len; 466203869Skib } 46737446Srnordier 46837446Srnordier ivoff = (u_char *)(esp + 1) + 0; 469203869Skib ivlen = sa->ivlen; 47037446Srnordier secret = sa->secret; 47137446Srnordier espsecret_keylen = sa->secretlen; 47241586Srnordier ep = ep - sa->authlen; 473203869Skib 47437446Srnordier if (sa->evp) { 47537446Srnordier memset(&ctx, 0, sizeof(ctx)); 476203869Skib if (EVP_CipherInit(&ctx, sa->evp, secret, NULL, 0) < 0) 47737446Srnordier (*ndo->ndo_warning)(ndo, "espkey init failed"); 478203869Skib 47937446Srnordier blocksz = EVP_CIPHER_CTX_block_size(&ctx); 480203869Skib 48137446Srnordier p = ivoff; 48237446Srnordier EVP_CipherInit(&ctx, NULL, NULL, p, 0); 48337446Srnordier EVP_Cipher(&ctx, p + ivlen, p + ivlen, ep - (p + ivlen)); 48437446Srnordier advance = ivoff - (u_char *)esp + ivlen; 48537446Srnordier } else 48637446Srnordier advance = sizeof(struct newesp); 48742261Sjkh 48837446Srnordier /* sanity check for pad length */ 48937446Srnordier if (ep - bp < *(ep - 2)) 49037446Srnordier goto fail; 49137446Srnordier 49237446Srnordier if (padlen) 493203869Skib *padlen = *(ep - 2) + 2; 494229550Spfg 495229550Spfg if (nhdr) 49637446Srnordier *nhdr = *(ep - 1); 497203869Skib 49837446Srnordier (ndo->ndo_printf)(ndo, ": "); 499203869Skib return advance; 500203869Skib#endif 50148954Sbillf 502203869Skibfail: 503203869Skib return -1; 504229550Spfg} 505229550Spfg 506203869Skib/* 507203869Skib * Local Variables: 508203869Skib * c-style: whitesmith 509203869Skib * c-basic-offset: 8 510229550Spfg * End: 511229550Spfg */ 51237446Srnordier