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
fbc52057
Commit
fbc52057
authored
Dec 23, 2014
by
Simon Kelley
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix problems validating NSEC3 and wildcards.
parent
cbc65242
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
128 additions
and
125 deletions
+128
-125
src/dnssec.c
src/dnssec.c
+128
-125
No files found.
src/dnssec.c
View file @
fbc52057
...
@@ -615,6 +615,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
...
@@ -615,6 +615,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
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_SECURE_WILDCARD if it validates and is the result of wildcard expansion.
(In this case *wildcard_out points to the "body" of the wildcard within name.)
STAT_NO_SIG no RRsigs found.
STAT_NO_SIG no RRsigs found.
STAT_INSECURE RRset empty.
STAT_INSECURE RRset empty.
STAT_BOGUS signature is wrong, bad packet.
STAT_BOGUS signature is wrong, bad packet.
...
@@ -625,8 +626,8 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
...
@@ -625,8 +626,8 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
name is unchanged on exit. keyname is used as workspace and trashed.
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
,
int
type
,
char
*
name
,
char
*
keyname
,
struct
blockdata
*
key
,
int
keylen
,
int
algo_in
,
int
keytag_in
)
char
*
name
,
char
*
keyname
,
char
**
wildcard_out
,
struct
blockdata
*
key
,
int
keylen
,
int
algo_in
,
int
keytag_in
)
{
{
static
unsigned
char
**
rrset
=
NULL
,
**
sigs
=
NULL
;
static
unsigned
char
**
rrset
=
NULL
,
**
sigs
=
NULL
;
static
int
rrset_sz
=
0
,
sig_sz
=
0
;
static
int
rrset_sz
=
0
,
sig_sz
=
0
;
...
@@ -798,8 +799,16 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
...
@@ -798,8 +799,16 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
{
{
int
k
;
int
k
;
for
(
k
=
name_labels
-
labels
;
k
!=
0
;
k
--
)
for
(
k
=
name_labels
-
labels
;
k
!=
0
;
k
--
)
while
(
*
name_start
!=
'.'
&&
*
name_start
!=
0
)
{
name_start
++
;
while
(
*
name_start
!=
'.'
&&
*
name_start
!=
0
)
name_start
++
;
if
(
k
!=
1
)
name_start
++
;
}
if
(
wildcard_out
)
*
wildcard_out
=
name_start
+
1
;
name_start
--
;
name_start
--
;
*
name_start
=
'*'
;
*
name_start
=
'*'
;
}
}
...
@@ -974,7 +983,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
...
@@ -974,7 +983,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
if
(
recp1
->
addr
.
ds
.
keylen
==
(
int
)
hash
->
digest_size
&&
if
(
recp1
->
addr
.
ds
.
keylen
==
(
int
)
hash
->
digest_size
&&
(
ds_digest
=
blockdata_retrieve
(
recp1
->
addr
.
key
.
keydata
,
recp1
->
addr
.
ds
.
keylen
,
NULL
))
&&
(
ds_digest
=
blockdata_retrieve
(
recp1
->
addr
.
key
.
keydata
,
recp1
->
addr
.
ds
.
keylen
,
NULL
))
&&
memcmp
(
ds_digest
,
digest
,
recp1
->
addr
.
ds
.
keylen
)
==
0
&&
memcmp
(
ds_digest
,
digest
,
recp1
->
addr
.
ds
.
keylen
)
==
0
&&
validate_rrset
(
now
,
header
,
plen
,
class
,
T_DNSKEY
,
name
,
keyname
,
key
,
rdlen
-
4
,
algo
,
keytag
)
==
STAT_SECURE
)
validate_rrset
(
now
,
header
,
plen
,
class
,
T_DNSKEY
,
name
,
keyname
,
NULL
,
key
,
rdlen
-
4
,
algo
,
keytag
)
==
STAT_SECURE
)
{
{
valid
=
1
;
valid
=
1
;
break
;
break
;
...
@@ -1443,11 +1452,88 @@ static int base32_decode(char *in, unsigned char *out)
...
@@ -1443,11 +1452,88 @@ static int base32_decode(char *in, unsigned char *out)
return
p
-
out
;
return
p
-
out
;
}
}
static
int
check_nsec3_coverage
(
struct
dns_header
*
header
,
size_t
plen
,
int
digest_len
,
unsigned
char
*
digest
,
int
type
,
char
*
workspace1
,
char
*
workspace2
,
unsigned
char
**
nsecs
,
int
nsec_count
)
{
int
i
,
hash_len
,
salt_len
,
base32_len
,
rdlen
;
unsigned
char
*
p
,
*
psave
;
for
(
i
=
0
;
i
<
nsec_count
;
i
++
)
if
((
p
=
nsecs
[
i
]))
{
if
(
!
extract_name
(
header
,
plen
,
&
p
,
workspace1
,
1
,
0
)
||
!
(
base32_len
=
base32_decode
(
workspace1
,
(
unsigned
char
*
)
workspace2
)))
return
0
;
p
+=
8
;
/* class, type, TTL */
GETSHORT
(
rdlen
,
p
);
psave
=
p
;
p
+=
4
;
/* algo, flags, iterations */
salt_len
=
*
p
++
;
/* salt_len */
p
+=
salt_len
;
/* salt */
hash_len
=
*
p
++
;
/* p now points to next hashed name */
if
(
!
CHECK_LEN
(
header
,
p
,
plen
,
hash_len
))
return
0
;
if
(
digest_len
==
base32_len
&&
hash_len
==
base32_len
)
{
int
rc
=
memcmp
(
workspace2
,
digest
,
digest_len
);
if
(
rc
==
0
)
{
/* We found an NSEC3 whose hashed name exactly matches the query, so
we just need to check the type map. p points to the RR data for the record. */
int
offset
=
(
type
&
0xff
)
>>
3
;
int
mask
=
0x80
>>
(
type
&
0x07
);
p
+=
hash_len
;
/* skip next-domain hash */
rdlen
-=
p
-
psave
;
if
(
!
CHECK_LEN
(
header
,
p
,
plen
,
rdlen
))
return
0
;
while
(
rdlen
>=
2
)
{
if
(
p
[
0
]
==
type
>>
8
)
{
/* Does the NSEC3 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
1
;
}
else
if
(
rc
<=
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
,
digest
,
digest_len
)
>
0
||
memcmp
(
workspace2
,
p
,
digest_len
)
>
0
)
return
1
;
}
else
{
/* wrap around case, name falls between start and next domain name */
if
(
memcmp
(
workspace2
,
p
,
digest_len
)
>
0
&&
memcmp
(
p
,
digest
,
digest_len
)
>
0
)
return
1
;
}
}
}
return
0
;
}
static
int
prove_non_existence_nsec3
(
struct
dns_header
*
header
,
size_t
plen
,
unsigned
char
**
nsecs
,
int
nsec_count
,
static
int
prove_non_existence_nsec3
(
struct
dns_header
*
header
,
size_t
plen
,
unsigned
char
**
nsecs
,
int
nsec_count
,
char
*
workspace1
,
char
*
workspace2
,
char
*
name
,
int
type
)
char
*
workspace1
,
char
*
workspace2
,
char
*
name
,
int
type
,
char
*
wildname
)
{
{
unsigned
char
*
salt
,
*
p
,
*
digest
;
unsigned
char
*
salt
,
*
p
,
*
digest
;
int
digest_len
,
i
,
iterations
,
salt_len
,
hash_len
,
base32_len
,
algo
=
0
;
int
digest_len
,
i
,
iterations
,
salt_len
,
base32_len
,
algo
=
0
;
struct
nettle_hash
const
*
hash
;
struct
nettle_hash
const
*
hash
;
char
*
closest_encloser
,
*
next_closest
,
*
wildcard
;
char
*
closest_encloser
,
*
next_closest
,
*
wildcard
;
...
@@ -1520,7 +1606,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
...
@@ -1520,7 +1606,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
if
(
!
(
hash
=
hash_find
(
"sha1"
)))
if
(
!
(
hash
=
hash_find
(
"sha1"
)))
return
STAT_BOGUS
;
return
STAT_BOGUS
;
/* Now, we need the "closest encloser NSEC3" */
if
((
digest_len
=
hash_name
(
name
,
&
digest
,
hash
,
salt
,
salt_len
,
iterations
))
==
0
)
return
STAT_BOGUS
;
if
(
check_nsec3_coverage
(
header
,
plen
,
digest_len
,
digest
,
type
,
workspace1
,
workspace2
,
nsecs
,
nsec_count
))
return
STAT_SECURE
;
/* Can't find an NSEC3 which covers the name directly, we need the "closest encloser NSEC3"
or an answer inferred from a wildcard record. */
closest_encloser
=
name
;
closest_encloser
=
name
;
next_closest
=
NULL
;
next_closest
=
NULL
;
...
@@ -1529,6 +1622,9 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
...
@@ -1529,6 +1622,9 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
if
(
*
closest_encloser
==
'.'
)
if
(
*
closest_encloser
==
'.'
)
closest_encloser
++
;
closest_encloser
++
;
if
(
wildname
&&
hostname_isequal
(
closest_encloser
,
wildname
))
break
;
if
((
digest_len
=
hash_name
(
closest_encloser
,
&
digest
,
hash
,
salt
,
salt_len
,
iterations
))
==
0
)
if
((
digest_len
=
hash_name
(
closest_encloser
,
&
digest
,
hash
,
salt
,
salt_len
,
iterations
))
==
0
)
return
STAT_BOGUS
;
return
STAT_BOGUS
;
...
@@ -1551,127 +1647,33 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
...
@@ -1551,127 +1647,33 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
}
}
while
((
closest_encloser
=
strchr
(
closest_encloser
,
'.'
)));
while
((
closest_encloser
=
strchr
(
closest_encloser
,
'.'
)));
/* No usable NSEC3s */
if
(
!
closest_encloser
)
if
(
i
==
nsec_count
)
return
STAT_BOGUS
;
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
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_BOGUS
;
/* bad packet */
p
+=
hash_len
;
rdlen
-=
p
-
psave
;
while
(
rdlen
>=
2
)
{
if
(
!
CHECK_LEN
(
header
,
p
,
plen
,
rdlen
))
return
STAT_BOGUS
;
if
(
p
[
0
]
==
type
>>
8
)
{
/* Does the NSEC3 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-existence of the next-closest encloser */
/* Look for NSEC3 that proves the non-existence of the next-closest encloser */
if
((
digest_len
=
hash_name
(
next_closest
,
&
digest
,
hash
,
salt
,
salt_len
,
iterations
))
==
0
)
if
((
digest_len
=
hash_name
(
next_closest
,
&
digest
,
hash
,
salt
,
salt_len
,
iterations
))
==
0
)
return
STAT_BOGUS
;
return
STAT_BOGUS
;
for
(
i
=
0
;
i
<
nsec_count
;
i
++
)
if
(
!
check_nsec3_coverage
(
header
,
plen
,
digest_len
,
digest
,
type
,
workspace1
,
workspace2
,
nsecs
,
nsec_count
))
if
((
p
=
nsecs
[
i
]))
{
if
(
!
extract_name
(
header
,
plen
,
&
p
,
workspace1
,
1
,
0
)
||
!
(
base32_len
=
base32_decode
(
workspace1
,
(
unsigned
char
*
)
workspace2
)))
return
STAT_BOGUS
;
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_BOGUS
;
if
(
digest_len
==
base32_len
&&
hash_len
==
base32_len
)
{
if
(
memcmp
(
workspace2
,
digest
,
digest_len
)
<=
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
,
digest
,
digest_len
)
>
0
||
memcmp
(
workspace2
,
p
,
digest_len
)
>
0
)
return
STAT_SECURE
;
}
else
{
/* wrap around case, name falls between start and next domain name */
if
(
memcmp
(
workspace2
,
p
,
digest_len
)
>
0
&&
memcmp
(
p
,
digest
,
digest_len
)
>
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_len
=
hash_name
(
wildcard
,
&
digest
,
hash
,
salt
,
salt_len
,
iterations
))
==
0
)
return
STAT_BOGUS
;
return
STAT_BOGUS
;
for
(
i
=
0
;
i
<
nsec_count
;
i
++
)
/* Finally, check that there's no seat of wildcard synthesis */
if
((
p
=
nsecs
[
i
]))
if
(
!
wildname
)
{
{
if
(
!
extract_name
(
header
,
plen
,
&
p
,
workspace1
,
1
,
0
)
||
if
(
!
(
wildcard
=
strchr
(
next_closest
,
'.'
))
||
wildcard
==
next_closest
)
!
(
base32_len
=
base32_decode
(
workspace1
,
(
unsigned
char
*
)
workspace2
)))
return
STAT_BOGUS
;
return
STAT_BOGUS
;
wildcard
--
;
p
+=
15
+
salt_len
;
/* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */
*
wildcard
=
'*'
;
hash_len
=
*
p
++
;
/* p now points to next hashed name */
if
((
digest_len
=
hash_name
(
wildcard
,
&
digest
,
hash
,
salt
,
salt_len
,
iterations
))
==
0
)
if
(
!
CHECK_LEN
(
header
,
p
,
plen
,
hash_len
))
return
STAT_BOGUS
;
return
STAT_BOGUS
;
if
(
!
check_nsec3_coverage
(
header
,
plen
,
digest_len
,
digest
,
type
,
workspace1
,
workspace2
,
nsecs
,
nsec_count
))
if
(
digest_len
==
base32_len
&&
hash_len
==
base32_len
)
return
STAT_BOGUS
;
{
}
if
(
memcmp
(
workspace2
,
digest
,
digest_len
)
<=
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
,
digest
,
digest_len
)
>
0
||
memcmp
(
workspace2
,
p
,
digest_len
)
>
0
)
return
STAT_SECURE
;
}
else
{
/* wrap around case, name falls between start and next domain name */
if
(
memcmp
(
workspace2
,
p
,
digest_len
)
>
0
&&
memcmp
(
p
,
digest
,
digest_len
)
>
0
)
return
STAT_SECURE
;
}
}
}
return
STAT_
BOGUS
;
return
STAT_
SECURE
;
}
}
/* 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) */
...
@@ -1792,8 +1794,9 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
...
@@ -1792,8 +1794,9 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
struct
all_addr
a
;
struct
all_addr
a
;
struct
blockdata
*
key
;
struct
blockdata
*
key
;
struct
crec
*
crecp
;
struct
crec
*
crecp
;
char
*
wildname
;
rc
=
validate_rrset
(
now
,
header
,
plen
,
class1
,
type1
,
name
,
keyname
,
NULL
,
0
,
0
,
0
);
rc
=
validate_rrset
(
now
,
header
,
plen
,
class1
,
type1
,
name
,
keyname
,
&
wildname
,
NULL
,
0
,
0
,
0
);
if
(
rc
==
STAT_SECURE_WILDCARD
)
if
(
rc
==
STAT_SECURE_WILDCARD
)
{
{
...
@@ -1807,7 +1810,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
...
@@ -1807,7 +1810,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if
(
nsec_type
==
T_NSEC
)
if
(
nsec_type
==
T_NSEC
)
rc
=
prove_non_existence_nsec
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
type1
);
rc
=
prove_non_existence_nsec
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
type1
);
else
else
rc
=
prove_non_existence_nsec3
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
type1
);
rc
=
prove_non_existence_nsec3
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
type1
,
wildname
);
if
(
rc
!=
STAT_SECURE
)
if
(
rc
!=
STAT_SECURE
)
return
rc
;
return
rc
;
...
@@ -1933,7 +1936,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
...
@@ -1933,7 +1936,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if
(
nsec_type
==
T_NSEC
)
if
(
nsec_type
==
T_NSEC
)
return
prove_non_existence_nsec
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
qtype
);
return
prove_non_existence_nsec
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
qtype
);
else
else
return
prove_non_existence_nsec3
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
qtype
);
return
prove_non_existence_nsec3
(
header
,
plen
,
nsecs
,
nsec_count
,
daemon
->
workspacename
,
keyname
,
name
,
qtype
,
NULL
);
}
}
/* Chase the CNAME chain in the packet until the first record which _doesn't validate.
/* Chase the CNAME chain in the packet until the first record which _doesn't validate.
...
@@ -1980,7 +1983,7 @@ int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char
...
@@ -1980,7 +1983,7 @@ int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char
return
STAT_INSECURE
;
return
STAT_INSECURE
;
/* validate CNAME chain, return if insecure or need more data */
/* validate CNAME chain, return if insecure or need more data */
rc
=
validate_rrset
(
now
,
header
,
plen
,
class
,
type
,
name
,
keyname
,
NULL
,
0
,
0
,
0
);
rc
=
validate_rrset
(
now
,
header
,
plen
,
class
,
type
,
name
,
keyname
,
NULL
,
NULL
,
0
,
0
,
0
);
if
(
rc
!=
STAT_SECURE
)
if
(
rc
!=
STAT_SECURE
)
{
{
if
(
rc
==
STAT_NO_SIG
)
if
(
rc
==
STAT_NO_SIG
)
...
...
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