# Copyright 2004-2013 SPARTA, Inc.  All rights reserved.
# See the COPYING file included with the dnssec-tools package for details.


			     DNSSEC-Tools
			Is your domain secure?


DNSSEC validator
----------------

The libval(3) library provides basic functionality for resource-record
validation.  It relies on the resolver component (see
libsres-implementation-notes for more details) to fetch answers from a
DNSSEC-aware name server.

Overview 
-------

The security extensions to DNS allows applications to test the authenticity of
data returned by the DNS. The validator library performs this test by checking
the cryptographic signatures that cover DNS records and by building a sequence 
of such verified records up to a trusted key. The library exports a number of 
basic interfaces that applications may use in lieu of older resolver interfaces 
provided by the libres(3) library. Other interfaces are also provided that 
allow applications more detail into the validation process. This range allows 
for the creation of applications that are either only interested in a basic
"validated" or "not-validated" result, or more sophisticated applications that
can look for specific errors as a sign of some network abnormality or attack.

See libval(3), val_gethostbyname(3), val_getaddrinfo(3), val_res_query(3) and
val_get_rrset(3) for the interfaces are exported by the validator. 

The validator supports the RSA/MD5 [RFC_2537], DSA/SHA-1 [RFC_2536] and 
RSA/SHA-1 [RFC_3110] DNSSEC algorithms.  It uses the OpenSSL software package 
for cryptographic processing.  


Validator Design Choices
------------------------

1. Trusted versus validated conditions 

The DNSSEC specification lists four validation states: success, bogus, 
indeterminate and provably insecure. While the validator library is 
able to gather details of each of these conditions by inspecting the
authentication chain, most applications are only interested in a high-level
answer that tells it if the response was trustworthy or not. Since 
the bogus and indeterminate states are effectively equivalent, the DNSSEC
specification really only allows for three conditions - Validated, Untrusted,
Provably Insecure.

The provably insecure condition is interesting, since some applications 
may view this as untrusted, while others may view this as a directive 
by the parent to say that there is no security expectation for the child,
in which the answer is trustworthy.  Validation policy may also specify 
all answers from a zone to be either trustworthy or untrustworthy. 

In view of the above considerations, the validator library defines three
high-level states for answers - trusted, validated, and untrusted.

The validated condition covers all the states where an answer was 
cryptographically validated from a locally configured trust anchor.
The trusted condition covers those states that are validated, and
also those states that are locally trusted. All other conditions 
are untrusted. 

A negative proof may similarly be trusted, validated or untrusted.

The provably insecure condition may be configured by local policy
to either be trusted or not.


2. Top-down versus bottom-up validation

There are two possible ways in which a validator may construct its
authentication chain: top-down and bottom-up. Top-down validation uses the
normative query process to also fetch and validate DNSSEC meta-data,
chaining down from a known trust anchor. Bottom-up validation builds the
authentication chain from the data to a known trust anchor.

For a security-aware recursive name server, the top-down approach is generally
more efficient. For a validating end-system, the type of data that must be
validated decides which approach is more efficient. The reason for this is that
validating end-systems are very often located behind some caching name server
In order to utilize the benefits provided by the DNS caching infrastructure, 
the validating stub resolver must not attempt to recursively answer all 
queries itself. Furthermore, firewall configurations in may environments 
also run recursive name server processes that preempt any attempt made
by the end system to run in recursive mode.

With this architecture in mind, the problems with top-down validation become
evident. In order for top-down validation to work, the validator must be able 
reconstruct the list of zonecuts all the way down from the trust anchor to the
query. It must do so by issuing SOA queries for the list of names obtained by 
adding labels present in the query to the zone name configured as the trust 
anchor, in succession, until the zonecut information can be determined, and 
repeat this process till the zonecut covering the original query is reached.
Bottom-up validation does not suffer from this problem to the extent that
zonecut information for is already available via the RRSIG meta-data.

The above reasoning holds only if zones are signed most of the time. If 
signatures are missing, the validator would have to perform a similar but 
less query-intensive process of determining if the zone that lacked DNSSEC 
meta-data was actually provably insecure. The provably insecure check is 
straightforward to perform if validation happens top-down.

