mirror of
https://github.com/isc-projects/bind9.git
synced 2026-06-09 07:42:07 -04:00
Reject negative and out-of-range TTLs in dnssec-* tools
strtottl() parsed the operator's TTL string with strtol() and assigned the long directly to dns_ttl_t (uint32_t) with no sign or ERANGE check. The only validation was the "no digits parsed" branch, so a fully-consumed "-1" became UINT32_MAX (~136 years) and was silently written into DNSKEY/key files by dnssec-keygen -L, dnssec-signzone -t, dnssec-settime -L, etc. Any signing pipeline interpolating the TTL from a variable could mint a key with a multi-decade TTL and never see an error. Switch to strtoul(), reject a leading '-' explicitly (strtoul silently negates), check errno == ERANGE, and reject values exceeding UINT32_MAX before handing the result to time_units(). The pre-existing multiplication wrap inside time_units() is tracked separately. Assisted-by: Claude:claude-opus-4-7
This commit is contained in:
parent
ffb5b76dbf
commit
51774decd2
2 changed files with 52 additions and 6 deletions
|
|
@ -17,7 +17,10 @@
|
|||
* DNSSEC Support Routines.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -223,19 +226,35 @@ isnone(const char *str) {
|
|||
dns_ttl_t
|
||||
strtottl(const char *str) {
|
||||
const char *orig = str;
|
||||
dns_ttl_t ttl;
|
||||
const char *p = str;
|
||||
unsigned long val;
|
||||
char *endp;
|
||||
|
||||
if (isnone(str)) {
|
||||
return (dns_ttl_t)0;
|
||||
}
|
||||
|
||||
ttl = strtol(str, &endp, 0);
|
||||
if (ttl == 0 && endp == str) {
|
||||
fatal("TTL must be numeric");
|
||||
/*
|
||||
* strtoul() silently negates a leading '-', producing
|
||||
* ULONG_MAX-class values that then truncate to a near-UINT32_MAX
|
||||
* TTL. Reject the sign explicitly before parsing.
|
||||
*/
|
||||
while (isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
ttl = time_units(ttl, endp, orig);
|
||||
return ttl;
|
||||
if (*p == '-') {
|
||||
fatal("TTL must be non-negative: %s", orig);
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
val = strtoul(str, &endp, 0);
|
||||
if (endp == str) {
|
||||
fatal("TTL must be numeric: %s", orig);
|
||||
}
|
||||
if (errno == ERANGE || val > UINT32_MAX) {
|
||||
fatal("TTL %s out of range (max %u)", orig, UINT32_MAX);
|
||||
}
|
||||
return time_units((dns_ttl_t)val, endp, orig);
|
||||
}
|
||||
|
||||
dst_key_state_t
|
||||
|
|
|
|||
|
|
@ -342,6 +342,33 @@ n=$((n + 1))
|
|||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "checking that a negative DNSKEY TTL is rejected ($n)"
|
||||
ret=0
|
||||
zone=example
|
||||
$KEYGEN -L -1 -a $DEFAULT_ALGORITHM $zone >dnssectools.out.test$n 2>&1 && ret=1
|
||||
grep -q "TTL must be non-negative" dnssectools.out.test$n || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "checking that an out-of-range DNSKEY TTL is rejected ($n)"
|
||||
ret=0
|
||||
zone=example
|
||||
$KEYGEN -L 9999999999 -a $DEFAULT_ALGORITHM $zone >dnssectools.out.test$n 2>&1 && ret=1
|
||||
grep -q "out of range" dnssectools.out.test$n || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "checking that a negative DNSKEY TTL with a unit suffix is rejected ($n)"
|
||||
ret=0
|
||||
zone=example
|
||||
$KEYGEN -L -1d -a $DEFAULT_ALGORITHM $zone >dnssectools.out.test$n 2>&1 && ret=1
|
||||
grep -q "TTL must be non-negative" dnssectools.out.test$n || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "checking that a DS record cannot be generated for a key using an unsupported algorithm ($n)"
|
||||
ret=0
|
||||
zone=example
|
||||
|
|
|
|||
Loading…
Reference in a new issue