From: Guy Harris Date: Sun, 31 Mar 2019 02:26:06 +0000 (-0700) Subject: Introduce a buffer stack, and use it for ESP decryption. X-Git-Tag: tcpdump-4.99-bp~859 X-Git-Url: https://git.tcpdump.org/tcpdump/commitdiff_plain/6da51b3ec9fc858728e5ea981757d05f970958e0 Introduce a buffer stack, and use it for ESP decryption. 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. --- diff --git a/netdissect.c b/netdissect.c index d32de4e9..08a3280c 100644 --- a/netdissect.c +++ b/netdissect.c @@ -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); +} diff --git a/netdissect.h b/netdissect.h index 3bcef726..2893fcee 100644 --- a/netdissect.h +++ b/netdissect.h @@ -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 */ diff --git a/print-esp.c b/print-esp.c index 60749992..151be1d6 100644 --- a/print-esp.c +++ b/print-esp.c @@ -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 diff --git a/print-isakmp.c b/print-isakmp.c index 8ad8a60f..1f1cd1bb 100644 --- a/print-isakmp.c +++ b/print-isakmp.c @@ -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 1669cb3b..21182ba2 100644 --- 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.