Using the above rationale, the libval library has been designed to do 
bottom-up validation by default and then switch over to top-down validation 
in case a provably insecure condition needs to be checked.

3. Multi-threading support (sharing validator contexts)

Multi-threaded applications should be able to share validator contexts.
The constraints to doing this is that data can time out. However the 
specifications say that data must be kept for the duration of the 
validation check (even if the TTL expires). Another constraint is that
multiple threads can issue the same query at the same time.

The design choices for context sharing are impacted by the following 
concerns:
    - Extent of sharing: applications that are able to access a 
      validator context that is being used by some other application 
      has security implications
    - lock granularity: overhead of acquiring locks for each assertion 
      chain element v/s typical operation of single threaded app
    - posix does not allow lock upgrade and downgrade

The following design choices were made for the validator:
    - "hold" relevant queries till such time that validate 
      does not complete (queries_for_query structure)  
    - queries can be deleted only if there is no qfq pointing to it.
      This is determined through the qc_refcount field in val_query_chain.
    - Locking strategy
        - CTX_LOCK_ACACHE : Mutex to ensure that only one thread can modify
            the context policies or cache at a given point in time.
        - VAL_CACHE_LOCK : R/W lock to ensure that the context-independent
            resolver cache is only modified when no other thread is reading 
            data from it. 
        - CTX_LOCK_POL : R/W lock to ensure that the context is not released 
            while it is still being used by another thread. 
        - LOCK_DEFAULT_CONTEXT : Mutex to ensure that the default context is
            not modified while it is being accessed in another thread.

//This impacts the resolver logic in that libsres should never block 
//also imply that we are not able to log query-related information since 
//this is unreliable after a call to the validator functions (queries may 
//time out).


4. NULL contexts 

All functions exported by libval have their first parameter as a 
"validator context". The context provides a handle to the validation 
policy as selected by the application. Since the results of validation 
depend on validator policy, the context also provides the handle to cached 
authentication chains. The expected usage is that applications will create 
a context using the val_create_context() function and supply this value to
different functions so that the results of validation are reused.

Successive calls to val_create_context() create fresh context objects, 
even if they were created using the same label. This allows applications to 
be able to validate using the same policy but without  reusing
older validation results.

libval also allows applications that are DNSSEC-agnostic to supply a NULL 
context for different functions.  In such cases libval will create a 
temporary context using some default policy. However, if this context is
deleted after use, it would mean that DNSSEC-agnostic applications will 
never be able to make use of the benefit of validator caches

In order to account for this we special case the handling of NULL labels 
during context creation.  Instead of creating a fresh context each time 
a NULL argument is given, libval will return a saved value if
the context was already created in the past. This behavior is for NULL 
labels only (and not even for contexts created using the ":" label). in 
other words, successive calls to val_create_contex(":",...) will still 
create fresh contexts. The rationale here is that applications that
know what value to pass as a non-NULL label should also be intelligent 
enough to reuse context objects that they create.

5. Environment overrides for validator policy

While it is always possible to write an application that uses a specific 
validator policy (or allow the user to supply one through a command line
option), we wanted to provide a less intrusive way for users to specify
a validation policy to libval. There were two approaches that we came up 
with - reading the policy label from an variable, and dynamically 
generating the policy label from the application name. 

        locked-down system with single policy
          An administrator that wants to (and is able to) lock down a
          system to a particular validator policy, must be able to
          disable other ways for a user to change the validator
          policy
 
        locked-down system with app-specific policies
          An administrator that wants to (and is able to) lock down a
          system to a particular dnsval.conf file, but wishes to use dif-
          ferent policies for different applications must still be allowed
          to disable reading policy labels from the environment. 

        User controlled
          It must be possible for an administrator to give complete 
          flexibility to the user over the choice of policy label,
          either on a per-application basis or on a system-wide basis.

The set of precedence rules are documented in dnsval.conf(3)


6. Minimizing false positives 

While constructing the chain of trust we are willing to look at more trust
anchors when trying to verify a proof. When trying to prove that DNSSEC is
not required (through the provably insecure condition), we only look at the 
closest matching trust anchor.


