mirror of
https://github.com/the-tcpdump-group/tcpdump.git
synced 2024-11-23 01:53:55 +08:00
d44658b2e4
It's described in https://github.com/openssl/openssl/issues/11715#issuecomment-2164442688 as being one of several routines that "are internal macros and not really suitable for the Brave New Provider World", and the bug in question complains that they're not documented. Instead, just map "3des" to "des-ede3-cbc" ourselves. This should fix #1175.
943 lines
24 KiB
C
943 lines
24 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.
|
|
*/
|
|
|
|
/* \summary: IPSEC Encapsulating Security Payload (ESP) printer */
|
|
|
|
#include <config.h>
|
|
|
|
#include "netdissect-stdinc.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef HAVE_LIBCRYPTO
|
|
#include <openssl/evp.h>
|
|
#endif
|
|
|
|
#include "netdissect.h"
|
|
#include "extract.h"
|
|
|
|
#include "diag-control.h"
|
|
|
|
#ifdef HAVE_LIBCRYPTO
|
|
#include "strtoaddr.h"
|
|
#include "ascii_strcasecmp.h"
|
|
#endif
|
|
|
|
#include "ip.h"
|
|
#include "ip6.h"
|
|
|
|
/*
|
|
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the project 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 BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* RFC1827/2406 Encapsulated Security Payload.
|
|
*/
|
|
|
|
struct newesp {
|
|
nd_uint32_t esp_spi; /* ESP */
|
|
nd_uint32_t esp_seq; /* Sequence number */
|
|
/*variable size*/ /* (IV and) Payload data */
|
|
/*variable size*/ /* padding */
|
|
/*8bit*/ /* pad size */
|
|
/*8bit*/ /* next header */
|
|
/*8bit*/ /* next header */
|
|
/*variable size, 32bit bound*/ /* Authentication data */
|
|
};
|
|
|
|
#ifdef HAVE_LIBCRYPTO
|
|
union inaddr_u {
|
|
nd_ipv4 in4;
|
|
nd_ipv6 in6;
|
|
};
|
|
struct sa_list {
|
|
struct sa_list *next;
|
|
u_int daddr_version;
|
|
union inaddr_u daddr;
|
|
uint32_t spi; /* if == 0, then IKEv2 */
|
|
int initiator;
|
|
u_char spii[8]; /* for IKEv2 */
|
|
u_char spir[8];
|
|
const EVP_CIPHER *evp;
|
|
u_int ivlen;
|
|
int authlen;
|
|
u_char authsecret[256];
|
|
int authsecret_len;
|
|
u_char secret[256]; /* is that big enough for all secrets? */
|
|
int secretlen;
|
|
};
|
|
|
|
#ifndef HAVE_EVP_CIPHER_CTX_NEW
|
|
/*
|
|
* Allocate an EVP_CIPHER_CTX.
|
|
* Used if we have an older version of OpenSSL that doesn't provide
|
|
* routines to allocate and free them.
|
|
*/
|
|
static EVP_CIPHER_CTX *
|
|
EVP_CIPHER_CTX_new(void)
|
|
{
|
|
EVP_CIPHER_CTX *ctx;
|
|
|
|
ctx = calloc(1, sizeof(*ctx));
|
|
return (ctx);
|
|
}
|
|
|
|
static void
|
|
EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx)
|
|
{
|
|
EVP_CIPHER_CTX_cleanup(ctx);
|
|
free(ctx);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_EVP_DECRYPTINIT_EX
|
|
/*
|
|
* Initialize the cipher by calling EVP_DecryptInit_ex(), because
|
|
* calling EVP_DecryptInit() will reset the cipher context, clearing
|
|
* the cipher, so calling it twice, with the second call having a
|
|
* null cipher, will clear the already-set cipher. EVP_DecryptInit_ex(),
|
|
* however, won't reset the cipher context, so you can use it to specify
|
|
* the IV in a second call after a first call to EVP_DecryptInit_ex()
|
|
* to set the cipher and the key.
|
|
*
|
|
* XXX - is there some reason why we need to make two calls?
|
|
*/
|
|
static int
|
|
set_cipher_parameters(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
|
|
const unsigned char *key,
|
|
const unsigned char *iv)
|
|
{
|
|
return EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv);
|
|
}
|
|
#else
|
|
/*
|
|
* Initialize the cipher by calling EVP_DecryptInit(), because we don't
|
|
* have EVP_DecryptInit_ex(); we rely on it not trashing the context.
|
|
*/
|
|
static int
|
|
set_cipher_parameters(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
|
|
const unsigned char *key,
|
|
const unsigned char *iv)
|
|
{
|
|
return EVP_DecryptInit(ctx, cipher, key, iv);
|
|
}
|
|
#endif
|
|
|
|
static u_char *
|
|
do_decrypt(netdissect_options *ndo, const char *caller, struct sa_list *sa,
|
|
const u_char *iv, const u_char *ct, unsigned int ctlen)
|
|
{
|
|
EVP_CIPHER_CTX *ctx;
|
|
unsigned int block_size;
|
|
unsigned int ptlen;
|
|
u_char *pt;
|
|
int len;
|
|
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
if (ctx == NULL) {
|
|
/*
|
|
* Failed to initialize the cipher context.
|
|
* From a look at the OpenSSL code, this appears to
|
|
* mean "couldn't allocate memory for the cipher context";
|
|
* note that we're not passing any parameters, so there's
|
|
* not much else it can mean.
|
|
*/
|
|
(*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
|
|
"%s: can't allocate memory for cipher context", caller);
|
|
return NULL;
|
|
}
|
|
|
|
if (set_cipher_parameters(ctx, sa->evp, sa->secret, NULL) < 0) {
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
(*ndo->ndo_warning)(ndo, "%s: espkey init failed", caller);
|
|
return NULL;
|
|
}
|
|
if (set_cipher_parameters(ctx, NULL, NULL, iv) < 0) {
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
(*ndo->ndo_warning)(ndo, "%s: IV init failed", caller);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* At least as I read RFC 5996 section 3.14 and RFC 4303 section 2.4,
|
|
* if the cipher has a block size of which the ciphertext's size must
|
|
* be a multiple, the payload must be padded to make that happen, so
|
|
* the ciphertext length must be a multiple of the block size. Fail
|
|
* if that's not the case.
|
|
*/
|
|
block_size = (unsigned int)EVP_CIPHER_CTX_block_size(ctx);
|
|
if ((ctlen % block_size) != 0) {
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
(*ndo->ndo_warning)(ndo,
|
|
"%s: ciphertext size %u is not a multiple of the cipher block size %u",
|
|
caller, ctlen, block_size);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Attempt to allocate a buffer for the decrypted data, because
|
|
* we can't decrypt on top of the input buffer.
|
|
*/
|
|
ptlen = ctlen;
|
|
pt = (u_char *)calloc(1, ptlen);
|
|
if (pt == NULL) {
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
(*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
|
|
"%s: can't allocate memory for decryption buffer", caller);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* The size of the ciphertext handed to us is a multiple of the
|
|
* cipher block size, so we don't need to worry about padding.
|
|
*/
|
|
if (!EVP_CIPHER_CTX_set_padding(ctx, 0)) {
|
|
free(pt);
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
(*ndo->ndo_warning)(ndo,
|
|
"%s: EVP_CIPHER_CTX_set_padding failed", caller);
|
|
return NULL;
|
|
}
|
|
if (!EVP_DecryptUpdate(ctx, pt, &len, ct, ctlen)) {
|
|
free(pt);
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
(*ndo->ndo_warning)(ndo, "%s: EVP_DecryptUpdate failed",
|
|
caller);
|
|
return NULL;
|
|
}
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
return pt;
|
|
}
|
|
|
|
/*
|
|
* This will allocate a new buffer containing the decrypted data.
|
|
* It returns 1 on success and 0 on failure.
|
|
*
|
|
* It will push the new buffer and the values of ndo->ndo_packetp and
|
|
* ndo->ndo_snapend onto the buffer stack, and change ndo->ndo_packetp
|
|
* and ndo->ndo_snapend to refer to the new buffer.
|
|
*
|
|
* Our caller must pop the buffer off the stack when it's finished
|
|
* dissecting anything in it and before it does any dissection of
|
|
* anything in the old buffer. That will free the new buffer.
|
|
*/
|
|
DIAG_OFF_DEPRECATION
|
|
int esp_decrypt_buffer_by_ikev2_print(netdissect_options *ndo,
|
|
int initiator,
|
|
const u_char spii[8],
|
|
const u_char spir[8],
|
|
const u_char *buf, const u_char *end)
|
|
{
|
|
struct sa_list *sa;
|
|
const u_char *iv;
|
|
const u_char *ct;
|
|
unsigned int ctlen;
|
|
u_char *pt;
|
|
|
|
/* initiator arg is any non-zero value */
|
|
if(initiator) initiator=1;
|
|
|
|
/* see if we can find the SA, and if so, decode it */
|
|
for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) {
|
|
if (sa->spi == 0
|
|
&& initiator == sa->initiator
|
|
&& memcmp(spii, sa->spii, 8) == 0
|
|
&& memcmp(spir, sa->spir, 8) == 0)
|
|
break;
|
|
}
|
|
|
|
if(sa == NULL) return 0;
|
|
if(sa->evp == NULL) return 0;
|
|
|
|
/*
|
|
* remove authenticator, and see if we still have something to
|
|
* work with
|
|
*/
|
|
end = end - sa->authlen;
|
|
iv = buf;
|
|
ct = iv + sa->ivlen;
|
|
ctlen = end-ct;
|
|
|
|
if(end <= ct) return 0;
|
|
|
|
pt = do_decrypt(ndo, __func__, sa, iv,
|
|
ct, ctlen);
|
|
if (pt == NULL)
|
|
return 0;
|
|
|
|
/*
|
|
* Switch to the output buffer for dissection, and save it
|
|
* on the buffer stack so it can be freed; our caller must
|
|
* pop it when done.
|
|
*/
|
|
if (!nd_push_buffer(ndo, pt, pt, ctlen)) {
|
|
free(pt);
|
|
(*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
|
|
"%s: can't push buffer on buffer stack", __func__);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
DIAG_ON_DEPRECATION
|
|
|
|
static void esp_print_addsa(netdissect_options *ndo,
|
|
const struct sa_list *sa, int sa_def)
|
|
{
|
|
/* copy the "sa" */
|
|
|
|
struct sa_list *nsa;
|
|
|
|
/* malloc() return used in a 'struct sa_list': do not free() */
|
|
nsa = (struct sa_list *)malloc(sizeof(struct sa_list));
|
|
if (nsa == NULL)
|
|
(*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
|
|
"%s: malloc", __func__);
|
|
|
|
*nsa = *sa;
|
|
|
|
if (sa_def)
|
|
ndo->ndo_sa_default = nsa;
|
|
|
|
nsa->next = ndo->ndo_sa_list_head;
|
|
ndo->ndo_sa_list_head = nsa;
|
|
}
|
|
|
|
|
|
static 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_warning)(ndo,
|
|
"invalid hex digit %c in espsecret\n", hex);
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* returns size of binary, 0 on failure.
|
|
*/
|
|
static int
|
|
espprint_decode_hex(netdissect_options *ndo,
|
|
u_char *binbuf, unsigned int binbuf_len, char *hex)
|
|
{
|
|
size_t len;
|
|
int i;
|
|
|
|
/*
|
|
* XXX - fail if the string length isn't a multiple of 2?
|
|
*/
|
|
len = strlen(hex) / 2;
|
|
|
|
if (len > binbuf_len) {
|
|
(*ndo->ndo_warning)(ndo, "secret is too big: %zu\n", len);
|
|
return 0;
|
|
}
|
|
|
|
i = 0;
|
|
while (hex[0] != '\0' && hex[1]!='\0') {
|
|
int upper_nibble, lower_nibble;
|
|
|
|
upper_nibble = hexdigit(ndo, hex[0]);
|
|
if (upper_nibble < 0) {
|
|
/*
|
|
* Invalid hex digit; a warning has already been
|
|
* printed.
|
|
*/
|
|
return 0;
|
|
}
|
|
lower_nibble = hexdigit(ndo, hex[1]);
|
|
if (lower_nibble < 0) {
|
|
/*
|
|
* Invalid hex digit; a warning has already been
|
|
* printed.
|
|
*/
|
|
return 0;
|
|
}
|
|
binbuf[i] = (((u_int)upper_nibble) << 4) + (((u_int)lower_nibble) << 0);
|
|
hex += 2;
|
|
i++;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* decode the form: SPINUM@IP <tab> ALGONAME:0xsecret
|
|
*/
|
|
|
|
DIAG_OFF_DEPRECATION
|
|
static int
|
|
espprint_decode_encalgo(netdissect_options *ndo,
|
|
char *decode, struct sa_list *sa)
|
|
{
|
|
size_t i;
|
|
const EVP_CIPHER *evp;
|
|
int authlen = 0;
|
|
char *colon, *p;
|
|
const char *real_decode;
|
|
|
|
colon = strchr(decode, ':');
|
|
if (colon == NULL) {
|
|
(*ndo->ndo_warning)(ndo, "failed to decode espsecret: %s\n", decode);
|
|
return 0;
|
|
}
|
|
*colon = '\0';
|
|
|
|
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';
|
|
}
|
|
/*
|
|
* Not all versions of libcrypto support calls to add aliases
|
|
* to ciphers - newer versions of libressl don't - so, instead
|
|
* of making "3des" an alias for "des_ede3_cbc", if attempting
|
|
* to get the cipher fails and the name is "3des", we try
|
|
* "des_ede3_cbc".
|
|
*/
|
|
real_decode = decode;
|
|
if (strcmp(real_decode, "3des") == 0)
|
|
real_decode = "des-ede3-cbc";
|
|
evp = EVP_get_cipherbyname(real_decode);
|
|
|
|
if (!evp) {
|
|
if (decode != real_decode)
|
|
(*ndo->ndo_warning)(ndo, "failed to find cipher algo %s (%s)\n", real_decode, decode);
|
|
else
|
|
(*ndo->ndo_warning)(ndo, "failed to find cipher algo %s\n", decode);
|
|
sa->evp = NULL;
|
|
sa->authlen = 0;
|
|
sa->ivlen = 0;
|
|
return 0;
|
|
}
|
|
|
|
sa->evp = evp;
|
|
sa->authlen = authlen;
|
|
/* This returns an int, but it should never be negative */
|
|
sa->ivlen = EVP_CIPHER_iv_length(evp);
|
|
|
|
colon++;
|
|
if (colon[0] == '0' && colon[1] == 'x') {
|
|
/* decode some hex! */
|
|
|
|
colon += 2;
|
|
sa->secretlen = espprint_decode_hex(ndo, sa->secret, sizeof(sa->secret), colon);
|
|
if(sa->secretlen == 0) return 0;
|
|
} else {
|
|
i = strlen(colon);
|
|
|
|
if (i < sizeof(sa->secret)) {
|
|
memcpy(sa->secret, colon, i);
|
|
sa->secretlen = i;
|
|
} else {
|
|
memcpy(sa->secret, colon, sizeof(sa->secret));
|
|
sa->secretlen = sizeof(sa->secret);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
DIAG_ON_DEPRECATION
|
|
|
|
/*
|
|
* for the moment, ignore the auth algorithm, just hard code the authenticator
|
|
* length. Need to research how openssl looks up HMAC stuff.
|
|
*/
|
|
static int
|
|
espprint_decode_authalgo(netdissect_options *ndo,
|
|
char *decode, struct sa_list *sa)
|
|
{
|
|
char *colon;
|
|
|
|
colon = strchr(decode, ':');
|
|
if (colon == NULL) {
|
|
(*ndo->ndo_warning)(ndo, "failed to decode espsecret: %s\n", decode);
|
|
return 0;
|
|
}
|
|
*colon = '\0';
|
|
|
|
if(ascii_strcasecmp(decode,"sha1") == 0 ||
|
|
ascii_strcasecmp(decode,"md5") == 0) {
|
|
sa->authlen = 12;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void esp_print_decode_ikeline(netdissect_options *ndo, char *line,
|
|
const char *file, int lineno)
|
|
{
|
|
/* it's an IKEv2 secret, store it instead */
|
|
struct sa_list sa1;
|
|
|
|
char *init;
|
|
char *icookie, *rcookie;
|
|
int ilen, rlen;
|
|
char *authkey;
|
|
char *enckey;
|
|
|
|
init = strsep(&line, " \t");
|
|
icookie = strsep(&line, " \t");
|
|
rcookie = strsep(&line, " \t");
|
|
authkey = strsep(&line, " \t");
|
|
enckey = strsep(&line, " \t");
|
|
|
|
/* if any fields are missing */
|
|
if(!init || !icookie || !rcookie || !authkey || !enckey) {
|
|
(*ndo->ndo_warning)(ndo, "print_esp: failed to find all fields for ikev2 at %s:%u",
|
|
file, lineno);
|
|
|
|
return;
|
|
}
|
|
|
|
ilen = strlen(icookie);
|
|
rlen = strlen(rcookie);
|
|
|
|
if((init[0]!='I' && init[0]!='R')
|
|
|| icookie[0]!='0' || icookie[1]!='x'
|
|
|| rcookie[0]!='0' || rcookie[1]!='x'
|
|
|| ilen!=18
|
|
|| rlen!=18) {
|
|
(*ndo->ndo_warning)(ndo, "print_esp: line %s:%u improperly formatted.",
|
|
file, lineno);
|
|
|
|
(*ndo->ndo_warning)(ndo, "init=%s icookie=%s(%u) rcookie=%s(%u)",
|
|
init, icookie, ilen, rcookie, rlen);
|
|
|
|
return;
|
|
}
|
|
|
|
sa1.spi = 0;
|
|
sa1.initiator = (init[0] == 'I');
|
|
if(espprint_decode_hex(ndo, sa1.spii, sizeof(sa1.spii), icookie+2)!=8)
|
|
return;
|
|
|
|
if(espprint_decode_hex(ndo, sa1.spir, sizeof(sa1.spir), rcookie+2)!=8)
|
|
return;
|
|
|
|
if(!espprint_decode_encalgo(ndo, enckey, &sa1)) return;
|
|
|
|
if(!espprint_decode_authalgo(ndo, authkey, &sa1)) return;
|
|
|
|
esp_print_addsa(ndo, &sa1, FALSE);
|
|
}
|
|
|
|
/*
|
|
*
|
|
* special form: file /name
|
|
* causes us to go read from this file instead.
|
|
*
|
|
*/
|
|
static void esp_print_decode_onesecret(netdissect_options *ndo, char *line,
|
|
const char *file, int lineno)
|
|
{
|
|
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;
|
|
/* sa1.daddr.version = 0; */
|
|
/* memset(&sa1.daddr, 0, sizeof(sa1.daddr)); */
|
|
/* sa1.spi = 0; */
|
|
sa_def = 1;
|
|
} else
|
|
decode = line;
|
|
|
|
if (spikey && ascii_strcasecmp(spikey, "file") == 0) {
|
|
/* open file and read it */
|
|
FILE *secretfile;
|
|
char fileline[1024];
|
|
int subfile_lineno=0;
|
|
char *nl;
|
|
char *filename = line;
|
|
|
|
secretfile = fopen(filename, FOPEN_READ_TXT);
|
|
if (secretfile == NULL) {
|
|
(*ndo->ndo_error)(ndo, S_ERR_ND_OPEN_FILE,
|
|
"%s: can't open %s: %s\n",
|
|
__func__, filename, strerror(errno));
|
|
}
|
|
|
|
while (fgets(fileline, sizeof(fileline)-1, secretfile) != NULL) {
|
|
subfile_lineno++;
|
|
/* 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, filename, subfile_lineno);
|
|
}
|
|
fclose(secretfile);
|
|
|
|
return;
|
|
}
|
|
|
|
if (spikey && ascii_strcasecmp(spikey, "ikev2") == 0) {
|
|
esp_print_decode_ikeline(ndo, line, file, lineno);
|
|
return;
|
|
}
|
|
|
|
if (spikey) {
|
|
|
|
char *spistr, *foo;
|
|
uint32_t spino;
|
|
|
|
spistr = strsep(&spikey, "@");
|
|
if (spistr == NULL) {
|
|
(*ndo->ndo_warning)(ndo, "print_esp: failed to find the @ token");
|
|
return;
|
|
}
|
|
|
|
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;
|
|
|
|
if (strtoaddr6(spikey, &sa1.daddr.in6) == 1) {
|
|
sa1.daddr_version = 6;
|
|
} else if (strtoaddr(spikey, &sa1.daddr.in4) == 1) {
|
|
sa1.daddr_version = 4;
|
|
} else {
|
|
(*ndo->ndo_warning)(ndo, "print_esp: can not decode IP# %s\n", spikey);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (decode) {
|
|
/* skip any blank spaces */
|
|
while (*decode == ' ' || *decode == '\t' || *decode == '\r' || *decode == '\n')
|
|
decode++;
|
|
|
|
if(!espprint_decode_encalgo(ndo, decode, &sa1)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
esp_print_addsa(ndo, &sa1, sa_def);
|
|
}
|
|
|
|
DIAG_OFF_DEPRECATION
|
|
static void esp_init(netdissect_options *ndo _U_)
|
|
{
|
|
/*
|
|
* 0.9.6 doesn't appear to define OPENSSL_API_COMPAT, so
|
|
* we check whether it's undefined or it's less than the
|
|
* value for 1.1.0.
|
|
*/
|
|
#if !defined(OPENSSL_API_COMPAT) || OPENSSL_API_COMPAT < 0x10100000L
|
|
OpenSSL_add_all_algorithms();
|
|
#endif
|
|
}
|
|
DIAG_ON_DEPRECATION
|
|
|
|
void esp_decodesecret_print(netdissect_options *ndo)
|
|
{
|
|
char *line;
|
|
char *p;
|
|
static int initialized = 0;
|
|
|
|
if (!initialized) {
|
|
esp_init(ndo);
|
|
initialized = 1;
|
|
}
|
|
|
|
p = ndo->ndo_espsecret;
|
|
|
|
while (p && p[0] != '\0') {
|
|
/* pick out the first line or first thing until a comma */
|
|
if ((line = strsep(&p, "\n,")) == NULL) {
|
|
line = p;
|
|
p = NULL;
|
|
}
|
|
|
|
esp_print_decode_onesecret(ndo, line, "cmdline", 0);
|
|
}
|
|
|
|
ndo->ndo_espsecret = NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBCRYPTO
|
|
#define USED_IF_LIBCRYPTO
|
|
#else
|
|
#define USED_IF_LIBCRYPTO _U_
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBCRYPTO
|
|
DIAG_OFF_DEPRECATION
|
|
#endif
|
|
void
|
|
esp_print(netdissect_options *ndo,
|
|
const u_char *bp, u_int length,
|
|
const u_char *bp2 USED_IF_LIBCRYPTO,
|
|
u_int ver USED_IF_LIBCRYPTO,
|
|
int fragmented USED_IF_LIBCRYPTO,
|
|
u_int ttl_hl USED_IF_LIBCRYPTO)
|
|
{
|
|
const struct newesp *esp;
|
|
const u_char *ep;
|
|
#ifdef HAVE_LIBCRYPTO
|
|
const struct ip *ip;
|
|
struct sa_list *sa = NULL;
|
|
const struct ip6_hdr *ip6 = NULL;
|
|
const u_char *iv;
|
|
u_int ivlen;
|
|
u_int payloadlen;
|
|
const u_char *ct;
|
|
u_char *pt;
|
|
u_int padlen;
|
|
u_int nh;
|
|
#endif
|
|
|
|
ndo->ndo_protocol = "esp";
|
|
esp = (const struct newesp *)bp;
|
|
|
|
/* 'ep' points to the end of available data. */
|
|
ep = ndo->ndo_snapend;
|
|
|
|
if ((const u_char *)(esp + 1) >= ep) {
|
|
nd_print_trunc(ndo);
|
|
return;
|
|
}
|
|
ND_PRINT("ESP(spi=0x%08x", GET_BE_U_4(esp->esp_spi));
|
|
ND_PRINT(",seq=0x%x)", GET_BE_U_4(esp->esp_seq));
|
|
ND_PRINT(", length %u", length);
|
|
|
|
#ifdef HAVE_LIBCRYPTO
|
|
/* initialize SAs */
|
|
if (ndo->ndo_sa_list_head == NULL) {
|
|
if (!ndo->ndo_espsecret)
|
|
return;
|
|
|
|
esp_decodesecret_print(ndo);
|
|
}
|
|
|
|
if (ndo->ndo_sa_list_head == NULL)
|
|
return;
|
|
|
|
ip = (const struct ip *)bp2;
|
|
switch (ver) {
|
|
case 6:
|
|
ip6 = (const struct ip6_hdr *)bp2;
|
|
/* we do not attempt to decrypt jumbograms */
|
|
if (!GET_BE_U_2(ip6->ip6_plen))
|
|
return;
|
|
/* XXX - check whether it's fragmented? */
|
|
/* if we can't get nexthdr, we do not need to decrypt it */
|
|
|
|
/* see if we can find the SA, and if so, decode it */
|
|
for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) {
|
|
if (sa->spi == GET_BE_U_4(esp->esp_spi) &&
|
|
sa->daddr_version == 6 &&
|
|
UNALIGNED_MEMCMP(&sa->daddr.in6, &ip6->ip6_dst,
|
|
sizeof(nd_ipv6)) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 4:
|
|
/* nexthdr & padding are in the last fragment */
|
|
if (fragmented)
|
|
return;
|
|
|
|
/* see if we can find the SA, and if so, decode it */
|
|
for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) {
|
|
if (sa->spi == GET_BE_U_4(esp->esp_spi) &&
|
|
sa->daddr_version == 4 &&
|
|
UNALIGNED_MEMCMP(&sa->daddr.in4, &ip->ip_dst,
|
|
sizeof(nd_ipv4)) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
/* 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)
|
|
return;
|
|
|
|
/* pointer to the IV, if there is one */
|
|
iv = (const u_char *)(esp + 1) + 0;
|
|
/* length of the IV, if there is one; 0, if there isn't */
|
|
ivlen = sa->ivlen;
|
|
|
|
/*
|
|
* Get a pointer to the ciphertext.
|
|
*
|
|
* p points to the beginning of the payload, i.e. to the
|
|
* initialization vector, so if we skip past the initialization
|
|
* vector, it points to the beginning of the ciphertext.
|
|
*/
|
|
ct = iv + ivlen;
|
|
|
|
/*
|
|
* Make sure the authentication data/integrity check value length
|
|
* isn't bigger than the total amount of data available after
|
|
* the ESP header and initialization vector is removed and,
|
|
* if not, slice the authentication data/ICV off.
|
|
*/
|
|
if (ep - ct < sa->authlen) {
|
|
nd_print_trunc(ndo);
|
|
return;
|
|
}
|
|
ep = ep - sa->authlen;
|
|
|
|
/*
|
|
* Calculate the length of the ciphertext. ep points to
|
|
* the beginning of the authentication data/integrity check
|
|
* value, i.e. right past the end of the ciphertext;
|
|
*/
|
|
payloadlen = ep - ct;
|
|
|
|
if (sa->evp == NULL)
|
|
return;
|
|
|
|
/*
|
|
* If the next header value is past the end of the available
|
|
* data, we won't be able to fetch it once we've decrypted
|
|
* the ciphertext, so there's no point in decrypting the data.
|
|
*
|
|
* Report it as truncation.
|
|
*/
|
|
if (!ND_TTEST_1(ep - 1)) {
|
|
nd_print_trunc(ndo);
|
|
return;
|
|
}
|
|
|
|
pt = do_decrypt(ndo, __func__, sa, iv, ct, payloadlen);
|
|
if (pt == NULL)
|
|
return;
|
|
|
|
/*
|
|
* Switch to the output buffer for dissection, and
|
|
* save it on the buffer stack so it can be freed.
|
|
*/
|
|
if (!nd_push_buffer(ndo, pt, pt, payloadlen)) {
|
|
free(pt);
|
|
(*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
|
|
"%s: can't push buffer on buffer stack", __func__);
|
|
}
|
|
|
|
/*
|
|
* Sanity check for pad length; if it, plus 2 for the pad
|
|
* length and next header fields, is bigger than the ciphertext
|
|
* length (which is also the plaintext length), it's too big.
|
|
*
|
|
* XXX - the check can fail if the packet is corrupt *or* if
|
|
* it was not decrypted with the correct key, so that the
|
|
* "plaintext" is not what was being sent.
|
|
*/
|
|
padlen = GET_U_1(pt + payloadlen - 2);
|
|
if (padlen + 2 > payloadlen) {
|
|
nd_print_trunc(ndo);
|
|
return;
|
|
}
|
|
|
|
/* Get the next header */
|
|
nh = GET_U_1(pt + payloadlen - 1);
|
|
|
|
ND_PRINT(": ");
|
|
|
|
/*
|
|
* Don't put padding + padding length(1 byte) + next header(1 byte)
|
|
* in the buffer because they are not part of the plaintext to decode.
|
|
*/
|
|
if (!nd_push_snaplen(ndo, pt, payloadlen - (padlen + 2))) {
|
|
(*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
|
|
"%s: can't push snaplen on buffer stack", __func__);
|
|
}
|
|
|
|
/* Now dissect the plaintext. */
|
|
ip_demux_print(ndo, pt, payloadlen - (padlen + 2), ver, fragmented,
|
|
ttl_hl, nh, bp2);
|
|
|
|
/* Pop the buffer, freeing it. */
|
|
nd_pop_packet_info(ndo);
|
|
/* Pop the nd_push_snaplen */
|
|
nd_pop_packet_info(ndo);
|
|
#endif
|
|
}
|
|
#ifdef HAVE_LIBCRYPTO
|
|
DIAG_ON_DEPRECATION
|
|
#endif
|