Home | History | Annotate | Download | only in tcpdump
      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.55.2.1 2005/04/21 06:44:57 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