7. Fallback and recovery

During the initial years of DNSSEC deployment there are expected to be various
cases where a DNSSEc validator will encounter a response returned from a
DNSSEC-unaware name server. While the DNSSEC specifications state the 
"clear-path" requirement where there is an expectation that all name servers
along the validation path must be DNSSEC-aware, from a user-experience perspective, 
it behooves the validator to do some sort of fallback in order to route around 
such (temporarily broken or badly behaving) middle boxes. libval has the 
following fallback and recovery strategy:

a) Resolver fallback

libval (via libsres) will send queries in parallel to all name servers
listed in the NSset, staggering the queries every five seconds. Thus 
it will only return a DNS error if all name servers fail to answer the query.
See libsres-implementation-notes for additional details.

b) EDNS0 fallback

In querying various name servers, libsres will also attempt multiple EDNS0 
sizes, ending with a query that has EDNS0 disabled (i.e. no CD bit set).
The following EDNS0 sizes are tried by default: 4096, 1492, 512
The "edns0-size" policy knob in dnsval.conf can be used to change the 
largest EDNS0 size that is attempted.

c) Validation error fallback

If the  validator encounters a validation failure it will re-attempt the 
lookup, but this time recursing from root (unless it is already recursively
looking up the name in which case this fallback is not attempted). This 
gives libval the opportunity to recover from failure scenarios where the recursive 
name server is lying or is broken DNSSEC-wise, but also allows libval to use
the caching properties of the recursive name server to the extent possible.


Validator Algorithm
-------------------

The val_resolve_and_check() interface forms the core interface for the
validator library. Its algorithm is as follows:

i)	A new validator context is created if one does not exist already.

ii)	The next step in val_resolve_and_check() is to build a data request
	entry for the missing data. The data request entry is stored in the
	"queries" linked list which is of type "struct queries_for_query". 
    Each element in this linked list internally maps to an element in 
    another linked list of type 'struct val_query_chain'. 
    The queries linked list is created separately for each call to 
    val_resolve_and_check. The val_query_chain linked list is maintained 
    on a per-context basis and is the "query cache" for the context.

iii)Not all data requests get translated into actual DNS queries; some
	answers may be obtained from the local cache. Answers are always 
    accessed through the struct val_query_chain element. This is because, data may
    time out. The struct val_query_chain element maintains the state 
    of the response; if it times out the query state will revert to the
    initial state. 

iv)	For every answer that is received, the validator assimilates this data
	and identifies any additional queries need to be made in order to
	either verify the data contained in the response or to build the chain-
	of-trust to a trust anchor. Assimilated data is stored in the "struct
	val_digested_auth_chain" structure, which is also a linked list. A
	separate data request entry is created for each such data component
	that is required and all of them are added to the "queries"
	linked-list. The val_digested_auth_chain linked list is also maintained
    on a per-context basis. 

v)	Data contained in an element of the authentcation chain can be verified
	as long as all cryptographic components necessary to complete this
	operation is available. The verification operation may be
	performed even if other components in the chain of trust are still
	being built.

vi) Since multiple RRsets may be returned in response to a query there
    can be multiple authentication chains that are returned.
    Multiple authentication chains are also possible for negative 
    proofs where the proof is actually comprised of an SOA RR, and one 
    or more NSEC (or NSEC3) RRsets. Multiple answers are represented 
    through multiple elements in the struct val_internal_result linked 
    list. Each val_internal_result element wraps around an authentication 
    chain. It points to the chain and also contains a consolidated 
    validation status value for the authentication chain.

vii)For RRsets in the response that are not validated, the validator may
    selectively perform the DLV logic to identify if the data can be validated
    that way. The DLV logic is only applied if the entry point from the DLV
    tree to the main tree is at a closer point to the query name than any
    configured trust anchor for the main tree.

viii) For status values that map to unsuccessful conditions, an attempt is
    made to see if the answer is actually provably insecure. This test
    test is made in a top-down fashion starting from the closest configured
    trust anchor and ending at the zone cut for the query in question.

