tcpdump/print-esp.c

411 lines
8.8 KiB
C

/* $NetBSD: print-ah.c,v 1.4 1996/05/20 00:41:16 fvdl Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header: /tcpdump/master/tcpdump/print-esp.c,v 1.21 2002-01-23 05:23:16 itojun Exp $ (LBL)";
#endif
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#ifdef HAVE_LIBCRYPTO
#include <openssl/des.h>
#include <openssl/blowfish.h>
#ifdef HAVE_RC5_H
#include <openssl/rc5.h>
#endif
#ifdef HAVE_CAST_H
#include <openssl/cast.h>
#endif
#endif
#include <stdio.h>
#include "ip.h"
#include "esp.h"
#ifdef INET6
#include "ip6.h"
#endif
#define AVOID_CHURN 1
#include "interface.h"
#include "addrtoname.h"
static struct esp_algorithm *espsecret_xform=NULL; /* cache of decoded alg. */
static char *espsecret_key=NULL;
enum cipher { NONE,
DESCBC,
BLOWFISH,
RC5,
CAST128,
DES3CBC};
struct esp_algorithm {
char *name;
enum cipher algo;
int ivlen;
int authlen;
int replaysize;
};
struct esp_algorithm esp_xforms[]={
{"none", NONE, 0, 0, 0},
{"des-cbc", DESCBC, 8, 0, 0},
{"des-cbc-hmac96", DESCBC, 8, 12, 4},
{"blowfish-cbc", BLOWFISH,8, 0, 0},
{"blowfish-cbc-hmac96", BLOWFISH,8, 12, 4},
{"rc5-cbc", RC5, 8, 0, 0},
{"rc5-cbc-hmac96", RC5, 8, 12, 4},
{"cast128-cbc", CAST128, 8, 0, 0},
{"cast128-cbc-hmac96", CAST128, 8, 12, 4},
{"3des-cbc-hmac96", DES3CBC, 8, 12, 4},
};
static int hexdigit(char hex)
{
if(hex >= '0' && hex <= '9') {
return (hex - '0');
} else if(hex >= 'A' && hex <= 'F') {
return (hex - 'A' + 10);
} else if(hex >= 'a' && hex <= 'f') {
return (hex - 'a' + 10);
} else {
printf("invalid hex digit %c in espsecret\n", hex);
return 0;
}
}
static int hex2byte(char *hexstring)
{
int byte;
byte = (hexdigit(hexstring[0]) << 4) +
hexdigit(hexstring[1]);
return byte;
}
static void esp_print_decodesecret(void)
{
char *colon;
int len, i;
struct esp_algorithm *xf;
if(espsecret == NULL) {
/* set to NONE transform */
espsecret_xform = esp_xforms;
return;
}
if(espsecret_key != NULL) {
return;
}
colon = strchr(espsecret, ':');
if(colon == NULL) {
printf("failed to decode espsecret: %s\n",
espsecret);
/* set to NONE transform */
espsecret_xform = esp_xforms;
}
len = colon - espsecret;
xf = esp_xforms;
while(xf->name && strncasecmp(espsecret, xf->name, len)!=0) {
xf++;
}
if(xf->name == NULL) {
printf("failed to find cipher algo %s\n",
espsecret);
espsecret_xform = esp_xforms;
return;
}
espsecret_xform = xf;
colon++;
if(colon[0]=='0' && colon[1]=='x') {
/* decode some hex! */
colon+=2;
len = strlen(colon) / 2;
espsecret_key = (char *)malloc(len);
if(espsecret_key == NULL) {
fprintf(stderr, "%s: ran out of memory (%d) to allocate secret key\n",
program_name, len);
exit(2);
}
i = 0;
while(colon[0] != '\0' && colon[1]!='\0') {
espsecret_key[i]=hex2byte(colon);
colon+=2;
i++;
}
} else {
espsecret_key = colon;
}
}
int
esp_print(register const u_char *bp, register const u_char *bp2,
int *nhdr, int *padlen)
{
register const struct esp *esp;
register const u_char *ep;
u_int32_t spi;
struct ip *ip = NULL;
#ifdef INET6
struct ip6_hdr *ip6 = NULL;
#endif
int advance;
int len;
char *secret;
int ivlen = 0;
u_char *ivoff;
u_char *p;
esp = (struct esp *)bp;
spi = (u_int32_t)ntohl(esp->esp_spi);
secret = NULL;
#if 0
/* keep secret out of a register */
p = (u_char *)&secret;
#endif
/* 'ep' points to the end of available data. */
ep = snapend;
if ((u_char *)(esp + 1) >= ep - sizeof(struct esp)) {
fputs("[|ESP]", stdout);
goto fail;
}
printf("ESP(spi=0x%08x", spi);
printf(",seq=0x%x", (u_int32_t)ntohl(*(u_int32_t *)(esp + 1)));
printf(")");
/* if we don't have decryption key, we can't decrypt this packet. */
if (!espsecret)
goto fail;
if(!espsecret_xform) {
esp_print_decodesecret();
}
if(espsecret_xform->algo == NONE) {
goto fail;
}
ip = (struct ip *)bp2;
switch (IP_V(ip)) {
#ifdef INET6
case 6:
ip6 = (struct ip6_hdr *)bp2;
ip = NULL;
/* we do not attempt to decrypt jumbograms */
if (!ntohs(ip6->ip6_plen))
goto fail;
/* if we can't get nexthdr, we do not need to decrypt it */
len = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen);
break;
#endif /*INET6*/
case 4:
/* nexthdr & padding are in the last fragment */
if (ntohs(ip->ip_off) & IP_MF)
goto fail;
#ifdef INET6
ip6 = NULL;
#endif
len = ntohs(ip->ip_len);
break;
default:
goto fail;
}
/* if we can't get nexthdr, we do not need to decrypt it */
if (ep - bp2 < len)
goto fail;
ivoff = (u_char *)(esp + 1) + espsecret_xform->replaysize;
ivlen = espsecret_xform->ivlen;
secret = espsecret_key;
switch (espsecret_xform->algo) {
case DESCBC:
#ifdef HAVE_LIBCRYPTO
{
u_char iv[8];
des_key_schedule schedule;
switch (ivlen) {
case 4:
memcpy(iv, ivoff, 4);
memcpy(&iv[4], ivoff, 4);
p = &iv[4];
*p++ ^= 0xff;
*p++ ^= 0xff;
*p++ ^= 0xff;
*p++ ^= 0xff;
break;
case 8:
memcpy(iv, ivoff, 8);
break;
default:
goto fail;
}
des_check_key = 0;
des_set_key((void *)secret, schedule);
p = ivoff + ivlen;
des_cbc_encrypt((void *)p, (void *)p,
(long)(ep - p), schedule, (void *)iv,
DES_DECRYPT);
advance = ivoff - (u_char *)esp + ivlen;
break;
}
#else
goto fail;
#endif /*HAVE_LIBCRYPTO*/
case BLOWFISH:
#ifdef HAVE_LIBCRYPTO
{
BF_KEY schedule;
BF_set_key(&schedule, strlen(secret), secret);
p = ivoff + ivlen;
BF_cbc_encrypt(p, p, (long)(ep - p), &schedule, ivoff,
BF_DECRYPT);
advance = ivoff - (u_char *)esp + ivlen;
break;
}
#else
goto fail;
#endif /*HAVE_LIBCRYPTO*/
case RC5:
#if defined(HAVE_LIBCRYPTO) && defined(HAVE_RC5_H)
{
RC5_32_KEY schedule;
RC5_32_set_key(&schedule, strlen(secret), secret,
RC5_16_ROUNDS);
p = ivoff + ivlen;
RC5_32_cbc_encrypt(p, p, (long)(ep - p), &schedule, ivoff,
RC5_DECRYPT);
advance = ivoff - (u_char *)esp + ivlen;
break;
}
#else
goto fail;
#endif /*HAVE_LIBCRYPTO*/
case CAST128:
#if defined(HAVE_LIBCRYPTO) && defined(HAVE_CAST_H) && !defined(HAVE_BUGGY_CAST128)
{
CAST_KEY schedule;
CAST_set_key(&schedule, strlen(secret), secret);
p = ivoff + ivlen;
CAST_cbc_encrypt(p, p, (long)(ep - p), &schedule, ivoff,
CAST_DECRYPT);
advance = ivoff - (u_char *)esp + ivlen;
break;
}
#else
goto fail;
#endif /*HAVE_LIBCRYPTO*/
case DES3CBC:
#if defined(HAVE_LIBCRYPTO)
{
des_key_schedule s1, s2, s3;
des_check_key = 1;
des_set_odd_parity((void *)secret);
des_set_odd_parity((void *)secret+8);
des_set_odd_parity((void *)secret+16);
if(des_set_key((void *)secret, s1) != 0) {
printf("failed to schedule key 1\n");
}
if(des_set_key((void *)(secret + 8), s2)!=0) {
printf("failed to schedule key 2\n");
}
if(des_set_key((void *)(secret + 16), s3)!=0) {
printf("failed to schedule key 3\n");
}
p = ivoff + ivlen;
des_ede3_cbc_encrypt((void *)p, (void *)p,
(long)(ep - p),
s1, s2, s3,
(void *)ivoff, DES_DECRYPT);
advance = ivoff - (u_char *)esp + ivlen;
break;
}
#else
goto fail;
#endif /*HAVE_LIBCRYPTO*/
case NONE:
default:
advance = sizeof(struct esp) + espsecret_xform->replaysize;
break;
}
ep = ep - espsecret_xform->authlen;
/* sanity check for pad length */
if (ep - bp < *(ep - 2))
goto fail;
if (padlen)
*padlen = *(ep - 2) + 2;
if (nhdr)
*nhdr = *(ep - 1);
printf(": ");
return advance;
fail:
if (nhdr)
*nhdr = -1;
return 65536;
}