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