]> The Tcpdump Group git mirrors - tcpdump/commitdiff
Introduce a buffer stack, and use it for ESP decryption.
authorGuy Harris <[email protected]>
Sun, 31 Mar 2019 02:26:06 +0000 (19:26 -0700)
committerGuy Harris <[email protected]>
Sun, 31 Mar 2019 02:26:06 +0000 (19:26 -0700)
If a dissector has to process its input - decryption, decompression,
etc. - rather than dissect the raw input, it should push the processed
input onto the buffer stack.  As soon as the dissection is done, the
stack should be popped, to free the buffer into which the processing was
done, and restore the "pointer to packet data" and "pointer to end of
packet data" members of the netdissect_options structure, so the code
can go back to dissecting the original data.

The stack will get everything popped off it when dissection is done.

Use this mechanism in the ESP decryption code rather than scribbling on
top of the input packet data.

netdissect.c
netdissect.h
print-esp.c
print-isakmp.c
print.c

index d32de4e97816f8f96acab65397345fe099f7204b..08a3280c43dc0e2463c07f8930ea5bfdd8f47ea0 100644 (file)
@@ -144,3 +144,46 @@ nd_smi_version_string(void)
        return (NULL);
 #endif
 }
+
+
+int
+nd_push_buffer(netdissect_options *ndo, u_char *new_buffer,
+    const u_char *new_packetp, const u_char *new_snapend)
+{
+       struct netdissect_saved_info *ndsi;
+
+       ndsi = (struct netdissect_saved_info *)malloc(sizeof(struct netdissect_saved_info));
+       if (ndsi == NULL)
+               return (0);     /* fail */
+       ndsi->ndsi_buffer = new_buffer;
+       ndsi->ndsi_packetp = ndo->ndo_packetp;
+       ndsi->ndsi_snapend = ndo->ndo_snapend;
+       ndsi->ndsi_prev = ndo->ndo_buffer_stack;
+
+       ndo->ndo_packetp = new_packetp;
+       ndo->ndo_snapend = new_snapend;
+       ndo->ndo_buffer_stack = ndsi;
+
+       return (1);     /* success */
+}
+
+void
+nd_pop_buffer(netdissect_options *ndo)
+{
+       struct netdissect_saved_info *ndsi;
+
+       ndsi = ndo->ndo_buffer_stack;
+       ndo->ndo_packetp = ndsi->ndsi_packetp;
+       ndo->ndo_snapend = ndsi->ndsi_snapend;
+       ndo->ndo_buffer_stack = ndsi->ndsi_prev;
+
+       free(ndsi->ndsi_buffer);
+       free(ndsi);
+}
+
+void
+nd_pop_all_buffers(netdissect_options *ndo)
+{
+       while (ndo->ndo_buffer_stack != NULL)
+               nd_pop_buffer(ndo);
+}
index 3bcef7263df648cc52d05f1f849541520aa61c84..2893fceeb823aca32f8a7c367696b8f1c10a527b 100644 (file)
@@ -168,6 +168,26 @@ typedef struct netdissect_options netdissect_options;
 
 typedef u_int (*if_printer) IF_PRINTER_ARGS;
 
