Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Patch] Add support for decrypting ESP packets in packet_dump mode #279

Open
guyharris opened this issue Apr 16, 2013 · 2 comments
Open

Comments

@guyharris
Copy link
Member

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

@guyharris
Copy link
Member Author

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.

@infrastation
Copy link
Member

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);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants