diff --git a/src/dnsmasq.c b/src/dnsmasq.c index db3ff5a..cc199e2 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -118,8 +118,9 @@ int main (int argc, char **argv) daemon->namebuff = safe_malloc(MAXDNAME * 2); daemon->keyname = safe_malloc(MAXDNAME * 2); daemon->workspacename = safe_malloc(MAXDNAME * 2); - /* one char flag per possible RR in answer section. */ - daemon->rr_status = safe_malloc(256); + /* one char flag per possible RR in answer section (may get extended). */ + daemon->rr_status_sz = 64; + daemon->rr_status = safe_malloc(daemon->rr_status_sz); } #endif diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 5b678bd..036597f 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1030,7 +1030,8 @@ extern struct daemon { #ifdef HAVE_DNSSEC char *keyname; /* MAXDNAME size buffer */ char *workspacename; /* ditto */ - char *rr_status; /* 256 bytes as flags for individual RRs */ + char *rr_status; /* flags for individual RRs */ + int rr_status_sz; #endif unsigned int local_answer, queries_forwarded, auth_answer; struct frec *frec_list; @@ -1145,7 +1146,7 @@ size_t setup_reply(struct dns_header *header, size_t qlen, unsigned long ttl); int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, char **ipsets, int is_sign, int check_rebind, - int no_cache_dnssec, int secure, int *doctored, char *rr_status); + int no_cache_dnssec, int secure, int *doctored); size_t answer_request(struct dns_header *header, char *limit, size_t qlen, struct in_addr local_addr, struct in_addr local_netmask, time_t now, int ad_reqd, int do_bit, int have_pseudoheader); @@ -1178,7 +1179,7 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, - int check_unsigned, int *neganswer, int *nons, char *rr_status); + int check_unsigned, int *neganswer, int *nons); int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen); size_t filter_rrsigs(struct dns_header *header, size_t plen); unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name); diff --git a/src/dnssec.c b/src/dnssec.c index 5b6e095..5fc1714 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -859,7 +859,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char if (qtype != T_DS || qclass != class) rc = STAT_BOGUS; else - rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, NULL); + rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons); if (rc == STAT_INSECURE) rc = STAT_BOGUS; @@ -1645,11 +1645,11 @@ static int zone_status(char *name, int class, char *keyname, time_t now) STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname, class in *class) STAT_NEED_DS need DS to complete validation (name is returned in keyname) - If non-NULL, rr_status points to a char array which corressponds to the RRs in the + daemon->rr_status points to a char array which corressponds to the RRs in the answer section (only). This is set to 1 for each RR which is validated, and 0 for any which aren't. */ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, - int *class, int check_unsigned, int *neganswer, int *nons, char *rr_status) + int *class, int check_unsigned, int *neganswer, int *nons) { static unsigned char **targets = NULL; static int target_sz = 0; @@ -1659,8 +1659,20 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch int i, j, rc; int secure = STAT_SECURE; - if (rr_status) - memset(rr_status, 0, ntohs(header->ancount)); + /* extend rr_status if necessary */ + if (daemon->rr_status_sz < ntohs(header->ancount)) + { + char *new = whine_malloc(ntohs(header->ancount) + 64); + + if (!new) + return STAT_BOGUS; + + free(daemon->rr_status); + daemon->rr_status = new; + daemon->rr_status_sz = ntohs(header->ancount) + 64; + } + + memset(daemon->rr_status, 0, ntohs(header->ancount)); if (neganswer) *neganswer = 0; @@ -1754,8 +1766,8 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch if (j != i) { /* Done already: copy the validation status */ - if (rr_status && (i < ntohs(header->ancount))) - rr_status[i] = rr_status[j]; + if (i < ntohs(header->ancount)) + daemon->rr_status[i] = daemon->rr_status[j]; } else { @@ -1814,8 +1826,8 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch /* rc is now STAT_SECURE or STAT_SECURE_WILDCARD */ /* Note that RR is validated */ - if (rr_status && (i < ntohs(header->ancount))) - rr_status[i] = 1; + if (i < ntohs(header->ancount)) + daemon->rr_status[i] = 1; /* Note if we've validated either the answer to the question or the target of a CNAME. Any not noted will need NSEC or diff --git a/src/forward.c b/src/forward.c index 4824759..9ef0e02 100644 --- a/src/forward.c +++ b/src/forward.c @@ -560,7 +560,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server char **sets = 0; int munged = 0, is_sign; size_t plen; - char *rr_status = NULL; (void)ad_reqd; (void)do_bit; @@ -651,11 +650,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server server->flags |= SERV_WARNED_RECURSIVE; } -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID)) - rr_status = daemon->rr_status; -#endif - if (daemon->bogus_addr && RCODE(header) != NXDOMAIN && check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now)) { @@ -681,7 +675,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server cache_secure = 0; } - if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored, rr_status)) + if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored)) { my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff); munged = 1; @@ -906,7 +900,7 @@ void reply_query(int fd, int family, time_t now) else status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, option_bool(OPT_DNSSEC_NO_SIGN) && (server->flags & SERV_DO_DNSSEC), - NULL, NULL, daemon->rr_status); + NULL, NULL); } /* Can't validate, as we're missing key data. Put this @@ -1491,7 +1485,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si else new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, option_bool(OPT_DNSSEC_NO_SIGN) && (server->flags & SERV_DO_DNSSEC), - NULL, NULL, daemon->rr_status); + NULL, NULL); if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY) break; diff --git a/src/rfc1035.c b/src/rfc1035.c index 12b3b95..11bfc49 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -585,7 +585,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc Return 1 if we reject an address because it look like part of dns-rebinding attack. */ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec, - int secure, int *doctored, char *rr_status) + int secure, int *doctored) { unsigned char *p, *p1, *endrr, *namep; int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0; @@ -610,9 +610,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t { if (secure) return 0; - if (rr_status) + if (option_bool(OPT_DNSSEC_VALID)) for (i = 0; i < ntohs(header->ancount); i++) - if (rr_status[i]) + if (daemon->rr_status[i]) return 0; } } @@ -682,7 +682,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (!extract_name(header, qlen, &p1, name, 1, 0)) return 0; - if (rr_status && rr_status[j]) + if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j]) { /* validated RR anywhere in CNAME chain, don't cache. */ if (cname_short || aqtype == T_CNAME) @@ -766,7 +766,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype)) { #ifdef HAVE_DNSSEC - if (rr_status && rr_status[j]) + if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j]) secflag = F_DNSSECOK; #endif if (aqtype == T_CNAME)