+/*
+ * In case the data in a buffer needs to be processed by being decrypted,
+ * decompressed, etc. before it's dissected, we can't process it in place,
+ * we have to allocate a new buffer for the processed data.
+ *
+ * We keep a stack of those buffers; when we allocate a new buffer, we
+ * push the current one onto a stack, and when we're done with the new
+ * buffer, we free the current buffer and pop the previous one off the
+ * stack.
+ *
+ * A buffer has a beginnning and end pointer, and a link to the previous
+ * buffer on the stack.
+ */
+struct netdissect_saved_info {
+  u_char *ndsi_buffer;                         /* pointer to allocated buffer data */
+  const u_char *ndsi_packetp;                  /* saved beginning of data */
+  const u_char *ndsi_snapend;                  /* saved end of data */
+  struct netdissect_saved_info *ndsi_prev;     /* previous buffer on the stack */
+};
+
 struct netdissect_options {
   int ndo_bflag;               /* print 4 byte ASes in ASDOT notation */
   int ndo_eflag;               /* print ethernet header */
@@ -208,6 +228,9 @@ struct netdissect_options {
   const u_char *ndo_packetp;
   const u_char *ndo_snapend;
 
+  /* stack of saved buffer information */
+  struct netdissect_saved_info *ndo_buffer_stack;
+
   /* pointer to the if_printer function */
   if_printer ndo_if_printer;
 
@@ -230,6 +253,11 @@ struct netdissect_options {
                      PRINTFLIKE_FUNCPTR(2, 3);
 };
 
+extern int nd_push_buffer(netdissect_options *, u_char *, const u_char *,
+    const u_char *);
+extern void nd_pop_buffer(netdissect_options *);
+extern void nd_pop_all_buffers(netdissect_options *);
+
 #define PT_VAT         1       /* Visual Audio Tool */
 #define PT_WB          2       /* distributed White Board */
 #define PT_RPC         3       /* Remote Procedure Call */
index 60749992586ed1d40677a9c5e0037566b20d2f04..151be1d632676efae39856111092180bbbc59603 100644 (file)
@@ -179,7 +179,16 @@ set_cipher_parameters(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
 #endif
 
 /*
- * this will adjust ndo_packetp and ndo_snapend to new buffer!
+ * 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.
  */
 USES_APPLE_DEPRECATED_API
 int esp_print_decrypt_buffer_by_ikev2(netdissect_options *ndo,
@@ -190,10 +199,12 @@ int esp_print_decrypt_buffer_by_ikev2(netdissect_options *ndo,
 {
        struct sa_list *sa;
        const u_char *iv;
+       const u_char *ct;
        unsigned int len;
        EVP_CIPHER_CTX *ctx;
        unsigned int block_size, buffer_size;
        u_char *input_buffer, *output_buffer;
+       const u_char *pt;
 
        /* initiator arg is any non-zero value */
        if(initiator) initiator=1;
@@ -216,10 +227,10 @@ int esp_print_decrypt_buffer_by_ikev2(netdissect_options *ndo,
         */
        end = end - sa->authlen;
        iv  = buf;
-       buf = buf + sa->ivlen;
-       len = end-buf;
+       ct = iv + sa->ivlen;
+       len = end-ct;
 
-       if(end <= buf) return 0;
+       if(end <= ct) return 0;
 
        ctx = EVP_CIPHER_CTX_new();
        if (ctx == NULL)
@@ -254,7 +265,7 @@ int esp_print_decrypt_buffer_by_ikev2(netdissect_options *ndo,
         * Copy the input data to the encrypted data buffer, and pad it
         * with zeroes.
         */
-       memcpy(input_buffer, buf, len);
+       memcpy(input_buffer, ct, len);
        memset(input_buffer + len, 0, buffer_size - len);
 
        /*
@@ -274,15 +285,24 @@ int esp_print_decrypt_buffer_by_ikev2(netdissect_options *ndo,
        EVP_CIPHER_CTX_free(ctx);
 
        /*
-        * XXX - of course this is wrong, because buf is a const buffer,
-        * but changing this would require a more complicated fix.
+        * Free the input buffer; we no longer need it.
         */
-       memcpy(buf, output_buffer, len);
        free(input_buffer);
-       free(output_buffer);
 
-       ndo->ndo_packetp = buf;
-       ndo->ndo_snapend = end;
+       /*
+        * Get a pointer to the plaintext.
+        */
+       pt = output_buffer;
+
+       /*
+        * 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, output_buffer, pt, pt + len)) {
+               free(output_buffer);
+               return 0;
+       }
 
        return 1;
 }
@@ -695,6 +715,8 @@ esp_print(netdissect_options *ndo,
        EVP_CIPHER_CTX *ctx;
        unsigned int block_size, buffer_size;
        u_char *input_buffer, *output_buffer;
+       const u_char *pt;
+       u_int ptlen;
        u_int padlen;
        u_int nh;
 #endif
@@ -888,13 +910,28 @@ esp_print(netdissect_options *ndo,
        }
        free(input_buffer);
        EVP_CIPHER_CTX_free(ctx);
+
+       /*
+        * Pointer to the plaintext.
+        */
+       pt = output_buffer;
+
+       /*
+        * Length of the plaintext, which is the same as the length
+        * of the ciphertext.
+        */
+       ptlen = ctlen;
+
        /*
-        * XXX - of course this is wrong, because buf is a
-        * const buffer, but changing this would require a
-        * more complicated fix.
+        * Switch to the output buffer for dissection, and
+        * save it on the buffer stack so it can be freed.
         */
-       memcpy(ct, output_buffer, ctlen);
-       free(output_buffer);
+       if (!nd_push_buffer(ndo, output_buffer, pt, pt + ctlen)) {
+               free(output_buffer);
+               (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+                       "esp_print: can't push buffer on buffer stack");
+       }
+       ep = pt + ptlen;
 
        /*
         * Sanity check for pad length; if it, plus 2 for the pad
@@ -906,7 +943,7 @@ esp_print(netdissect_options *ndo,
         * "plaintext" is not what was being sent.
         */
        padlen = GET_U_1(ep - 2);
-       if (padlen + 2 > ctlen) {
+       if (padlen + 2 > ptlen) {
                nd_print_trunc(ndo);
                return;
        }
@@ -916,9 +953,12 @@ esp_print(netdissect_options *ndo,
 
        ND_PRINT(": ");
 
-       /* Now print the payload. */
-       ip_print_demux(ndo, ct, ctlen - (padlen + 2),  ver, fragmented,
+       /* Now dissect the plaintext. */
+       ip_print_demux(ndo, pt, ptlen - (padlen + 2), ver, fragmented,
            ttl_hl, nh, bp2);
+
+       /* Pop the buffer, freeing it. */
+       nd_pop_buffer(ndo);
 #endif
 }
 #ifdef HAVE_LIBCRYPTO
index 8ad8a60f73b4830f76938dbbce1de2f9f178e4a8..1f1cd1bb987bc74f479ce01e9418103af8e5a1f2 100644 (file)
@@ -2697,6 +2697,13 @@ ikev2_e_print(netdissect_options *ndo,
                /* got it decrypted, print stuff inside. */
                ikev2_sub_print(ndo, base, np, ext,
                                ndo->ndo_snapend, phase, doi, proto, depth+1);
+
+               /*
+                * esp_print_decrypt_buffer_by_ikev2 pushed information
+                * on the buffer stack; we're done with the buffer, so
+                * pop it (which frees the buffer)
+                */
+               nd_pop_buffer(ndo);
        }
 #endif
 
diff --git a/print.c b/print.c
index 1669cb3b4db680eeab7ae3a4fda9504b3ea1573b..21182ba26d2604ae51c54587d8a5bb7216933757 100644 (file)
--- a/print.c
+++ b/print.c
@@ -405,6 +405,13 @@ pretty_print_packet(netdissect_options *ndo, const struct pcap_pkthdr *h,
                ND_PRINT(" [|%s]", ndo->ndo_protocol);
        }
 
+       /*
+        * Free all pushed buffers; if we got here by a printer quitting,
+        * we need to release anything that didn't get released because
+        * we longjmped out of the code before it popped a buffer.
+        */
+       nd_pop_all_buffers(ndo);
+
        /*
         * Restore the original snapend, as a printer might have
         * changed it.