xref: /dragonfly/contrib/tcpdump/print-esp.c (revision a68e0df0)
1 /*	$NetBSD: print-ah.c,v 1.4 1996/05/20 00:41:16 fvdl Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994
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 
24 #ifndef lint
25 static const char rcsid[] _U_ =
26     "@(#) $Header: /tcpdump/master/tcpdump/print-esp.c,v 1.56 2005-04-21 06:44:40 guy Exp $ (LBL)";
27 #endif
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include <string.h>
34 
35 #include <tcpdump-stdinc.h>
36 
37 #include <stdlib.h>
38 
39 #ifdef HAVE_LIBCRYPTO
40 #ifdef HAVE_OPENSSL_EVP_H
41 #include <openssl/evp.h>
42 #endif
43 #endif
44 
45 #include <stdio.h>
46 
47 #include "ip.h"
48 #include "esp.h"
49 #ifdef INET6
50 #include "ip6.h"
51 #endif
52 
53 #include "netdissect.h"
54 #include "addrtoname.h"
55 #include "extract.h"
56 
57 #ifndef HAVE_SOCKADDR_STORAGE
58 #ifdef INET6
59 struct sockaddr_storage {
60 	union {
61 		struct sockaddr_in sin;
62 		struct sockaddr_in6 sin6;
63 	} un;
64 };
65 #else
66 #define sockaddr_storage sockaddr
67 #endif
68 #endif /* HAVE_SOCKADDR_STORAGE */
69 
70 #ifdef HAVE_LIBCRYPTO
71 struct sa_list {
72 	struct sa_list	*next;
73 	struct sockaddr_storage daddr;
74 	u_int32_t	spi;
75 	const EVP_CIPHER *evp;
76 	int		ivlen;
77 	int		authlen;
78 	u_char		secret[256];  /* is that big enough for all secrets? */
79 	int		secretlen;
80 };
81 
82 static void esp_print_addsa(netdissect_options *ndo,
83 			    struct sa_list *sa, int sa_def)
84 {
85 	/* copy the "sa" */
86 
87 	struct sa_list *nsa;
88 
89 	nsa = (struct sa_list *)malloc(sizeof(struct sa_list));
90 	if (nsa == NULL)
91 		(*ndo->ndo_error)(ndo, "ran out of memory to allocate sa structure");
92 
93 	*nsa = *sa;
94 
95 	if (sa_def)
96 		ndo->ndo_sa_default = nsa;
97 
98 	nsa->next = ndo->ndo_sa_list_head;
99 	ndo->ndo_sa_list_head = nsa;
100 }
101 
102 
103 static u_int hexdigit(netdissect_options *ndo, char hex)
104 {
105 	if (hex >= '0' && hex <= '9')
106 		return (hex - '0');
107 	else if (hex >= 'A' && hex <= 'F')
108 		return (hex - 'A' + 10);
109 	else if (hex >= 'a' && hex <= 'f')
110 		return (hex - 'a' + 10);
111 	else {
112 		(*ndo->ndo_error)(ndo, "invalid hex digit %c in espsecret\n", hex);
113 		return 0;
114 	}
115 }
116 
117 static u_int hex2byte(netdissect_options *ndo, char *hexstring)
118 {
119 	u_int byte;
120 
121 	byte = (hexdigit(ndo, hexstring[0]) << 4) + hexdigit(ndo, hexstring[1]);
122 	return byte;
123 }
124 
125 /*
126  * decode the form:    SPINUM@IP <tab> ALGONAME:0xsecret
127  *
128  * special form: file /name
129  * causes us to go read from this file instead.
130  *
131  */
132 static void esp_print_decode_onesecret(netdissect_options *ndo, char *line)
133 {
134 	struct sa_list sa1;
135 	int sa_def;
136 
137 	char *spikey;
138 	char *decode;
139 
140 	spikey = strsep(&line, " \t");
141 	sa_def = 0;
142 	memset(&sa1, 0, sizeof(struct sa_list));
143 
144 	/* if there is only one token, then it is an algo:key token */
145 	if (line == NULL) {
146 		decode = spikey;
147 		spikey = NULL;
148 		/* memset(&sa1.daddr, 0, sizeof(sa1.daddr)); */
149 		/* sa1.spi = 0; */
150 		sa_def    = 1;
151 	} else
152 		decode = line;
153 
154 	if (spikey && strcasecmp(spikey, "file") == 0) {
155 		/* open file and read it */
156 		FILE *secretfile;
157 		char  fileline[1024];
158 		char  *nl;
159 
160 		secretfile = fopen(line, FOPEN_READ_TXT);
161 		if (secretfile == NULL) {
162 			perror(line);
163 			exit(3);
164 		}
165 
166 		while (fgets(fileline, sizeof(fileline)-1, secretfile) != NULL) {
167 			/* remove newline from the line */
168 			nl = strchr(fileline, '\n');
169 			if (nl)
170 				*nl = '\0';
171 			if (fileline[0] == '#') continue;
172 			if (fileline[0] == '\0') continue;
173 
174 			esp_print_decode_onesecret(ndo, fileline);
175 		}
176 		fclose(secretfile);
177 
178 		return;
179 	}
180 
181 	if (spikey) {
182 		char *spistr, *foo;
183 		u_int32_t spino;
184 		struct sockaddr_in *sin;
185 #ifdef INET6
186 		struct sockaddr_in6 *sin6;
187 #endif
188 
189 		spistr = strsep(&spikey, "@");
190 
191 		spino = strtoul(spistr, &foo, 0);
192 		if (spistr == foo || !spikey) {
193 			(*ndo->ndo_warning)(ndo, "print_esp: failed to decode spi# %s\n", foo);
194 			return;
195 		}
196 
197 		sa1.spi = spino;
198 
199 		sin = (struct sockaddr_in *)&sa1.daddr;
200 #ifdef INET6
201 		sin6 = (struct sockaddr_in6 *)&sa1.daddr;
202 		if (inet_pton(AF_INET6, spikey, &sin6->sin6_addr) == 1) {
203 #ifdef HAVE_SOCKADDR_SA_LEN
204 			sin6->sin6_len = sizeof(struct sockaddr_in6);
205 #endif
206 			sin6->sin6_family = AF_INET6;
207 		} else
208 #endif
209 		if (inet_pton(AF_INET, spikey, &sin->sin_addr) == 1) {
210 #ifdef HAVE_SOCKADDR_SA_LEN
211 			sin->sin_len = sizeof(struct sockaddr_in);
212 #endif
213 			sin->sin_family = AF_INET;
214 		} else {
215 			(*ndo->ndo_warning)(ndo, "print_esp: can not decode IP# %s\n", spikey);
216 			return;
217 		}
218 	}
219 
220 	if (decode) {
221 		char *colon, *p;
222 		u_char espsecret_key[256];
223 		int len;
224 		size_t i;
225 		const EVP_CIPHER *evp;
226 		int authlen = 0;
227 
228 		/* skip any blank spaces */
229 		while (isspace((unsigned char)*decode))
230 			decode++;
231 
232 		colon = strchr(decode, ':');
233 		if (colon == NULL) {
234 			(*ndo->ndo_warning)(ndo, "failed to decode espsecret: %s\n", decode);
235 			return;
236 		}
237 		*colon = '\0';
238 
239 		len = colon - decode;
240 		if (strlen(decode) > strlen("-hmac96") &&
241 		    !strcmp(decode + strlen(decode) - strlen("-hmac96"),
242 		    "-hmac96")) {
243 			p = strstr(decode, "-hmac96");
244 			*p = '\0';
245 			authlen = 12;
246 		}
247 		if (strlen(decode) > strlen("-cbc") &&
248 		    !strcmp(decode + strlen(decode) - strlen("-cbc"), "-cbc")) {
249 			p = strstr(decode, "-cbc");
250 			*p = '\0';
251 		}
252 		evp = EVP_get_cipherbyname(decode);
253 		if (!evp) {
254 			(*ndo->ndo_warning)(ndo, "failed to find cipher algo %s\n", decode);
255 			sa1.evp = NULL;
256 			sa1.authlen = 0;
257 			sa1.ivlen = 0;
258 			return;
259 		}
260 
261 		sa1.evp = evp;
262 		sa1.authlen = authlen;
263 		sa1.ivlen = EVP_CIPHER_iv_length(evp);
264 
265 		colon++;
266 		if (colon[0] == '0' && colon[1] == 'x') {
267 			/* decode some hex! */
268 			colon += 2;
269 			len = strlen(colon) / 2;
270 
271 			if (len > 256) {
272 				(*ndo->ndo_warning)(ndo, "secret is too big: %d\n", len);
273 				return;
274 			}
275 
276 			i = 0;
277 			while (colon[0] != '\0' && colon[1]!='\0') {
278 				espsecret_key[i] = hex2byte(ndo, colon);
279 				colon += 2;
280 				i++;
281 			}
282 
283 			memcpy(sa1.secret, espsecret_key, i);
284 			sa1.secretlen = i;
285 		} else {
286 			i = strlen(colon);
287 
288 			if (i < sizeof(sa1.secret)) {
289 				memcpy(sa1.secret, colon, i);
290 				sa1.secretlen = i;
291 			} else {
292 				memcpy(sa1.secret, colon, sizeof(sa1.secret));
293 				sa1.secretlen = sizeof(sa1.secret);
294 			}
295 		}
296 	}
297 
298 	esp_print_addsa(ndo, &sa1, sa_def);
299 }
300 
301 static void esp_print_decodesecret(netdissect_options *ndo)
302 {
303 	char *line;
304 	char *p;
305 
306 	p = ndo->ndo_espsecret;
307 
308 	while (ndo->ndo_espsecret && ndo->ndo_espsecret[0] != '\0') {
309 		/* pick out the first line or first thing until a comma */
310 		if ((line = strsep(&ndo->ndo_espsecret, "\n,")) == NULL) {
311 			line = ndo->ndo_espsecret;
312 			ndo->ndo_espsecret = NULL;
313 		}
314 
315 		esp_print_decode_onesecret(ndo, line);
316 	}
317 }
318 
319 static void esp_init(netdissect_options *ndo _U_)
320 {
321 
322 	OpenSSL_add_all_algorithms();
323 	EVP_add_cipher_alias(SN_des_ede3_cbc, "3des");
324 }
325 #endif
326 
327 int
328 esp_print(netdissect_options *ndo,
329 	  const u_char *bp, const int length, const u_char *bp2
330 #ifndef HAVE_LIBCRYPTO
331 	_U_
332 #endif
333 	,
334 	int *nhdr
335 #ifndef HAVE_LIBCRYPTO
336 	_U_
337 #endif
338 	,
339 	int *padlen
340 #ifndef HAVE_LIBCRYPTO
341 	_U_
342 #endif
343 	)
344 {
345 	register const struct newesp *esp;
346 	register const u_char *ep;
347 #ifdef HAVE_LIBCRYPTO
348 	struct ip *ip;
349 	struct sa_list *sa = NULL;
350 	int espsecret_keylen;
351 #ifdef INET6
352 	struct ip6_hdr *ip6 = NULL;
353 #endif
354 	int advance;
355 	int len;
356 	u_char *secret;
357 	int ivlen = 0;
358 	u_char *ivoff;
359 	u_char *p;
360 	EVP_CIPHER_CTX ctx;
361 	int blocksz;
362 	static int initialized = 0;
363 #endif
364 
365 	esp = (struct newesp *)bp;
366 
367 #ifdef HAVE_LIBCRYPTO
368 	secret = NULL;
369 	advance = 0;
370 
371 	if (!initialized) {
372 		esp_init(ndo);
373 		initialized = 1;
374 	}
375 #endif
376 
377 #if 0
378 	/* keep secret out of a register */
379 	p = (u_char *)&secret;
380 #endif
381 
382 	/* 'ep' points to the end of available data. */
383 	ep = ndo->ndo_snapend;
384 
385 	if ((u_char *)(esp + 1) >= ep) {
386 		fputs("[|ESP]", stdout);
387 		goto fail;
388 	}
389 	(*ndo->ndo_printf)(ndo, "ESP(spi=0x%08x", EXTRACT_32BITS(&esp->esp_spi));
390 	(*ndo->ndo_printf)(ndo, ",seq=0x%x)", EXTRACT_32BITS(&esp->esp_seq));
391         (*ndo->ndo_printf)(ndo, ", length %u", length);
392 
393 #ifndef HAVE_LIBCRYPTO
394 	goto fail;
395 #else
396 	/* initiailize SAs */
397 	if (ndo->ndo_sa_list_head == NULL) {
398 		if (!ndo->ndo_espsecret)
399 			goto fail;
400 
401 		esp_print_decodesecret(ndo);
402 	}
403 
404 	if (ndo->ndo_sa_list_head == NULL)
405 		goto fail;
406 
407 	ip = (struct ip *)bp2;
408 	switch (IP_V(ip)) {
409 #ifdef INET6
410 	case 6:
411 		ip6 = (struct ip6_hdr *)bp2;
412 		/* we do not attempt to decrypt jumbograms */
413 		if (!EXTRACT_16BITS(&ip6->ip6_plen))
414 			goto fail;
415 		/* if we can't get nexthdr, we do not need to decrypt it */
416 		len = sizeof(struct ip6_hdr) + EXTRACT_16BITS(&ip6->ip6_plen);
417 
418 		/* see if we can find the SA, and if so, decode it */
419 		for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) {
420 			struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa->daddr;
421 			if (sa->spi == ntohl(esp->esp_spi) &&
422 			    sin6->sin6_family == AF_INET6 &&
423 			    memcmp(&sin6->sin6_addr, &ip6->ip6_dst,
424 				   sizeof(struct in6_addr)) == 0) {
425 				break;
426 			}
427 		}
428 		break;
429 #endif /*INET6*/
430 	case 4:
431 		/* nexthdr & padding are in the last fragment */
432 		if (EXTRACT_16BITS(&ip->ip_off) & IP_MF)
433 			goto fail;
434 		len = EXTRACT_16BITS(&ip->ip_len);
435 
436 		/* see if we can find the SA, and if so, decode it */
437 		for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) {
438 			struct sockaddr_in *sin = (struct sockaddr_in *)&sa->daddr;
439 			if (sa->spi == ntohl(esp->esp_spi) &&
440 			    sin->sin_family == AF_INET &&
441 			    sin->sin_addr.s_addr == ip->ip_dst.s_addr) {
442 				break;
443 			}
444 		}
445 		break;
446 	default:
447 		goto fail;
448 	}
449 
450 	/* if we didn't find the specific one, then look for
451 	 * an unspecified one.
452 	 */
453 	if (sa == NULL)
454 		sa = ndo->ndo_sa_default;
455 
456 	/* if not found fail */
457 	if (sa == NULL)
458 		goto fail;
459 
460 	/* if we can't get nexthdr, we do not need to decrypt it */
461 	if (ep - bp2 < len)
462 		goto fail;
463 	if (ep - bp2 > len) {
464 		/* FCS included at end of frame (NetBSD 1.6 or later) */
465 		ep = bp2 + len;
466 	}
467 
468 	ivoff = (u_char *)(esp + 1) + 0;
469 	ivlen = sa->ivlen;
470 	secret = sa->secret;
471 	espsecret_keylen = sa->secretlen;
472 	ep = ep - sa->authlen;
473 
474 	if (sa->evp) {
475 		memset(&ctx, 0, sizeof(ctx));
476 		if (EVP_CipherInit(&ctx, sa->evp, secret, NULL, 0) < 0)
477 			(*ndo->ndo_warning)(ndo, "espkey init failed");
478 
479 		blocksz = EVP_CIPHER_CTX_block_size(&ctx);
480 
481 		p = ivoff;
482 		EVP_CipherInit(&ctx, NULL, NULL, p, 0);
483 		EVP_Cipher(&ctx, p + ivlen, p + ivlen, ep - (p + ivlen));
484 		advance = ivoff - (u_char *)esp + ivlen;
485 	} else
486 		advance = sizeof(struct newesp);
487 
488 	/* sanity check for pad length */
489 	if (ep - bp < *(ep - 2))
490 		goto fail;
491 
492 	if (padlen)
493 		*padlen = *(ep - 2) + 2;
494 
495 	if (nhdr)
496 		*nhdr = *(ep - 1);
497 
498 	(ndo->ndo_printf)(ndo, ": ");
499 	return advance;
500 #endif
501 
502 fail:
503 	return -1;
504 }
505 
506 /*
507  * Local Variables:
508  * c-style: whitesmith
509  * c-basic-offset: 8
510  * End:
511  */
512