Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
D
Dnsmasq
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nanahira
Dnsmasq
Commits
7fa836e1
Commit
7fa836e1
authored
Feb 10, 2014
by
Simon Kelley
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Handle validation when more one key is needed.
parent
1633e308
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
142 additions
and
119 deletions
+142
-119
src/config.h
src/config.h
+1
-0
src/dnsmasq.h
src/dnsmasq.h
+1
-1
src/forward.c
src/forward.c
+140
-118
No files found.
src/config.h
View file @
7fa836e1
...
@@ -19,6 +19,7 @@
...
@@ -19,6 +19,7 @@
#define CHILD_LIFETIME 150
/* secs 'till terminated (RFC1035 suggests > 120s) */
#define CHILD_LIFETIME 150
/* secs 'till terminated (RFC1035 suggests > 120s) */
#define EDNS_PKTSZ 4096
/* default max EDNS.0 UDP packet from RFC5625 */
#define EDNS_PKTSZ 4096
/* default max EDNS.0 UDP packet from RFC5625 */
#define KEYBLOCK_LEN 35
/* choose to mininise fragmentation when storing DNSSEC keys */
#define KEYBLOCK_LEN 35
/* choose to mininise fragmentation when storing DNSSEC keys */
#define DNSSEC_WORK 50
/* Max number of queries to validate one question */
#define TIMEOUT 10
/* drop UDP queries after TIMEOUT seconds */
#define TIMEOUT 10
/* drop UDP queries after TIMEOUT seconds */
#define FORWARD_TEST 50
/* try all servers every 50 queries */
#define FORWARD_TEST 50
/* try all servers every 50 queries */
#define FORWARD_TIME 20
/* or 20 seconds */
#define FORWARD_TIME 20
/* or 20 seconds */
...
...
src/dnsmasq.h
View file @
7fa836e1
...
@@ -560,7 +560,7 @@ struct frec {
...
@@ -560,7 +560,7 @@ struct frec {
time_t
time
;
time_t
time
;
unsigned
char
*
hash
[
HASH_SIZE
];
unsigned
char
*
hash
[
HASH_SIZE
];
#ifdef HAVE_DNSSEC
#ifdef HAVE_DNSSEC
int
class
;
int
class
,
work_counter
;
struct
blockdata
*
stash
;
/* Saved reply, whilst we validate */
struct
blockdata
*
stash
;
/* Saved reply, whilst we validate */
size_t
stash_len
;
size_t
stash_len
;
struct
frec
*
dependent
;
/* Query awaiting internally-generated DNSKEY or DS query */
struct
frec
*
dependent
;
/* Query awaiting internally-generated DNSKEY or DS query */
...
...
src/forward.c
View file @
7fa836e1
...
@@ -331,6 +331,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
...
@@ -331,6 +331,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward
->
flags
|=
FREC_NOREBIND
;
forward
->
flags
|=
FREC_NOREBIND
;
if
(
header
->
hb4
&
HB4_CD
)
if
(
header
->
hb4
&
HB4_CD
)
forward
->
flags
|=
FREC_CHECKING_DISABLED
;
forward
->
flags
|=
FREC_CHECKING_DISABLED
;
#ifdef HAVE_DNSSEC
forward
->
work_counter
=
DNSSEC_WORK
;
#endif
header
->
id
=
htons
(
forward
->
new_id
);
header
->
id
=
htons
(
forward
->
new_id
);
...
@@ -772,15 +775,31 @@ void reply_query(int fd, int family, time_t now)
...
@@ -772,15 +775,31 @@ void reply_query(int fd, int family, time_t now)
status
=
dnssec_validate_ds
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
forward
->
class
);
status
=
dnssec_validate_ds
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
forward
->
class
);
else
else
status
=
dnssec_validate_reply
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
&
forward
->
class
);
status
=
dnssec_validate_reply
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
&
forward
->
class
);
/* Can't validate, as we're missing key data. Put this
/* Can't validate, as we're missing key data. Put this
answer aside, whilst we get that. */
answer aside, whilst we get that. */
if
(
status
==
STAT_NEED_DS
||
status
==
STAT_NEED_KEY
)
if
(
status
==
STAT_NEED_DS
||
status
==
STAT_NEED_KEY
)
{
{
struct
frec
*
new
;
struct
frec
*
new
,
*
orig
;
/* Free any saved query */
if
(
forward
->
stash
)
blockdata_free
(
forward
->
stash
);
/* Now save reply pending receipt of key data */
if
(
!
(
forward
->
stash
=
blockdata_alloc
((
char
*
)
header
,
n
)))
return
;
forward
->
stash_len
=
n
;
if
((
new
=
get_new_frec
(
now
,
NULL
,
1
)))
anotherkey:
/* Find the original query that started it all.... */
for
(
orig
=
forward
;
orig
->
dependent
;
orig
=
orig
->
dependent
);
if
(
--
orig
->
work_counter
==
0
||
!
(
new
=
get_new_frec
(
now
,
NULL
,
1
)))
status
=
STAT_INSECURE
;
else
{
{
int
fd
;
struct
frec
*
next
=
new
->
next
;
struct
frec
*
next
=
new
->
next
;
*
new
=
*
forward
;
/* copy everything, then overwrite */
*
new
=
*
forward
;
/* copy everything, then overwrite */
new
->
next
=
next
;
new
->
next
=
next
;
...
@@ -791,80 +810,67 @@ void reply_query(int fd, int family, time_t now)
...
@@ -791,80 +810,67 @@ void reply_query(int fd, int family, time_t now)
#endif
#endif
new
->
flags
&=
~
(
FREC_DNSKEY_QUERY
|
FREC_DS_QUERY
);
new
->
flags
&=
~
(
FREC_DNSKEY_QUERY
|
FREC_DS_QUERY
);
/* Free any saved query */
new
->
dependent
=
forward
;
/* to find query awaiting new one. */
if
(
forward
->
stash
)
forward
->
blocking_query
=
new
;
/* for garbage cleaning */
blockdata_free
(
forward
->
stash
);
/* validate routines leave name of required record in daemon->keyname */
if
(
status
==
STAT_NEED_KEY
)
{
new
->
flags
|=
FREC_DNSKEY_QUERY
;
nn
=
dnssec_generate_query
(
header
,
((
char
*
)
header
)
+
daemon
->
packet_buff_sz
,
daemon
->
keyname
,
forward
->
class
,
T_DNSKEY
,
&
server
->
addr
);
}
else
{
new
->
flags
|=
FREC_DS_QUERY
;
nn
=
dnssec_generate_query
(
header
,((
char
*
)
header
)
+
daemon
->
packet_buff_sz
,
daemon
->
keyname
,
forward
->
class
,
T_DS
,
&
server
->
addr
);
}
if
((
hash
=
hash_questions
(
header
,
nn
,
daemon
->
namebuff
)))
memcpy
(
new
->
hash
,
hash
,
HASH_SIZE
);
new
->
new_id
=
get_id
();
header
->
id
=
htons
(
new
->
new_id
);
/* Save query for retransmission */
new
->
stash
=
blockdata_alloc
((
char
*
)
header
,
nn
);
new
->
stash_len
=
nn
;
/* Don't resend this. */
daemon
->
srv_save
=
NULL
;
/* Now save reply pending receipt of key data */
if
(
server
->
sfd
)
if
(
!
(
forward
->
stash
=
blockdata_alloc
((
char
*
)
header
,
n
)))
fd
=
server
->
sfd
->
fd
;
free_frec
(
new
);
/* malloc failure, unwind */
else
else
{
{
int
fd
;
fd
=
-
1
;
#ifdef HAVE_IPV6
forward
->
stash_len
=
n
;
if
(
server
->
addr
.
sa
.
sa_family
==
AF_INET6
)
new
->
dependent
=
forward
;
/* to find query awaiting new one. */
forward
->
blocking_query
=
new
;
/* for garbage cleaning */
/* validate routines leave name of required record in daemon->keyname */
if
(
status
==
STAT_NEED_KEY
)
{
new
->
flags
|=
FREC_DNSKEY_QUERY
;
nn
=
dnssec_generate_query
(
header
,
((
char
*
)
header
)
+
daemon
->
packet_buff_sz
,
daemon
->
keyname
,
forward
->
class
,
T_DNSKEY
,
&
server
->
addr
);
}
else
{
{
new
->
flags
|=
FREC_DS_QUERY
;
if
(
new
->
rfd6
||
(
new
->
rfd6
=
allocate_rfd
(
AF_INET6
)))
nn
=
dnssec_generate_query
(
header
,((
char
*
)
header
)
+
daemon
->
packet_buff_sz
,
fd
=
new
->
rfd6
->
fd
;
daemon
->
keyname
,
forward
->
class
,
T_DS
,
&
server
->
addr
);
}
}
if
((
hash
=
hash_questions
(
header
,
nn
,
daemon
->
namebuff
)))
memcpy
(
new
->
hash
,
hash
,
HASH_SIZE
);
new
->
new_id
=
get_id
();
header
->
id
=
htons
(
new
->
new_id
);
/* Save query for retransmission */
new
->
stash
=
blockdata_alloc
((
char
*
)
header
,
nn
);
new
->
stash_len
=
nn
;
/* Don't resend this. */
daemon
->
srv_save
=
NULL
;
if
(
server
->
sfd
)
fd
=
server
->
sfd
->
fd
;
else
else
{
fd
=
-
1
;
#ifdef HAVE_IPV6
if
(
server
->
addr
.
sa
.
sa_family
==
AF_INET6
)
{
if
(
new
->
rfd6
||
(
new
->
rfd6
=
allocate_rfd
(
AF_INET6
)))
fd
=
new
->
rfd6
->
fd
;
}
else
#endif
#endif
{
if
(
new
->
rfd4
||
(
new
->
rfd4
=
allocate_rfd
(
AF_INET
)))
fd
=
new
->
rfd4
->
fd
;
}
}
if
(
fd
!=
-
1
)
{
{
while
(
sendto
(
fd
,
(
char
*
)
header
,
nn
,
0
,
&
server
->
addr
.
sa
,
sa_len
(
&
server
->
addr
))
==
-
1
&&
retry_send
());
if
(
new
->
rfd4
||
(
new
->
rfd4
=
allocate_rfd
(
AF_INET
)))
server
->
queries
++
;
fd
=
new
->
rfd4
->
fd
;
}
}
}
}
if
(
fd
!=
-
1
)
{
while
(
sendto
(
fd
,
(
char
*
)
header
,
nn
,
0
,
&
server
->
addr
.
sa
,
sa_len
(
&
server
->
addr
))
==
-
1
&&
retry_send
());
server
->
queries
++
;
}
return
;
}
}
return
;
}
}
/* Ok, we reached far enough up the chain-of-trust that we can validate something.
/* Ok, we reached far enough up the chain-of-trust that we can validate something.
Now wind back down, pulling back answers which wouldn't previously validate
Now wind back down, pulling back answers which wouldn't previously validate
and validate them with the new data. Failure to find needed data here is an internal error.
and validate them with the new data. Note that if an answer needs multiple
Once we get to the original answer (FREC_DNSSEC_QUERY not set) and it validates,
keys to validate, we may find another key is needed, in which case we set off
return it to the original requestor. */
down another branch of the tree. Once we get to the original answer
(FREC_DNSSEC_QUERY not set) and it validates, return it to the original requestor. */
while
(
forward
->
dependent
)
while
(
forward
->
dependent
)
{
{
struct
frec
*
prev
=
forward
->
dependent
;
struct
frec
*
prev
=
forward
->
dependent
;
...
@@ -884,18 +890,23 @@ void reply_query(int fd, int family, time_t now)
...
@@ -884,18 +890,23 @@ void reply_query(int fd, int family, time_t now)
status
=
dnssec_validate_reply
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
&
forward
->
class
);
status
=
dnssec_validate_reply
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
&
forward
->
class
);
if
(
status
==
STAT_NEED_DS
||
status
==
STAT_NEED_KEY
)
if
(
status
==
STAT_NEED_DS
||
status
==
STAT_NEED_KEY
)
{
goto
anotherkey
;
my_syslog
(
LOG_ERR
,
_
(
"Unexpected missing data for DNSSEC validation"
));
status
=
STAT_INSECURE
;
}
}
}
}
}
if
(
status
==
STAT_TRUNCATED
)
if
(
status
==
STAT_TRUNCATED
)
header
->
hb3
|=
HB3_TC
;
header
->
hb3
|=
HB3_TC
;
else
else
log_query
(
F_KEYTAG
|
F_SECSTAT
,
"result"
,
NULL
,
{
status
==
STAT_SECURE
?
"SECURE"
:
(
status
==
STAT_INSECURE
?
"INSECURE"
:
"BOGUS"
));
char
*
result
;
if
(
forward
->
work_counter
==
0
)
result
=
"ABANDONED"
;
else
result
=
(
status
==
STAT_SECURE
?
"SECURE"
:
(
status
==
STAT_INSECURE
?
"INSECURE"
:
"BOGUS"
));
log_query
(
F_KEYTAG
|
F_SECSTAT
,
"result"
,
NULL
,
result
);
}
no_cache_dnssec
=
0
;
no_cache_dnssec
=
0
;
...
@@ -1173,60 +1184,73 @@ void receive_query(struct listener *listen, time_t now)
...
@@ -1173,60 +1184,73 @@ void receive_query(struct listener *listen, time_t now)
}
}
#ifdef HAVE_DNSSEC
#ifdef HAVE_DNSSEC
static
int
tcp_key_recurse
(
time_t
now
,
int
status
,
int
class
,
char
*
keyname
,
struct
server
*
server
)
static
int
tcp_key_recurse
(
time_t
now
,
int
status
,
struct
dns_header
*
header
,
size_t
n
,
int
class
,
char
*
name
,
char
*
keyname
,
struct
server
*
server
,
int
*
keycount
)
{
{
/* Recurse up the key heirarchy */
/* Recurse up the key heirarchy */
size_t
n
;
unsigned
char
*
packet
=
whine_malloc
(
65536
+
MAXDNAME
+
RRFIXEDSZ
+
sizeof
(
u16
));
unsigned
char
*
payload
=
&
packet
[
2
];
struct
dns_header
*
header
=
(
struct
dns_header
*
)
payload
;
u16
*
length
=
(
u16
*
)
packet
;
int
new_status
;
int
new_status
;
unsigned
char
c1
,
c2
;
n
=
dnssec_generate_query
(
header
,
((
char
*
)
header
)
+
65536
,
keyname
,
class
,
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
status
==
STAT_NEED_KEY
?
T_DNSKEY
:
T_DS
,
&
server
->
addr
);
if
(
--
(
*
keycount
)
==
0
)
return
STAT_INSECURE
;
*
length
=
htons
(
n
);
if
(
!
read_write
(
server
->
tcpfd
,
packet
,
n
+
sizeof
(
u16
),
0
)
||
if
(
status
==
STAT_NEED_KEY
)
!
read_write
(
server
->
tcpfd
,
&
c1
,
1
,
1
)
||
new_status
=
dnssec_validate_by_ds
(
now
,
header
,
n
,
name
,
keyname
,
class
);
!
read_write
(
server
->
tcpfd
,
&
c2
,
1
,
1
)
||
else
if
(
status
==
STAT_NEED_DS
)
!
read_write
(
server
->
tcpfd
,
payload
,
(
c1
<<
8
)
|
c2
,
1
))
new_status
=
dnssec_validate_ds
(
now
,
header
,
n
,
name
,
keyname
,
class
);
{
close
(
server
->
tcpfd
);
server
->
tcpfd
=
-
1
;
new_status
=
STAT_INSECURE
;
}
else
else
new_status
=
dnssec_validate_reply
(
now
,
header
,
n
,
name
,
keyname
,
&
class
);
/* Can't validate because we need a key/DS whose name now in keyname.
Make query for same, and recurse to validate */
if
(
new_status
==
STAT_NEED_DS
||
new_status
==
STAT_NEED_KEY
)
{
{
n
=
(
c1
<<
8
)
|
c2
;
size_t
m
;
unsigned
char
*
packet
=
whine_malloc
(
65536
+
MAXDNAME
+
RRFIXEDSZ
+
sizeof
(
u16
));
unsigned
char
*
payload
=
&
packet
[
2
];
struct
dns_header
*
new_header
=
(
struct
dns_header
*
)
payload
;
u16
*
length
=
(
u16
*
)
packet
;
unsigned
char
c1
,
c2
;
if
(
!
packet
)
return
STAT_INSECURE
;
another_tcp_key:
m
=
dnssec_generate_query
(
new_header
,
((
char
*
)
new_header
)
+
65536
,
keyname
,
class
,
new_status
==
STAT_NEED_KEY
?
T_DNSKEY
:
T_DS
,
&
server
->
addr
);
if
(
status
==
STAT_NEED_KEY
)
*
length
=
htons
(
m
);
new_status
=
dnssec_validate_by_ds
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
class
);
else
new_status
=
dnssec_validate_ds
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
class
);
if
(
new_status
==
STAT_NEED_DS
||
new_status
==
STAT_NEED_KEY
)
if
(
!
read_write
(
server
->
tcpfd
,
packet
,
m
+
sizeof
(
u16
),
0
)
||
!
read_write
(
server
->
tcpfd
,
&
c1
,
1
,
1
)
||
!
read_write
(
server
->
tcpfd
,
&
c2
,
1
,
1
)
||
!
read_write
(
server
->
tcpfd
,
payload
,
(
c1
<<
8
)
|
c2
,
1
))
new_status
=
STAT_INSECURE
;
else
{
{
if
((
new_status
=
tcp_key_recurse
(
now
,
new_status
,
class
,
daemon
->
keyname
,
server
)
==
STAT_SECURE
))
m
=
(
c1
<<
8
)
|
c2
;
if
(
tcp_key_recurse
(
now
,
new_status
,
new_header
,
m
,
class
,
name
,
keyname
,
server
,
keycount
)
==
STAT_SECURE
)
{
{
if
(
status
==
STAT_NEED_KEY
)
/* Reached a validated record, now try again at this level.
new_status
=
dnssec_validate_by_ds
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
class
);
Note that we may get ANOTHER NEED_* if an answer needs more than one key.
else
If so, go round again. */
new_status
=
dnssec_validate_ds
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
class
);
if
(
status
==
STAT_NEED_KEY
)
new_status
=
dnssec_validate_by_ds
(
now
,
header
,
n
,
name
,
keyname
,
class
);
else
if
(
status
==
STAT_NEED_DS
)
new_status
=
dnssec_validate_ds
(
now
,
header
,
n
,
name
,
keyname
,
class
);
else
new_status
=
dnssec_validate_reply
(
now
,
header
,
n
,
name
,
keyname
,
&
class
);
if
(
new_status
==
STAT_NEED_DS
||
new_status
==
STAT_NEED_KEY
)
if
(
new_status
==
STAT_NEED_DS
||
new_status
==
STAT_NEED_KEY
)
{
goto
another_tcp_key
;
my_syslog
(
LOG_ERR
,
_
(
"Unexpected missing data for DNSSEC validation"
));
status
=
STAT_INSECURE
;
}
}
}
}
}
}
free
(
packet
);
free
(
packet
);
}
return
new_status
;
return
new_status
;
}
}
#endif
#endif
...
@@ -1454,22 +1478,20 @@ unsigned char *tcp_request(int confd, time_t now,
...
@@ -1454,22 +1478,20 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_DNSSEC
#ifdef HAVE_DNSSEC
if
(
option_bool
(
OPT_DNSSEC_VALID
)
&&
!
checking_disabled
)
if
(
option_bool
(
OPT_DNSSEC_VALID
)
&&
!
checking_disabled
)
{
{
int
class
,
status
;
int
keycount
=
DNSSEC_WORK
;
/* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
int
status
=
tcp_key_recurse
(
now
,
STAT_TRUNCATED
,
header
,
m
,
0
,
daemon
->
namebuff
,
daemon
->
keyname
,
last_server
,
&
keycount
);
char
*
result
;
if
(
keycount
==
0
)
result
=
"ABANDONED"
;
else
result
=
(
status
==
STAT_SECURE
?
"SECURE"
:
(
status
==
STAT_INSECURE
?
"INSECURE"
:
"BOGUS"
));
status
=
dnssec_validate_reply
(
now
,
header
,
m
,
daemon
->
namebuff
,
daemon
->
keyname
,
&
class
);
log_query
(
F_KEYTAG
|
F_SECSTAT
,
"result"
,
NULL
,
result
);
if
(
status
==
STAT_NEED_DS
||
status
==
STAT_NEED_KEY
)
{
if
((
status
=
tcp_key_recurse
(
now
,
status
,
class
,
daemon
->
keyname
,
last_server
))
==
STAT_SECURE
)
status
=
dnssec_validate_reply
(
now
,
header
,
m
,
daemon
->
namebuff
,
daemon
->
keyname
,
&
class
);
}
log_query
(
F_KEYTAG
|
F_SECSTAT
,
"result"
,
NULL
,
status
==
STAT_SECURE
?
"SECURE"
:
(
status
==
STAT_INSECURE
?
"INSECURE"
:
"BOGUS"
));
if
(
status
==
STAT_BOGUS
)
if
(
status
==
STAT_BOGUS
)
no_cache_dnssec
=
1
;
no_cache_dnssec
=
1
;
if
(
status
==
STAT_SECURE
)
if
(
status
==
STAT_SECURE
)
cache_secure
=
1
;
cache_secure
=
1
;
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment