From 1663ab541b37675dec5bcf235605568ff36ac65a Mon Sep 17 00:00:00 2001 From: Qualys Security Advisory Date: Sun, 21 Feb 2021 22:36:10 -0800 Subject: [PATCH 22/29] CVE-2020-28019: Failure to reset function pointer after BDAT error Based on Phil Pennock's commits 4715403e and 151ffd72, and Jeremy Harris's commits aa171254 and 9aceb5c2. --- src/globals.c | 1 + src/globals.h | 1 + src/smtp_in.c | 55 +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/globals.c b/src/globals.c index b3362a34c..894b8487b 100644 --- a/src/globals.c +++ b/src/globals.c @@ -247,6 +247,7 @@ struct global_flags f = .authentication_local = FALSE, .background_daemon = TRUE, + .bdat_readers_wanted = FALSE, .chunking_offered = FALSE, .config_changed = FALSE, diff --git a/src/globals.h b/src/globals.h index f71f104e2..58f7ae55f 100644 --- a/src/globals.h +++ b/src/globals.h @@ -173,6 +173,7 @@ extern struct global_flags { BOOL authentication_local :1; /* TRUE if non-smtp (implicit authentication) */ BOOL background_daemon :1; /* Set FALSE to keep in foreground */ + BOOL bdat_readers_wanted :1; /* BDAT-handling to be pushed on readfunc stack */ BOOL chunking_offered :1; BOOL config_changed :1; /* True if -C used */ diff --git a/src/smtp_in.c b/src/smtp_in.c index 1a5fbfea3..016c44c0f 100644 --- a/src/smtp_in.c +++ b/src/smtp_in.c @@ -602,6 +602,10 @@ if (n > 0) #endif } +/* Forward declarations */ +static inline void bdat_push_receive_functions(void); +static inline void bdat_pop_receive_functions(void); + /* Get a byte from the smtp input, in CHUNKING mode. Handle ack of the previous BDAT chunk and getting new ones when we run out. Uses the @@ -634,9 +638,7 @@ for(;;) if (chunking_data_left > 0) return lwr_receive_getc(chunking_data_left--); - receive_getc = lwr_receive_getc; - receive_getbuf = lwr_receive_getbuf; - receive_ungetc = lwr_receive_ungetc; + bdat_pop_receive_functions(); #ifndef DISABLE_DKIM dkim_save = dkim_collect_input; dkim_collect_input = 0; @@ -740,9 +742,7 @@ next_cmd: goto repeat_until_rset; } - receive_getc = bdat_getc; - receive_getbuf = bdat_getbuf; /* r~getbuf is never actually used */ - receive_ungetc = bdat_ungetc; + bdat_push_receive_functions(); #ifndef DISABLE_DKIM dkim_collect_input = dkim_save; #endif @@ -775,9 +775,7 @@ while (chunking_data_left) if (!bdat_getbuf(&n)) break; } -receive_getc = lwr_receive_getc; -receive_getbuf = lwr_receive_getbuf; -receive_ungetc = lwr_receive_ungetc; +bdat_pop_receive_functions(); if (chunking_state != CHUNKING_LAST) { @@ -787,6 +785,45 @@ if (chunking_state != CHUNKING_LAST) } +static inline void +bdat_push_receive_functions(void) +{ +/* push the current receive_* function on the "stack", and +replace them by bdat_getc(), which in turn will use the lwr_receive_* +functions to do the dirty work. */ +if (lwr_receive_getc == NULL) + { + lwr_receive_getc = receive_getc; + lwr_receive_getbuf = receive_getbuf; + lwr_receive_ungetc = receive_ungetc; + } +else + { + DEBUG(D_receive) debug_printf("chunking double-push receive functions\n"); + } + +receive_getc = bdat_getc; +receive_getbuf = bdat_getbuf; +receive_ungetc = bdat_ungetc; +} + +static inline void +bdat_pop_receive_functions(void) +{ +if (lwr_receive_getc == NULL) + { + DEBUG(D_receive) debug_printf("chunking double-pop receive functions\n"); + return; + } + +receive_getc = lwr_receive_getc; +receive_getbuf = lwr_receive_getbuf; +receive_ungetc = lwr_receive_ungetc; + +lwr_receive_getc = NULL; +lwr_receive_getbuf = NULL; +lwr_receive_ungetc = NULL; +} /************************************************* -- 2.30.2