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