DNS Extensions A. Hayatnagarkar Internet-Draft S. Krishnaswamy Expires: August 30, 2006 SPARTA, Inc. February 26, 2006 DNSSEC Validator API draft-hayatnagarkar-dnsext-validator-api-00 Status of this Memo By submitting this Internet-Draft, each author represents that any applicable patent or other IPR claims of which he or she is aware have been or will be disclosed, and any of which he or she becomes aware will be disclosed, in accordance with Section 6 of BCP 79. Internet-Drafts are working documents of the Internet Engineering Task Force (IETF), its areas, and its working groups. Note that other groups may also distribute working documents as Internet- Drafts. Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." The list of current Internet-Drafts can be accessed at http://www.ietf.org/ietf/1id-abstracts.txt. The list of Internet-Draft Shadow Directories can be accessed at http://www.ietf.org/shadow.html. This Internet-Draft will expire on August 30, 2006. Copyright Notice Copyright (C) The Internet Society (2006). Abstract The DNS Security Extensions (DNSSEC) provide origin authentication and integrity of DNS data. However, the current resolver API does not allow a security-aware resolver to communicate detailed results of DNSSEC processing back to the application. This document describes an Application Programming Interface between applications and a validating security-aware stub resolver that allows applications to control the validation process and obtain results of DNSSEC processing. Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 1] Internet-Draft DNSSEC Validator API February 2006 Table of Contents 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3 2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 3 3. High-level Validator API . . . . . . . . . . . . . . . . . . . 4 3.1. val_gethostbyname, val_gethostbyname_r, val_gethostbyaddr, val_gethostbyaddr_r . . . . . . . . . . 5 3.2. val_getaddrinfo, val_getnameinfo, val_freeaddrinfo . . . . 7 3.3. val_query . . . . . . . . . . . . . . . . . . . . . . . . 8 4. Low-level Validator API . . . . . . . . . . . . . . . . . . . 9 4.1. val_resolve_and_check, val_free_result, val_free_assertion_chain, val_free_query_chain . . . . . . 9 4.2. Return Values and Error codes . . . . . . . . . . . . . . 13 5. Context Management and Validator Policy API . . . . . . . . . 14 5.1. val_get_context, val_free_context . . . . . . . . . . . . 15 5.2. val_switch_policy_scope . . . . . . . . . . . . . . . . . 16 5.3. val_get_scopes_within_context . . . . . . . . . . . . . . 16 5.4. val_get_scope_definition, val_set_scope_definition . . . . 16 6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 17 7. Security Considerations . . . . . . . . . . . . . . . . . . . 17 8. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 18 9. References . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 19 Intellectual Property and Copyright Statements . . . . . . . . . . 20 Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 2] Internet-Draft DNSSEC Validator API February 2006 1. Introduction DNS Security Extensions (RFC 4033 [4], RFC 4034 [5], RFC 4035 [6]) allow applications to test the origin authenticity and integrity of data returned by the DNS. The validator, or more formally, the validating security-aware stub resolver is a piece of software that performs this test by checking the cryptographic signatures that cover DNS records and by verifying a sequence of such records from a trust anchor [4] to this data. This document presents an Application Programming Interface (API) between the application and the validator. The API functions provide a convenient way for applications to control the DNSSEC validation process and to obtain detailed results of the validation process. The API can be broadly divided into three groups: the high-Level validator API, the low-Level validator API and the context management API. The high-level validator API is designed for ease of use and mirrors existing DNS-related functions. This API is best suited for existing applications that already use legacy DNS-related functions such as gethostbyname(), getaddrinfo() and res_query(). The low-level validator API gives more control over the validation process and allows detailed inspection of validation status for each element of the authentication chain [4]. Results returned by the validator can be guided by local policy decisions. The context management API provides functions to manage validator policies. The range of functions provided in this API allows for the creation of applications that are either only interested in basic results such as "validated" or "not-validated", or more sophisticated applications that can look for specific errors as a sign of a network abnormality or attack. Section 3, Section 4, and Section 5 describe these interfaces in greater detail. 2. Terminology Some of the terminology used in this specification are defined below: Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 3] Internet-Draft DNSSEC Validator API February 2006 validator policy: a set of configuration parameters for the validator, which can influence the eventual outcome of the validation process. policy attribute: a particular configurable component of the validator policy; for instance a trust anchor setting or an untrusted algorithm definition. validator context: an opaque structure encapsulating the validator policy. The validator context is the application's handle to the validator policy. Contexts also maintain a reference to a "policy scope" (see definition below). base policy: the default policy associated with a validator context when the context is created. This policy is looked up whenever a policy attribute not existing in other more specific policy scopes is required. A "default" base policy can also be defined to provide the default definitions for system-wide policy attributes. policy scope: a customization within the base-policy to address a particular scenario. Policy scopes are useful when it becomes necessary to override certain policy attributes in specific environments. As an example, an application may require different validator policies for a web browser and a mail client. In such cases the application may define a common base policy and have overrides for specific scopes: one for the web browser and one for the mail client. effective policy: the policy that finally governs the validator action after all relevant policy scopes have been applied. Policy scopes may be defined hierarchically, one below the other. The effective policy is computed by applying the default policy, the base policy for the context, and overrides defined for all valid scopes in the validator configuration file in increasing order of specificity. 3. High-level Validator API The high-level validator API provides DNSSEC-aware substitutes for commonly used DNS functions such as gethostbyname(), getaddrinfo(), and res_query(). This API provides an easy path for applications already using these legacy functions to transition towards becoming DNSSEC-aware. The ctx parameter of type val_context_t* used in the following functions points to the validator context. An application may Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 4] Internet-Draft DNSSEC Validator API February 2006 explicitly create a context using the val_get_context() function described later, or allow the API to create one internally on its behalf by specifying the value of NULL for the ctx parameter. Details for the validator context appear in Section 5. 3.1. val_gethostbyname, val_gethostbyname_r, val_gethostbyaddr, val_gethostbyaddr_r struct hostent *val_gethostbyname( const val_context_t *ctx, const char *name, val_status_t *val_status ); int val_gethostbyname_r( const val_context_t *ctx, const char *name, struct hostent *ret, char *buf, size_t buflen, struct hostent **result, int *h_errnop, val_status_t *val_status ); struct hostent *val_gethostbyaddr( const val_context_t *ctx, const char *addr, int len, int type, val_status_t *val_status ); int val_gethostbyaddr_r( const val_context_t *ctx, const char *addr, int len, int type, struct hostent *ret, char *buf, int buflen, struct hostent **result, int *h_errnop, val_status_t *val_status ); The val_gethostbyname() and val_gethostbyname_r() functions perform name-to-address translation. The val_gethostbyaddr() and val_gethostbyaddr_r() functions perform the address-to-name translation. These functions are DNSSEC-aware versions of the gethostbyname(), gethostbyname_r(), gethostbyaddr() and gethostbyaddr_r() functions and can be used by applications to get the validation status of DNS queries performed during the name-to- address or address-to-name translations. The val_gethostbyname() function returns a pointer to a structure of Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 5] Internet-Draft DNSSEC Validator API February 2006 type hostent for the given host name. The val_gethostbyaddr() function returns a pointer to a structure of type hostent for the given host address addr whose length is given by the len parameter and address type is given by the type parameter. Valid address types are AF_INET and AF_INET6. As with gethostbyname() and gethostbyaddr(), the val_gethostbyname() and val_gethostbyaddr() functions set the value of the global h_errno variable. Their return value may point to static data, and it may be overwritten by subsequent calls. These functions return a pointer to a struct hostent value on success, and NULL on error. The val_gethostbyname_r() and val_gethostbyaddr_r() functions are reentrant versions of the val_gethostbyname() and val_gethostbyaddr() functions. They should be used by multi-threaded applications or applications that make multiple calls to these functions and store the return values. These functions return 0 on success, and a non- zero value on error. The result of the call is stored in the hostent structure whose address is passed in the ret parameter. These functions do not modify the global h_errno variable, but return the error numbers in the variable whose address is passed in the h_errnop parameter. After the call, *result will be NULL on error or point to the result on success. Auxiliary data is stored in the buffer buf of length buflen. If the buffer is too small, these functions will return an error. The val_status parameter returns the status of DNSSEC validation, and must contain the address of a variable of type val_status_t. Possible values for val_status_t are defined in Section 4.2. A validation status of VAL_SUCCESS will be returned only if both the address and canonical name(s), if present, have been validated successfully. Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 6] Internet-Draft DNSSEC Validator API February 2006 3.2. val_getaddrinfo, val_getnameinfo, val_freeaddrinfo int val_getaddrinfo( const val_context_t *ctx, const char *nodename, const char *servname, const struct addrinfo *hints, struct val_addrinfo **res ); int val_getnameinfo( const val_context_t *ctx, const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags, val_status_t *val_status ); void val_freeaddrinfo( struct val_addrinfo *ainfo ); struct val_addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; size_t ai_addrlen; struct sockaddr *ai_addr; char *ai_canonname; struct val_addrinfo *ai_next; val_status_t val_status; } The val_getaddrinfo() function returns the address and service information for the specified domain name and service. It is a DNSSEC-aware version of the getaddrinfo() function (RFC 3493 [3]). This function supports both IPv4 and IPv6 addresses. It returns a pointer to a value of type val_addrinfo in *res on success, and NULL on error. The val_addrinfo structure is an augmented form of the addrinfo structure. It contains an additional val_status field that represents the status of DNSSEC validation for that particular answer. It will contain a validation status of VAL_SUCCESS only if both the address and canonical name (if present) have been validated successfully. The memory for the value returned in *res is dynamically allocated by this function. The caller should free it after use with the val_freeaddrinfo() function. The val_getnameinfo() function performs an address-to-name translation in a protocol independent manner. It is a DNSSEC-aware Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 7] Internet-Draft DNSSEC Validator API February 2006 version of the getnameinfo() function (RFC 3493 [3]). In addition to the values returned by the getnameinfo() function, it returns the DNSSEC validation status via the val_status parameter. The val_getaddrinfo() and val_getnameinfo() functions return 0 on success and a non-zero value on error. 3.3. val_query int val_query( const val_context_t *ctx, const char *domain_name, const u_int16_t class, const u_int16_t type, const u_int8_t flags, struct val_response *resp, int *resp_count ); struct val_response { unsigned char *response; int response_length; val_status_t val_status; }; The val_query() function queries the name server for a fully qualified domain name of the given class and type. In addition to the answers to the query, it gives the status of DNSSEC validation for each answer returned. It is intended as a DNSSEC-aware replacement for the res_query() function. Multiple RRsets may be returned in response to a query, each with a potentially different validation status. The answers are returned in an array, resp, of val_response structures. This array must be allocated by the caller to be of sufficient size to hold all RRsets returned by the DNS name server. The caller must also allocate memory for the response field within each val_response structure. This field is a buffer that will contain the actual answer (RRset) on a successful return from this function. By default, each val_response structure holds a single RRset response. The format of the response field is similar to the format of the answer returned by res_query(). The val_status field contains the status of DNSSEC validation for that particular RRset. The resp_count parameter is a value-result parameter. When this function is called, *resp_count must contain the number of elements in the resp array. On return, it will contain the actual number of responses returned by the validator. Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 8] Internet-Draft DNSSEC Validator API February 2006 The flags parameter controls the scope of validation and name resolution, and the output format. At present only one flag is defined: VAL_QUERY_MERGE_RRSETS. This flag has been provided for legacy applications that already use res_query() and want to transition to val_query() with minimal change. When this flag is specified, all RRsets in the answer are merged into a single response and returned in the first element of the resp array. The response field of this element will have a format similar to the answer returned by res_query(). The validation status will be VAL_SUCCESS only if all the individual RRsets have been successfully validated. Otherwise, the validation status will be an error code. If this flag is used and a value other than VAL_SUCCESS is returned with multiple RRsets in the answer, it will not be possible to know which RRset resulted in the error status. The val_query() function returns 0 on success and a non-zero error code on failure. The error code VAL_NO_SPACE is returned if the resp array does not contain enough elements or memory to hold all results returned by the validator. In this case, on return *resp_count will contain the total number of elements in the answer. The caller can use this value to retry val_query() with the appropriate number of elements in the resp array. 4. Low-level Validator API The low-level validator API provides the application with greater control and visibility into the validation process. The functions and data structures defined in this API are summarized below. 4.1. val_resolve_and_check, val_free_result, val_free_assertion_chain, val_free_query_chain Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 9] Internet-Draft DNSSEC Validator API February 2006 int val_resolve_and_check( const val_context_t *context, const char *domain_name, const u_int16_t class, const u_int16_t type, const u_int8_t flags, struct val_result_chain **results, struct val_assertion_chain **assertions, struct val_query_chain **queries); void val_free_result( struct val_result_chain *results ); void val_free_assertion_chain( struct val_assertion_chain *assertions ); void val_free_query_chain ( struct val_query_chain *queries ); struct val_result_chain { val_status_t val_rc_status; struct val_assertion_chain *val_rc_trust; struct val_result_chain *val_rc_next; }; struct val_assertion_chain { val_astatus_t val_ac_status; struct val_rrset *val_ac_rrset; struct val_query_chain *val_ac_query_more; struct val_assertion_chain *val_ac_trust; struct val_assertion_chain *val_ac_rrset_next; struct val_assertion_chain *val_ac_next; }; struct val_rrset { u_int8_t val_rrset_section; u_int16_t val_rrset_len; u_int8_t *val_rrset_data; }; struct val_query_chain { val_qstatus_t val_qc_status; u_int16_t val_qc_raw_response_length; u_int8_t *val_qc_raw_response; struct val_assertion_chain *val_qc_answer; struct val_assertion_chain *val_qc_creator; struch val_query_chain *val_qc_next; }; Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 10] Internet-Draft DNSSEC Validator API February 2006 The val_resolve_and_check() function queries a set of name servers for the tuple and verifies and validates the responses received. The verification step checks RRSIGs and the validation step performs verification down the authentication chain from a trust anchor. All the information necessary for inspecting the authentication chain is available through the results, assertions and queries parameters. Answers to the query are returned in the val_result_chain linked list. Each answer corresponds to a distinct RRset and is stored as a different element in the linked list; multiple RRs within the RRset are all part of the same answer. Multiple answers are possible when the query type is ANY or when a proof of non-existence is returned, in which case RRsets of type NSEC and SOA may also be returned. The val_rc_trust field points to the parent element in the authentication chain sequence. val_rc_next can be used to iterate through the list of all results returned by the validator. The consolidated validation status value for an RRset in the DNS response based on the individual status values for all components in the authentication chain is stored in val_rc_status, which is of type val_status_t. Possible values for this type are listed in Section 4.2. The validator may initiate multiple queries to the DNS while trying to construct the authentication chain. Each response is encapsulated within one or more elements of the val_assertion_chain linked list. However not all elements in this list are present in the authentication chain; for instance, responses for glue-fetching operations are not directly a part of the authentication chain and are not validated. The assertions parameter in val_resolve_and_check() returns a pointer to the set of all responses received for each query sent during validation including data received for any ancillary information. assertions may also be thought of as a cache of responses obtained during the validation process. The queries parameter in val_resolve_and_check() returns a pointer to a linked list containing the complete set of DNS queries that was issued while resolving and validating the original DNS query. This parameter may also be thought of as a cache of queries sent during the validation process. val_qc_status returns the status for a particular query: if it was answered, if there was no response or some other DNS-related value. The on-the-wire bytes returned as the response to a query are stored as a length-value field in val_qc_raw_response_length and val_qc_raw_response respectively. val_qc_answer points to the set of members in assertions that are created for the response and may be NULL when no responses are returned. val_qc_creator identifies the link in the authentication chain that led to the generation of this query and is NULL for the Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 11] Internet-Draft DNSSEC Validator API February 2006 initial query issued by the val_resolve_and_check() function. val_qc_next can be used to iterate through the list of DNS queries within the struct val_query_chain linked list. Most applications would only be interested in the results parameter since this provides a single error code for representing the authenticity of returned data. Sophisticated applications, such as a DNSSEC troubleshooting utility, may look more closely at an individual RRset within assertions or an element within queries to identify the particular component in the authentication chain that led to a validation failure. Given a pointer to an element of type struct val_assertion_chain within the authentication chain, it is possible to inspect further details of the validation status of each component therein using this structure's fields. The val_ac_status field returns the validation status for the specified RRset. The val_ac_rrset_next field points to the next answer within the set of responses returned for a query. In cases where validation fails due to some non-DNSSEC error, the val_ac_query_more field identifies the query where the error was encountered. The val_ac_trust field points to the parent element in the authentication chain. Its value is NULL when the current element in the linked list points to a valid trust anchor. The val_ac_next field can be used to iterate through the list of DNS responses within the struct val_assertion_chain linked list. The val_ac_rrset field contains the actual RRset data. This is stored in the val_rrset structure as the length-value pair, val_rrset_len and val_rrset_data. The section where the RRset appears in the DNS response is saved in the val_rrset_section field. The following values may be returned in this field. VAL_FROM_ANS: returned if the RRset was present in the answer section of the DNS response. VAL_FROM_AUT: returned if the RRset was present in the authority section of the DNS response. VAL_FROM_ADD: returned if the RRset was present in the additional section of the DNS response. The val_resolve_and_check() function internally allocates memory for the values of the results, assertions and queries parameters so they must be freed by the caller using the val_free_result(), val_free_assertion_chain() and val_free_query_chain() functions respectively. Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 12] Internet-Draft DNSSEC Validator API February 2006 4.2. Return Values and Error codes The functions listed above return an integer value to represent success and failure conditions. The return values may be one of the following. VAL_NOT_IMPLEMENTED: returned if the implementation did not support a particular feature. VAL_OUT_OF_MEMORY: returned if memory could not be allocated for an operation. VAL_BAD_ARGUMENT: returned if an unexpected value was passed as an argument to a function. VAL_INTERNAL_ERROR: returned if an internal error was encountered in the validator library. VAL_NO_PERMISSION: returned if the application lacked sufficient privileges to perform an operation. VAL_RESOURCE_UNAVAILABLE: returned if some resource necessary for an operation was unavailable. VAL_CONF_PARSE_ERROR: returned if the validator configuration file contained errors. VAL_CONF_NOT_FOUND: returned if the validator configuration file could not be located. VAL_NO_POLICY: returned if the policy identifier being referenced could not be located or was invalid for the current context. val_status_t defines the list of error codes returned by the validator to an application. Possible values for val_status_t are listed below. VAL_SUCCESS: returned if the response was verified and validated. VAL_LOCAL_ANSWER: returned if the response was obtained locally from a configuration file such as /etc/hosts. Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 13] Internet-Draft DNSSEC Validator API February 2006 VAL_BARE_RRSIG: returned if the response was for a query of type RRSIG. RRSIGs contain the cryptographic signatures for other DNS data and cannot themselves be validated. VAL_NONEXISTENT_NAME: returned if the proof for denial of existence for a domain name was validated. VAL_NONEXISTENT_TYPE: returned if the proof for denial of existence for the resource record type for the name queried was validated. VAL_INDETERMINATE: returned if the validator lacked detail to complete validation down the authentication chain. VAL_BOGUS: returned if the response could not be validated due to signature verification failures. VAL_ERROR: returned if the DNS returned an error for some component in the authentication chain. VAL_PROVABLY_UNSECURE: returned if the zone to which the response belongs was inferred to be un-signed. Individual components of the authentication chain have their own val_ac_status for maintaining per-rrset error codes and queries have a separate val_qc_status to track errors in name resolution. (author's note: the definition of possible values for val_astatus_t, as in the case of values for val_qstatus_t, will draw upon efforts within the IETF to generate a complete list of error codes for the validator.) 5. Context Management and Validator Policy API Applications can use local policy to influence the decision about when the validator must break out from the process of constructing the authentication chain with either a success or failure condition. Examples of local policy elements include trust anchors for different zones, untrusted algorithms for cryptographic keys and hashes, and acceptable clock skew for signature inception and expiration times. All of these may be different for different applications and operation scenarios. Local policy for the validator is stored in the validator configuration file, /etc/dnsval.conf. Multiple validator policies can be specified in the same validator configuration file. Policies are identified by labels or simple text strings, which must be unique within the configuration file. As an example, "browser" could be used as the label for a policy that defines the base policy for all Hayatnagarkar & Krishnaswamy Expires August 30, 2006 [Page 14] Internet-Draft DNSSEC Validator API February 2006 web-browsers in a system. The ':' character in the label string specifies a new scope within a given policy. Thus, "mail:mozilla: browser" could be used as the identifier to refer to the mail- specific validator policy for the mozilla suite. The default base policy in the configuration file is identified by a label value of ':'. Policy definitions have the following structure.