/*      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.
*/

#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: print-esp.c,v 1.13 2024/09/02 16:15:31 christos Exp $");
#endif

/* \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 = malloc(sizeof(*ctx));
       if (ctx == NULL)
               return (NULL);
       memset(ctx, 0, 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 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, S_ERR_ND_ESP_SECRET,
                                 "invalid hex digit %c in espsecret\n", hex);
       }
}

static u_int hex2byte(netdissect_options *ndo, char *hexstring)
{
       u_int byte;

       byte = (hexdigit(ndo, hexstring[0]) << 4) + hexdigit(ndo, hexstring[1]);
       return byte;
}

/*
* returns size of binary, 0 on failure.
*/
static int
espprint_decode_hex(netdissect_options *ndo,
                   u_char *binbuf, unsigned int binbuf_len, char *hex)
{
       unsigned int len;
       int i;

       len = strlen(hex) / 2;

       if (len > binbuf_len) {
               (*ndo->ndo_warning)(ndo, "secret is too big: %u\n", len);
               return 0;
       }

       i = 0;
       while (hex[0] != '\0' && hex[1]!='\0') {
               binbuf[i] = hex2byte(ndo, hex);
               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