Compare commits

...
14 Commits
Author SHA1 Message Date
DL6ER 10fbc39cf5 Return an empty block from blockdata_alloc() for zero length.
738c049 turned the allocation loop in blockdata_alloc_real() from
do/while into for, so a zero length now returns NULL. find_soa() and
extract_addresses() start the chain for an RR with
blockdata_alloc(NULL, 0) and fill it with blockdata_expand(), so they
now fail on every SOA and every RR that is not an address, e.g., MX,
SRV, or SVCB, and those answers are no longer cached. A zero-length
RR also no longer survives cache_recv_insert().

Allocate one empty block for zero length, as before.

Signed-off-by: DL6ER <dl6er@dl6er.de>
2026-11-02 20:46:27 +00:00
Mario Madersbacher 2463798e03 Fix checks on TFTP filenames to avoid directory escape.
Commit d152e81 which fixed escape via pathname of the form "file/.."
introduced a new bug in which pathnames of the form "file/..dir/.."
are not caught.

Note that to be a practical attack, a directory with name
"..somename" has to exist the correct place in the tree.
This seems unlikely on most systems, and a it's difficult to see
how an attacker could create such a directory and not already have
the read access that it gives them.

Thanks to Mario Madersbacher for spotting this, and supplying a very
complete report and patch.
2026-10-26 21:46:00 +00:00
DL6ER a570a139d5 Don't add an empty CNAME alias for a space before a comma.
split() replaces the comma with a space and then writes a NUL over
every space before it, so "a.example ,target.example" becomes
"a.example\0\0target.example". The --cname loop steps over one NUL
and the following spaces only, stops on the second NUL and adds an
empty alias, i.e., a CNAME for the root name. A second such entry is
then rejected as "duplicate CNAME".

Step over the NULs, too.

Signed-off-by: DL6ER <dl6er@dl6er.de>
2026-10-21 22:06:58 +01:00
Simon Kelley 738c049988 Fix OOB heap write in blockdata_read()
A flaw in the blockdata_read() function allows an attacker to overflow
heap buffers. If memory allocation fails whilst reading data from
an IPC pipe, some data is left unread in the pipe. This has the effect of
desynchronising further reads from the pipe and allows an attacker
to overflow a heap buffer.

Note that the attack depends on running the dnsmasq process out of memory
so that calls to malloc() begin to fail. This is difficult to do on
most platforms, unless a small ulimit is set on the size of the
dnsmasq process. This makes practical attacks somewhat difficult.

Thanks to Michel Belleau for finding and characterising this vulnerability.
2026-10-20 23:19:11 +01:00
Simon Kelley 145abca7f6 Fix DNS name buffer overflow.
There are a couple of instances in the DNSSEC validation code where
the code steps along a DNS name to remove some number if left-hand
labels and replace them with * to make a wildcard. The name then
gets passed to to_wire() to translate it in-place to the wire format.

If external parameters dictate that all the labels are removed, and the name
is of exactly the maximum size, then this causes to_wire() to overwrite the
buffer by one byte.

Having created the wildcard, move it to the start of the buffer to avoid
this problem.

Thanks to Lucas Celant <lcelant@redhat.com> for spotting this.

CVE-2026-93011 may apply.
2026-10-20 00:07:34 +01:00
Simon Kelley a627a7847f Fix off-by-one bounds check in DHCPv6 FQDN option.
Thanks to Lucas Celant <lcelant@redhat.com> for spotting this.

CVE-2026-93009 may apply.
2026-10-20 00:07:33 +01:00
Simon Kelley cdbfb97227 Fix logic error in TFTP caching.
The TFTP subsystem prefetches data from disk. If a TFTP transfer
is freed, and a new one allocated the same memory, then previously
prefetched data could be wrongly supplied.

Thanks to Lucas Celant <lcelant@redhat.com> for spotting this.

CVE-2026-93006 may apply.
2026-10-20 00:07:33 +01:00
Simon Kelley 11493fe218 Improve resistance to many simultaneous TCP connections.
Dnsmasq limits the number of simultaneous TCP connections,
to avoid resource-exhaustion attacks. Once the limit if reached,
dnsmasq stops accepting TCP connections from the kernel.

When doing dnssec validation, the same resources are used to
handle internally generated queries with large answers
needed to validate incoming queries which can still arrive via UDP.
If a dnsmasq daemon has reached the limit on incoming
TCP connections then these queries will fail to validate needlessly.

Bump the number of TCP-process resources by 50% when dnssec validation
is enabled, and reserve the extra 50% only for internally-generated
dnssec validation queries. This allows validation to continue even
when dnsmasq is ignoring new TCP requests.

Thanks to Lucas Celant <lcelant@redhat.com> for spotting this.

CVE-2026-93007 may apply.
2026-10-20 00:07:33 +01:00
Simon Kelley 7719737f36 Fix subdomain-test in find_soa()
Do correct subdomain test, rather than a substring match.

Thanks to Lucas Celant <lcelant@redhat.com> for spotting this.

CVE-2026-93008 may apply.
2026-10-20 00:07:33 +01:00
DL6ER 3a3e7a049f Fix the NSEC3 closest encloser check for deeper wildcard answers
`validate_rrset()` returns the start of the closest encloser as an offset
into the owner name, and `prove_non_existence_nsec3()` is meant to stop
its search there. It compares `name - closest_encloser` with that offset,
but `closest_encloser` only moves forward from `name`, so the difference
is never positive and the search never stops at the encloser the RRSIG
labels field gives.

A wildcard answer one label below the closest encloser is not affected,
as the NSEC3 covering the next closer name covers the query name itself.
Two or more labels below, dnsmasq looks for an NSEC3 matching the closest
encloser, which RFC 5155 section 7.2.6 does not require the zone to send,
and returns SERVFAIL with EDE 12 (NSEC Missing). An example is
foo.login.us2.yubico.com, answered from *.us2.yubico.com.

