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