512 lines
11 KiB
C
512 lines
11 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[] _U_ =
|
|
"@(#) $Header: /tcpdump/master/tcpdump/print-esp.c,v 1.56 2005-04-21 06:44:40 guy Exp $ (LBL)";
|
|
#endif
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include <tcpdump-stdinc.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#ifdef HAVE_LIBCRYPTO
|
|
#ifdef HAVE_OPENSSL_EVP_H
|
|
#include <openssl/evp.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "ip.h"
|
|
#include "esp.h"
|
|
#ifdef INET6
|
|
#include "ip6.h"
|
|
#endif
|
|
|
|
#include "netdissect.h"
|
|
#include "addrtoname.h"
|
|
#include "extract.h"
|
|
|
|
#ifndef HAVE_SOCKADDR_STORAGE
|
|
#ifdef INET6
|
|
struct sockaddr_storage {
|
|
union {
|
|
struct sockaddr_in sin;
|
|
struct sockaddr_in6 sin6;
|
|
} un;
|
|
};
|
|
#else
|
|
#define sockaddr_storage sockaddr
|
|
#endif
|
|
#endif /* HAVE_SOCKADDR_STORAGE */
|
|
|
|
#ifdef HAVE_LIBCRYPTO
|
|
struct sa_list {
|
|
struct sa_list *next;
|
|
struct sockaddr_storage daddr;
|
|
u_int32_t spi;
|
|
const EVP_CIPHER *evp;
|
|
int ivlen;
|
|
int authlen;
|
|
u_char secret[256]; /* is that big enough for all secrets? */
|
|
int secretlen;
|
|
};
|
|
|
|
static void esp_print_addsa(netdissect_options *ndo,
|
|
struct sa_list *sa, int sa_def)
|
|
{
|
|
/* copy the "sa" */
|
|
|
|
struct sa_list *nsa;
|
|
|
|
nsa = (struct sa_list *)malloc(sizeof(struct sa_list));
|
|
if (nsa == NULL)
|
|
(*ndo->ndo_error)(ndo, "ran out of memory to allocate sa structure");
|
|
|
|
*nsa = *sa;
|
|
|
|
if (sa_def)
|
|
ndo->ndo_sa_default = nsa;
|
|
|
|
nsa->next = ndo->ndo_sa_list_head;
|
|
ndo->ndo_sa_list_head = nsa;
|
|
}
|
|
|
|
|
|
static u_int hexdigit(netdissect_options *ndo, 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 {
|
|
(*ndo->ndo_error)(ndo, "invalid hex digit %c in espsecret\n", hex);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static u_int hex2byte(netdissect_options *ndo, char *hexstring)
|
|
{
|
|
u_int byte;
|
|
|
|
byte = (hexdigit(ndo, hexstring[0]) << 4) + hexdigit(ndo, hexstring[1]);
|
|
return byte;
|
|
}
|
|
|
|
/*
|
|
* decode the form: SPINUM@IP <tab> ALGONAME:0xsecret
|
|
*
|
|
* special form: file /name
|
|
* causes us to go read from this file instead.
|
|
*
|
|
*/
|
|
static void esp_print_decode_onesecret(netdissect_options *ndo, char *line)
|
|
{
|
|
struct sa_list sa1;
|
|
int sa_def;
|
|
|
|
char *spikey;
|
|
char *decode;
|
|
|
|
spikey = strsep(&line, " \t");
|
|
sa_def = 0;
|
|
memset(&sa1, 0, sizeof(struct sa_list));
|
|
|
|
/* if there is only one token, then it is an algo:key token */
|
|
if (line == NULL) {
|
|
decode = spikey;
|
|
spikey = NULL;
|
|
/* memset(&sa1.daddr, 0, sizeof(sa1.daddr)); */
|
|
/* sa1.spi = 0; */
|
|
sa_def = 1;
|
|
} else
|
|
decode = line;
|
|
|
|
if (spikey && strcasecmp(spikey, "file") == 0) {
|
|
/* open file and read it */
|
|
FILE *secretfile;
|
|
char fileline[1024];
|
|
char *nl;
|
|
|
|
secretfile = fopen(line, FOPEN_READ_TXT);
|
|
if (secretfile == NULL) {
|
|
perror(line);
|
|
exit(3);
|
|
}
|
|
|
|
while (fgets(fileline, sizeof(fileline)-1, secretfile) != NULL) {
|
|
/* remove newline from the line */
|
|
nl = strchr(fileline, '\n');
|
|
if (nl)
|
|
*nl = '\0';
|
|
if (fileline[0] == '#') continue;
|
|
if (fileline[0] == '\0') continue;
|
|
|
|
esp_print_decode_onesecret(ndo, fileline);
|
|
}
|
|
fclose(secretfile);
|
|
|
|
return;
|
|
}
|
|
|
|
if (spikey) {
|
|
char *spistr, *foo;
|
|
u_int32_t spino;
|
|
struct sockaddr_in *sin;
|
|
#ifdef INET6
|
|
struct sockaddr_in6 *sin6;
|
|
#endif
|
|
|
|
spistr = strsep(&spikey, "@");
|
|
|
|
spino = strtoul(spistr, &foo, 0);
|
|
if (spistr == foo || !spikey) {
|
|
(*ndo->ndo_warning)(ndo, "print_esp: failed to decode spi# %s\n", foo);
|
|
return;
|
|
}
|
|
|
|
sa1.spi = spino;
|
|
|
|
sin = (struct sockaddr_in *)&sa1.daddr;
|
|
#ifdef INET6
|
|
sin6 = (struct sockaddr_in6 *)&sa1.daddr;
|
|
if (inet_pton(AF_INET6, spikey, &sin6->sin6_addr) == 1) {
|
|
#ifdef HAVE_SOCKADDR_SA_LEN
|
|
sin6->sin6_len = sizeof(struct sockaddr_in6);
|
|
#endif
|
|
sin6->sin6_family = AF_INET6;
|
|
} else
|
|
#endif
|
|
if (inet_pton(AF_INET, spikey, &sin->sin_addr) == 1) {
|
|
#ifdef HAVE_SOCKADDR_SA_LEN
|
|
sin->sin_len = sizeof(struct sockaddr_in);
|
|
#endif
|
|
sin->sin_family = AF_INET;
|
|
} else {
|
|
(*ndo->ndo_warning)(ndo, "print_esp: can not decode IP# %s\n", spikey);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (decode) {
|
|
char *colon, *p;
|
|
u_char espsecret_key[256];
|
|
int len;
|
|
size_t i;
|
|
const EVP_CIPHER *evp;
|
|
int authlen = 0;
|
|
|
|
/* skip any blank spaces */
|
|
while (isspace((unsigned char)*decode))
|
|
decode++;
|
|
|
|
colon = strchr(decode, ':');
|
|
if (colon == NULL) {
|
|
(*ndo->ndo_warning)(ndo, "failed to decode espsecret: %s\n", decode);
|
|
return;
|
|
}
|
|
*colon = '\0';
|
|
|
|
len = colon - decode;
|
|
if (strlen(decode) > strlen("-hmac96") &&
|
|
!strcmp(decode + strlen(decode) - strlen("-hmac96"),
|
|
"-hmac96")) {
|
|
p = strstr(decode, "-hmac96");
|
|
*p = '\0';
|
|
authlen = 12;
|
|
}
|
|
if (strlen(decode) > strlen("-cbc") &&
|
|
!strcmp(decode + strlen(decode) - strlen("-cbc"), "-cbc")) {
|
|
p = strstr(decode, "-cbc");
|
|
*p = '\0';
|
|
}
|
|
evp = EVP_get_cipherbyname(decode);
|
|
if (!evp) {
|
|
(*ndo->ndo_warning)(ndo, "failed to find cipher algo %s\n", decode);
|
|
sa1.evp = NULL;
|
|
sa1.authlen = 0;
|
|
sa1.ivlen = 0;
|
|
return;
|
|
}
|
|
|
|
sa1.evp = evp;
|
|
sa1.authlen = authlen;
|
|
sa1.ivlen = EVP_CIPHER_iv_length(evp);
|
|
|
|
colon++;
|
|
if (colon[0] == '0' && colon[1] == 'x') {
|
|
/* decode some hex! */
|
|
colon += 2;
|
|
len = strlen(colon) / 2;
|
|
|
|
if (len > 256) {
|
|
(*ndo->ndo_warning)(ndo, "secret is too big: %d\n", len);
|
|
return;
|
|
}
|
|
|
|
i = 0;
|
|
while (colon[0] != '\0' && colon[1]!='\0') {
|
|
espsecret_key[i] = hex2byte(ndo, colon);
|
|
colon += 2;
|
|
i++;
|
|
}
|
|
|
|
memcpy(sa1.secret, espsecret_key, i);
|
|
sa1.secretlen = i;
|
|
} else {
|
|
i = strlen(colon);
|
|
|
|
if (i < sizeof(sa1.secret)) {
|
|
memcpy(sa1.secret, colon, i);
|
|
sa1.secretlen = i;
|
|
} else {
|
|
memcpy(sa1.secret, colon, sizeof(sa1.secret));
|
|
sa1.secretlen = sizeof(sa1.secret);
|
|
}
|
|
}
|
|
}
|
|
|
|
esp_print_addsa(ndo, &sa1, sa_def);
|
|
}
|
|
|
|
static void esp_print_decodesecret(netdissect_options *ndo)
|
|
{
|
|
char *line;
|
|
char *p;
|
|
|
|
p = ndo->ndo_espsecret;
|
|
|
|
while (ndo->ndo_espsecret && ndo->ndo_espsecret[0] != '\0') {
|
|
/* pick out the first line or first thing until a comma */
|
|
if ((line = strsep(&ndo->ndo_espsecret, "\n,")) == NULL) {
|
|
line = ndo->ndo_espsecret;
|
|
ndo->ndo_espsecret = NULL;
|
|
}
|
|
|
|
esp_print_decode_onesecret(ndo, line);
|
|
}
|
|
}
|
|
|
|
static void esp_init(netdissect_options *ndo _U_)
|
|
{
|
|
|
|
OpenSSL_add_all_algorithms();
|
|
EVP_add_cipher_alias(SN_des_ede3_cbc, "3des");
|
|
}
|
|
#endif
|
|
|
|
int
|
|
esp_print(netdissect_options *ndo,
|
|
const u_char *bp, const int length, const u_char *bp2
|
|
#ifndef HAVE_LIBCRYPTO
|
|
_U_
|
|
#endif
|
|
,
|
|
int *nhdr
|
|
#ifndef HAVE_LIBCRYPTO
|
|
_U_
|
|
#endif
|
|
,
|
|
int *padlen
|
|
#ifndef HAVE_LIBCRYPTO
|
|
_U_
|
|
#endif
|
|
)
|
|
{
|
|
register const struct newesp *esp;
|
|
register const u_char *ep;
|
|
#ifdef HAVE_LIBCRYPTO
|
|
struct ip *ip;
|
|
struct sa_list *sa = NULL;
|
|
int espsecret_keylen;
|
|
#ifdef INET6
|
|
struct ip6_hdr *ip6 = NULL;
|
|
#endif
|
|
int advance;
|
|
int len;
|
|
u_char *secret;
|
|
int ivlen = 0;
|
|
u_char *ivoff;
|
|
u_char *p;
|
|
EVP_CIPHER_CTX ctx;
|
|
int blocksz;
|
|
static int initialized = 0;
|
|
#endif
|
|
|
|
esp = (struct newesp *)bp;
|
|
|
|
#ifdef HAVE_LIBCRYPTO
|
|
secret = NULL;
|
|
advance = 0;
|
|
|
|
if (!initialized) {
|
|
esp_init(ndo);
|
|
initialized = 1;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
/* keep secret out of a register */
|
|
p = (u_char *)&secret;
|
|
#endif
|
|
|
|
/* 'ep' points to the end of available data. */
|
|
ep = ndo->ndo_snapend;
|
|
|
|
if ((u_char *)(esp + 1) >= ep) {
|
|
fputs("[|ESP]", stdout);
|
|
goto fail;
|
|
}
|
|
(*ndo->ndo_printf)(ndo, "ESP(spi=0x%08x", EXTRACT_32BITS(&esp->esp_spi));
|
|
(*ndo->ndo_printf)(ndo, ",seq=0x%x)", EXTRACT_32BITS(&esp->esp_seq));
|
|
(*ndo->ndo_printf)(ndo, ", length %u", length);
|
|
|
|
#ifndef HAVE_LIBCRYPTO
|
|
goto fail;
|
|
#else
|
|
/* initiailize SAs */
|
|
if (ndo->ndo_sa_list_head == NULL) {
|
|
if (!ndo->ndo_espsecret)
|
|
goto fail;
|
|
|
|
esp_print_decodesecret(ndo);
|
|
}
|
|
|
|
if (ndo->ndo_sa_list_head == NULL)
|
|
goto fail;
|
|
|
|
ip = (struct ip *)bp2;
|
|
switch (IP_V(ip)) {
|
|
#ifdef INET6
|
|
case 6:
|
|
ip6 = (struct ip6_hdr *)bp2;
|
|
/* we do not attempt to decrypt jumbograms */
|
|
if (!EXTRACT_16BITS(&ip6->ip6_plen))
|
|
goto fail;
|
|
/* if we can't get nexthdr, we do not need to decrypt it */
|
|
len = sizeof(struct ip6_hdr) + EXTRACT_16BITS(&ip6->ip6_plen);
|
|
|
|
/* see if we can find the SA, and if so, decode it */
|
|
for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) {
|
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa->daddr;
|
|
if (sa->spi == ntohl(esp->esp_spi) &&
|
|
sin6->sin6_family == AF_INET6 &&
|
|
memcmp(&sin6->sin6_addr, &ip6->ip6_dst,
|
|
sizeof(struct in6_addr)) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
#endif /*INET6*/
|
|
case 4:
|
|
/* nexthdr & padding are in the last fragment */
|
|
if (EXTRACT_16BITS(&ip->ip_off) & IP_MF)
|
|
goto fail;
|
|
len = EXTRACT_16BITS(&ip->ip_len);
|
|
|
|
/* see if we can find the SA, and if so, decode it */
|
|
for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) {
|
|
struct sockaddr_in *sin = (struct sockaddr_in *)&sa->daddr;
|
|
if (sa->spi == ntohl(esp->esp_spi) &&
|
|
sin->sin_family == AF_INET &&
|
|
sin->sin_addr.s_addr == ip->ip_dst.s_addr) {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
goto fail;
|
|
}
|
|
|
|
/* if we didn't find the specific one, then look for
|
|
* an unspecified one.
|
|
*/
|
|
if (sa == NULL)
|
|
sa = ndo->ndo_sa_default;
|
|
|
|
/* if not found fail */
|
|
if (sa == NULL)
|
|
goto fail;
|
|
|
|
/* if we can't get nexthdr, we do not need to decrypt it */
|
|
if (ep - bp2 < len)
|
|
goto fail;
|
|
if (ep - bp2 > len) {
|
|
/* FCS included at end of frame (NetBSD 1.6 or later) */
|
|
ep = bp2 + len;
|
|
}
|
|
|
|
ivoff = (u_char *)(esp + 1) + 0;
|
|
ivlen = sa->ivlen;
|
|
secret = sa->secret;
|
|
espsecret_keylen = sa->secretlen;
|
|
ep = ep - sa->authlen;
|
|
|
|
if (sa->evp) {
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
if (EVP_CipherInit(&ctx, sa->evp, secret, NULL, 0) < 0)
|
|
(*ndo->ndo_warning)(ndo, "espkey init failed");
|
|
|
|
blocksz = EVP_CIPHER_CTX_block_size(&ctx);
|
|
|
|
p = ivoff;
|
|
EVP_CipherInit(&ctx, NULL, NULL, p, 0);
|
|
EVP_Cipher(&ctx, p + ivlen, p + ivlen, ep - (p + ivlen));
|
|
advance = ivoff - (u_char *)esp + ivlen;
|
|
} else
|
|
advance = sizeof(struct newesp);
|
|
|
|
/* sanity check for pad length */
|
|
if (ep - bp < *(ep - 2))
|
|
goto fail;
|
|
|
|
if (padlen)
|
|
*padlen = *(ep - 2) + 2;
|
|
|
|
if (nhdr)
|
|
*nhdr = *(ep - 1);
|
|
|
|
(ndo->ndo_printf)(ndo, ": ");
|
|
return advance;
|
|
#endif
|
|
|
|
fail:
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Local Variables:
|
|
* c-style: whitesmith
|
|
* c-basic-offset: 8
|
|
* End:
|
|
*/
|