viii)Steps iii) through viii) are repeated as long as there are additional
	queries	that have still not received a response or failure from the
	DNS. Upon completion, the authentication chain linked list contains the
	list of all answers that were returned from the DNS with a field
	describing each of their validation status values and a consolidated
    validation status value for the entire authentication chain. If no 
    answers were returned or some DNS error was encountered for a particular 
    query, an empty authentication chain node is created to contain this 
    error value.

ix) Once all authentication chains have been completed, a number of 
    sanity checks are performed on the elements of the . 
     - Any non-existence proofs are checked at this time, but only 
       if all components of the proof are trustworthy.
     - Wildcard responses are checked to see if they actually expand 
       the correct type and the original name itself does not exist.
     - Any CNAME and DNAME chains are checked to ensure that they 
       are loop-free and that they are complete. If the final element
       in a CNAME or DNAME chain is also an alias then any proof of
       non-existence is checked to ensure that the name being pointed
       to is provably non-existent.
   Each element in the val_internal_result linked list that has passed 
   the sanity check above is converted to a similar entry in the 
   val_result_chain linked list. The elements in the val_result_chain
   linked list (including the authentication chain elements) are copied 
   indivually from their val_internal_result counterparts; they are 
   not merely a reference. 

x) Any left over elements in the val_internal_result linked list that
   have not been consumed by the above sanity check process is transformed
   into a similar entry into the val_result_chain linked list. If the element
   is a component of a proof of non-existence it is considered extraneous
   and marked as bogus. 
   
xi)	The val_resolve_and_check() method returns the val_result_chain
   to the calling application only if the application has requested this
   information.


Lower-level routines: digesting answers received from libsres
-------------------------------------------------------------

The validator uses the asynchronous query_send() and response_rcv()
resolver interfaces (see man page for libsres(3)) to fetch answers from the DNS.
The D0 and CD bit on the query is only set if a trust anchor is configured
above the name being queried for. Note that since checks for the provably 
insecure condition are not made as the referral is being traversed, the 
D0 and CD bits are also set while issuing queries for names that are under
a provably insecure domain.

After the answer is received, the first step is "digesting" the response --
RRsets in the answer and authority sections are extracted as is any
referral information present in the additional section of the response. Any
DS or DNSKEY information is also cached for future use. One aspect
of the caching functionality that will change in future is the notion of
credibility of data stored within the cache.

The validator follows any referrals or aliases that are returned as part of 
the response and issues queries to fetch any missing glue. Information gathered 
as part of following referrals are maintained separately within the val_query_chain 
structure. Once the referral operation completes, all information within 
this entry are merged into the validator cache.

The validator keeps track of nameservers that it actually used while following
referrals.  These are re-used in future requests for data in the same zone.

Digestion of answers is followed by assimilation of answers. Here, the data
returned by the digestion process is transformed into an authentication chain
element.  Checks are made to determine the answer kind and if the data or NSEC
was actually relevant.  A sanity check is also made to ensure that all answers
received as part of a query with type as ANY have the same answer kind.  

Query, Assertion and Result states
----------------------------------

Query states transition as follows:

      (cached)
   +------------>Q_ANSWERED
   |                  ^
   |                  | (answer recieved)
   |                  |
   |                  |
   |  (query sent)    |    (error)
Q_INIT ----------> Q_SENT --------> (Q_ERROR_BASE + <error_condition>) 
   ^                | |                   ^
   |                | |                   |
   +<---------------+ |                   |
   |   (referral)     |                   |
   |                  |                   |
   |                  |                   |
   |  (success)       v           (fail)  |
   +<----------- Q_WAIT_FOR_GLUE ---------+
                    (A|AAAA)



Query Flags
-----------

Cached assertions for a given validator context are accessed through
the handle from the corresponding val_query_chain structure. The 
add_to_query_chain() functions provides the mechanism to locate the 
particular val_query_chain object (if it exists in the context cache)
to the corresponds to a give name,type,class tuple. 

Applications, however, can be multi-threaded and can issue parallel 
lookup operations from multiple threads using the same validator 
context. In order to ensure that the correct locking semantics are 
preserved for all auxillary queries issued as part of validating 
the primary name, the validator tracks this information through 
the queries_for_query linked list. This list is merely a linked 
list of handles to the various val_query_chain objects associated 
with the query. If a query_for_query_chain object exists, a 
corresponding val_query_chain object MUST exist.

The query behaviour for a particular lookup operation is guided by
the query flags set for that query.  The queries_for_structure contains 
the qfq_flags member while the val_query_chain structure contains the 
qc_flags member. While the values that can be saved in these variables
are the same there is a subtle difference in the usage of these flags.

All query flags specified by the user are stored in the qfq_flags 
member. libval uses the VAL_QFLAGS_USERMASK mask to ensure that the
flags specified by the user are acceptible. libval then uses these flags
to look for cached val_query_chain structures in the context cache
and sets and resets other internal flags as necessary while resolving 
this name. Not all flags are matched while checking for cached 
val_query_chain structures -- the VAL_QFLAGS_CACHE_MASK, 
VAL_QFLAGS_CACHE_PREF_MASK and VAL_QFLAGS_NOCACHE_MASK bitmasks select 
those flags that are relevant for the given query. Not all
val_query_chain objects referenced with a query_for_query list
will match the qfq_flags value.

A useful rule-of-thumb while deciding whether to use qfq_flags 
or qc_flags in a partular line of code is the following -- While 
setting the flags for any query use qfq_flags; When testing for 
a particular flag use qc_flags.


===============================================================================================

   
Assertion states transition as follows:

     VAL_AC_UNSET
         |
         v
     VAL_AC_INIT  
          |                                           
          +----------------->  VAL_AC_TRUST_NOCHK --------------------------------------->+
          |                                                                               |
          |                  +-----------------------+                                    |
          +----------------->| VAL_AC_NEGATIVE_PROOF |----------------------------------->+
          |                  +-----------------------+                                    |
          |                                                                               |
          +----------------->  VAL_AC_WAIT_FOR_RRSIG --------+                            |
          |                              |                   |                            |
          |                              |                   v                            |
          |                              |              +==============+                  |
          |                              |              |    ERROR     |----------------->|
          |                              |              +==============+                  |
          |                              |                   ^                            |
          |                              v                   |                            |
          +----------------->  VAL_AC_WAIT_FOR_TRUST --------+                            |
          |                              |                                                |
          |                              |                                                |
          |                              v              +==============+                  |
          |                     VAL_AC_CAN_VERIFY ----> |  SUCCESS     |                  |
          |                              |              +==============+                  |
          |                              |                                                |
          |                              |                                                |
          |                              v                                                |
          | VAL_AC_FAIL_BASE    +====================+                                    |
          +-------||----------->|       FAIL         | ---------------------------------->+
          | VAL_AC_LAST_FAILURE +====================+                                    |
          |                                                                               |
          |                                                                               |
          |                                                                               |
          | VAL_AC_BAD_BASE     +====================+                                    |
          +-------||----------->|       BAD          |                                    |
          | VAL_AC_LAST_BAD     +====================+                                    |
          |                                                                               |
          |                                                                               |
          |                                                                               |
          |                                                                               |
          |<----------------------------------------------------------------------------->+
          |
          v
  +====================+
  |  DONT GO FURTHER   | 
  +====================+

