xref: /openbsd-src/usr.sbin/tcpdump/print-ipsec.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: print-ipsec.c,v 1.13 2009/02/18 01:53:06 mcbride 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     "@(#) $Id: print-ipsec.c,v 1.13 2009/02/18 01:53:06 mcbride 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 #ifdef INET6
53 #include <netinet/ip6.h>
54 #endif
55 
56 #include "addrtoname.h"
57 #include "interface.h"
58 #include "extract.h"		    /* must come after interface.h */
59 
60 #include <openssl/evp.h>
61 #include <ctype.h>
62 
63 /*
64  * IPsec/ESP header
65  */
66 struct esp_hdr {
67 	u_int esp_spi;
68 	u_int esp_seq;
69 };
70 
71 static int espinit = 0;
72 static int espauthlen = 12;
73 static EVP_CIPHER_CTX ctx;
74 
75 int
76 esp_init (char *espspec)
77 {
78 	const EVP_CIPHER *evp;
79 	char *p, *espkey, s[3], name[1024];
80 	u_char *key;
81 	int i, klen, len;
82 
83 	evp = EVP_aes_128_cbc();	/* default */
84 	espkey = espspec;
85 	if ((p = strchr(espspec, ':')) != NULL) {
86 		len = p - espspec;
87 		if (len >= sizeof(name))
88 			error("espalg too long");
89 		memcpy(name, espspec, len);
90 		name[len] = '\0';
91 		espkey = p + 1;
92 
93 		/* strip auth alg */
94 		espauthlen = 0;
95 		if ((p = strstr(name, "-hmac96")) != NULL) {
96 			espauthlen = 12;
97 			*p = '\0';
98 		}
99 		OpenSSL_add_all_algorithms();
100 		if ((evp = EVP_get_cipherbyname(name)) == NULL)
101 			error("espalg `%s' not supported", name);
102 	}
103 	klen = EVP_CIPHER_key_length(evp);
104 	if (strlen(espkey) != klen * 2)
105 		error("espkey size mismatch, %d bytes needed", klen);
106 	if ((key = malloc(klen)) == NULL)
107 		error("malloc failed");
108 	for (i = 0; i < klen; i++) {
109 		s[0] = espkey[2*i];
110 		s[1] = espkey[2*i + 1];
111 		s[2] = 0;
112 		if (!isxdigit(s[0]) || !isxdigit(s[1]))
113 			error("espkey must be specified in hex");
114 		key[i] = strtoul(s, NULL, 16);
115 	}
116 	EVP_CIPHER_CTX_init(&ctx);
117 	if (EVP_CipherInit(&ctx, evp, key, NULL, 0) < 0) {
118 		free(key);
119 		error("espkey init failed");
120 	}
121 	free(key);
122 	espinit = 1;
123 	return (0);
124 }
125 
126 void
127 esp_decrypt (const u_char *bp, u_int len, const u_char *bp2)
128 {
129 	const struct ip *ip;
130 	u_char *data, pad, nh;
131 	int blocksz;
132 
133 	ip = (const struct ip *)bp2;
134 
135 	blocksz = EVP_CIPHER_CTX_block_size(&ctx);
136 
137 	/* Skip fragments and short packets */
138 	if (ntohs(ip->ip_off) & 0x3fff)
139 		return;
140 	if (snapend - bp < len) {
141 		printf(" [|esp]");
142 		return;
143 	}
144 	/*
145 	 * Skip ESP header and ignore authentication trailer.
146 	 * For decryption we need at least 2 blocks: IV and
147 	 * one cipher block.
148 	 */
149 	if (len < sizeof(struct esp_hdr) + espauthlen + 2 * blocksz) {
150 		printf(" [|esp]");
151 		return;
152 	}
153 
154 	data = (char *)bp;
155 	data += sizeof(struct esp_hdr);
156 	len -= sizeof(struct esp_hdr);
157 	len -= espauthlen;
158 
159 	/* the first block contains the IV */
160 	EVP_CipherInit(&ctx, NULL, NULL, data, 0);
161 	len -= blocksz;
162 	data += blocksz;
163 
164 	/* decrypt remaining payload */
165 	EVP_Cipher(&ctx, data, data, len);
166 
167 	nh = data[len - 1];
168 	pad = data[len - 2];
169 
170 	/* verify padding */
171 	if (pad + 2 > len)
172 		return;
173 	if (data[len - 3]  != pad)
174 		return;
175 	if (vflag > 1)
176 		printf(" pad %d", pad);
177 	len -= (pad + 2);
178 	printf(": ");
179 	switch (nh) {
180 	case IPPROTO_TCP:
181 		tcp_print(data, len, bp2);
182 		break;
183 	case IPPROTO_UDP:
184 		udp_print(data, len, bp2);
185 		break;
186 	case IPPROTO_IPV6:
187 		ip6_print(data, len);
188 		break;
189 	case IPPROTO_IPV4:
190 		ip_print(data, len);
191 		break;
192 	case IPPROTO_ICMP:
193 		icmp_print(data, bp2);
194 		break;
195 	case IPPROTO_ICMPV6:
196 		icmp6_print(data, bp2);
197 		break;
198 	default:
199 		printf("ip-proto-%d %d", nh, len);
200 		break;
201 	}
202 	if (vflag)
203 		printf(" (esp)");
204 }
205 
206 void
207 esp_print (register const u_char *bp, register u_int len,
208 	   register const u_char *bp2)
209 {
210 	const struct ip *ip;
211 	const struct esp_hdr *esp;
212 	u_int plen = len;
213 #ifdef INET6
214 	const struct ip6_hdr *ip6;
215 #endif
216 
217 	ip = (const struct ip *)bp2;
218 #ifdef INET6
219 	if (ip->ip_v == 6) {
220 		ip6 = (const struct ip6_hdr *)bp2;
221 		printf("esp %s > %s", ip6addr_string(&ip6->ip6_src),
222 		    ip6addr_string(&ip6->ip6_dst));
223 	} else
224 #endif
225 	{
226 		printf("esp %s > %s",
227 	    	    ipaddr_string(&ip->ip_src), ipaddr_string(&ip->ip_dst));
228 	}
229 
230 	if (plen < sizeof(struct esp_hdr)) {
231 		printf("[|esp]");
232 		return;
233 	}
234 	esp = (const struct esp_hdr *)bp;
235 
236 	printf(" spi 0x%08x seq %d len %d",
237 	    ntohl(esp->esp_spi), ntohl(esp->esp_seq), len);
238 
239 	if (espinit)
240 		esp_decrypt(bp, len, bp2);
241 }
242 
243 /*
244  * IPsec/AH header
245  */
246 struct ah_hdr {
247 	u_char  ah_nxt_hdr;
248 	u_char  ah_pl_len;
249 	u_short ah_reserved;
250 	u_int   ah_spi;
251 	u_int   ah_seq;
252 };
253 
254 void
255 ah_print (register const u_char *bp, register u_int len,
256 	  register const u_char *bp2)
257 {
258 	const struct ip *ip;
259 	const struct ah_hdr *ah;
260 	u_int pl_len = len;
261 #ifdef INET6
262 	const struct ip6_hdr *ip6;
263 #endif
264 
265 	ip = (const struct ip *)bp2;
266 #ifdef INET6
267 	if (ip->ip_v == 6) {
268 		ip6 = (const struct ip6_hdr *)bp2;
269 		printf("ah %s > %s", ip6addr_string(&ip6->ip6_src),
270 		    ip6addr_string(&ip6->ip6_dst));
271 	} else
272 #endif
273 	{
274 		printf("ah %s > %s",
275 	    	    ipaddr_string(&ip->ip_src), ipaddr_string(&ip->ip_dst));
276 	}
277 
278 	if (pl_len < sizeof(struct ah_hdr)) {
279 		printf("[|ah]");
280 		return;
281 	}
282 	ah = (const struct ah_hdr *)bp;
283 
284 	printf(" spi 0x%08X seq %d len %d",
285 	    ntohl(ah->ah_spi), ntohl(ah->ah_seq), len);
286 
287 	if (vflag) {
288 	        (void)printf("\n\t[ ");
289 
290 	        pl_len = (ah->ah_pl_len + 2) << 2; /* RFC2402, sec 2.2 */
291 
292 		if (len <= pl_len) {
293 		        (void)printf("truncated");
294 			goto out;
295 		}
296 
297 		switch (ah->ah_nxt_hdr) {
298 
299 		case IPPROTO_IPIP: /* Tunnel Mode, IP-in-IP */
300 		        ip_print(bp + pl_len, len - pl_len);
301 			break;
302 
303 	        case IPPROTO_ICMP: /* From here and down; Transport mode */
304 		        icmp_print(bp + pl_len, (const u_char *) ip);
305 			break;
306 
307 	        case IPPROTO_ICMPV6:
308 		        icmp6_print(bp + pl_len, (const u_char *) ip);
309 			break;
310 
311 	        case IPPROTO_TCP:
312 		        tcp_print(bp + pl_len, len - pl_len,
313 				  (const u_char *) ip);
314 			break;
315 
316 	        case IPPROTO_UDP:
317 		        udp_print(bp + pl_len, len - pl_len,
318 				  (const u_char *) ip);
319 			break;
320 
321 		case IPPROTO_ESP:
322 		        esp_print(bp + pl_len, len - pl_len,
323 				  (const u_char *) ip);
324 			break;
325 
326 		case IPPROTO_AH:
327 		        ah_print(bp + pl_len, len - pl_len,
328 				 (const u_char *) ip);
329 			break;
330 
331 		default:
332 		        (void)printf("ip-proto-%d len %d", ah->ah_nxt_hdr,
333 				     len - pl_len);
334 		}
335 out:
336 		(void)printf(" ]");
337 	}
338 
339 }
340 
341 struct ipcomp_hdr {
342 	u_char  ipcomp_nxt_hdr;
343 	u_char	ipcomp_flags;
344 	u_short	ipcomp_cpi;
345 };
346 
347 void
348 ipcomp_print (register const u_char *bp, register u_int len,
349 	  register const u_char *bp2)
350 {
351 	const struct ip *ip;
352 	const struct ipcomp_hdr *ipc;
353 	u_int plen = len;
354 
355 	ip = (const struct ip *)bp2;
356 
357 	printf("ipcomp %s > %s",
358 	    ipaddr_string(&ip->ip_src), ipaddr_string(&ip->ip_dst));
359 
360 	if (plen < sizeof(struct ipcomp_hdr)) {
361 		printf("[|ipcomp]");
362 		return;
363 	}
364 	ipc = (const struct ipcomp_hdr *)bp;
365 
366 	printf(" cpi 0x%04X flags %x next %x",
367 	    ntohs(ipc->ipcomp_cpi), ipc->ipcomp_flags, ipc->ipcomp_nxt_hdr);
368 }
369