xref: /openbsd-src/usr.sbin/tcpdump/print-ipsec.c (revision b725ae7711052a2233e31a66fefb8a752c388d7a)
1 /*	$OpenBSD: print-ipsec.c,v 1.8 2003/07/17 08:45:37 markus Exp $	*/
2 
3 /*
4  * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that: (1) source code distributions
9  * retain the above copyright notice and this paragraph in its entirety, (2)
10  * distributions including binary code include the above copyright notice and
11  * this paragraph in its entirety in the documentation or other materials
12  * provided with the distribution, and (3) all advertising materials mentioning
13  * features or use of this software display the following acknowledgement:
14  * ``This product includes software developed by the University of California,
15  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
16  * the University nor the names of its contributors may be used to endorse
17  * or promote products derived from this software without specific prior
18  * written permission.
19  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
20  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22  *
23  * Format and print IPsec (ESP/AH) packets.
24  *      By Tero Kivinen <kivinen@ssh.fi>, Tero Mononen <tmo@ssh.fi>,
25  *         Tatu Ylonen <ylo@ssh.fi> and Timo J. Rinne <tri@ssh.fi>
26  *         in co-operation with SSH Communications Security, Espoo, Finland
27  */
28 
29 #ifndef lint
30 static const char rcsid[] =
31     "@(#) $Header: /home/cvs/src/usr.sbin/tcpdump/print-ipsec.c,v 1.8 2003/07/17 08:45:37 markus Exp $ (XXX)";
32 #endif
33 
34 #include <sys/param.h>
35 #include <sys/time.h>
36 #include <sys/socket.h>
37 
38 #include <netinet/in.h>
39 #include <netinet/in_systm.h>
40 #include <netinet/ip.h>
41 #include <netinet/ip_var.h>
42 #include <netinet/udp.h>
43 #include <netinet/udp_var.h>
44 #include <netinet/tcp.h>
45 #include <netinet/tcpip.h>
46 
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 
52 #include "addrtoname.h"
53 #include "interface.h"
54 #include "extract.h"		    /* must come after interface.h */
55 
56 #include <openssl/evp.h>
57 #include <ctype.h>
58 
59 /*
60  * IPsec/ESP header
61  */
62 struct esp_hdr {
63 	u_int esp_spi;
64 	u_int esp_seq;
65 };
66 
67 static int espinit = 0;
68 static int espauthlen = 12;
69 static EVP_CIPHER_CTX ctx;
70 
71 int
72 esp_init (char *espspec)
73 {
74 	const EVP_CIPHER *evp;
75 	char *p, *espkey, s[3], name[1024];
76 	u_char *key;
77 	int i, klen, len;
78 
79 	evp = EVP_aes_128_cbc();	/* default */
80 	espkey = espspec;
81 	if ((p = strchr(espspec, ':')) != NULL) {
82 		len = p - espspec;
83 		if (len >= sizeof(name))
84 			error("espalg too long");
85 		memcpy(name, espspec, len);
86 		name[len] = '\0';
87 		espkey = p + 1;
88 
89 		/* strip auth alg */
90 		espauthlen = 0;
91 		if ((p = strstr(name, "-hmac96")) != NULL) {
92 			espauthlen = 12;
93 			*p = '\0';
94 		}
95 		OpenSSL_add_all_algorithms();
96 		if ((evp = EVP_get_cipherbyname(name)) == NULL)
97 			error("espalg `%s' not supported", name);
98 	}
99 	klen = EVP_CIPHER_key_length(evp);
100 	if (strlen(espkey) != klen * 2)
101 		error("espkey size mismatch, %d bytes needed", klen);
102 	if ((key = malloc(klen)) == NULL)
103 		error("malloc failed");
104 	for (i = 0; i < klen; i++) {
105 		s[0] = espkey[2*i];
106 		s[1] = espkey[2*i + 1];
107 		s[2] = 0;
108 		if (!isxdigit(s[0]) || !isxdigit(s[1]))
109 			error("espkey must be specified in hex");
110 		key[i] = strtoul(s, NULL, 16);
111 	}
112 	EVP_CIPHER_CTX_init(&ctx);
113 	if (EVP_CipherInit(&ctx, evp, key, NULL, 0) < 0) {
114 		free(key);
115 		error("espkey init failed");
116 	}
117 	free(key);
118 	espinit = 1;
119 	return (0);
120 }
121 
122 void
123 esp_decrypt (const u_char *bp, u_int len, const u_char *bp2)
124 {
125 	const struct ip *ip;
126 	u_char *data, pad, nh;
127 	int blocksz;
128 
129 	ip = (const struct ip *)bp2;
130 
131 	blocksz = EVP_CIPHER_CTX_block_size(&ctx);
132 
133 	/* Skip fragments and short packets */
134 	if (ntohs(ip->ip_off) & 0x3fff)
135 		return;
136 	if (snapend - bp < len) {
137 		printf(" [|esp]");
138 		return;
139 	}
140 	/*
141 	 * Skip ESP header and ignore authentication trailer.
142 	 * For decryption we need at least 2 blocks: IV and
143 	 * one cipher block.
144 	 */
145 	if (len < sizeof(struct esp_hdr) + espauthlen + 2 * blocksz) {
146 		printf(" [|esp]");
147 		return;
148 	}
149 
150 	data = (char *)bp;
151 	data += sizeof(struct esp_hdr);
152 	len -= sizeof(struct esp_hdr);
153 	len -= espauthlen;
154 
155 	/* the first block contains the IV */
156 	EVP_CipherInit(&ctx, NULL, NULL, data, 0);
157 	len -= blocksz;
158 	data += blocksz;
159 
160 	/* decrypt remaining payload */
161 	EVP_Cipher(&ctx, data, data, len);
162 
163 	nh = data[len - 1];
164 	pad = data[len - 2];
165 
166 	/* verify padding */
167 	if (pad + 2 > len)
168 		return;
169 	if (data[len - 3]  != pad)
170 		return;
171 	if (vflag > 1)
172 		printf(" pad %d", pad);
173 	len -= (pad + 2);
174 	printf(": ");
175 	switch (nh) {
176 	case IPPROTO_TCP:
177 		tcp_print(data, len, bp2);
178 		break;
179 	case IPPROTO_UDP:
180 		udp_print(data, len, bp2);
181 		break;
182 	case IPPROTO_IPV6:
183 		ip6_print(data, len);
184 		break;
185 	case IPPROTO_IPV4:
186 		ip_print(data, len);
187 		break;
188 	case IPPROTO_ICMP:
189 		icmp_print(data, bp2);
190 		break;
191 	default:
192 		printf("ip-proto-%d %d", nh, len);
193 		break;
194 	}
195 	if (vflag)
196 		printf(" (esp)");
197 }
198 
199 void
200 esp_print (register const u_char *bp, register u_int len,
201 	   register const u_char *bp2)
202 {
203 	const struct ip *ip;
204 	const struct esp_hdr *esp;
205 	u_int plen = len;
206 
207 	ip = (const struct ip *)bp2;
208 
209 	printf("esp %s > %s",
210 	    ipaddr_string(&ip->ip_src), ipaddr_string(&ip->ip_dst));
211 
212 	if (plen < sizeof(struct esp_hdr)) {
213 		printf("[|esp]");
214 		return;
215 	}
216 	esp = (const struct esp_hdr *)bp;
217 
218 	printf(" spi 0x%08X seq %d len %d",
219 	    ntohl(esp->esp_spi), ntohl(esp->esp_seq), len);
220 
221 	if (espinit)
222 		esp_decrypt(bp, len, bp2);
223 }
224 
225 /*
226  * IPsec/AH header
227  */
228 struct ah_hdr {
229 	u_char  ah_nxt_hdr;
230 	u_char  ah_pl_len;
231 	u_short ah_reserved;
232 	u_int   ah_spi;
233 	u_int   ah_seq;
234 };
235 
236 void
237 ah_print (register const u_char *bp, register u_int len,
238 	  register const u_char *bp2)
239 {
240 	const struct ip *ip;
241 	const struct ah_hdr *ah;
242 	u_int pl_len = len;
243 
244 	ip = (const struct ip *)bp2;
245 
246 	printf("ah %s > %s",
247 	    ipaddr_string(&ip->ip_src), ipaddr_string(&ip->ip_dst));
248 
249 	if (pl_len < sizeof(struct ah_hdr)) {
250 		printf("[|ah]");
251 		return;
252 	}
253 	ah = (const struct ah_hdr *)bp;
254 
255 	printf(" spi 0x%08X seq %d len %d",
256 	    ntohl(ah->ah_spi), ntohl(ah->ah_seq), len);
257 
258 	if (vflag) {
259 	        (void)printf("\n\t[ ");
260 
261 	        pl_len = (ah->ah_pl_len + 2) << 2; /* RFC2402, sec 2.2 */
262 
263 		if (len - pl_len <= 0) {
264 		        (void)printf("truncated");
265 			goto out;
266 		}
267 
268 		switch (ah->ah_nxt_hdr) {
269 
270 		case IPPROTO_IPIP: /* Tunnel Mode, IP-in-IP */
271 		        ip_print(bp + pl_len, len - pl_len);
272 			break;
273 
274 	        case IPPROTO_ICMP: /* From here and down; Transport mode */
275 		        icmp_print(bp + pl_len, (const u_char *) ip);
276 			break;
277 
278 	        case IPPROTO_TCP:
279 		        tcp_print(bp + pl_len, len - pl_len,
280 				  (const u_char *) ip);
281 			break;
282 
283 	        case IPPROTO_UDP:
284 		        udp_print(bp + pl_len, len - pl_len,
285 				  (const u_char *) ip);
286 			break;
287 
288 		case IPPROTO_ESP:
289 		        esp_print(bp + pl_len, len - pl_len,
290 				  (const u_char *) ip);
291 			break;
292 
293 		case IPPROTO_AH:
294 		        ah_print(bp + pl_len, len - pl_len,
295 				 (const u_char *) ip);
296 			break;
297 
298 		default:
299 		        (void)printf("ip-proto-%d len %d", ah->ah_nxt_hdr,
300 				     len - pl_len);
301 		}
302 out:
303 		(void)printf(" ]");
304 	}
305 
306 }
307 
308 struct ipcomp_hdr {
309 	u_char  ipcomp_nxt_hdr;
310 	u_char	ipcomp_flags;
311 	u_short	ipcomp_cpi;
312 };
313 
314 void
315 ipcomp_print (register const u_char *bp, register u_int len,
316 	  register const u_char *bp2)
317 {
318 	const struct ip *ip;
319 	const struct ipcomp_hdr *ipc;
320 	u_int plen = len;
321 
322 	ip = (const struct ip *)bp2;
323 
324 	printf("ipcomp %s > %s",
325 	    ipaddr_string(&ip->ip_src), ipaddr_string(&ip->ip_dst));
326 
327 	if (plen < sizeof(struct ipcomp_hdr)) {
328 		printf("[|ipcomp]");
329 		return;
330 	}
331 	ipc = (const struct ipcomp_hdr *)bp;
332 
333 	printf(" cpi 0x%04X flags %x next %x",
334 	    ntohs(ipc->ipcomp_cpi), ipc->ipcomp_flags, ipc->ipcomp_nxt_hdr);
335 }
336