where:

              +====================+
              |  DONT GO FURTHER   | 
              +====================+
                      |---> VAL_AC_IGNORE_VALIDATION 
                      |---> VAL_AC_UNTRUSTED_ZONE
                      |---> VAL_AC_PINSECURE
                      |---> VAL_AC_BARE_RRSIG
                      |---> VAL_AC_NO_LINK
                      +---> VAL_AC_TRUST

              +====================+
              |       ERROR         | 
              +====================+
                      |---> VAL_AC_RRSIG_MISSING 
                      |---> VAL_AC_DNSKEY_MISSING 
                      |---> VAL_AC_DATA_MISSING 
                      +---> VAL_AC_DS_MISSING
                      
              +====================+
              |        BAD         | 
              +====================+
                      +---> VAL_AC_DNS_ERROR
                      
              +====================+
              |       FAIL         | 
              +====================+
                      |
                      |---> VAL_AC_NOT_VERIFIED 
                      |
                      |---> DS status
                      |         |-------------> VAL_AC_INVALID_DS
                      |         +-------------> VAL_AC_ALGORITHM_NOT_SUPPORTED
                      |
                      |---> Key status
                      |         |-------------> VAL_AC_UNKNOWN_DNSKEY_PROTOCOL
                      |         |-------------> VAL_AC_DS_NOMATCH
                      |         |-------------> VAL_AC_INVALID_KEY
                      |         +-------------> VAL_AC_ALGORITHM_NOT_SUPPORTED
                      |
                      +---> sig status 
                                |-------------> VAL_AC_WRONG_LABEL_COUNT
                                |-------------> VAL_AC_INVALID_RRSIG
                                |-------------> VAL_AC_RRSIG_NOTYETACTIVE
                                |-------------> VAL_AC_RRSIG_EXPIRED
                                |-------------> VAL_AC_RRSIG_VERIFY_FAILED 
                                |-------------> VAL_AC_RRSIG_ALGORITHM_MISMATCH 
                                |-------------> VAL_AC_DNSKEY_NOMATCH
                                +-------------> VAL_AC_ALGORITHM_NOT_SUPPORTED
            
              +====================+
              |    SUCCESS         | 
              +====================+
                      |
                      |---> VAL_AC_VERIFIED 
                      |
                      |---> Key status  
                      |         |-------------> VAL_AC_TRUST_POINT 
                      |         |-------------> VAL_AC_SIGNING_KEY 
                      |         |-------------> VAL_AC_VERIFIED_LINK
                      |         +-------------> VAL_AC_UNKNOWN_ALGORITHM_LINK
                      |
                      +---> sig status 
                                |
                                |-------------> VAL_AC_RRSIG_VERIFIED 
                                |-------------> VAL_AC_WCARD_VERIFIED
                                |-------------> VAL_AC_RRSIG_VERIFIED_SKEW
                                +-------------> VAL_AC_WCARD_VERIFIED_SKEW
                      

The consolidated dnssec validation results obtained in the results array
are derived from the authentication chain states in the following manner:
(Note: see val_errors.h for values of *_BASE and *_LAST)

VAL_OOB_ANSWER is returned if an answer is returned from some non-DNS database
such as /etc/hosts.


any auth.chain.elem    result  
status                 status             
====================================================

> FAIL_BASE,                VAL_BOGUS
<=LAST_FAILURE   

VAL_AC_DNS_ERROR            VAL_DNS_ERROR 

> ERROR_BASE,               VAL_INDETERMINATE
<=LAST_ERROR   

VAL_AC_BARE_RRSIG           VAL_BARE_RRSIG
VAL_AC_IGNORE_VALIDATION    VAL_IGNORE_VALIDATION
VAL_AC_UNTRUSTED_ZONE       VAL_UNTRUSTED_ZONE


top-as          every          final result status
status          status  
============================================================================
VAL_AC_TRUST    VERIFIED        VAL_SUCCESS
VAL_AC_NO_LINK  VERIFIED        VAL_NOTRUST


The following status values are defined for results of negative proof checks for
the main DNS query:

Condition                         final result
==========================================================================
All components of the proof of    VAL_NONEXISTENT_NAME
non-existence, also validated     or VAL_NONEXISTENT_TYPE

All components of the proof of    VAL_NONEXISTENT_NAME_NOCHAIN
non-existence, trusted but        or VAL_NONEXISTENT_TYPE_NOCHAIN
not validated

Any component of the proof of     VAL_BOGUS
non-existence is not trusted
or proof components are trusted   
but are incomplete

For negative proofs in the middle of the authentication chain, if 
the check for provably insecure condition could not be performed, the
status is set to VAL_BOGUS_PROOF.

For the VAL_AC_PINSECURE condition, the following values exist:

Provably insecure trust status      final result
==========================================================================
Trusted                             VAL_PINSECURE
Untrusted                           VAL_PINSECURE_UNTRUSTED


Acknowledgements
----------------

Some of the code for this library was borrowed from an older implementation
of the secure resolver library written by Ed Lewis at Tislabs. 

