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
00a5b5d4
Commit
00a5b5d4
authored
Feb 28, 2014
by
Simon Kelley
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Check that unsigned replies come from unsigned zones if --dnssec-check-unsigned set.
parent
b8eac191
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
469 additions
and
110 deletions
+469
-110
man/dnsmasq.8
man/dnsmasq.8
+9
-0
src/dnsmasq.h
src/dnsmasq.h
+9
-2
src/dnssec.c
src/dnssec.c
+191
-85
src/forward.c
src/forward.c
+256
-22
src/option.c
src/option.c
+3
-0
src/rfc1035.c
src/rfc1035.c
+1
-1
No files found.
man/dnsmasq.8
View file @
00a5b5d4
...
...
@@ -608,6 +608,15 @@ key(s) of the root zone,
but trust anchors for limited domains are also possible. The current
root-zone trust anchors may be donwloaded from https://data.iana.org/root-anchors/root-anchors.xml
.TP
.B --dnssec-check-unsigned
As a default, dnsmasq does not check that unsigned DNS replies are
legitimate: they are assumed to be valid and passed on (without the
"authentic data" bit set, of course). This does not protect against an
attacker forging unsigned replies for signed DNS zones, but it is
fast. If this flag is set, dnsmasq will check the zones of unsigned
replies, to ensure that unsigned replies are allowed in those
zones. The cost of this is more upstream queries and slower performance.
.TP
.B --proxy-dnssec
Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it. This is an
alternative to having dnsmasq validate DNSSEC, but it depends on the security of the network between
...
...
src/dnsmasq.h
View file @
00a5b5d4
...
...
@@ -232,7 +232,8 @@ struct event_desc {
#define OPT_DNSSEC_VALID 45
#define OPT_DNSSEC_PERMISS 46
#define OPT_DNSSEC_DEBUG 47
#define OPT_LAST 48
#define OPT_DNSSEC_NO_SIGN 48
#define OPT_LAST 49
/* extra flags for my_syslog, we use a couple of facilities since they are known
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
...
...
@@ -535,6 +536,10 @@ struct hostsfile {
#define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6
#define STAT_SECURE_WILDCARD 7
#define STAT_NO_SIG 8
#define STAT_NO_DS 9
#define STAT_NEED_DS_NEG 10
#define STAT_CHASE_CNAME 11
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2
...
...
@@ -544,6 +549,7 @@ struct hostsfile {
#define FREC_AD_QUESTION 32
#define FREC_DO_QUESTION 64
#define FREC_ADDED_PHEADER 128
#define FREC_CHECK_NOSIGN 256
#ifdef HAVE_DNSSEC
#define HASH_SIZE 20
/* SHA-1 digest size */
...
...
@@ -1085,7 +1091,8 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
size_t
dnssec_generate_query
(
struct
dns_header
*
header
,
char
*
end
,
char
*
name
,
int
class
,
int
type
,
union
mysockaddr
*
addr
);
int
dnssec_validate_by_ds
(
time_t
now
,
struct
dns_header
*
header
,
size_t
n
,
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
dnssec_validate_reply
(
time_t
now
,
struct
dns_header
*
header
,
size_t
plen
,
char
*
name
,
char
*
keyname
,
int
*
class
,
int
*
neganswer
);
int
dnssec_chase_cname
(
time_t
now
,
struct
dns_header
*
header
,
size_t
plen
,
char
*
name
,
char
*
keyname
);
int
dnskey_keytag
(
int
alg
,
int
flags
,
unsigned
char
*
rdata
,
int
rdlen
);
size_t
filter_rrsigs
(
struct
dns_header
*
header
,
size_t
plen
);
unsigned
char
*
hash_questions
(
struct
dns_header
*
header
,
size_t
plen
,
char
*
name
);
...
...
src/dnssec.c
View file @
00a5b5d4
...
...
@@ -496,6 +496,8 @@ static int expand_workspace(unsigned char ***wkspc, int *sz, int new)
*
wkspc
=
p
;
*
sz
=
new_sz
;
return
1
;
}
/* Bubble sort the RRset into the canonical order.
...
...
@@ -588,6 +590,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
Return code:
STAT_SECURE if it validates.
STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion.
STAT_NO_SIG no RRsigs found.
STAT_INSECURE can't validate (no RRSIG, bad packet).
STAT_BOGUS signature is wrong.
STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
...
...
@@ -670,9 +673,13 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
return
STAT_INSECURE
;
}
/* RRset empty
, no RRSIGs
*/
if
(
rrsetidx
==
0
||
sigidx
==
0
)
/* RRset empty */
if
(
rrsetidx
==
0
)
return
STAT_INSECURE
;
/* no RRSIGs */
if
(
sigidx
==
0
)
return
STAT_NO_SIG
;
/* Sort RRset records into canonical order.
Note that at this point keyname and daemon->workspacename buffs are
...
...
@@ -1058,6 +1065,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
return codes:
STAT_INSECURE bad packet, no DS in reply, proven no DS in reply.
STAT_SECURE At least one valid DS found and in cache.
STAT_NO_DS It's proved there's no DS here.
STAT_BOGUS At least one DS found, which fails validation.
STAT_NEED_DNSKEY DNSKEY records to validate a DS not found, name in keyname
*/
...
...
@@ -1065,7 +1073,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
int
dnssec_validate_ds
(
time_t
now
,
struct
dns_header
*
header
,
size_t
plen
,
char
*
name
,
char
*
keyname
,
int
class
)
{
unsigned
char
*
p
=
(
unsigned
char
*
)(
header
+
1
);
int
qtype
,
qclass
,
val
,
i
;
int
qtype
,
qclass
,
val
,
i
,
neganswer
;
if
(
ntohs
(
header
->
qdcount
)
!=
1
||
!
(
p
=
skip_name
(
p
,
header
,
plen
,
4
)))
...
...
@@ -1077,25 +1085,36 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
if
(
qtype
!=
T_DS
||
qclass
!=
class
)
val
=
STAT_BOGUS
;
else
val
=
dnssec_validate_reply
(
now
,
header
,
plen
,
name
,
keyname
,
NULL
);
val
=
dnssec_validate_reply
(
now
,
header
,
plen
,
name
,
keyname
,
NULL
,
&
neganswer
);
if
(
val
==
STAT_NO_SIG
)
val
=
STAT_INSECURE
;
p
=
(
unsigned
char
*
)(
header
+
1
);
extract_name
(
header
,
plen
,
&
p
,
name
,
1
,
4
);
p
+=
4
;
/* qtype, qclass */
if
(
!
(
p
=
skip_section
(
p
,
ntohs
(
header
->
ancount
),
header
,
plen
)))
return
STAT_INSECURE
;
if
(
val
==
STAT_BOGUS
)
log_query
(
F_UPSTREAM
,
name
,
NULL
,
"BOGUS DS"
);
/* proved that no DS exists, cache neg answer, can't validate */
if
(
val
==
STAT_SECURE
&&
ntohs
(
header
->
ancount
)
==
0
)
if
((
val
==
STAT_SECURE
||
val
==
STAT_INSECURE
)
&&
neganswer
)
{
int
rdlen
,
rc
;
int
rdlen
,
flags
=
F_FORWARD
|
F_DS
|
F_NEG
;
unsigned
long
ttl
,
minttl
=
ULONG_MAX
;
struct
all_addr
a
;
if
(
RCODE
(
header
)
==
NXDOMAIN
)
flags
|=
F_NXDOMAIN
;
if
(
val
==
STAT_SECURE
)
flags
|=
F_DNSSECOK
;
for
(
i
=
ntohs
(
header
->
nscount
);
i
!=
0
;
i
--
)
{
if
(
!
(
rc
=
extract_name
(
header
,
plen
,
&
p
,
name
,
0
,
1
0
)))
if
(
!
(
p
=
skip_name
(
p
,
header
,
plen
,
0
)))
return
STAT_INSECURE
;
GETSHORT
(
qtype
,
p
);
...
...
@@ -1103,10 +1122,10 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
GETLONG
(
ttl
,
p
);
GETSHORT
(
rdlen
,
p
);
if
(
!
CHECK_LEN
(
header
,
p
,
plen
,
rdlen
)
||
rdlen
<
4
)
if
(
!
CHECK_LEN
(
header
,
p
,
plen
,
rdlen
))
return
STAT_INSECURE
;
/* bad packet */
if
(
qclass
!=
class
||
qtype
!=
T_SOA
||
rc
==
2
)
if
(
qclass
!=
class
||
qtype
!=
T_SOA
)
{
p
+=
rdlen
;
continue
;
...
...
@@ -1126,16 +1145,21 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
GETLONG
(
ttl
,
p
);
/* minTTL */
if
(
ttl
<
minttl
)
minttl
=
ttl
;
break
;
}
cache_start_insert
();
a
.
addr
.
dnssec
.
class
=
class
;
cache_insert
(
name
,
&
a
,
now
,
ttl
,
F_FORWARD
|
F_DS
|
F_DNSSECOK
|
F_NEG
|
(
RCODE
(
header
)
==
NXDOMAIN
?
F_NXDOMAIN
:
0
));
cache_end_insert
();
if
(
i
!=
0
)
{
cache_start_insert
();
a
.
addr
.
dnssec
.
class
=
class
;
cache_insert
(
name
,
&
a
,
now
,
ttl
,
flags
);
cache_end_insert
();
}
return
STAT_INSECURE
;
return
(
val
==
STAT_SECURE
)
?
STAT_NO_DS
:
STAT_INSECURE
;
}
return
val
;
...
...
@@ -1624,22 +1648,76 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
/* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */
/* Returns are the same as validate_rrset, plus the class if the missing key is in *class */
int
dnssec_validate_reply
(
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
*
neganswer
)
{
unsigned
char
*
ans_start
,
*
p1
,
*
p2
,
**
nsecs
;
int
type1
,
class1
,
rdlen1
,
type2
,
class2
,
rdlen2
;
unsigned
char
*
ans_start
,
*
qname
,
*
p1
,
*
p2
,
**
nsecs
;
int
type1
,
class1
,
rdlen1
,
type2
,
class2
,
rdlen2
,
qclass
,
qtype
;
int
i
,
j
,
rc
,
nsec_count
,
cname_count
=
10
;
int
nsec_type
=
0
;
int
nsec_type
=
0
,
have_answer
=
0
;
if
(
neganswer
)
*
neganswer
=
0
;
if
(
RCODE
(
header
)
==
SERVFAIL
)
return
STAT_BOGUS
;
if
((
RCODE
(
header
)
!=
NXDOMAIN
&&
RCODE
(
header
)
!=
NOERROR
)
||
ntohs
(
header
->
qdcount
)
!=
1
)
return
STAT_INSECURE
;
qname
=
p1
=
(
unsigned
char
*
)(
header
+
1
);
if
(
!
(
ans_start
=
skip_questions
(
header
,
plen
)))
if
(
!
extract_name
(
header
,
plen
,
&
p1
,
name
,
1
,
4
))
return
STAT_INSECURE
;
GETSHORT
(
qtype
,
p1
);
GETSHORT
(
qclass
,
p1
);
ans_start
=
p1
;
/* Can't validate an RRISG query */
if
(
qtype
==
T_RRSIG
)
return
STAT_INSECURE
;
cname_loop:
for
(
j
=
ntohs
(
header
->
ancount
);
j
!=
0
;
j
--
)
{
/* leave pointer to missing name in qname */
if
(
!
(
rc
=
extract_name
(
header
,
plen
,
&
p1
,
name
,
0
,
10
)))
return
STAT_INSECURE
;
/* bad packet */
GETSHORT
(
type2
,
p1
);
GETSHORT
(
class2
,
p1
);
p1
+=
4
;
/* TTL */
GETSHORT
(
rdlen2
,
p1
);
if
(
rc
==
1
&&
qclass
==
class2
)
{
/* Do we have an answer for the question? */
if
(
type2
==
qtype
)
{
have_answer
=
1
;
break
;
}
else
if
(
type2
==
T_CNAME
)
{
qname
=
p1
;
/* looped CNAMES */
if
(
!
cname_count
--
||
!
extract_name
(
header
,
plen
,
&
p1
,
name
,
1
,
0
))
return
STAT_INSECURE
;
p1
=
ans_start
;
goto
cname_loop
;
}
}
if
(
!
ADD_RDLEN
(
header
,
p1
,
plen
,
rdlen2
))
return
STAT_INSECURE
;
}
if
(
neganswer
&&
!
have_answer
)
*
neganswer
=
1
;
for
(
p1
=
ans_start
,
i
=
0
;
i
<
ntohs
(
header
->
ancount
)
+
ntohs
(
header
->
nscount
);
i
++
)
{
if
(
!
extract_name
(
header
,
plen
,
&
p1
,
name
,
1
,
10
))
...
...
@@ -1812,70 +1890,98 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
}
/* OK, all the RRsets validate, now see if we have a NODATA or NXDOMAIN reply */
p1
=
(
unsigned
char
*
)(
header
+
1
);
if
(
!
extract_name
(
header
,
plen
,
&
p1
,
name
,
1
,
4
))
return
STAT_INSECURE
;
GETSHORT
(
type1
,
p1
);
GETSHORT
(
class1
,
p1
);
/* Can't validate RRSIG query */
if
(
type1
==
T_RRSIG
)
return
STAT_INSECURE
;
cname_loop:
for
(
j
=
ntohs
(
header
->
ancount
);
j
!=
0
;
j
--
)
{
if
(
!
(
rc
=
extract_name
(
header
,
plen
,
&
p1
,
name
,
0
,
10
)))
return
STAT_INSECURE
;
/* bad packet */
GETSHORT
(
type2
,
p1
);
GETSHORT
(
class2
,
p1
);
p1
+=
4
;
/* TTL */
GETSHORT
(
rdlen2
,
p1
);
if
(
rc
==
1
&&
class1
==
class2
)
{
/* Do we have an answer for the question? */
if
(
type1
==
type2
)
return
RCODE
(
header
)
==
NXDOMAIN
?
STAT_BOGUS
:
STAT_SECURE
;
else
if
(
type2
==
T_CNAME
)
{
/* looped CNAMES */
if
(
!
cname_count
--
||
!
extract_name
(
header
,
plen
,
&
p1
,
name
,
1
,
0
)
||
!
(
p1
=
skip_questions
(
header
,
plen
)))
return
STAT_INSECURE
;
goto
cname_loop
;
}
}
if
(
!
ADD_RDLEN
(
header
,
p1
,
plen
,
rdlen2
))
return
STAT_INSECURE
;
}
if
(
have_answer
)
return
STAT_SECURE
;
/* NXDOMAIN or NODATA reply, prove that (name, class1, type1) can't exist */
/* First marshall the NSEC records, if we've not done it previously */
if
(
!
nsec_type
)
{
nsec_type
=
find_nsec_records
(
header
,
plen
,
&
nsecs
,
&
nsec_count
,
class1
);
nsec_type
=
find_nsec_records
(
header
,
plen
,
&
nsecs
,
&
nsec_count
,
qclass
);
if
(
nsec_type
==
0
)
return
STAT_INSECURE
;
/* Bad packet */
if
(
nsec_type
==
-
1
)
return
STAT_BOGUS
;
/* No NSECs */
}
/* Get name of missing answer */
if
(
!
extract_name
(
header
,
plen
,
&
qname
,
name
,
1
,
0
))
return
STAT_INSECURE
;
if
(
nsec_type
==
T_NSEC
)
return
prove_non_existence_nsec
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
type1
);
return
prove_non_existence_nsec
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
qtype
);
else
return
prove_non_existence_nsec3
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
type1
);
return
prove_non_existence_nsec3
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
qtype
);
}
/* Chase the CNAME chain in the packet until the first record which _doesn't validate.
Needed for proving answer in unsigned space.
Return STAT_NEED_*
STAT_BOGUS - error
STAT_INSECURE - name of first non-secure record in name
*/
int
dnssec_chase_cname
(
time_t
now
,
struct
dns_header
*
header
,
size_t
plen
,
char
*
name
,
char
*
keyname
)
{
unsigned
char
*
p
=
(
unsigned
char
*
)(
header
+
1
);
int
type
,
class
,
qtype
,
qclass
,
rdlen
,
j
,
rc
;
int
cname_count
=
10
;
/* Get question */
if
(
!
extract_name
(
header
,
plen
,
&
p
,
name
,
1
,
4
))
return
STAT_BOGUS
;
GETSHORT
(
qtype
,
p
);
GETSHORT
(
qclass
,
p
);
while
(
1
)
{
for
(
j
=
ntohs
(
header
->
ancount
);
j
!=
0
;
j
--
)
{
if
(
!
(
rc
=
extract_name
(
header
,
plen
,
&
p
,
name
,
0
,
10
)))
return
STAT_BOGUS
;
/* bad packet */
GETSHORT
(
type
,
p
);
GETSHORT
(
class
,
p
);
p
+=
4
;
/* TTL */
GETSHORT
(
rdlen
,
p
);
/* Not target, loop */
if
(
rc
==
2
||
qclass
!=
class
)
{
if
(
!
ADD_RDLEN
(
header
,
p
,
plen
,
rdlen
))
return
STAT_BOGUS
;
continue
;
}
/* Got to end of CNAME chain. */
if
(
type
!=
T_CNAME
)
return
STAT_INSECURE
;
/* validate CNAME chain, return if insecure or need more data */
rc
=
validate_rrset
(
now
,
header
,
plen
,
class
,
type
,
name
,
keyname
,
NULL
,
0
,
0
,
0
);
if
(
rc
!=
STAT_SECURE
)
{
if
(
rc
==
STAT_NO_SIG
)
rc
=
STAT_INSECURE
;
return
rc
;
}
/* Loop down CNAME chain/ */
if
(
!
cname_count
--
||
!
extract_name
(
header
,
plen
,
&
p
,
name
,
1
,
0
)
||
!
(
p
=
skip_questions
(
header
,
plen
)))
return
STAT_BOGUS
;
break
;
}
/* End of CNAME chain */
return
STAT_INSECURE
;
}
}
/* Compute keytag (checksum to quickly index a key). See RFC4034 */
int
dnskey_keytag
(
int
alg
,
int
flags
,
unsigned
char
*
key
,
int
keylen
)
{
...
...
@@ -1951,7 +2057,8 @@ static int check_name(unsigned char **namep, struct dns_header *header, size_t p
if
(
label_type
==
0xc0
)
{
/* pointer for compression. */
unsigned
int
offset
,
i
;
unsigned
int
offset
;
int
i
;
unsigned
char
*
p
;
if
(
!
CHECK_LEN
(
header
,
ansp
,
plen
,
2
))
...
...
@@ -1971,7 +2078,7 @@ static int check_name(unsigned char **namep, struct dns_header *header, size_t p
/* does the pointer end up in an elided RR? */
if
(
i
&
1
)
return
-
1
;
return
0
;
/* No, scale the pointer */
if
(
fixup
)
...
...
@@ -2023,27 +2130,26 @@ static int check_name(unsigned char **namep, struct dns_header *header, size_t p
static
int
check_rrs
(
unsigned
char
*
p
,
struct
dns_header
*
header
,
size_t
plen
,
int
fixup
,
unsigned
char
**
rrs
,
int
rr_count
)
{
int
i
,
type
,
class
,
rdlen
;
unsigned
char
*
pp
;
for
(
i
=
0
;
i
<
ntohs
(
header
->
ancount
)
+
ntohs
(
header
->
nscount
);
i
++
)
{
if
(
type
!=
T_NSEC
&&
type
!=
T_NSEC3
&&
type
!=
T_RRSIG
)
{
if
(
!
check_name
(
&
p
,
header
,
plen
,
fixup
,
rrs
,
rr_count
))
return
0
;
}
else
{
if
(
!
(
p
=
skip_name
(
p
,
header
,
plen
,
10
)))
return
0
;
}
pp
=
p
;
if
(
!
(
p
=
skip_name
(
p
,
header
,
plen
,
10
)))
return
0
;
GETSHORT
(
type
,
p
);
GETSHORT
(
class
,
p
);
p
+=
4
;
/* TTL */
GETSHORT
(
rdlen
,
p
);
if
(
type
!=
T_NSEC
&&
type
!=
T_NSEC3
&&
type
!=
T_RRSIG
)
{
/* fixup name of RR */
if
(
!
check_name
(
&
pp
,
header
,
plen
,
fixup
,
rrs
,
rr_count
))
return
0
;
if
(
class
==
C_IN
)
{
u16
*
d
;
...
...
src/forward.c
View file @
00a5b5d4
...
...
@@ -24,6 +24,14 @@ static unsigned short get_id(void);
static
void
free_frec
(
struct
frec
*
f
);
static
struct
randfd
*
allocate_rfd
(
int
family
);
#ifdef HAVE_DNSSEC
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
);
static
int
do_check_sign
(
time_t
now
,
struct
dns_header
*
header
,
size_t
plen
,
char
*
name
,
char
*
keyname
,
int
class
);
static
int
send_check_sign
(
time_t
now
,
struct
dns_header
*
header
,
size_t
plen
,
char
*
name
,
char
*
keyname
);
#endif
/* Send a UDP packet with its source address set as "source"
unless nowild is true, when we just send it with the kernel default */
int
send_from
(
int
fd
,
int
nowild
,
char
*
packet
,
size_t
len
,
...
...
@@ -250,6 +258,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#endif
unsigned
int
gotname
=
extract_request
(
header
,
plen
,
daemon
->
namebuff
,
NULL
);
(
void
)
do_bit
;
/* may be no servers available. */
if
(
!
daemon
->
servers
)
forward
=
NULL
;
...
...
@@ -522,6 +532,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
size_t
plen
;
(
void
)
ad_reqd
;
(
void
)
do_bit
;
#ifdef HAVE_IPSET
/* Similar algorithm to search_servers. */
...
...
@@ -793,13 +804,27 @@ void reply_query(int fd, int family, time_t now)
else
if
(
forward
->
flags
&
FREC_DNSKEY_QUERY
)
status
=
dnssec_validate_by_ds
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
forward
->
class
);
else
if
(
forward
->
flags
&
FREC_DS_QUERY
)
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
);
if
(
status
==
STAT_NO_DS
)
status
=
STAT_INSECURE
;
}
else
if
(
forward
->
flags
&
FREC_CHECK_NOSIGN
)
status
=
do_check_sign
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
forward
->
class
);
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
,
NULL
);
if
(
status
==
STAT_NO_SIG
)
{
if
(
option_bool
(
OPT_DNSSEC_NO_SIGN
))
status
=
send_check_sign
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
);
else
status
=
STAT_INSECURE
;
}
}
/* Can't validate, as we're missing key data. Put this
answer aside, whilst we get that. */
if
(
status
==
STAT_NEED_DS
||
status
==
STAT_NEED_KEY
)
if
(
status
==
STAT_NEED_DS
||
status
==
STAT_NEED_
DS_NEG
||
status
==
STAT_NEED_
KEY
)
{
struct
frec
*
new
,
*
orig
;
...
...
@@ -829,7 +854,7 @@ void reply_query(int fd, int family, time_t now)
#ifdef HAVE_IPV6
new
->
rfd6
=
NULL
;
#endif
new
->
flags
&=
~
(
FREC_DNSKEY_QUERY
|
FREC_DS_QUERY
);
new
->
flags
&=
~
(
FREC_DNSKEY_QUERY
|
FREC_DS_QUERY
|
FREC_CHECK_NOSIGN
);
new
->
dependent
=
forward
;
/* to find query awaiting new one. */
forward
->
blocking_query
=
new
;
/* for garbage cleaning */
...
...
@@ -842,7 +867,10 @@ void reply_query(int fd, int family, time_t now)
}
else
{
new
->
flags
|=
FREC_DS_QUERY
;
if
(
status
==
STAT_NEED_DS_NEG
)
new
->
flags
|=
FREC_CHECK_NOSIGN
;
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
);
}
...
...
@@ -906,11 +934,26 @@ void reply_query(int fd, int family, time_t now)
if
(
forward
->
flags
&
FREC_DNSKEY_QUERY
)
status
=
dnssec_validate_by_ds
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
forward
->
class
);
else
if
(
forward
->
flags
&
FREC_DS_QUERY
)
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
);
if
(
status
==
STAT_NO_DS
)
status
=
STAT_INSECURE
;
}
else
if
(
forward
->
flags
&
FREC_CHECK_NOSIGN
)
status
=
do_check_sign
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
forward
->
class
);
else
status
=
dnssec_validate_reply
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
&
forward
->
class
);
if
(
status
==
STAT_NEED_DS
||
status
==
STAT_NEED_KEY
)
{
status
=
dnssec_validate_reply
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
,
&
forward
->
class
,
NULL
);
if
(
status
==
STAT_NO_SIG
)
{
if
(
option_bool
(
OPT_DNSSEC_NO_SIGN
))
status
=
send_check_sign
(
now
,
header
,
n
,
daemon
->
namebuff
,
daemon
->
keyname
);
else
status
=
STAT_INSECURE
;
}
}
if
(
status
==
STAT_NEED_DS
||
status
==
STAT_NEED_DS_NEG
||
status
==
STAT_NEED_KEY
)
goto
anotherkey
;
}
}
...
...
@@ -1207,6 +1250,164 @@ void receive_query(struct listener *listen, time_t now)
}
#ifdef HAVE_DNSSEC
/* UDP: we've got an unsigned answer, return STAT_INSECURE if we can prove there's no DS
and therefore the answer shouldn't be signed, or STAT_BOGUS if it should be, or
STAT_NEED_DS_NEG and keyname if we need to do the query. */
static
int
send_check_sign
(
time_t
now
,
struct
dns_header
*
header
,
size_t
plen
,
char
*
name
,
char
*
keyname
)
{
struct
crec
*
crecp
;
char
*
name_start
=
name
;
int
status
=
dnssec_chase_cname
(
now
,
header
,
plen
,
name
,
keyname
);
if
(
status
!=
STAT_INSECURE
)
return
status
;
while
(
1
)
{
crecp
=
cache_find_by_name
(
NULL
,
name_start
,
now
,
F_DS
);
if
(
crecp
&&
(
crecp
->
flags
&
F_DNSSECOK
))
return
(
crecp
->
flags
&
F_NEG
)
?
STAT_INSECURE
:
STAT_BOGUS
;
if
(
crecp
&&
(
crecp
->
flags
&
F_NEG
)
&&
(
name_start
=
strchr
(
name_start
,
'.'
)))
{
name_start
++
;
/* chop a label off and try again */
continue
;
}
strcpy
(
keyname
,
name_start
);
return
STAT_NEED_DS_NEG
;
}
}
/* Got answer to DS query from send_check_sign, check for proven non-existence, or make the next DS query to try. */
static
int
do_check_sign
(
time_t
now
,
struct
dns_header
*
header
,
size_t
plen
,
char
*
name
,
char
*
keyname
,
int
class
)
{
char
*
name_start
;
unsigned
char
*
p
;
int
status
=
dnssec_validate_ds
(
now
,
header
,
plen
,
name
,
keyname
,
class
);
if
(
status
!=
STAT_INSECURE
)
{
if
(
status
==
STAT_NO_DS
)
status
=
STAT_INSECURE
;
return
status
;
}
p
=
(
unsigned
char
*
)(
header
+
1
);
if
(
extract_name
(
header
,
plen
,
&
p
,
name
,
1
,
4
)
&&
(
name_start
=
strchr
(
name
,
'.'
)))
{
name_start
++
;
/* chop a label off and try again */
strcpy
(
keyname
,
name_start
);
return
STAT_NEED_DS_NEG
;
}
return
STAT_BOGUS
;
}
/* Move toward the root, until we find a signed non-existance of a DS, in which case
an unsigned answer is OK, or we find a signed DS, in which case there should be
a signature, and the answer is BOGUS */
static
int
tcp_check_for_unsigned_zone
(
time_t
now
,
struct
dns_header
*
header
,
size_t
plen
,
int
class
,
char
*
name
,
char
*
keyname
,
struct
server
*
server
,
int
*
keycount
)
{
size_t
m
;
unsigned
char
*
packet
,
*
payload
;
u16
*
length
;
unsigned
char
*
p
=
(
unsigned
char
*
)(
header
+
1
);
int
status
;
char
*
name_start
=
name
;
/* Get first insecure entry in CNAME chain */
status
=
tcp_key_recurse
(
now
,
STAT_CHASE_CNAME
,
header
,
plen
,
class
,
name
,
keyname
,
server
,
keycount
);
if
(
status
==
STAT_BOGUS
)
return
STAT_BOGUS
;
if
(
!
(
packet
=
whine_malloc
(
65536
+
MAXDNAME
+
RRFIXEDSZ
+
sizeof
(
u16
))))
return
STAT_BOGUS
;
payload
=
&
packet
[
2
];
header
=
(
struct
dns_header
*
)
payload
;
length
=
(
u16
*
)
packet
;
while
(
1
)
{
unsigned
char
*
newhash
,
hash
[
HASH_SIZE
];
unsigned
char
c1
,
c2
;
struct
crec
*
crecp
=
cache_find_by_name
(
NULL
,
name_start
,
now
,
F_DS
);
if
(
--
(
*
keycount
)
==
0
)
return
STAT_BOGUS
;
if
(
crecp
&&
(
crecp
->
flags
&
F_DNSSECOK
))
{
free
(
packet
);
return
(
crecp
->
flags
&
F_NEG
)
?
STAT_INSECURE
:
STAT_BOGUS
;
}
/* If we have cached insecurely that a DS doesn't exist,
ise that is a hit for where to start looking for the secure one */
if
(
crecp
&&
(
crecp
->
flags
&
F_NEG
)
&&
(
name_start
=
strchr
(
name_start
,
'.'
)))
{
name_start
++
;
/* chop a label off and try again */
continue
;
}
m
=
dnssec_generate_query
(
header
,
((
char
*
)
header
)
+
65536
,
name_start
,
class
,
T_DS
,
&
server
->
addr
);
/* We rely on the question section coming back unchanged, ensure it is with the hash. */
if
((
newhash
=
hash_questions
(
header
,
(
unsigned
int
)
m
,
name
)))
memcpy
(
hash
,
newhash
,
HASH_SIZE
);
*
length
=
htons
(
m
);
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
))
{
m
=
(
c1
<<
8
)
|
c2
;
newhash
=
hash_questions
(
header
,
(
unsigned
int
)
m
,
name
);
if
(
newhash
&&
memcmp
(
hash
,
newhash
,
HASH_SIZE
)
==
0
)
{
/* Note this trashes all three name workspaces */
status
=
tcp_key_recurse
(
now
,
STAT_NEED_DS_NEG
,
header
,
m
,
class
,
name
,
keyname
,
server
,
keycount
);
/* We've found a DS which proves the bit of the DNS where the
original query is, is unsigned, so the answer is OK,
if unvalidated. */
if
(
status
==
STAT_NO_DS
)
{
free
(
packet
);
return
STAT_INSECURE
;
}
/* No DS, not got to DNSSEC-land yet, go up. */
if
(
status
==
STAT_INSECURE
)
{
p
=
(
unsigned
char
*
)(
header
+
1
);
if
(
extract_name
(
header
,
plen
,
&
p
,
name
,
1
,
4
)
&&
(
name_start
=
strchr
(
name
,
'.'
)))
{
name_start
++
;
/* chop a label off and try again */
continue
;
}
}
}
}
free
(
packet
);
return
STAT_BOGUS
;
}
}
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
)
{
...
...
@@ -1219,11 +1420,27 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
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
);
else
if
(
status
==
STAT_NEED_DS
||
status
==
STAT_NEED_DS_NEG
)
{
new_status
=
dnssec_validate_ds
(
now
,
header
,
n
,
name
,
keyname
,
class
);
if
(
status
==
STAT_NEED_DS
&&
new_status
==
STAT_NO_DS
)
new_status
=
STAT_INSECURE
;
}
else
if
(
status
==
STAT_CHASE_CNAME
)
new_status
=
dnssec_chase_cname
(
now
,
header
,
n
,
name
,
keyname
);
else
{
new_status
=
dnssec_validate_reply
(
now
,
header
,
n
,
name
,
keyname
,
&
class
,
NULL
);
if
(
new_status
==
STAT_NO_SIG
)
{
if
(
option_bool
(
OPT_DNSSEC_NO_SIGN
))
new_status
=
tcp_check_for_unsigned_zone
(
now
,
header
,
n
,
class
,
name
,
keyname
,
server
,
keycount
);
else
new_status
=
STAT_INSECURE
;
}
}
/* 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
)
...
...
@@ -1253,7 +1470,9 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
{
m
=
(
c1
<<
8
)
|
c2
;
if
(
tcp_key_recurse
(
now
,
new_status
,
new_header
,
m
,
class
,
name
,
keyname
,
server
,
keycount
)
==
STAT_SECURE
)
new_status
=
tcp_key_recurse
(
now
,
new_status
,
new_header
,
m
,
class
,
name
,
keyname
,
server
,
keycount
);
if
(
new_status
==
STAT_SECURE
)
{
/* Reached a validated record, now try again at this level.
Note that we may get ANOTHER NEED_* if an answer needs more than one key.
...
...
@@ -1261,11 +1480,27 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
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
);
else
if
(
status
==
STAT_NEED_DS
||
status
==
STAT_NEED_DS_NEG
)
{
new_status
=
dnssec_validate_ds
(
now
,
header
,
n
,
name
,
keyname
,
class
);
if
(
status
==
STAT_NEED_DS
&&
new_status
==
STAT_NO_DS
)
new_status
=
STAT_INSECURE
;
/* Validated no DS */
}
else
if
(
status
==
STAT_CHASE_CNAME
)
new_status
=
dnssec_chase_cname
(
now
,
header
,
n
,
name
,
keyname
);
else
{
new_status
=
dnssec_validate_reply
(
now
,
header
,
n
,
name
,
keyname
,
&
class
,
NULL
);
if
(
new_status
==
STAT_NO_SIG
)
{
if
(
option_bool
(
OPT_DNSSEC_NO_SIGN
))
new_status
=
tcp_check_for_unsigned_zone
(
now
,
header
,
n
,
class
,
name
,
keyname
,
server
,
keycount
);
else
new_status
=
STAT_INSECURE
;
}
}
if
(
new_status
==
STAT_NEED_DS
||
new_status
==
STAT_NEED_KEY
)
goto
another_tcp_key
;
}
...
...
@@ -1273,7 +1508,6 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
free
(
packet
);
}
return
new_status
;
}
#endif
...
...
src/option.c
View file @
00a5b5d4
...
...
@@ -143,6 +143,7 @@ struct myoption {
#define LOPT_DNSSEC_DEBUG 331
#define LOPT_REV_SERV 332
#define LOPT_SERVERS_FILE 333
#define LOPT_DNSSEC_CHECK 334
#ifdef HAVE_GETOPT_LONG
static
const
struct
option
opts
[]
=
...
...
@@ -283,6 +284,7 @@ static const struct myoption opts[] =
{
"dnssec"
,
0
,
0
,
LOPT_SEC_VALID
},
{
"trust-anchor"
,
1
,
0
,
LOPT_TRUST_ANCHOR
},
{
"dnssec-debug"
,
0
,
0
,
LOPT_DNSSEC_DEBUG
},
{
"dnssec-check-unsigned"
,
0
,
0
,
LOPT_DNSSEC_CHECK
},
#ifdef OPTION6_PREFIX_CLASS
{
"dhcp-prefix-class"
,
1
,
0
,
LOPT_PREF_CLSS
},
#endif
...
...
@@ -438,6 +440,7 @@ static struct {
{
LOPT_SEC_VALID
,
OPT_DNSSEC_VALID
,
NULL
,
gettext_noop
(
"Activate DNSSEC validation"
),
NULL
},
{
LOPT_TRUST_ANCHOR
,
ARG_DUP
,
"<domain>,[<class>],..."
,
gettext_noop
(
"Specify trust anchor key digest."
),
NULL
},
{
LOPT_DNSSEC_DEBUG
,
OPT_DNSSEC_DEBUG
,
NULL
,
gettext_noop
(
"Disable upstream checking for DNSSEC debugging."
),
NULL
},
{
LOPT_DNSSEC_CHECK
,
OPT_DNSSEC_NO_SIGN
,
NULL
,
gettext_noop
(
"Ensure answers without DNSSEC are in unsigned zones."
),
NULL
},
#ifdef OPTION6_PREFIX_CLASS
{
LOPT_PREF_CLSS
,
ARG_DUP
,
"set:tag,<class>"
,
gettext_noop
(
"Specify DHCPv6 prefix class"
),
NULL
},
#endif
...
...
src/rfc1035.c
View file @
00a5b5d4
...
...
@@ -927,7 +927,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
for
(
i
=
ntohs
(
header
->
qdcount
);
i
!=
0
;
i
--
)
{
int
found
=
0
,
cname_count
=
5
;
int
found
=
0
,
cname_count
=
10
;
struct
crec
*
cpp
=
NULL
;
int
flags
=
RCODE
(
header
)
==
NXDOMAIN
?
F_NXDOMAIN
:
0
;
int
secflag
=
secure
?
F_DNSSECOK
:
0
;
...
...
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