2015-12-03 18:31:24 +01:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright 2015 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
2016-01-04 20:38:21 +01:00
# include <sd-messages.h>
2015-12-03 18:31:24 +01:00
# include "alloc-util.h"
2016-01-02 22:12:13 +01:00
# include "conf-files.h"
# include "def.h"
# include "dns-domain.h"
# include "fd-util.h"
# include "fileio.h"
# include "hexdecoct.h"
# include "parse-util.h"
2015-12-03 18:31:24 +01:00
# include "resolved-dns-trust-anchor.h"
2016-01-04 20:38:21 +01:00
# include "resolved-dns-dnssec.h"
2016-01-02 22:12:13 +01:00
# include "set.h"
# include "string-util.h"
# include "strv.h"
2015-12-03 18:31:24 +01:00
2016-01-05 14:18:18 +01:00
static const char trust_anchor_dirs [ ] = CONF_PATHS_NULSTR ( " dnssec-trust-anchors.d " ) ;
2016-01-02 22:12:13 +01:00
/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */
2015-12-03 18:31:24 +01:00
static const uint8_t root_digest [ ] =
{ 0x49 , 0xAA , 0xC1 , 0x1D , 0x7B , 0x6F , 0x64 , 0x46 , 0x70 , 0x2E , 0x54 , 0xA1 , 0x60 , 0x73 , 0x71 , 0x60 ,
0x7A , 0x1A , 0x41 , 0x85 , 0x52 , 0x00 , 0xFD , 0x2C , 0xE1 , 0xCD , 0xDE , 0x32 , 0xF2 , 0x4E , 0x8F , 0xB5 } ;
2016-01-06 00:58:26 +01:00
static bool dns_trust_anchor_knows_domain_positive ( DnsTrustAnchor * d , const char * name ) {
assert ( d ) ;
/* Returns true if there's an entry for the specified domain
* name in our trust anchor */
return
hashmap_contains ( d - > positive_by_key , & DNS_RESOURCE_KEY_CONST ( DNS_CLASS_IN , DNS_TYPE_DNSKEY , name ) ) | |
hashmap_contains ( d - > positive_by_key , & DNS_RESOURCE_KEY_CONST ( DNS_CLASS_IN , DNS_TYPE_DS , name ) ) ;
}
2016-01-06 00:59:51 +01:00
static int dns_trust_anchor_add_builtin_positive ( DnsTrustAnchor * d ) {
2015-12-03 18:31:24 +01:00
_cleanup_ ( dns_resource_record_unrefp ) DnsResourceRecord * rr = NULL ;
_cleanup_ ( dns_answer_unrefp ) DnsAnswer * answer = NULL ;
int r ;
assert ( d ) ;
2016-01-02 22:12:13 +01:00
r = hashmap_ensure_allocated ( & d - > positive_by_key , & dns_resource_key_hash_ops ) ;
2015-12-03 18:31:24 +01:00
if ( r < 0 )
return r ;
2016-01-06 00:58:26 +01:00
/* Only add the built-in trust anchor if there's neither a DS
* nor a DNSKEY defined for the root domain . That way users
* have an easy way to override the root domain DS / DNSKEY
* data . */
if ( dns_trust_anchor_knows_domain_positive ( d , " . " ) )
2016-01-05 14:19:05 +01:00
return 0 ;
2015-12-03 18:31:24 +01:00
/* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */
rr = dns_resource_record_new_full ( DNS_CLASS_IN , DNS_TYPE_DS , " " ) ;
if ( ! rr )
return - ENOMEM ;
rr - > ds . key_tag = 19036 ;
rr - > ds . algorithm = DNSSEC_ALGORITHM_RSASHA256 ;
rr - > ds . digest_type = DNSSEC_DIGEST_SHA256 ;
rr - > ds . digest_size = sizeof ( root_digest ) ;
rr - > ds . digest = memdup ( root_digest , rr - > ds . digest_size ) ;
if ( ! rr - > ds . digest )
return - ENOMEM ;
answer = dns_answer_new ( 1 ) ;
if ( ! answer )
return - ENOMEM ;
2015-12-18 14:37:06 +01:00
r = dns_answer_add ( answer , rr , 0 , DNS_ANSWER_AUTHENTICATED ) ;
2015-12-03 18:31:24 +01:00
if ( r < 0 )
return r ;
2016-01-02 22:12:13 +01:00
r = hashmap_put ( d - > positive_by_key , rr - > key , answer ) ;
if ( r < 0 )
return r ;
answer = NULL ;
return 0 ;
}
2016-01-06 00:59:51 +01:00
static int dns_trust_anchor_add_builtin_negative ( DnsTrustAnchor * d ) {
static const char private_domains [ ] =
/* RFC 6761 says that .test is a special domain for
* testing and not to be installed in the root zone */
" test \0 "
/* RFC 6761 says that these reverse IP lookup ranges
* are for private addresses , and hence should not
* show up in the root zone */
" 10.in-addr.arpa \0 "
" 16.172.in-addr.arpa \0 "
" 17.172.in-addr.arpa \0 "
" 18.172.in-addr.arpa \0 "
" 19.172.in-addr.arpa \0 "
" 20.172.in-addr.arpa \0 "
" 21.172.in-addr.arpa \0 "
" 22.172.in-addr.arpa \0 "
" 23.172.in-addr.arpa \0 "
" 24.172.in-addr.arpa \0 "
" 25.172.in-addr.arpa \0 "
" 26.172.in-addr.arpa \0 "
" 27.172.in-addr.arpa \0 "
" 28.172.in-addr.arpa \0 "
" 29.172.in-addr.arpa \0 "
" 30.172.in-addr.arpa \0 "
" 31.172.in-addr.arpa \0 "
" 168.192.in-addr.arpa \0 "
/* RFC 6762 reserves the .local domain for Multicast
* DNS , it hence cannot appear in the root zone . ( Note
* that we by default do not route . local traffic to
* DNS anyway , except when a configured search domain
* suggests so . ) */
" local \0 "
/* These two are well known, popular private zone
* TLDs , that are blocked from delegation , according
* to :
* http : //icannwiki.com/Name_Collision#NGPC_Resolution
*
* There ' s also ongoing work on making this official
* in an RRC :
* https : //www.ietf.org/archive/id/draft-chapin-additional-reserved-tlds-02.txt */
" home \0 "
" corp \0 "
/* The following four TLDs are suggested for private
* zones in RFC 6762 , Appendix G , and are hence very
* unlikely to be made official TLDs any day soon */
" lan \0 "
" intranet \0 "
" internal \0 "
" private \0 " ;
const char * name ;
int r ;
assert ( d ) ;
/* Only add the built-in trust anchor if there's no negative
* trust anchor defined at all . This enables easy overriding
* of negative trust anchors . */
if ( set_size ( d - > negative_by_name ) > 0 )
return 0 ;
r = set_ensure_allocated ( & d - > negative_by_name , & dns_name_hash_ops ) ;
if ( r < 0 )
return r ;
/* We add a couple of domains as default negative trust
* anchors , where it ' s very unlikely they will be installed in
* the root zone . If they exist they must be private , and thus
* unsigned . */
NULSTR_FOREACH ( name , private_domains ) {
if ( dns_trust_anchor_knows_domain_positive ( d , name ) )
continue ;
r = set_put_strdup ( d - > negative_by_name , name ) ;
if ( r < 0 )
return r ;
}
return 0 ;
}
2016-01-02 22:12:13 +01:00
static int dns_trust_anchor_load_positive ( DnsTrustAnchor * d , const char * path , unsigned line , const char * s ) {
_cleanup_ ( dns_resource_record_unrefp ) DnsResourceRecord * rr = NULL ;
_cleanup_free_ char * domain = NULL , * class = NULL , * type = NULL ;
_cleanup_ ( dns_answer_unrefp ) DnsAnswer * answer = NULL ;
DnsAnswer * old_answer = NULL ;
const char * p = s ;
int r ;
assert ( d ) ;
assert ( line ) ;
r = extract_first_word ( & p , & domain , NULL , EXTRACT_QUOTES ) ;
if ( r < 0 )
return log_warning_errno ( r , " Unable to parse domain in line %s:%u: %m " , path , line ) ;
if ( ! dns_name_is_valid ( domain ) ) {
log_warning ( " Domain name %s is invalid, at line %s:%u, ignoring line. " , domain , path , line ) ;
return - EINVAL ;
}
r = extract_many_words ( & p , NULL , 0 , & class , & type , NULL ) ;
if ( r < 0 )
return log_warning_errno ( r , " Unable to parse class and type in line %s:%u: %m " , path , line ) ;
if ( r ! = 2 ) {
log_warning ( " Missing class or type in line %s:%u " , path , line ) ;
return - EINVAL ;
}
if ( ! strcaseeq ( class , " IN " ) ) {
log_warning ( " RR class %s is not supported, ignoring line %s:%u. " , class , path , line ) ;
return - EINVAL ;
}
if ( strcaseeq ( type , " DS " ) ) {
_cleanup_free_ char * key_tag = NULL , * algorithm = NULL , * digest_type = NULL , * digest = NULL ;
_cleanup_free_ void * dd = NULL ;
uint16_t kt ;
int a , dt ;
size_t l ;
r = extract_many_words ( & p , NULL , 0 , & key_tag , & algorithm , & digest_type , & digest , NULL ) ;
if ( r < 0 ) {
log_warning_errno ( r , " Failed to parse DS parameters on line %s:%u: %m " , path , line ) ;
return - EINVAL ;
}
if ( r ! = 4 ) {
log_warning ( " Missing DS parameters on line %s:%u " , path , line ) ;
return - EINVAL ;
}
r = safe_atou16 ( key_tag , & kt ) ;
if ( r < 0 )
return log_warning_errno ( r , " Failed to parse DS key tag %s on line %s:%u: %m " , key_tag , path , line ) ;
a = dnssec_algorithm_from_string ( algorithm ) ;
if ( a < 0 ) {
log_warning ( " Failed to parse DS algorithm %s on line %s:%u " , algorithm , path , line ) ;
return - EINVAL ;
}
dt = dnssec_digest_from_string ( digest_type ) ;
if ( dt < 0 ) {
log_warning ( " Failed to parse DS digest type %s on line %s:%u " , digest_type , path , line ) ;
return - EINVAL ;
}
r = unhexmem ( digest , strlen ( digest ) , & dd , & l ) ;
if ( r < 0 ) {
log_warning ( " Failed to parse DS digest %s on line %s:%u " , digest , path , line ) ;
return - EINVAL ;
}
rr = dns_resource_record_new_full ( DNS_CLASS_IN , DNS_TYPE_DS , domain ) ;
if ( ! rr )
return log_oom ( ) ;
rr - > ds . key_tag = kt ;
rr - > ds . algorithm = a ;
rr - > ds . digest_type = dt ;
rr - > ds . digest_size = l ;
rr - > ds . digest = dd ;
dd = NULL ;
} else if ( strcaseeq ( type , " DNSKEY " ) ) {
_cleanup_free_ char * flags = NULL , * protocol = NULL , * algorithm = NULL , * key = NULL ;
_cleanup_free_ void * k = NULL ;
uint16_t f ;
size_t l ;
int a ;
r = extract_many_words ( & p , NULL , 0 , & flags , & protocol , & algorithm , & key , NULL ) ;
if ( r < 0 )
return log_warning_errno ( r , " Failed to parse DNSKEY parameters on line %s:%u: %m " , path , line ) ;
if ( r ! = 4 ) {
log_warning ( " Missing DNSKEY parameters on line %s:%u " , path , line ) ;
return - EINVAL ;
}
if ( ! streq ( protocol , " 3 " ) ) {
log_warning ( " DNSKEY Protocol is not 3 on line %s:%u " , path , line ) ;
return - EINVAL ;
}
r = safe_atou16 ( flags , & f ) ;
if ( r < 0 )
return log_warning_errno ( r , " Failed to parse DNSKEY flags field %s on line %s:%u " , flags , path , line ) ;
2016-01-03 17:57:44 +01:00
if ( ( f & DNSKEY_FLAG_ZONE_KEY ) = = 0 ) {
log_warning ( " DNSKEY lacks zone key bit set on line %s:%u " , path , line ) ;
return - EINVAL ;
}
if ( ( f & DNSKEY_FLAG_REVOKE ) ) {
log_warning ( " DNSKEY is already revoked on line %s:%u " , path , line ) ;
return - EINVAL ;
}
2016-01-02 22:12:13 +01:00
a = dnssec_algorithm_from_string ( algorithm ) ;
if ( a < 0 ) {
log_warning ( " Failed to parse DNSKEY algorithm %s on line %s:%u " , algorithm , path , line ) ;
return - EINVAL ;
}
r = unbase64mem ( key , strlen ( key ) , & k , & l ) ;
if ( r < 0 )
return log_warning_errno ( r , " Failed to parse DNSKEY key data %s on line %s:%u " , key , path , line ) ;
rr = dns_resource_record_new_full ( DNS_CLASS_IN , DNS_TYPE_DNSKEY , domain ) ;
if ( ! rr )
return log_oom ( ) ;
rr - > dnskey . flags = f ;
rr - > dnskey . protocol = 3 ;
rr - > dnskey . algorithm = a ;
rr - > dnskey . key_size = l ;
rr - > dnskey . key = k ;
k = NULL ;
} else {
log_warning ( " RR type %s is not supported, ignoring line %s:%u. " , type , path , line ) ;
return - EINVAL ;
}
if ( ! isempty ( p ) ) {
log_warning ( " Trailing garbage on line %s:%u, ignoring line. " , path , line ) ;
return - EINVAL ;
}
r = hashmap_ensure_allocated ( & d - > positive_by_key , & dns_resource_key_hash_ops ) ;
2015-12-03 18:31:24 +01:00
if ( r < 0 )
2016-01-06 00:59:32 +01:00
return log_oom ( ) ;
2015-12-03 18:31:24 +01:00
2016-01-02 22:12:13 +01:00
old_answer = hashmap_get ( d - > positive_by_key , rr - > key ) ;
answer = dns_answer_ref ( old_answer ) ;
r = dns_answer_add_extend ( & answer , rr , 0 , DNS_ANSWER_AUTHENTICATED ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to add trust anchor RR: %m " ) ;
r = hashmap_replace ( d - > positive_by_key , rr - > key , answer ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to add answer to trust anchor: %m " ) ;
old_answer = dns_answer_unref ( old_answer ) ;
2015-12-03 18:31:24 +01:00
answer = NULL ;
2016-01-02 22:12:13 +01:00
return 0 ;
}
static int dns_trust_anchor_load_negative ( DnsTrustAnchor * d , const char * path , unsigned line , const char * s ) {
_cleanup_free_ char * domain = NULL ;
const char * p = s ;
int r ;
assert ( d ) ;
assert ( line ) ;
r = extract_first_word ( & p , & domain , NULL , EXTRACT_QUOTES ) ;
if ( r < 0 )
return log_warning_errno ( r , " Unable to parse line %s:%u: %m " , path , line ) ;
if ( ! dns_name_is_valid ( domain ) ) {
log_warning ( " Domain name %s is invalid, at line %s:%u, ignoring line. " , domain , path , line ) ;
return - EINVAL ;
}
if ( ! isempty ( p ) ) {
log_warning ( " Trailing garbage at line %s:%u, ignoring line. " , path , line ) ;
return - EINVAL ;
}
r = set_ensure_allocated ( & d - > negative_by_name , & dns_name_hash_ops ) ;
if ( r < 0 )
2016-01-06 00:59:32 +01:00
return log_oom ( ) ;
2016-01-02 22:12:13 +01:00
r = set_put ( d - > negative_by_name , domain ) ;
if ( r < 0 )
return log_oom ( ) ;
if ( r > 0 )
domain = NULL ;
return 0 ;
}
static int dns_trust_anchor_load_files (
DnsTrustAnchor * d ,
const char * suffix ,
int ( * loader ) ( DnsTrustAnchor * d , const char * path , unsigned n , const char * line ) ) {
_cleanup_strv_free_ char * * files = NULL ;
char * * f ;
int r ;
assert ( d ) ;
assert ( suffix ) ;
assert ( loader ) ;
r = conf_files_list_nulstr ( & files , suffix , NULL , trust_anchor_dirs ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to enumerate %s trust anchor files: %m " , suffix ) ;
STRV_FOREACH ( f , files ) {
_cleanup_fclose_ FILE * g = NULL ;
char line [ LINE_MAX ] ;
unsigned n = 0 ;
g = fopen ( * f , " r " ) ;
if ( ! g ) {
if ( errno = = ENOENT )
continue ;
log_warning_errno ( errno , " Failed to open %s: %m " , * f ) ;
continue ;
}
FOREACH_LINE ( line , g , log_warning_errno ( errno , " Failed to read %s, ignoring: %m " , * f ) ) {
char * l ;
n + + ;
l = strstrip ( line ) ;
if ( isempty ( l ) )
continue ;
if ( * l = = ' ; ' )
continue ;
( void ) loader ( d , * f , n , l ) ;
}
}
return 0 ;
}
2016-01-06 01:01:00 +01:00
static int domain_name_cmp ( const void * a , const void * b ) {
char * * x = ( char * * ) a , * * y = ( char * * ) b ;
return dns_name_compare_func ( * x , * y ) ;
}
static int dns_trust_anchor_dump ( DnsTrustAnchor * d ) {
2016-01-02 22:12:13 +01:00
DnsAnswer * a ;
Iterator i ;
assert ( d ) ;
2016-01-05 20:27:29 +01:00
if ( hashmap_isempty ( d - > positive_by_key ) )
log_info ( " No positive trust anchors defined. " ) ;
else {
log_info ( " Positive Trust Anchors: " ) ;
HASHMAP_FOREACH ( a , d - > positive_by_key , i ) {
DnsResourceRecord * rr ;
DNS_ANSWER_FOREACH ( rr , a )
log_info ( " %s " , dns_resource_record_to_string ( rr ) ) ;
}
2016-01-02 22:12:13 +01:00
}
2016-01-05 20:27:29 +01:00
if ( set_isempty ( d - > negative_by_name ) )
log_info ( " No negative trust anchors defined. " ) ;
else {
2016-01-06 01:01:00 +01:00
_cleanup_free_ char * * l = NULL , * j = NULL ;
l = set_get_strv ( d - > negative_by_name ) ;
if ( ! l )
return log_oom ( ) ;
2016-01-02 22:12:13 +01:00
2016-01-06 01:01:00 +01:00
qsort_safe ( l , set_size ( d - > negative_by_name ) , sizeof ( char * ) , domain_name_cmp ) ;
j = strv_join ( l , " " ) ;
if ( ! j )
return log_oom ( ) ;
log_info ( " Negative trust anchors: %s " , j ) ;
2016-01-02 22:12:13 +01:00
}
2016-01-06 01:01:00 +01:00
return 0 ;
2016-01-02 22:12:13 +01:00
}
int dns_trust_anchor_load ( DnsTrustAnchor * d ) {
int r ;
assert ( d ) ;
/* If loading things from disk fails, we don't consider this fatal */
( void ) dns_trust_anchor_load_files ( d , " .positive " , dns_trust_anchor_load_positive ) ;
( void ) dns_trust_anchor_load_files ( d , " .negative " , dns_trust_anchor_load_negative ) ;
/* However, if the built-in DS fails, then we have a problem. */
2016-01-06 00:59:51 +01:00
r = dns_trust_anchor_add_builtin_positive ( d ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to add built-in positive trust anchor: %m " ) ;
r = dns_trust_anchor_add_builtin_negative ( d ) ;
2016-01-02 22:12:13 +01:00
if ( r < 0 )
2016-01-06 00:59:51 +01:00
return log_error_errno ( r , " Failed to add built-in negative trust anchor: %m " ) ;
2016-01-02 22:12:13 +01:00
dns_trust_anchor_dump ( d ) ;
2015-12-03 18:31:24 +01:00
return 0 ;
}
void dns_trust_anchor_flush ( DnsTrustAnchor * d ) {
DnsAnswer * a ;
2016-01-07 20:33:31 +01:00
DnsResourceRecord * rr ;
2015-12-03 18:31:24 +01:00
assert ( d ) ;
2016-01-02 22:12:13 +01:00
while ( ( a = hashmap_steal_first ( d - > positive_by_key ) ) )
2015-12-03 18:31:24 +01:00
dns_answer_unref ( a ) ;
2016-01-02 22:12:13 +01:00
d - > positive_by_key = hashmap_free ( d - > positive_by_key ) ;
2016-01-07 20:33:31 +01:00
while ( ( rr = set_steal_first ( d - > revoked_by_rr ) ) )
dns_resource_record_unref ( rr ) ;
d - > revoked_by_rr = set_free ( d - > revoked_by_rr ) ;
2016-01-02 22:12:13 +01:00
d - > negative_by_name = set_free_free ( d - > negative_by_name ) ;
2015-12-03 18:31:24 +01:00
}
2016-01-02 22:12:13 +01:00
int dns_trust_anchor_lookup_positive ( DnsTrustAnchor * d , const DnsResourceKey * key , DnsAnswer * * ret ) {
2015-12-03 18:31:24 +01:00
DnsAnswer * a ;
assert ( d ) ;
assert ( key ) ;
assert ( ret ) ;
/* We only serve DS and DNSKEY RRs. */
if ( ! IN_SET ( key - > type , DNS_TYPE_DS , DNS_TYPE_DNSKEY ) )
return 0 ;
2016-01-02 22:12:13 +01:00
a = hashmap_get ( d - > positive_by_key , key ) ;
2015-12-03 18:31:24 +01:00
if ( ! a )
return 0 ;
* ret = dns_answer_ref ( a ) ;
return 1 ;
}
2016-01-02 22:12:13 +01:00
int dns_trust_anchor_lookup_negative ( DnsTrustAnchor * d , const char * name ) {
assert ( d ) ;
assert ( name ) ;
return set_contains ( d - > negative_by_name , name ) ;
}
2016-01-04 20:38:21 +01:00
2016-01-07 20:33:31 +01:00
static int dns_trust_anchor_revoked_put ( DnsTrustAnchor * d , DnsResourceRecord * rr ) {
int r ;
assert ( d ) ;
r = set_ensure_allocated ( & d - > revoked_by_rr , & dns_resource_record_hash_ops ) ;
if ( r < 0 )
return r ;
r = set_put ( d - > revoked_by_rr , rr ) ;
if ( r < 0 )
return r ;
if ( r > 0 )
dns_resource_record_ref ( rr ) ;
return r ;
}
2016-01-04 20:38:21 +01:00
static int dns_trust_anchor_remove_revoked ( DnsTrustAnchor * d , DnsResourceRecord * rr ) {
_cleanup_ ( dns_answer_unrefp ) DnsAnswer * new_answer = NULL ;
DnsAnswer * old_answer ;
int r ;
2016-01-07 20:33:31 +01:00
/* Remember that this is a revoked trust anchor RR */
r = dns_trust_anchor_revoked_put ( d , rr ) ;
if ( r < 0 )
return r ;
/* Remove this from the positive trust anchor */
2016-01-04 20:38:21 +01:00
old_answer = hashmap_get ( d - > positive_by_key , rr - > key ) ;
if ( ! old_answer )
return 0 ;
new_answer = dns_answer_ref ( old_answer ) ;
r = dns_answer_remove_by_rr ( & new_answer , rr ) ;
if ( r < = 0 )
return r ;
/* We found the key! Warn the user */
log_struct ( LOG_WARNING ,
LOG_MESSAGE_ID ( SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED ) ,
LOG_MESSAGE ( " DNSSEC Trust anchor %s has been revoked. Please update the trust anchor, or upgrade your operating system. " ) , strna ( dns_resource_record_to_string ( rr ) ) ,
" TRUST_ANCHOR=%s " , dns_resource_record_to_string ( rr ) ,
NULL ) ;
if ( dns_answer_size ( new_answer ) < = 0 ) {
assert_se ( hashmap_remove ( d - > positive_by_key , rr - > key ) = = old_answer ) ;
dns_answer_unref ( old_answer ) ;
return 1 ;
}
r = hashmap_replace ( d - > positive_by_key , new_answer - > items [ 0 ] . rr - > key , new_answer ) ;
if ( r < 0 )
return r ;
new_answer = NULL ;
dns_answer_unref ( old_answer ) ;
return 1 ;
}
static int dns_trust_anchor_check_revoked_one ( DnsTrustAnchor * d , DnsResourceRecord * revoked_dnskey ) {
DnsAnswer * a ;
int r ;
assert ( d ) ;
assert ( revoked_dnskey ) ;
assert ( revoked_dnskey - > key - > type = = DNS_TYPE_DNSKEY ) ;
assert ( revoked_dnskey - > dnskey . flags & DNSKEY_FLAG_REVOKE ) ;
a = hashmap_get ( d - > positive_by_key , revoked_dnskey - > key ) ;
if ( a ) {
DnsResourceRecord * anchor ;
/* First, look for the precise DNSKEY in our trust anchor database */
DNS_ANSWER_FOREACH ( anchor , a ) {
if ( anchor - > dnskey . protocol ! = revoked_dnskey - > dnskey . protocol )
continue ;
if ( anchor - > dnskey . algorithm ! = revoked_dnskey - > dnskey . algorithm )
continue ;
if ( anchor - > dnskey . key_size ! = revoked_dnskey - > dnskey . key_size )
continue ;
2016-01-07 20:33:31 +01:00
/* Note that we allow the REVOKE bit to be
* different ! It will be set in the revoked
* key , but unset in our version of it */
2016-01-04 20:38:21 +01:00
if ( ( ( anchor - > dnskey . flags ^ revoked_dnskey - > dnskey . flags ) | DNSKEY_FLAG_REVOKE ) ! = DNSKEY_FLAG_REVOKE )
continue ;
if ( memcmp ( anchor - > dnskey . key , revoked_dnskey - > dnskey . key , anchor - > dnskey . key_size ) ! = 0 )
continue ;
dns_trust_anchor_remove_revoked ( d , anchor ) ;
break ;
}
}
a = hashmap_get ( d - > positive_by_key , & DNS_RESOURCE_KEY_CONST ( revoked_dnskey - > key - > class , DNS_TYPE_DS , DNS_RESOURCE_KEY_NAME ( revoked_dnskey - > key ) ) ) ;
if ( a ) {
DnsResourceRecord * anchor ;
/* Second, look for DS RRs matching this DNSKEY in our trust anchor database */
DNS_ANSWER_FOREACH ( anchor , a ) {
2016-01-07 20:33:31 +01:00
/* We set mask_revoke to true here, since our
* DS fingerprint will be the one of the
* unrevoked DNSKEY , but the one we got passed
* here has the bit set . */
2016-01-14 20:11:11 +01:00
r = dnssec_verify_dnskey_by_ds ( revoked_dnskey , anchor , true ) ;
2016-01-04 20:38:21 +01:00
if ( r < 0 )
return r ;
if ( r = = 0 )
continue ;
dns_trust_anchor_remove_revoked ( d , anchor ) ;
break ;
}
}
return 0 ;
}
2016-01-07 17:03:31 +01:00
int dns_trust_anchor_check_revoked ( DnsTrustAnchor * d , DnsResourceRecord * dnskey , DnsAnswer * rrs ) {
DnsResourceRecord * rrsig ;
2016-01-04 20:38:21 +01:00
int r ;
assert ( d ) ;
2016-01-07 17:03:31 +01:00
assert ( dnskey ) ;
/* Looks if "dnskey" is a self-signed RR that has been revoked
* and matches one of our trust anchor entries . If so , removes
* it from the trust anchor and returns > 0. */
2016-01-04 20:38:21 +01:00
2016-01-07 17:03:31 +01:00
if ( dnskey - > key - > type ! = DNS_TYPE_DNSKEY )
return 0 ;
2016-01-04 20:38:21 +01:00
2016-01-07 17:03:31 +01:00
/* Is this DNSKEY revoked? */
if ( ( dnskey - > dnskey . flags & DNSKEY_FLAG_REVOKE ) = = 0 )
2016-01-04 20:38:21 +01:00
return 0 ;
2016-01-07 17:03:31 +01:00
/* Could this be interesting to us at all? If not,
* there ' s no point in looking for and verifying a
* self - signed RRSIG . */
if ( ! dns_trust_anchor_knows_domain_positive ( d , DNS_RESOURCE_KEY_NAME ( dnskey - > key ) ) )
return 0 ;
/* Look for a self-signed RRSIG in the other rrs belonging to this DNSKEY */
DNS_ANSWER_FOREACH ( rrsig , rrs ) {
2016-01-04 20:38:21 +01:00
DnssecResult result ;
2016-01-07 17:03:31 +01:00
if ( rrsig - > key - > type ! = DNS_TYPE_RRSIG )
continue ;
r = dnssec_rrsig_match_dnskey ( rrsig , dnskey , true ) ;
2016-01-04 20:38:21 +01:00
if ( r < 0 )
return r ;
if ( r = = 0 )
continue ;
2016-01-07 17:03:31 +01:00
r = dnssec_verify_rrset ( rrs , dnskey - > key , rrsig , dnskey , USEC_INFINITY , & result ) ;
if ( r < 0 )
return r ;
if ( result ! = DNSSEC_VALIDATED )
2016-01-04 20:38:21 +01:00
continue ;
2016-01-07 17:03:31 +01:00
/* Bingo! This is a revoked self-signed DNSKEY. Let's
* see if this precise one exists in our trust anchor
* database , too . */
r = dns_trust_anchor_check_revoked_one ( d , dnskey ) ;
if ( r < 0 )
return r ;
2016-01-04 20:38:21 +01:00
2016-01-07 17:03:31 +01:00
return 1 ;
2016-01-04 20:38:21 +01:00
}
return 0 ;
}
2016-01-07 20:33:31 +01:00
int dns_trust_anchor_is_revoked ( DnsTrustAnchor * d , DnsResourceRecord * rr ) {
assert ( d ) ;
if ( ! IN_SET ( rr - > key - > type , DNS_TYPE_DS , DNS_TYPE_DNSKEY ) )
return 0 ;
return set_contains ( d - > revoked_by_rr , rr ) ;
}