From: W.C.A. Wijngaards Date: Thu, 3 Mar 2022 13:19:59 +0000 (+0100) Subject: - Fix #637: Integer Overflow in sldns_str2period function. X-Git-Tag: release-1.16.0rc1~30 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=debe5c665f5a14f43a712c76bb252becb50b76ce;p=thirdparty%2Funbound.git - Fix #637: Integer Overflow in sldns_str2period function. --- diff --git a/doc/Changelog b/doc/Changelog index ff410c212..2236e5550 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +3 March 2022: Wouter + - Fix #637: Integer Overflow in sldns_str2period function. + 2 March 2022: George - Merge PR #632 from scottrw93: Match cnames in ipset. - Various fixes for #632: variable initialisation, convert the qinfo diff --git a/services/authzone.c b/services/authzone.c index 66d118b02..b8ce8e73b 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -4456,7 +4456,7 @@ chunkline_get_line_collated(struct auth_chunk** chunk, size_t* chunk_pos, return 1; } -/** process $ORIGIN for http */ +/** process $ORIGIN for http, 0 nothing, 1 done, 2 error */ static int http_parse_origin(sldns_buffer* buf, struct sldns_file_parse_state* pstate) { @@ -4467,13 +4467,16 @@ http_parse_origin(sldns_buffer* buf, struct sldns_file_parse_state* pstate) pstate->origin_len = sizeof(pstate->origin); s = sldns_str2wire_dname_buf(sldns_strip_ws(line+8), pstate->origin, &pstate->origin_len); - if(s) pstate->origin_len = 0; + if(s) { + pstate->origin_len = 0; + return 2; + } return 1; } return 0; } -/** process $TTL for http */ +/** process $TTL for http, 0 nothing, 1 done, 2 error */ static int http_parse_ttl(sldns_buffer* buf, struct sldns_file_parse_state* pstate) { @@ -4481,8 +4484,12 @@ http_parse_ttl(sldns_buffer* buf, struct sldns_file_parse_state* pstate) if(strncmp(line, "$TTL", 4) == 0 && isspace((unsigned char)line[4])) { const char* end = NULL; + int overflow = 0; pstate->default_ttl = sldns_str2period( - sldns_strip_ws(line+5), &end); + sldns_strip_ws(line+5), &end, &overflow); + if(overflow) { + return 2; + } return 1; } return 0; @@ -4493,15 +4500,20 @@ static int chunkline_non_comment_RR(struct auth_chunk** chunk, size_t* chunk_pos, sldns_buffer* buf, struct sldns_file_parse_state* pstate) { + int ret; while(chunkline_get_line_collated(chunk, chunk_pos, buf)) { if(chunkline_is_comment_line_or_empty(buf)) { /* a comment, go to next line */ continue; } - if(http_parse_origin(buf, pstate)) { + if((ret=http_parse_origin(buf, pstate))!=0) { + if(ret == 2) + return 0; continue; /* $ORIGIN has been handled */ } - if(http_parse_ttl(buf, pstate)) { + if((ret=http_parse_ttl(buf, pstate))!=0) { + if(ret == 2) + return 0; continue; /* $TTL has been handled */ } return 1; @@ -5007,6 +5019,7 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z, struct sldns_file_parse_state pstate; struct auth_chunk* chunk; size_t chunk_pos; + int ret; memset(&pstate, 0, sizeof(pstate)); pstate.default_ttl = 3600; if(xfr->namelen < sizeof(pstate.origin)) { @@ -5063,10 +5076,24 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z, continue; } /* parse line and add RR */ - if(http_parse_origin(scratch_buffer, &pstate)) { + if((ret=http_parse_origin(scratch_buffer, &pstate))!=0) { + if(ret == 2) { + verbose(VERB_ALGO, "error parsing ORIGIN on line [%s:%d] %s", + xfr->task_transfer->master->file, + pstate.lineno, + sldns_buffer_begin(scratch_buffer)); + return 0; + } continue; /* $ORIGIN has been handled */ } - if(http_parse_ttl(scratch_buffer, &pstate)) { + if((ret=http_parse_ttl(scratch_buffer, &pstate))!=0) { + if(ret == 2) { + verbose(VERB_ALGO, "error parsing TTL on line [%s:%d] %s", + xfr->task_transfer->master->file, + pstate.lineno, + sldns_buffer_begin(scratch_buffer)); + return 0; + } continue; /* $TTL has been handled */ } if(!http_parse_add_rr(xfr, z, scratch_buffer, &pstate)) { diff --git a/sldns/parseutil.c b/sldns/parseutil.c index ba71df55d..e69c568db 100644 --- a/sldns/parseutil.c +++ b/sldns/parseutil.c @@ -209,11 +209,13 @@ sldns_hexdigit_to_int(char ch) } uint32_t -sldns_str2period(const char *nptr, const char **endptr) +sldns_str2period(const char *nptr, const char **endptr, int* overflow) { int sign = 0; uint32_t i = 0; uint32_t seconds = 0; + const uint32_t maxint = 0xffffffff; + *overflow = 0; for(*endptr = nptr; **endptr; (*endptr)++) { switch (**endptr) { @@ -236,26 +238,46 @@ sldns_str2period(const char *nptr, const char **endptr) break; case 's': case 'S': + if(seconds > maxint-i) { + *overflow = 1; + return 0; + } seconds += i; i = 0; break; case 'm': case 'M': + if(i > maxint/60 || seconds > maxint-(i*60)) { + *overflow = 1; + return 0; + } seconds += i * 60; i = 0; break; case 'h': case 'H': + if(i > maxint/(60*60) || seconds > maxint-(i*60*60)) { + *overflow = 1; + return 0; + } seconds += i * 60 * 60; i = 0; break; case 'd': case 'D': + if(i > maxint/(60*60*24) || seconds > maxint-(i*60*60*24)) { + *overflow = 1; + return 0; + } seconds += i * 60 * 60 * 24; i = 0; break; case 'w': case 'W': + if(i > maxint/(60*60*24*7) || seconds > maxint-(i*60*60*24*7)) { + *overflow = 1; + return 0; + } seconds += i * 60 * 60 * 24 * 7; i = 0; break; @@ -269,15 +291,27 @@ sldns_str2period(const char *nptr, const char **endptr) case '7': case '8': case '9': + if(i > maxint/10 || i > maxint - (**endptr - '0')) { + *overflow = 1; + return 0; + } i *= 10; i += (**endptr - '0'); break; default: + if(seconds > maxint-i) { + *overflow = 1; + return 0; + } seconds += i; /* disregard signedness */ return seconds; } } + if(seconds > maxint-i) { + *overflow = 1; + return 0; + } seconds += i; /* disregard signedness */ return seconds; diff --git a/sldns/parseutil.h b/sldns/parseutil.h index 208fd2fbc..683f34e23 100644 --- a/sldns/parseutil.h +++ b/sldns/parseutil.h @@ -74,9 +74,11 @@ struct tm * sldns_serial_arithmetics_gmtime_r(int32_t time, time_t now, struct t * converts a ttl value (like 5d2h) to a long. * \param[in] nptr the start of the string * \param[out] endptr points to the last char in case of error + * \param[out] overflow returns if the string causes integer overflow error, + * the number is too big, string of digits too long. * \return the convert duration value */ -uint32_t sldns_str2period(const char *nptr, const char **endptr); +uint32_t sldns_str2period(const char *nptr, const char **endptr, int* overflow); /** * Returns the int value of the given (hex) digit diff --git a/sldns/str2wire.c b/sldns/str2wire.c index 4e9da2b10..09dedb0d7 100644 --- a/sldns/str2wire.c +++ b/sldns/str2wire.c @@ -249,11 +249,16 @@ rrinternal_get_ttl(sldns_buffer* strbuf, char* token, size_t token_len, int* not_there, uint32_t* ttl, uint32_t default_ttl) { const char* endptr; + int overflow; if(sldns_bget_token(strbuf, token, "\t\n ", token_len) == -1) { return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_TTL, sldns_buffer_position(strbuf)); } - *ttl = (uint32_t) sldns_str2period(token, &endptr); + *ttl = (uint32_t) sldns_str2period(token, &endptr, &overflow); + if(overflow) { + return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW, + sldns_buffer_position(strbuf)); + } if (strlen(token) > 0 && !isdigit((unsigned char)token[0])) { *not_there = 1; @@ -1055,12 +1060,15 @@ int sldns_fp2wire_rr_buf(FILE* in, uint8_t* rr, size_t* len, size_t* dname_len, return s; } else if(strncmp(line, "$TTL", 4) == 0 && isspace((unsigned char)line[4])) { const char* end = NULL; + int overflow = 0; strlcpy((char*)rr, line, *len); *len = 0; *dname_len = 0; if(!parse_state) return LDNS_WIREPARSE_ERR_OK; parse_state->default_ttl = sldns_str2period( - sldns_strip_ws(line+5), &end); + sldns_strip_ws(line+5), &end, &overflow); + if(overflow) + return LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW; } else if (strncmp(line, "$INCLUDE", 8) == 0) { strlcpy((char*)rr, line, *len); *len = 0; @@ -2157,9 +2165,13 @@ int sldns_str2wire_tsigtime_buf(const char* str, uint8_t* rd, size_t* len) int sldns_str2wire_period_buf(const char* str, uint8_t* rd, size_t* len) { const char* end; - uint32_t p = sldns_str2period(str, &end); + int overflow; + uint32_t p = sldns_str2period(str, &end, &overflow); if(*end != 0) return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_PERIOD, end-str); + if(overflow) + return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW, + end-str); if(*len < 4) return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; sldns_write_uint32(rd, p);