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