tcpdump
tcpdump copied to clipboard
[Patch] Add support for decrypting ESP packets in packet_dump mode
Converted from SourceForge issue 3431245, submitted by alanre
I got fed up decoding ESP manually so I added the functionality into tcpdump. I've enhanced the existing -E flag which currently only prints the decrypted ESP packets tp the screen
Now if -E and -w flags are present then both the raw ESP packet and the decoded ESP packet are dumped to the file.
( I use a perl based wrapper around tcpdump which extracts the SPIs and encryption keys from a setkey -D output)
cheers AlanE
Submitted by guy_harris
A version that dynamically allocates and reallocates the buffer, rather than imposing an arbitrary 1500-byte limit, might be better.
Even a version that prints a warning and continues, rather than exiting (error() exits) would be better.
The originally proposed patch:
diff -rupN tcpdump-org/Makefile.in tcpdump/Makefile.in
--- tcpdump-org/Makefile.in 2011-10-31 15:26:18.526869894 +0000
+++ tcpdump/Makefile.in 2011-10-31 15:30:29.964230206 +0000
@@ -93,7 +93,7 @@ CSRC = addrtoname.c af.c checksum.c cpac
print-symantec.c print-syslog.c print-tcp.c print-telnet.c print-tftp.c \
print-timed.c print-token.c print-udld.c print-udp.c print-usb.c \
print-vjc.c print-vqp.c print-vrrp.c print-vtp.c print-forces.c \
- print-wb.c print-zephyr.c signature.c setsignal.c tcpdump.c util.c
+ print-wb.c print-zephyr.c signature.c setsignal.c tcpdump.c util.c decrypt-esp.c
LIBNETDISSECT_SRC=print-isakmp.c
LIBNETDISSECT_OBJ=$(LIBNETDISSECT_SRC:.c=.o)
diff -rupN tcpdump-org/decrypt-esp.c tcpdump/decrypt-esp.c
--- tcpdump-org/decrypt-esp.c 1970-01-01 01:00:00.000000000 +0100
+++ tcpdump/decrypt-esp.c 2011-10-31 15:30:36.030853743 +0000
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <tcpdump-stdinc.h>
+
+#include <stdlib.h>
+
+#ifdef HAVE_LIBCRYPTO
+#ifdef HAVE_OPENSSL_EVP_H
+#include <openssl/evp.h>
+#endif
+#endif
+
+#include <stdio.h>
+#include <netinet/in.h>
+
+#include "interface.h"
+#include "ether.h"
+#include "ip.h"
+#include "udp.h"
+#include "esp.h"
+#ifdef INET6
+#include "ip6.h"
+#endif
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#ifndef HAVE_SOCKADDR_STORAGE
+#ifdef INET6
+struct sockaddr_storage {
+ union {
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } un;
+};
+#else
+#define sockaddr_storage sockaddr
+#endif
+#endif /* HAVE_SOCKADDR_STORAGE */
+
+#ifdef HAVE_LIBCRYPTO
+struct sa_list {
+ struct sa_list *next;
+ struct sockaddr_storage daddr;
+ u_int32_t spi; /* if == 0, then IKEv2 */
+ int initiator;
+ u_char spii[8]; /* for IKEv2 */
+ u_char spir[8];
+ const EVP_CIPHER *evp;
+ int ivlen;
+ int authlen;
+ u_char authsecret[256];
+ int authsecret_len;
+ u_char secret[256]; /* is that big enough for all secrets? */
+ int secretlen;
+};
+
+
+int esp_decrypt(u_char *user, const struct pcap_pkthdr *h, const u_char *sp, struct pcap_pkthdr *decrypt_h, u_char *decrypt_sp);
+u_int16_t ip_cksum(u_int16_t len, u_char buff[]);
+
+int esp_decrypt(u_char *user, const struct pcap_pkthdr *h, const u_char *sp, struct pcap_pkthdr *decrypt_h, u_char *decrypt_sp) {
+
+ struct ether_header *ether_h;
+ struct ip *ip_h, *decrypt_ip_h;
+ struct udphdr *udp_h;
+ struct newesp *esp_h;
+ struct sa_list *sa;
+ netdissect_options *ndo;
+ u_char *iv;
+ int ilen, olen, olen_final;
+ u_char *esp_data;
+ u_char pad, next_hdr;
+
+ EVP_CIPHER_CTX ctx;
+
+
+ /* Sanity check pcap_pkthdr */
+ if (h->caplen > 1500) {
+ error("Can't handle packets > 1500 bytes\n");
+ return(0);
+ }
+ if (h->caplen < h->len) {
+ error("Captured packet is truncated\n");
+ return(0);
+ }
+
+ decrypt_h->ts = h->ts;
+ ether_h = (struct ether_header*)(sp);
+ memcpy(decrypt_sp, sp, ETHER_HDRLEN);
+ sp = sp + ETHER_HDRLEN;
+ ip_h = (struct ip *)sp;
+ sp = sp + (IP_HL(ip_h)*4);
+
+ if (ip_h->ip_p == IPPROTO_ESP) {
+ esp_h = (struct newesp *)sp;
+ ilen = ntohs(ip_h->ip_len) - (IP_HL(ip_h)*4);
+ } else if (ip_h->ip_p == IPPROTO_UDP) {
+
+ /* Check if ESP is UDP ENC UDP Port 4500 */
+
+ udp_h = (struct udphdr*)sp;
+ if (ntohs(udp_h->uh_sport) == ISAKMP_PORT_NATT || ntohs(udp_h->uh_dport) == ISAKMP_PORT_NATT) {
+ sp = sp + sizeof(struct udphdr);
+ esp_h = (struct newesp *)sp;
+ ilen = ntohs(udp_h->uh_ulen) - sizeof(struct udphdr);
+ } else {
+ return(0);
+ }
+ }
+ else {
+ return(0);
+ }
+
+ sp = sp + sizeof(struct newesp) + 8;
+ esp_data = (u_char *)sp;
+
+ /* Strip of the 24 byte ESP Header and 12 byte ESP Authentication tail */
+ ilen = ilen - 28;
+ iv = (u_char *)(esp_h + sizeof(struct newesp));
+
+ /* see if we can find the SA, and if so, decode it */
+ ndo = gndo;
+ esp_print_decodesecret(ndo);
+ for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&sa->daddr;
+ if (sa->spi == ntohl(esp_h->esp_spi) &&
+ sin->sin_family == AF_INET &&
+ sin->sin_addr.s_addr == ip_h->ip_dst.s_addr) {
+ break;
+ }
+ }
+ if(sa == NULL) {
+ return 0;
+ }
+ if(sa->evp == NULL) {
+ return 0;
+ }
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ if (EVP_DecryptInit_ex(&ctx, sa->evp, NULL, sa->secret, iv) != 1) {
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return 0;
+ }
+
+ EVP_CIPHER_CTX_set_padding(&ctx, 0); /* Disable padding */
+
+ if (EVP_DecryptUpdate(&ctx, decrypt_sp + ETHER_HDRLEN, &olen, esp_data, ilen) != 1) {
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return 0;
+ }
+ if (EVP_DecryptFinal_ex(&ctx, decrypt_sp + ETHER_HDRLEN + olen, &olen_final) != 1) {
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return 0;
+ }
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ olen = olen + olen_final;
+ pad = decrypt_sp[ETHER_HDRLEN + olen - 2];
+ next_hdr = decrypt_sp[ETHER_HDRLEN + olen - 1];
+ olen = olen - pad - 2; /* Remove Pad, Pad Length and Next Hdr */
+
+
+/* Fixup the IP Header */
+
+ decrypt_ip_h = (struct ip *)(decrypt_sp + ETHER_HDRLEN);
+
+ decrypt_ip_h->ip_vhl = ip_h->ip_vhl;
+ decrypt_ip_h->ip_tos = ip_h->ip_tos;
+ decrypt_ip_h->ip_len = htons(olen);
+ decrypt_ip_h->ip_off = ip_h->ip_off;
+ decrypt_ip_h->ip_sum = 0x0000;
+ decrypt_ip_h->ip_sum = htons(ip_cksum(20, (u_char *)decrypt_ip_h));
+
+ decrypt_h->len = olen + ETHER_HDRLEN;
+ decrypt_h->caplen = olen + ETHER_HDRLEN;
+
+ return (1);
+
+}
+
+
+u_int16_t ip_cksum(u_int16_t len, u_char buff[]) {
+
+u_int16_t word16;
+u_int32_t sum=0;
+u_int16_t i;
+
+// make 16 bit words out of every two adjacent 8 bit words in the packet and add them up
+ for (i=0;i<len;i=i+2) {
+ word16 =((buff[i]<<8)&0xFF00)+(buff[i+1]&0xFF);
+ sum = sum + (u_int32_t) word16;
+ }
+
+// take only 16 bits out of the 32 bit sum and add up the carries
+ while (sum>>16)
+ sum = (sum & 0xFFFF)+(sum >> 16);
+
+// one's complement the result
+ sum = ~sum;
+
+ return ((u_int16_t) sum);
+}
+
+
+#endif // End ifdef HAVE_LIBCRYPTO
diff -rupN tcpdump-org/tcpdump.c tcpdump/tcpdump.c
--- tcpdump-org/tcpdump.c 2011-10-31 15:26:18.657904541 +0000
+++ tcpdump/tcpdump.c 2011-10-31 15:32:57.282095007 +0000
@@ -97,6 +97,8 @@ static int Jflag; /* list available ti
#endif
static char *zflag = NULL; /* compress each savefile using a specified command (like gzip or bzip2) */
+static int decrypt_esp_flag;
+
static int infodelay;
static int infoprint;
@@ -727,6 +729,7 @@ main(int argc, char **argv)
warning("crypto code not compiled in");
#endif
gndo->ndo_espsecret = optarg;
+ decrypt_esp_flag++;
break;
case 'f':
@@ -1571,6 +1574,8 @@ static void
dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
{
struct dump_info *dump_info;
+ struct pcap_pkthdr decrypt_h;
+ u_char decrypt_sp[1500];
++packets_captured;
@@ -1681,6 +1686,11 @@ dump_packet_and_trunc(u_char *user, cons
}
pcap_dump((u_char *)dump_info->p, h, sp);
+ if (decrypt_esp_flag) {
+ if (esp_decrypt(user, h, sp, &decrypt_h, &decrypt_sp) == 1) {
+ pcap_dump(user, &decrypt_h, decrypt_sp);
+ }
+ }
#ifdef HAVE_PCAP_DUMP_FLUSH
if (Uflag)
pcap_dump_flush(dump_info->p);
@@ -1694,11 +1704,18 @@ dump_packet_and_trunc(u_char *user, cons
static void
dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
{
- ++packets_captured;
+ struct pcap_pkthdr decrypt_h;
+ u_char decrypt_sp[1500];
+ ++packets_captured;
++infodelay;
pcap_dump(user, h, sp);
+ if (decrypt_esp_flag) {
+ if (esp_decrypt(user, h, sp, &decrypt_h, &decrypt_sp) == 1) {
+ pcap_dump(user, &decrypt_h, decrypt_sp);
+ }
+ }
#ifdef HAVE_PCAP_DUMP_FLUSH
if (Uflag)
pcap_dump_flush((pcap_dumper_t *)user);