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
5107ace1
Commit
5107ace1
authored
Feb 23, 2014
by
Simon Kelley
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
NSEC3 validation. First pass.
parent
7b1eae4f
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
505 additions
and
113 deletions
+505
-113
src/dns-protocol.h
src/dns-protocol.h
+1
-0
src/dnsmasq.c
src/dnsmasq.c
+4
-1
src/dnsmasq.h
src/dnsmasq.h
+3
-0
src/dnssec.c
src/dnssec.c
+496
-111
src/rfc1035.c
src/rfc1035.c
+1
-1
No files found.
src/dns-protocol.h
View file @
5107ace1
...
@@ -68,6 +68,7 @@
...
@@ -68,6 +68,7 @@
#define T_RRSIG 46
#define T_RRSIG 46
#define T_NSEC 47
#define T_NSEC 47
#define T_DNSKEY 48
#define T_DNSKEY 48
#define T_NSEC3 50
#define T_TKEY 249
#define T_TKEY 249
#define T_TSIG 250
#define T_TSIG 250
#define T_AXFR 252
#define T_AXFR 252
...
...
src/dnsmasq.c
View file @
5107ace1
...
@@ -98,7 +98,10 @@ int main (int argc, char **argv)
...
@@ -98,7 +98,10 @@ int main (int argc, char **argv)
#ifdef HAVE_DNSSEC
#ifdef HAVE_DNSSEC
if
(
option_bool
(
OPT_DNSSEC_VALID
))
if
(
option_bool
(
OPT_DNSSEC_VALID
))
daemon
->
keyname
=
safe_malloc
(
MAXDNAME
);
{
daemon
->
keyname
=
safe_malloc
(
MAXDNAME
);
daemon
->
workspacename
=
safe_malloc
(
MAXDNAME
);
}
#endif
#endif
#ifdef HAVE_DHCP
#ifdef HAVE_DHCP
...
...
src/dnsmasq.h
View file @
5107ace1
...
@@ -534,6 +534,7 @@ struct hostsfile {
...
@@ -534,6 +534,7 @@ struct hostsfile {
#define STAT_NEED_DS 4
#define STAT_NEED_DS 4
#define STAT_NEED_KEY 5
#define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6
#define STAT_TRUNCATED 6
#define STAT_SECURE_WILDCARD 7
#define FREC_NOREBIND 1
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2
#define FREC_CHECKING_DISABLED 2
...
@@ -941,6 +942,7 @@ extern struct daemon {
...
@@ -941,6 +942,7 @@ extern struct daemon {
char
*
namebuff
;
/* MAXDNAME size buffer */
char
*
namebuff
;
/* MAXDNAME size buffer */
#ifdef HAVE_DNSSEC
#ifdef HAVE_DNSSEC
char
*
keyname
;
/* MAXDNAME size buffer */
char
*
keyname
;
/* MAXDNAME size buffer */
char
*
workspacename
;
/* ditto */
#endif
#endif
unsigned
int
local_answer
,
queries_forwarded
,
auth_answer
;
unsigned
int
local_answer
,
queries_forwarded
,
auth_answer
;
struct
frec
*
frec_list
;
struct
frec
*
frec_list
;
...
@@ -1035,6 +1037,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
...
@@ -1035,6 +1037,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char
*
name
,
int
isExtract
,
int
extrabytes
);
char
*
name
,
int
isExtract
,
int
extrabytes
);
unsigned
char
*
skip_name
(
unsigned
char
*
ansp
,
struct
dns_header
*
header
,
size_t
plen
,
int
extrabytes
);
unsigned
char
*
skip_name
(
unsigned
char
*
ansp
,
struct
dns_header
*
header
,
size_t
plen
,
int
extrabytes
);
unsigned
char
*
skip_questions
(
struct
dns_header
*
header
,
size_t
plen
);
unsigned
char
*
skip_questions
(
struct
dns_header
*
header
,
size_t
plen
);
unsigned
char
*
skip_section
(
unsigned
char
*
ansp
,
int
count
,
struct
dns_header
*
header
,
size_t
plen
);
unsigned
int
extract_request
(
struct
dns_header
*
header
,
size_t
qlen
,
unsigned
int
extract_request
(
struct
dns_header
*
header
,
size_t
qlen
,
char
*
name
,
unsigned
short
*
typep
);
char
*
name
,
unsigned
short
*
typep
);
size_t
setup_reply
(
struct
dns_header
*
header
,
size_t
qlen
,
size_t
setup_reply
(
struct
dns_header
*
header
,
size_t
qlen
,
...
...
src/dnssec.c
View file @
5107ace1
...
@@ -498,6 +498,8 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
...
@@ -498,6 +498,8 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
int
rdlen1
,
rdlen2
,
left1
,
left2
,
len1
,
len2
,
len
,
rc
;
int
rdlen1
,
rdlen2
,
left1
,
left2
,
len1
,
len2
,
len
,
rc
;
u16
*
dp1
,
*
dp2
;
u16
*
dp1
,
*
dp2
;
unsigned
char
*
end1
,
*
end2
;
unsigned
char
*
end1
,
*
end2
;
/* Note that these have been determined to be OK previously,
so we don't need to check for NULL return here. */
unsigned
char
*
p1
=
skip_name
(
rrset
[
i
],
header
,
plen
,
10
);
unsigned
char
*
p1
=
skip_name
(
rrset
[
i
],
header
,
plen
,
10
);
unsigned
char
*
p2
=
skip_name
(
rrset
[
i
+
1
],
header
,
plen
,
10
);
unsigned
char
*
p2
=
skip_name
(
rrset
[
i
+
1
],
header
,
plen
,
10
);
...
@@ -559,12 +561,15 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
...
@@ -559,12 +561,15 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
/* Validate a single RRset (class, type, name) in the supplied DNS reply
/* Validate a single RRset (class, type, name) in the supplied DNS reply
Return code:
Return code:
STAT_SECURE if it validates.
STAT_SECURE if it validates.
STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion.
STAT_INSECURE can't validate (no RRSIG, bad packet).
STAT_INSECURE can't validate (no RRSIG, bad packet).
STAT_BOGUS signature is wrong.
STAT_BOGUS signature is wrong.
STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
if key is non-NULL, use that key, which has the algo and tag given in the params of those names,
if key is non-NULL, use that key, which has the algo and tag given in the params of those names,
otherwise find the key in the cache.
otherwise find the key in the cache.
name is unchanged on exit. keyname is used as workspace and trashed.
*/
*/
static
int
validate_rrset
(
time_t
now
,
struct
dns_header
*
header
,
size_t
plen
,
int
class
,
static
int
validate_rrset
(
time_t
now
,
struct
dns_header
*
header
,
size_t
plen
,
int
class
,
int
type
,
char
*
name
,
char
*
keyname
,
struct
blockdata
*
key
,
int
keylen
,
int
algo_in
,
int
keytag_in
)
int
type
,
char
*
name
,
char
*
keyname
,
struct
blockdata
*
key
,
int
keylen
,
int
algo_in
,
int
keytag_in
)
...
@@ -797,7 +802,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
...
@@ -797,7 +802,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
crecp
->
addr
.
key
.
keytag
==
key_tag
&&
crecp
->
addr
.
key
.
keytag
==
key_tag
&&
crecp
->
uid
==
class
&&
crecp
->
uid
==
class
&&
verify
(
crecp
->
addr
.
key
.
keydata
,
crecp
->
addr
.
key
.
keylen
,
sig
,
sig_len
,
digest
,
hash
->
digest_size
,
algo
))
verify
(
crecp
->
addr
.
key
.
keydata
,
crecp
->
addr
.
key
.
keylen
,
sig
,
sig_len
,
digest
,
hash
->
digest_size
,
algo
))
return
STAT_SECURE
;
return
(
labels
<
name_labels
)
?
STAT_SECURE_WILDCARD
:
STAT_SECURE
;
}
}
}
}
...
@@ -1127,14 +1132,460 @@ static int hostname_cmp(const char *a, const char *b)
...
@@ -1127,14 +1132,460 @@ static int hostname_cmp(const char *a, const char *b)
}
}
}
}
/* Find all the NSEC or NSEC3 records in a reply.
return an array of pointers to them. */
static
int
find_nsec_records
(
struct
dns_header
*
header
,
size_t
plen
,
unsigned
char
***
nsecsetp
,
int
*
nsecsetl
,
int
class_reqd
)
{
static
unsigned
char
**
nsecset
=
NULL
;
static
int
nsecset_sz
=
0
;
int
type_found
=
-
1
;
unsigned
char
*
p
=
skip_questions
(
header
,
plen
);
int
type
,
class
,
rdlen
,
i
,
nsecs_found
;
/* Move to NS section */
if
(
!
p
||
!
(
p
=
skip_section
(
p
,
ntohs
(
header
->
ancount
),
header
,
plen
)))
return
0
;
for
(
nsecs_found
=
0
,
i
=
ntohs
(
header
->
nscount
);
i
!=
0
;
i
--
)
{
unsigned
char
*
pstart
=
p
;
if
(
!
(
p
=
skip_name
(
p
,
header
,
plen
,
10
)))
return
0
;
GETSHORT
(
type
,
p
);
GETSHORT
(
class
,
p
);
p
+=
4
;
/* TTL */
GETSHORT
(
rdlen
,
p
);
if
(
class
==
class_reqd
&&
(
type
==
T_NSEC
||
type
==
T_NSEC3
))
{
/* No mixed NSECing 'round here, thankyouverymuch */
if
(
type_found
==
T_NSEC
&&
type
==
T_NSEC3
)
return
0
;
if
(
type_found
==
T_NSEC3
&&
type
==
T_NSEC
)
return
0
;
type_found
=
type
;
if
(
nsecs_found
==
nsecset_sz
)
{
unsigned
char
**
new
;
/* Protect against insane/malicious queries which bloat the workspace
and eat CPU in the sort */
if
(
nsecs_found
>=
100
)
return
0
;
/* expand */
if
(
!
(
new
=
whine_malloc
((
nsecset_sz
+
5
)
*
sizeof
(
unsigned
char
**
))))
return
0
;
if
(
nsecset
)
{
memcpy
(
new
,
nsecset
,
nsecset_sz
*
sizeof
(
unsigned
char
**
));
free
(
nsecset
);
}
nsecset
=
new
;
nsecset_sz
+=
5
;
}
nsecset
[
nsecs_found
++
]
=
pstart
;
}
if
(
!
ADD_RDLEN
(
header
,
p
,
plen
,
rdlen
))
return
0
;
}
*
nsecsetp
=
nsecset
;
*
nsecsetl
=
nsecs_found
;
return
type_found
;
}
static
int
prove_non_existance_nsec
(
struct
dns_header
*
header
,
size_t
plen
,
unsigned
char
**
nsecs
,
int
nsec_count
,
char
*
workspace1
,
char
*
workspace2
,
char
*
name
,
int
type
)
{
int
i
,
rc
,
rdlen
;
unsigned
char
*
p
,
*
psave
;
int
offset
=
(
type
&
0xff
)
>>
3
;
int
mask
=
0x80
>>
(
type
&
0x07
);
/* Find NSEC record that proves name doesn't exist */
for
(
i
=
0
;
i
<
nsec_count
;
i
++
)
{
p
=
nsecs
[
i
];
if
(
!
extract_name
(
header
,
plen
,
&
p
,
workspace1
,
1
,
10
))
return
STAT_INSECURE
;
p
+=
8
;
/* class, type, TTL */
GETSHORT
(
rdlen
,
p
);
psave
=
p
;
if
(
!
extract_name
(
header
,
plen
,
&
p
,
workspace2
,
1
,
10
))
return
STAT_INSECURE
;
rc
=
hostname_cmp
(
workspace1
,
name
);
if
(
rc
==
0
)
{
/* NSEC with the same name as the RR we're testing, check
that the type in question doesn't appear in the type map */
rdlen
-=
p
-
psave
;
/* rdlen is now length of type map, and p points to it */
while
(
rdlen
>=
2
)
{
if
(
!
CHECK_LEN
(
header
,
p
,
plen
,
rdlen
))
return
STAT_INSECURE
;
if
(
p
[
0
]
==
type
>>
8
)
{
/* Does the NSEC say our type exists? */
if
(
offset
<
p
[
1
]
&&
(
p
[
offset
+
2
]
&
mask
)
!=
0
)
return
STAT_BOGUS
;
break
;
/* finshed checking */
}
rdlen
-=
p
[
1
];
p
+=
p
[
1
];
}
return
STAT_SECURE
;
}
else
if
(
rc
==
-
1
)
{
/* Normal case, name falls between NSEC name and next domain name,
wrap around case, name falls between NSEC name (rc == -1) and end */
if
(
hostname_cmp
(
workspace2
,
name
)
==
1
||
hostname_cmp
(
workspace1
,
workspace2
)
==
1
)
return
STAT_SECURE
;
}
else
{
/* wrap around case, name falls between start and next domain name */
if
(
hostname_cmp
(
workspace1
,
workspace2
)
==
1
&&
hostname_cmp
(
workspace2
,
name
)
==
1
)
return
STAT_SECURE
;
}
}
return
STAT_BOGUS
;
}
/* return digest length, or zero on error */
static
int
hash_name
(
char
*
in
,
unsigned
char
**
out
,
struct
nettle_hash
const
*
hash
,
unsigned
char
*
salt
,
int
salt_len
,
int
iterations
)
{
void
*
ctx
;
unsigned
char
*
digest
;
int
i
;
if
(
!
hash_init
(
hash
,
&
ctx
,
&
digest
))
return
0
;
hash
->
update
(
ctx
,
to_wire
(
in
),
(
unsigned
char
*
)
in
);
hash
->
update
(
ctx
,
salt_len
,
salt
);
hash
->
digest
(
ctx
,
hash
->
digest_size
,
digest
);
for
(
i
=
0
;
i
<
iterations
;
i
++
)
{
hash
->
update
(
ctx
,
hash
->
digest_size
,
digest
);
hash
->
update
(
ctx
,
salt_len
,
salt
);
hash
->
digest
(
ctx
,
hash
->
digest_size
,
digest
);
}
from_wire
(
in
);
*
out
=
digest
;
return
hash
->
digest_size
;
}
/* Decode base32 to first "." or end of string */
static
int
base32_decode
(
char
*
in
,
unsigned
char
*
out
)
{
int
oc
=
0
,
on
=
0
,
c
,
mask
,
i
;
unsigned
char
*
p
=
out
;
while
(
1
)
{
c
=
*
in
++
;
if
(
c
==
0
||
c
==
'.'
)
break
;
if
(
c
>=
'0'
&&
c
<=
'9'
)
c
-=
'0'
;
else
if
(
c
>=
'a'
&&
c
<=
'v'
)
c
-=
'a'
,
c
+=
10
;
else
if
(
c
>=
'A'
&&
c
<=
'V'
)
c
-=
'A'
,
c
+=
10
;
else
return
0
;
for
(
mask
=
0x10
,
i
=
0
;
i
<
5
;
i
++
)
{
if
(
c
&
mask
)
oc
|=
1
;
mask
=
mask
>>
1
;
if
(((
++
on
)
&
7
)
==
0
)
*
p
++
=
oc
;
oc
=
oc
<<
1
;
}
}
if
((
on
&
7
)
!=
0
)
return
0
;
return
p
-
out
;
}
static
int
prove_non_existance_nsec3
(
struct
dns_header
*
header
,
size_t
plen
,
unsigned
char
**
nsecs
,
int
nsec_count
,
char
*
workspace1
,
char
*
workspace2
,
char
*
name
,
int
type
)
{
unsigned
char
*
salt
,
*
p
,
*
digest
,
hash_len
;
int
digest_size
,
i
,
iterations
,
salt_len
,
algo
=
0
;
struct
nettle_hash
const
*
hash
;
char
*
closest_encloser
,
*
next_closest
,
*
wildcard
;
/* Look though the NSEC3 records to find the first one with
an algorithm we support (currently only algo == 1).
Take the algo, iterations, and salt of that record
as the ones we're going to use, and prune any
that don't match. */
for
(
i
=
0
;
i
<
nsec_count
;
i
++
)
{
if
(
!
(
p
=
skip_name
(
nsecs
[
i
],
header
,
plen
,
15
)))
return
STAT_INSECURE
;
/* bad packet */
p
+=
10
;
/* type, class, TTL, rdlen */
algo
=
*
p
++
;
if
(
algo
==
1
)
break
;
/* known algo */
}
/* No usable NSEC3s */
if
(
i
==
nsec_count
)
return
STAT_BOGUS
;
p
++
;
/* flags */
GETSHORT
(
iterations
,
p
);
salt_len
=
*
p
++
;
salt
=
p
;
if
(
!
CHECK_LEN
(
header
,
salt
,
plen
,
salt_len
))
return
STAT_INSECURE
;
/* bad packet */
/* Now prune so we only have NSEC3 records with same iterations, salt and algo */
for
(
i
=
0
;
i
<
nsec_count
;
i
++
)
{
unsigned
char
*
nsec3p
=
nsecs
[
i
];
int
this_iter
;
nsecs
[
i
]
=
NULL
;
/* Speculative, will be restored if OK. */
if
(
!
(
p
=
skip_name
(
nsec3p
,
header
,
plen
,
15
)))
return
STAT_INSECURE
;
/* bad packet */
p
+=
10
;
/* type, class, TTL, rdlen */
if
(
*
p
++
!=
algo
)
continue
;
p
++
;
/* flags */
GETSHORT
(
this_iter
,
p
);
if
(
this_iter
!=
iterations
)
continue
;
if
(
salt_len
!=
*
p
++
)
continue
;
if
(
!
CHECK_LEN
(
header
,
p
,
plen
,
salt_len
))
return
STAT_INSECURE
;
/* bad packet */
if
(
memcmp
(
p
,
salt
,
salt_len
)
!=
0
)
continue
;
/* All match, put the pointer back */
nsecs
[
i
]
=
nsec3p
;
}
/* Algo is checked as 1 above */
if
(
!
(
hash
=
hash_find
(
"sha1"
)))
return
STAT_INSECURE
;
/* Now, we need the "closest encloser NSEC3" */
closest_encloser
=
name
;
next_closest
=
NULL
;
do
{
if
(
*
closest_encloser
==
'.'
)
closest_encloser
++
;
if
((
digest_size
=
hash_name
(
closest_encloser
,
&
digest
,
hash
,
salt
,
salt_len
,
iterations
))
==
0
)
return
STAT_INSECURE
;
for
(
i
=
0
;
i
<
nsec_count
;
i
++
)
if
((
p
=
nsecs
[
i
]))
{
int
base32_size
;
if
(
!
extract_name
(
header
,
plen
,
&
p
,
workspace1
,
1
,
0
)
||
!
(
base32_size
=
base32_decode
(
workspace1
,
(
unsigned
char
*
)
workspace2
)))
return
STAT_INSECURE
;
if
(
digest_size
==
base32_size
&&
memcmp
(
digest
,
workspace2
,
digest_size
)
==
0
)
break
;
/* Gotit */
}
if
(
i
!=
nsec_count
)
break
;
next_closest
=
closest_encloser
;
}
while
((
closest_encloser
=
strchr
(
closest_encloser
,
'.'
)));
/* No usable NSEC3s */
if
(
i
==
nsec_count
)
return
STAT_BOGUS
;
if
(
!
next_closest
)
{
/* We found an NSEC3 whose hashed name exactly matches the query, so
Now we just need to check the type map. p points to the RR data for the record. */
int
hash_len
,
rdlen
;
unsigned
char
*
psave
;
int
offset
=
(
type
&
0xff
)
>>
3
;
int
mask
=
0x80
>>
(
type
&
0x07
);
p
+=
8
;
/* class, type, TTL */
GETSHORT
(
rdlen
,
p
);
psave
=
p
;
p
+=
5
+
salt_len
;
/* algo, flags, iterations, salt_len, salt */
hash_len
=
*
p
++
;
if
(
!
CHECK_LEN
(
header
,
p
,
plen
,
hash_len
))
return
STAT_INSECURE
;
/* bad packet */
p
+=
hash_len
;
rdlen
-=
p
-
psave
;
while
(
rdlen
>=
2
)
{
if
(
!
CHECK_LEN
(
header
,
p
,
plen
,
rdlen
))
return
STAT_INSECURE
;
if
(
p
[
0
]
==
type
>>
8
)
{
/* Does the NSEC say our type exists? */
if
(
offset
<
p
[
1
]
&&
(
p
[
offset
+
2
]
&
mask
)
!=
0
)
return
STAT_BOGUS
;
break
;
/* finshed checking */
}
rdlen
-=
p
[
1
];
p
+=
p
[
1
];
}
return
STAT_SECURE
;
}
/* Look for NSEC3 that proves the non-existance of the next-closest encloser */
if
((
digest_size
=
hash_name
(
next_closest
,
&
digest
,
hash
,
salt
,
salt_len
,
iterations
))
==
0
)
return
STAT_INSECURE
;
for
(
i
=
0
;
i
<
nsec_count
;
i
++
)
if
((
p
=
nsecs
[
i
]))
{
int
base32_size
;
if
(
!
extract_name
(
header
,
plen
,
&
p
,
workspace1
,
1
,
0
)
||
!
(
base32_size
=
base32_decode
(
workspace1
,
(
unsigned
char
*
)
workspace2
)))
return
STAT_INSECURE
;
p
+=
15
+
salt_len
;
/* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */
hash_len
=
*
p
++
;
/* p now points to next hashed name */
if
(
!
CHECK_LEN
(
header
,
p
,
plen
,
hash_len
))
return
STAT_INSECURE
;
if
(
digest_size
==
base32_size
&&
hash_len
==
base32_size
)
{
if
(
memcmp
(
workspace2
,
digest
,
digest_size
)
<=
0
)
{
/* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
wrap around case, name-hash falls between NSEC3 name-hash and end */
if
(
memcmp
(
p
,
name
,
digest_size
)
>
0
||
memcmp
(
workspace2
,
p
,
digest_size
)
>
0
)
return
STAT_SECURE
;
}
else
{
/* wrap around case, name falls between start and next domain name */
if
(
memcmp
(
workspace2
,
p
,
digest_size
)
>
0
&&
memcmp
(
p
,
name
,
digest_size
)
>
0
)
return
STAT_SECURE
;
}
}
}
/* Finally, check that there's no seat of wildcard synthesis */
if
(
!
(
wildcard
=
strchr
(
next_closest
,
'.'
))
||
wildcard
==
next_closest
)
return
STAT_BOGUS
;
wildcard
--
;
*
wildcard
=
'*'
;
if
((
digest_size
=
hash_name
(
wildcard
,
&
digest
,
hash
,
salt
,
salt_len
,
iterations
))
==
0
)
return
STAT_INSECURE
;
for
(
i
=
0
;
i
<
nsec_count
;
i
++
)
if
((
p
=
nsecs
[
i
]))
{
int
base32_size
;
if
(
!
extract_name
(
header
,
plen
,
&
p
,
workspace1
,
1
,
0
)
||
!
(
base32_size
=
base32_decode
(
workspace1
,
(
unsigned
char
*
)
workspace2
)))
return
STAT_INSECURE
;
p
+=
15
+
salt_len
;
/* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */
hash_len
=
*
p
++
;
/* p now points to next hashed name */
if
(
!
CHECK_LEN
(
header
,
p
,
plen
,
hash_len
))
return
STAT_INSECURE
;
if
(
digest_size
==
base32_size
&&
hash_len
==
base32_size
)
{
if
(
memcmp
(
workspace2
,
digest
,
digest_size
)
<=
0
)
{
/* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
wrap around case, name-hash falls between NSEC3 name-hash and end */
if
(
memcmp
(
p
,
name
,
digest_size
)
>
0
||
memcmp
(
workspace2
,
p
,
digest_size
)
>
0
)
return
STAT_SECURE
;
}
else
{
/* wrap around case, name falls between start and next domain name */
if
(
memcmp
(
workspace2
,
p
,
digest_size
)
>
0
&&
memcmp
(
p
,
name
,
digest_size
)
>
0
)
return
STAT_SECURE
;
}
}
}
return
STAT_BOGUS
;
}
/* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */
/* 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 */
/* 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
)
{
{
unsigned
char
*
ans_start
,
*
p1
,
*
p2
;
unsigned
char
*
ans_start
,
*
p1
,
*
p2
,
**
nsecs
;
int
type1
,
class1
,
rdlen1
,
type2
,
class2
,
rdlen2
;
int
type1
,
class1
,
rdlen1
,
type2
,
class2
,
rdlen2
;
int
i
,
j
,
rc
,
have_nsec
,
have_nsec_equal
,
cname_count
=
5
;
int
i
,
j
,
rc
,
nsec_count
,
cname_count
=
10
;
int
nsec_type
=
0
;
if
(
RCODE
(
header
)
==
SERVFAIL
)
if
(
RCODE
(
header
)
==
SERVFAIL
)
return
STAT_BOGUS
;
return
STAT_BOGUS
;
...
@@ -1185,13 +1636,39 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
...
@@ -1185,13 +1636,39 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
struct
blockdata
*
key
;
struct
blockdata
*
key
;
struct
crec
*
crecp
;
struct
crec
*
crecp
;
if
((
rc
=
validate_rrset
(
now
,
header
,
plen
,
class1
,
type1
,
name
,
keyname
,
NULL
,
0
,
0
,
0
))
!=
STAT_SECURE
)
rc
=
validate_rrset
(
now
,
header
,
plen
,
class1
,
type1
,
name
,
keyname
,
NULL
,
0
,
0
,
0
);
if
(
rc
==
STAT_SECURE_WILDCARD
)
{
/* An attacker replay a wildcard answer with a different
answer and overlay an genuine RR. To prove this
hasn't happened, the answer must prove that
a record doesn't exist. Check that here. */
if
(
!
nsec_type
)
{
nsec_type
=
find_nsec_records
(
header
,
plen
,
&
nsecs
,
&
nsec_count
,
class1
);
if
(
nsec_type
==
0
)
return
STAT_INSECURE
;
/* Bad packet */
if
(
nsec_type
==
-
1
)
return
STAT_BOGUS
;
/* No NSECs */
}
if
(
nsec_type
==
T_NSEC
)
rc
=
prove_non_existance_nsec
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
type1
);
else
rc
=
prove_non_existance_nsec3
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
type1
);
if
(
rc
!=
STAT_SECURE
)
return
rc
;
}
else
if
(
rc
!=
STAT_SECURE
)
{
{
if
(
class
)
if
(
class
)
*
class
=
class1
;
/* Class for DS or DNSKEY */
*
class
=
class1
;
/* Class for DS or DNSKEY */
return
rc
;
return
rc
;
}
}
/* Cache RRsigs in answer section, and if we just validated a DS RRset, cache it */
/* Cache RRsigs in answer section, and if we just validated a DS RRset, cache it */
cache_start_insert
();
cache_start_insert
();
...
@@ -1321,7 +1798,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
...
@@ -1321,7 +1798,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
{
{
/* Do we have an answer for the question? */
/* Do we have an answer for the question? */
if
(
type1
==
type2
)
if
(
type1
==
type2
)
return
RCODE
(
header
)
==
NXDOMAIN
?
STAT_
INSECURE
:
STAT_SECURE
;
return
RCODE
(
header
)
==
NXDOMAIN
?
STAT_
BOGUS
:
STAT_SECURE
;
else
if
(
type2
==
T_CNAME
)
else
if
(
type2
==
T_CNAME
)
{
{
/* looped CNAMES */
/* looped CNAMES */
...
@@ -1338,117 +1815,25 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
...
@@ -1338,117 +1815,25 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
return
STAT_INSECURE
;
return
STAT_INSECURE
;
}
}
/* NXDOMAIN or NODATA reply, look for NSEC records to support that.
/* NXDOMAIN or NODATA reply, prove that (name, class1, type1) can't exist */
At this point, p1 points to the start of the auth section.
Use keyname as workspace */
for
(
have_nsec
=
0
,
have_nsec_equal
=
0
,
p2
=
NULL
,
rdlen2
=
0
,
j
=
ntohs
(
header
->
nscount
);
j
!=
0
;
j
--
)
{
unsigned
char
*
nsec_start
=
p1
;
if
(
!
extract_name
(
header
,
plen
,
&
p1
,
keyname
,
1
,
10
))
return
STAT_INSECURE
;
/* bad packet */
GETSHORT
(
type2
,
p1
);
GETSHORT
(
class2
,
p1
);
p1
+=
4
;
/* TTL */
GETSHORT
(
rdlen1
,
p1
);
if
(
class1
==
class2
&&
type2
==
T_NSEC
)
{
have_nsec
=
1
;
rc
=
hostname_cmp
(
name
,
keyname
);
if
(
rc
>=
0
)
{
if
(
p2
)
{
unsigned
char
*
psave
=
p2
;
/* new NSEC is smaller than name,
is it bigger than previous one? */
/* get previous one into name buffer */
if
(
!
extract_name
(
header
,
plen
,
&
psave
,
name
,
1
,
0
))
return
STAT_INSECURE
;
/* bad packet */
if
(
hostname_cmp
(
name
,
keyname
)
<
0
)
{
p2
=
nsec_start
;
rdlen2
=
rdlen1
;
}
/* restore query name */
psave
=
(
unsigned
char
*
)(
header
+
1
);
if
(
!
extract_name
(
header
,
plen
,
&
psave
,
name
,
1
,
0
))
return
STAT_INSECURE
;
}
else
{
/* There was no previous best candidate */
p2
=
nsec_start
;
rdlen2
=
rdlen1
;
}
}
if
(
rc
==
0
)
have_nsec_equal
=
1
;
}
if
(
!
ADD_RDLEN
(
header
,
p1
,
plen
,
rdlen1
))
return
STAT_INSECURE
;
}
if
(
p2
)
/* First marshall the NSEC records, if we've not done it previously */
if
(
!
nsec_type
)
{
{
unsigned
char
*
psave
;
nsec_type
=
find_nsec_records
(
header
,
plen
,
&
nsecs
,
&
nsec_count
,
class1
);
p2
=
skip_name
(
p2
,
header
,
plen
,
0
);
p2
+=
10
;
/* type, class, ttl, rdlen */
psave
=
p2
;
extract_name
(
header
,
plen
,
&
p2
,
keyname
,
1
,
0
);
rdlen2
-=
p2
-
psave
;
}
/* At this point, have_nsec is set if there's at least one NSEC
have_nsec_equal is set if there's an NSEC with the same name as the query;
p2 points to the type bit maps of the biggest NSEC smaller than or equal to the query
or NULL if the query is smaller than all of them.
Keyname holds the next domain name for that NSEC.
rdlen2 is the length of the bitmap field */
if
(
RCODE
(
header
)
==
NOERROR
&&
have_nsec_equal
)
{
int
offset
=
(
type1
&
0xff
)
>>
3
;
int
mask
=
0x80
>>
(
type1
&
0x07
);
while
(
rdlen2
>=
2
)
{
if
(
p2
[
0
]
==
type1
>>
8
)
{
/* Does the NSEC say our type exists? */
if
(
offset
<
p2
[
1
]
&&
(
p2
[
offset
+
2
]
&
mask
)
!=
0
)
return
STAT_INSECURE
;
break
;
/* finshed checking */
}
rdlen2
-=
p2
[
1
];
p2
+=
p2
[
1
];
}
return
STAT_SECURE
;
if
(
nsec_type
==
0
)
return
STAT_INSECURE
;
/* Bad packet */
if
(
nsec_type
==
-
1
)
return
STAT_BOGUS
;
/* No NSECs */
}
}
if
(
RCODE
(
header
)
==
NXDOMAIN
&&
have_nsec
)
if
(
nsec_type
==
T_NSEC
)
{
return
prove_non_existance_nsec
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
type1
);
if
(
!
p2
||
hostname_cmp
(
name
,
keyname
)
<
0
)
else
return
STAT_SECURE
;
/* Before the first, or in a proven gap */
return
prove_non_existance_nsec3
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
type1
);
}
return
STAT_INSECURE
;
}
}
/* Compute keytag (checksum to quickly index a key). See RFC4034 */
/* Compute keytag (checksum to quickly index a key). See RFC4034 */
int
dnskey_keytag
(
int
alg
,
int
flags
,
unsigned
char
*
key
,
int
keylen
)
int
dnskey_keytag
(
int
alg
,
int
flags
,
unsigned
char
*
key
,
int
keylen
)
{
{
...
...
src/rfc1035.c
View file @
5107ace1
...
@@ -337,7 +337,7 @@ unsigned char *skip_questions(struct dns_header *header, size_t plen)
...
@@ -337,7 +337,7 @@ unsigned char *skip_questions(struct dns_header *header, size_t plen)
return
ansp
;
return
ansp
;
}
}
static
unsigned
char
*
skip_section
(
unsigned
char
*
ansp
,
int
count
,
struct
dns_header
*
header
,
size_t
plen
)
unsigned
char
*
skip_section
(
unsigned
char
*
ansp
,
int
count
,
struct
dns_header
*
header
,
size_t
plen
)
{
{
int
i
,
rdlen
;
int
i
,
rdlen
;
...
...
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