The comparison came with f603a4f ("Fix a corner-case in DNSSEC validation
with wildcards."), which replaced the name compare. Swap the operands.

Signed-off-by: DL6ER <dl6er@dl6er.de>
2026-10-20 00:05:21 +01:00
DL6ER bc80b93cd1 Answer queued TCP queries after a stale answer
When `tcp_request()` answers from stale cache data, it closes the
connection right away, so that the child process can refresh the name
and exit. A query the client has already sent on that connection is
never read, and its answer is lost.

glibc runs into this: after a truncated UDP reply, it sends the A and
AAAA queries together on one TCP connection, gets EOF after the first
answer and fails the lookup without retrying the second query. In a
test with both answers expired, 7 of 20 such lookups lost the second
answer.

Close the connection only if the client has not sent anything else
yet. Otherwise, refresh the name and go on answering the queries on the
connection, as for any other forwarded query.

Signed-off-by: DL6ER <dl6er@dl6er.de>
2026-10-19 23:42:29 +01:00
Simon Kelley 03fd0c19ef Clear down SLAAC-name data when a DHCP lease loses its hostname.
If a DHCPv4 lease loses its hostname, then the DHCPv6 name-address mapping
derived from it become invalid and need to be cleared.

Similarly, when a hostname is added to an existing lease, new
SLAAC-name data needs to be generated.

Thanks Martin Vlach (and Claude) for finding a symptom of this in
the SLAAC logging code. This patch fixes that and does the correct
thing more generally.
2026-10-01 20:43:10 +01:00
Martin VlachandClaude Sonnet 5.5 baeb7430e2 Don't bump arrsize in poll_listen() until the realloc has succeeded.
If whine_realloc() fails in poll_listen(), arrsize has already been
doubled while pollfds keeps its old size. The next call then sees
arrsize != nfds, skips the extension and writes past the end of the
array (or through NULL if the very first allocation failed).
poll_reset() only clears nfds, so this sticks around until restart.

Only update arrsize once the reallocation has succeeded.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 15:40:31 +01:00
朱耀辉 f90df6c532 Fix heap OOB read in zone_status()
Credit to Yaohui Zhu (朱耀辉) for finding this.

CNVD-C-2026-590208 applies.

Summary

zone_status() (src/dnssec.c:1867 in the 2.93 release; the same
construct is in current git master, 2.93-29-gad92083) computes
&name[name_start-2] in its backtrack loop without checking that
name_start >= 2. When name_start == 1 the loop starts at name[-1]
and dereferences it immediately; the guard (p != name) can never
fire for p < name, so the scan walks backwards through the heap
until it finds a '.' byte or hits an unmapped page. Under ASAN this
is a deterministic heap-buffer-overflow READ; on a plain build the
process dies with SIGSEGV.

A single, ordinary query plus one crafted upstream reply is enough.
My reproducer starts the ASAN binary with --dnssec and the root
trust anchor, with a mock upstream; on the first query the mock
answers with a captured crafted reply (a response carrying RRSIG(46),
NSEC(47) with an empty/root owner name, and NSEC3(50) records in the
authority section) and the process crashes on the first query.
2026-10-01 15:34:53 +01:00
14 changed files with 239 additions and 147 deletions

No files matched your search

+4
View File
@@ -8,6 +8,10 @@ version 2.94
constructed DHCPv6/RA - DHCPv6 and RA are inhibited by
--no-dhcpv4-interface when using constructed address ranges.
Thanks to David Nichols for discovery, diagnosis and patch.
Fix heap OOB read in zone_status. Thanks to Yaohui Zhu (朱耀辉)
for spotting and reporting this. A crafted reply can crash dnsmasq.
CNVD-C-2026-590208 applies.
version 2.93
+59 -30
View File
@@ -16,7 +16,9 @@
#include "dnsmasq.h"
static struct blockdata *keyblock_free;
static struct blockdata *new_block(void);
static struct blockdata *keyblock_free, *fallback;
static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced;
static void add_blocks(int n)
@@ -48,7 +50,11 @@ void blockdata_init(void)
/* Note that daemon->cachesize is enforced to have non-zero size if OPT_DNSSEC_VALID is set */
if (option_bool(OPT_DNSSEC_VALID))
add_blocks(daemon->cachesize);
}
/* make sure we have one block to fall back on. */
if (!(fallback = new_block()))
die(_("Blockdata init: no memory"), NULL, EC_MISC);
}
void blockdata_report(void)
{
@@ -84,36 +90,52 @@ static struct blockdata *blockdata_alloc_real(int fd, char *data, size_t len)
struct blockdata *block, *ret = NULL;
struct blockdata **prev = &ret;
size_t blen;
int fail;
do
{
if (!(block = new_block()))
{
/* failed to alloc, free partial chain */
blockdata_free(ret);
return NULL;
}
if ((blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len) > 0)
{
if (data)
{
memcpy(block->key, data, blen);
data += blen;
}
else if (!read_write(fd, block->key, blen, RW_READ))
{
/* failed read free partial chain */
blockdata_free(ret);
return NULL;
}
}
len -= blen;
*prev = block;
prev = &block->next;
} while (len != 0);
/* Zero length still gives one empty block, blockdata_alloc(NULL, 0)
is how callers start a chain to add to with blockdata_expand(). */
if (len == 0)
return new_block();
for (fail = 0; len != 0; len -= blen)
{
blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
if (!fail)
{
if ((block = new_block()))
{
*prev = block;
prev = &block->next;
}
else
{
/* Failed to alloc, use the pre-allocated fall-back.
This allows us to always read the specified amount of data from the fd. */
fail = 1;
block = fallback;
}
}
if (data)
{
memcpy(block->key, data, blen);
data += blen;
}
else if (!read_write(fd, block->key, blen, RW_READ))
{
fail = 1;
break;
}
}
if (fail)
{
/* failed, free partial chain */
blockdata_free(ret);
return NULL;
}
return ret;
}
@@ -235,6 +257,13 @@ void blockdata_write(struct blockdata *block, size_t len, int fd)
}
}
/* NOTE: this can fail (NULL return) for two possible reasons.
1) end of stream.
2) malloc failure when allocating blocks.
Unless EOF is encountered, len octets will always be read from the stream,
even in memory allocation fails.
*/
struct blockdata *blockdata_read(int fd, size_t len)
{
return blockdata_alloc_real(fd, NULL, len);
+12 -3
View File
@@ -1039,9 +1039,18 @@ int cache_recv_insert(time_t now, int fd)
unsigned short class = C_IN;
struct blockdata *block = NULL;
if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG)
&& !(block = addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen)))
continue;
/* Note that blockdata_read() can return NULL either
because the stream ended, OR memory allocation failed.
We continue here rather than returning 0 to finish draining
the pipe in the second case. If the stream ended, the read
of the next length at the top of the loop will fail
and we'll return 0 there. */
if (flags & F_RR)
{
if (!(flags & F_NEG) && (flags & F_KEYTAG) &&
!(block = addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen)))
continue;
}
#ifdef HAVE_DNSSEC
else if (flags & F_DNSKEY)
{
+24 -9
View File
@@ -140,6 +140,9 @@ int main (int argc, char **argv)
/* one char flag per possible RR in answer section (may get extended). */
daemon->rr_status_sz = 64;
daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz);
/* Increase TCP process slots by half, and reserve the extra for validation-to-tcp use only. */
daemon->dnssec_tcp_slot_reserved = daemon->max_procs/2;
daemon->max_procs += daemon->dnssec_tcp_slot_reserved;
}
#endif
@@ -427,7 +430,8 @@ int main (int argc, char **argv)
daemon->tcp_pids = safe_malloc(daemon->max_procs*sizeof(pid_t));
daemon->tcp_pipes = safe_malloc(daemon->max_procs*sizeof(int));
daemon->tcp_type = safe_malloc(daemon->max_procs);
for (i = 0; i < daemon->max_procs; i++)
daemon->tcp_pipes[i] = -1;
}
@@ -1652,7 +1656,11 @@ static void async_event(int pipe, time_t now)
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
if (daemon->tcp_pipes[i] == -1)
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
{
if (daemon->tcp_type[i])
daemon->dnssec_tcp_slot_reserved++;
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
}
}
break;
@@ -1888,7 +1896,7 @@ static void set_dns_listeners(void)
struct serverfd *serverfdp;
struct listener *listener;
struct randfd_list *rfl;
int i;
int i, j;
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
poll_listen(serverfdp->fd, POLLIN);
@@ -1902,8 +1910,8 @@ static void set_dns_listeners(void)
poll_listen(rfl->rfd->fd, POLLIN);
/* check to see if we have free tcp process slots. */
for (i = daemon->max_procs - 1; i >= 0; i--)
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
for (j = 0, i = daemon->max_procs - 1; i >= 0; i--)
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1 && ++j > daemon->dnssec_tcp_slot_reserved)
break;
for (listener = daemon->listeners; listener; listener = listener->next)
@@ -1930,7 +1938,7 @@ static void check_dns_listeners(time_t now)
struct serverfd *serverfdp;
struct listener *listener;
struct randfd_list *rfl;
int i;
int i, j;
/* Note that handling events here can create or destroy fds and
render the result of the last poll() call invalid. Once
@@ -1955,7 +1963,11 @@ static void check_dns_listeners(time_t now)
daemon->tcp_pipes[i] = -1;
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
if (daemon->tcp_pids[i] == 0)
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
{
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
if (daemon->tcp_type[i])
daemon->dnssec_tcp_slot_reserved++;
}
}
return;
}
@@ -1995,8 +2007,8 @@ static void check_dns_listeners(time_t now)
at least one a poll() time, that we still do.
There may be more waiting connections after
poll() returns then free process slots. */
for (i = daemon->max_procs - 1; i >= 0; i--)
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
for (j = 0,i = daemon->max_procs - 1; i >= 0; i--)
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1 && ++j > daemon->dnssec_tcp_slot_reserved)
break;
if (i >= 0)
@@ -2165,6 +2177,7 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
daemon->tcp_pids[slot] = p;
daemon->tcp_pipes[slot] = pipefd[0];
daemon->tcp_type[slot] = 0;
daemon->metrics[METRIC_TCP_CONNECTIONS]++;
if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used)
daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS];
@@ -2306,6 +2319,8 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
/* i holds index of free slot */
daemon->tcp_pids[i] = p;
daemon->tcp_pipes[i] = pipefd[0];
daemon->tcp_type[i] = 1;
daemon->dnssec_tcp_slot_reserved--;
daemon->metrics[METRIC_TCP_CONNECTIONS]++;
if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used)
daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS];
+2 -1
View File
@@ -1296,6 +1296,7 @@ extern struct daemon {
int fd_save; /* " " */
pid_t *tcp_pids;
int *tcp_pipes;
char *tcp_type;
int pipe_to_parent;
int numrrand;
struct randfd *randomsocks;
@@ -1355,7 +1356,7 @@ extern struct daemon {
/* file for packet dumps. */
int dumpfd;
#endif
int max_procs;
int max_procs, dnssec_tcp_slot_reserved;
uint max_procs_used;
} *daemon;
+14 -11
View File
@@ -486,7 +486,6 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
int i, wire_len, sig_len;
const struct nettle_hash *hash;
void *ctx;
char *name_start;
u32 nsigttl, ttl, orig_ttl;
failflags &= ~DNSSEC_FAIL_NOSIG;
@@ -576,13 +575,13 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 10))
return STAT_BOGUS;
name_start = name;
/* if more labels than in RRsig name, hash *.<no labels in rrsig labels field> 4035 5.3.2 */
/* If the name is already the wildcard, we're not going to change it. */
if (labels < name_labels &&
!(name_labels - labels == 1 && name_start[0] == '*' && name_start[1] == '.'))
!(name_labels - labels == 1 && name[0] == '*' && name[1] == '.'))
{
char *name_start = name;
for (j = name_labels - labels; j != 0; j--)
{
while (*name_start != '.' && *name_start != 0)
@@ -595,11 +594,13 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
*wildcard_offset_out = name_start - name + 1;
name_start--;
*name_start = '*';
*name_start = '*';
/* move our name to the start of the buffer to avoid pathalogical cases over-running the end in to_wire(). */
memmove(name, name_start, strlen(name_start) + 1);
}
wire_len = to_wire(name_start);
hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name_start);
wire_len = to_wire(name);
hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name);
hash->update(ctx, 4, p); /* class and type */
hash->update(ctx, 4, (unsigned char *)&nsigttl);
@@ -1643,7 +1644,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
if (*closest_encloser == '.')
closest_encloser++;
if (wild_offset != 0 && name - closest_encloser == wild_offset)
if (wild_offset != 0 && closest_encloser - name == wild_offset)
break;
if (dec_counter(validate_counter, NULL))
@@ -1694,11 +1695,13 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
wildcard--;
*wildcard = '*';
/* move our name to the start of the buffer to avoid pathalogical cases over-running the end in to_wire(). */
memmove(name, wildcard, strlen(wildcard) + 1);
if (dec_counter(validate_counter, NULL))
return DNSSEC_FAIL_WORK;
if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
return DNSSEC_FAIL_NONSEC;
if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL, 1))
@@ -1934,7 +1937,7 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
return STAT_INSECURE;
}
if (name_start == 0)
if (name_start <= 1)
break;
for (p = &name[name_start-2]; (*p != '.') && (p != name); p--);
+23 -7
View File
@@ -2406,7 +2406,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
unsigned char *pheader;
unsigned int mark = 0;
int have_mark = 0;
int first, last, filtered, do_stale = 0;
int first, last, filtered, do_stale = 0, closed = 0;
struct iovec out_iov[2];
bigbuff->iov_base = NULL;
@@ -2742,8 +2742,16 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
}
}
/* Fresh data is in the cache now. Go on with the queries the client
had already sent, if the connection is still open. */
if (do_stale)
break;
{
if (closed)
break;
do_stale = 0;
daemon->log_source_addr = &peer_addr;
continue;
}
/* In case of local answer or no connections made. */
if (m == 0)
@@ -2792,19 +2800,27 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
/* If we answered with stale data, this process will now try and get fresh data into
the cache and cannot therefore accept new queries. Close the incoming
connection to signal that to the client. Then set do_stale and loop round
once more to try and get fresh data, after which we exit. */
once more to try and get fresh data, after which we exit. A client which
has already sent its next query is waiting for the answer and would lose
it, so keep the connection open then and answer it after the refresh. */
if (stale)
{
shutdown(confd, SHUT_RDWR);
close(confd);
char c;
if (recv(confd, &c, 1, MSG_PEEK | MSG_DONTWAIT) != 1)
{
shutdown(confd, SHUT_RDWR);
close(confd);
closed = 1;
}
do_stale = 1;
/* Don't mark the query with the source when refreshing stale data. */
daemon->log_source_addr = NULL;
}
}
/* If we ran once to get fresh data, confd is already closed. */
if (!do_stale)
/* If we ran once to get fresh data, confd may be closed already. */
if (!closed)
{
shutdown(confd, SHUT_RDWR);
close(confd);
+12 -10
View File
@@ -1027,6 +1027,13 @@ static void kill_name(struct dhcp_lease *lease)
lease->old_hostname = lease->hostname;
lease->hostname = lease->fqdn = NULL;
#ifdef HAVE_DHCP6
/* A lease which had a hostname when slaac_add_addrs() was called on it
may have slaac-address records. Since it no longer has a hostname,
they are no-longer valid, so update to remove them */
slaac_add_addrs(lease, 0, 0);
#endif
}
void lease_calc_fqdns(void)
@@ -1151,6 +1158,10 @@ void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, ch
file_dirty = 1;
dns_dirty = 1;
lease->flags |= LEASE_CHANGED; /* run script on change */
#ifdef HAVE_DHCP6
slaac_add_addrs(lease, dnsmasq_time(), 0);
#endif
}
void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now)
@@ -1250,15 +1261,6 @@ int do_script_run(time_t now)
}
else
{
#ifdef HAVE_DHCP6
struct slaac_address *slaac, *tmp;
for (slaac = lease->slaac_address; slaac; slaac = tmp)
{
tmp = slaac->next;
free(slaac);
}
#endif
kill_name(lease);
#ifdef HAVE_SCRIPT
queue_script(ACTION_DEL, lease, lease->old_hostname, now);
#endif
@@ -1267,7 +1269,7 @@ int do_script_run(time_t now)
#endif
old_leases = lease->next;
free(lease->hostname);
kill_name(lease); /* frees slaac_addr, hostname and fqdn */
free(lease->clid);
free(lease->extradata);
free(lease->agent_id);
+1 -1
View File
@@ -5046,7 +5046,7 @@ err:
new->target = target;
new->ttl = ttl;
for (arg += arglen+1; *arg && isspace((unsigned char)*arg); arg++);
for (arg += arglen+1; arg != last && (!*arg || isspace((unsigned char)*arg)); arg++);
}
break;
+3 -3
View File
@@ -100,13 +100,13 @@ void poll_listen(int fd, short event)
{
/* Array too small. Extend. */
struct pollfd *new;
nfds_t newsize = (arrsize == 0) ? 64 : arrsize * 2;
arrsize = (arrsize == 0) ? 64 : arrsize * 2;
if (!(new = whine_realloc(pollfds, arrsize * sizeof(struct pollfd))))
if (!(new = whine_realloc(pollfds, newsize * sizeof(struct pollfd))))
return;
pollfds = new;
arrsize = newsize;
}
memmove(&pollfds[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
+1 -1
View File
@@ -560,7 +560,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
soa_len = strlen(daemon->workspacename);
/* SOA must be for the name we're interested in. */
if (soa_len <= name_len && memcmp(daemon->workspacename, name + name_len - soa_len, soa_len) == 0)
if (hostname_issubdomain(daemon->workspacename, name) != 0)
{
int prefix = name_len - soa_len;
+1 -1
View File
@@ -502,7 +502,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
state->fqdn_flags &= ~0x04;
if (len != 0 && len < 255)
if (len >= 1 && len < 255)
{
unsigned char *pp, *op = opt6_ptr(opt, 1);
char *pq = daemon->dhcp_buff;
+67 -67
View File
@@ -27,87 +27,87 @@ void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
struct slaac_address *slaac, *old, **up;
struct dhcp_context *context;
int dns_dirty = 0;
if (!(lease->flags & LEASE_HAVE_HWADDR) ||
(lease->flags & (LEASE_TA | LEASE_NA)) ||
lease->last_interface == 0 ||
!lease->hostname)
return ;
old = lease->slaac_address;
lease->slaac_address = NULL;
for (context = daemon->dhcp6; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME) &&
!(context->flags & CONTEXT_OLD) &&
lease->last_interface == context->if_index)
{
struct in6_addr addr = context->start6;
if (lease->hwaddr_len == 6 &&
(lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802))
if ((lease->flags & LEASE_HAVE_HWADDR) &&
!(lease->flags & (LEASE_TA | LEASE_NA)) &&
lease->last_interface != 0 &&
lease->hostname)
{
for (context = daemon->dhcp6; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME) &&
!(context->flags & CONTEXT_OLD) &&
lease->last_interface == context->if_index)
{
/* convert MAC address to EUI-64 */
memcpy(&addr.s6_addr[8], lease->hwaddr, 3);
memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3);
addr.s6_addr[11] = 0xff;
addr.s6_addr[12] = 0xfe;
}
struct in6_addr addr = context->start6;
if (lease->hwaddr_len == 6 &&
(lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802))
{
/* convert MAC address to EUI-64 */
memcpy(&addr.s6_addr[8], lease->hwaddr, 3);
memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3);
addr.s6_addr[11] = 0xff;
addr.s6_addr[12] = 0xfe;
}
#if defined(ARPHRD_EUI64)
else if (lease->hwaddr_len == 8 &&
lease->hwaddr_type == ARPHRD_EUI64)
memcpy(&addr.s6_addr[8], lease->hwaddr, 8);
else if (lease->hwaddr_len == 8 &&
lease->hwaddr_type == ARPHRD_EUI64)
memcpy(&addr.s6_addr[8], lease->hwaddr, 8);
#endif
#if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64)
else if (lease->clid_len == 9 &&
lease->clid[0] == ARPHRD_EUI64 &&
lease->hwaddr_type == ARPHRD_IEEE1394)
/* FireWire has EUI-64 identifier as clid */
memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
else if (lease->clid_len == 9 &&
lease->clid[0] == ARPHRD_EUI64 &&
lease->hwaddr_type == ARPHRD_IEEE1394)
/* FireWire has EUI-64 identifier as clid */
memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
#endif
else
continue;
addr.s6_addr[8] ^= 0x02;
/* check if we already have this one */
for (up = &old, slaac = old; slaac; slaac = slaac->next)
{
if (IN6_ARE_ADDR_EQUAL(&addr, &slaac->addr))
{
*up = slaac->next;
/* recheck when DHCPv4 goes through init-reboot */
if (force)
{
slaac->ping_time = now;
slaac->backoff = 1;
dns_dirty = 1;
}
break;
}
up = &slaac->next;
}
else
continue;
/* No, make new one */
if (!slaac && (slaac = whine_malloc(sizeof(struct slaac_address))))
{
slaac->ping_time = now;
slaac->backoff = 1;
slaac->addr = addr;
/* Do RA's to prod it */
ra_start_unsolicited(now, context);
addr.s6_addr[8] ^= 0x02;
/* check if we already have this one */
for (up = &old, slaac = old; slaac; slaac = slaac->next)
{
if (IN6_ARE_ADDR_EQUAL(&addr, &slaac->addr))
{
*up = slaac->next;
/* recheck when DHCPv4 goes through init-reboot */
if (force)
{
slaac->ping_time = now;
slaac->backoff = 1;
dns_dirty = 1;
}
break;
}
up = &slaac->next;
}
/* No, make new one */
if (!slaac && (slaac = whine_malloc(sizeof(struct slaac_address))))
{
slaac->ping_time = now;
slaac->backoff = 1;
slaac->addr = addr;
/* Do RA's to prod it */
ra_start_unsolicited(now, context);
}
if (slaac)
{
slaac->next = lease->slaac_address;
lease->slaac_address = slaac;
}
}
if (slaac)
{
slaac->next = lease->slaac_address;
lease->slaac_address = slaac;
}
}
}
if (old || dns_dirty)
lease_update_dns(1);
/* Free any no reused */
/* Free any not reused */
for (; old; old = slaac)
{
slaac = old->next;
+16 -3
View File
@@ -545,10 +545,17 @@ static struct tftp_file *check_tftp_fileperm(char *packet, ssize_t *len, char *p
/* trick to ban moving out of the subtree */
if (prefix)
{
char *suspect = strstr(namebuff, "/..");
char *suspect = namebuff;
if (suspect && (suspect[3] == '/' || suspect[3] == 0))
goto perm;
/* Every occurrence has to be checked: a harmless leading match
such as "/..data" otherwise hides a later "/../" from here. */
while ((suspect = strstr(suspect, "/..")))
{
if (suspect[3] == '/' || suspect[3] == 0)
goto perm;
suspect += 3;
}
}
if ((fd = open(namebuff, O_RDONLY)) == -1)
@@ -836,6 +843,12 @@ static void free_transfer(struct tftp_transfer *transfer)
close(transfer->file->fd);
free(transfer->file);
}
/* freeing this transfer and then allocating a new one
may result in the same memory, but the contents of
the transfer is no longer valid to cache. */
if (daemon->srv_save == transfer)
daemon->srv_save = NULL;
free(transfer);
}