#include <openssl/err.h>
#endif
+#ifndef max
+#define max( a, b ) ( ((a) > (b)) ? (a) : (b) )
+#endif
+
ldns_status
ldns_dane_create_tlsa_owner(ldns_rdf** tlsa_owner, const ldns_rdf* name,
uint16_t port, ldns_dane_transport transport)
return LDNS_STATUS_OK;
}
+
#ifdef HAVE_SSL
ldns_status
ldns_dane_cert2rdf(ldns_rdf** rdf, X509* cert,
}
+/* Ordinary PKIX validation of cert (with extra_certs to help)
+ * against the CA's in store
+ */
static ldns_status
ldns_dane_pkix_validate(X509* cert, STACK_OF(X509)* extra_certs,
X509_STORE* store)
}
+/* Orinary PKIX validation of cert (with extra_certs to help)
+ * against the CA's in store, but also return the validation chain.
+ */
static ldns_status
ldns_dane_pkix_validate_and_get_chain(STACK_OF(X509)** chain, X509* cert,
STACK_OF(X509)* extra_certs, X509_STORE* store)
}
+/* Return the validation chain that can be build out of cert, with extra_certs.
+ */
static ldns_status
ldns_dane_pkix_get_chain(STACK_OF(X509)** chain,
X509* cert, STACK_OF(X509)* extra_certs)
}
+/* Pop n+1 certs and return the last popped.
+ */
static ldns_status
ldns_dane_get_nth_cert_from_validation_chain(
X509** cert, STACK_OF(X509)* chain, int n)
}
+/* Create validation chain with cert and extra_certs and returns the last
+ * self-signed (if present).
+ */
static ldns_status
ldns_dane_pkix_get_last_self_signed(X509** out_cert,
X509* cert, STACK_OF(X509)* extra_certs)
}
+/* Return tlsas that actually are TLSA resource records with known values
+ * for the Certificate usage, Selector and Matching type rdata fields.
+ */
static ldns_rr_list*
ldns_dane_filter_unusable_records(const ldns_rr_list* tlsas)
{
}
+/* Return whether cert/selector/matching_type matches data.
+ */
static ldns_status
ldns_dane_match_cert_with_data(X509* cert, ldns_tlsa_selector selector,
ldns_tlsa_matching_type matching_type, ldns_rdf* data)
}
+/* Return whether any certificate from the chain with selector/matching_type
+ * matches data.
+ */
static ldns_status
ldns_dane_match_any_cert_with_data(STACK_OF(X509)* chain,
ldns_tlsa_selector selector,
ldns_status
ldns_dane_verify(ldns_rr_list* tlsas,
X509* cert, STACK_OF(X509)* extra_certs,
- X509_STORE* validate_store)
+ X509_STORE* pkix_validation_store)
{
size_t i;
ldns_rr* tlsa_rr;
- ldns_status s = LDNS_STATUS_DANE_TLSA_DID_NOT_MATCH;
+ ldns_status s = LDNS_STATUS_OK, ps;
assert(cert != NULL);
}
}
if (! tlsas || ldns_rr_list_rr_count(tlsas) == 0) {
-
- return ldns_dane_verify_rr(NULL,
- cert, extra_certs, validate_store);
+ /* No TLSA's, so regular PKIX validation
+ */
+ return ldns_dane_pkix_validate(cert, extra_certs,
+ pkix_validation_store);
} else {
for (i = 0; i < ldns_rr_list_rr_count(tlsas); i++) {
tlsa_rr = ldns_rr_list_rr(tlsas, i);
- s = ldns_dane_verify_rr(tlsa_rr,
- cert, extra_certs, validate_store);
+ ps = s;
+ s = ldns_dane_verify_rr(tlsa_rr, cert, extra_certs,
+ pkix_validation_store);
if (s != LDNS_STATUS_DANE_TLSA_DID_NOT_MATCH &&
- s != LDNS_STATUS_DANE_PKIX_DID_NOT_VALIDATE){
+ s != LDNS_STATUS_DANE_PKIX_DID_NOT_VALIDATE) {
/* which would be LDNS_STATUS_OK (match)
* or some fatal error preventing use from
*/
break;
}
+ s = max(s, ps); /* prefer PKIX_DID_NOT_VALIDATE
+ * over TLSA_DID_NOT_MATCH
+ */
}
ldns_rr_list_free(tlsas);
}
ldns_dname | ldns_dname_left_chop, ldns_dname_label_count, ldns_dname2canonical, ldns_dname_cat, ldns_dname_cat_clone, ldns_dname_new, ldns_dname_new_frm_str, ldns_dname_new_frm_data, ldns_dname_is_subdomain, ldns_dname_str_absolute, ldns_dname_label, ldns_dname_compare, ldns_dname_interval
### /dname.h
+### dane.h
+ldns_dane_create_tlsa_owner, ldns_dane_cert2rdf, ldns_dane_select_certificate, ldns_dane_create_tlsa_rr | ldns_dane_verify, ldns_dane_verify_rr
+ldns_dane_verify, ldns_dane_verify_rr | ldns_dane_create_tlsa_owner, ldns_dane_cert2rdf, ldns_dane_select_certificate, ldns_dane_create_tlsa_rr
+### /dane.h
+
### rdata.h
ldns_rdf, ldns_rdf_type | ldns_rdf_set_size, ldns_rdf_set_type, ldns_rdf_set_data, ldns_rdf_size, ldns_rdf_get_type, ldns_rdf_data, ldns_rdf_compare, ldns_rdf_new, ldns_rdf_clone, ldns_rdf_new_frm_data, ldns_rdf_new_frm_str, ldns_rdf_new_frm_fp, ldns_rdf_free, ldns_rdf_deep_free, ldns_rdf_print, ldns_native2rdf_int8, ldns_native2rdf_int16, ldns_native2rdf_int32, ldns_native2rdf_int16_data, ldns_rdf2native_int8, ldns_rdf2native_int16, ldns_rdf2native_int32, ldns_rdf2native_sockaddr_storage, ldns_rdf2native_time_t, ldns_native2rdf_int8, ldns_native2rdf_int16, ldns_native2rdf_int32, ldns_native2rdf_int16_data, ldns_rdf2native_int8, ldns_rdf2native_int16, ldns_rdf2native_int32, ldns_rdf2native_sockaddr_storage, ldns_rdf2native_time_t, ldns_native2rdf_int8, ldns_native2rdf_int16, ldns_native2rdf_int32, ldns_native2rdf_int16_data, ldns_rdf2native_int8, ldns_rdf2native_int16, ldns_rdf2native_int32, ldns_rdf2native_sockaddr_storage, ldns_rdf2native_time_t
ldns_rdf_set_size, ldns_rdf_set_type, ldns_rdf_set_data | ldns_rdf
ldns_rr_list*
dane_lookup_addresses(ldns_resolver* res, ldns_rdf* dname,
- ldns_dane_protocol protocol)
+ int ai_family)
{
ldns_status s;
ldns_rr_list *as = NULL;
if (r == NULL) {
MEMERR("ldns_rr_list_new");
}
- if (protocol == LDNS_DANE_PROTOCOL_UNSPEC ||
- (protocol & LDNS_DANE_PROTOCOL_IPV4)) {
+ if (ai_family == AF_UNSPEC || ai_family == AF_INET) {
s = dane_query(&as, res,
dname, LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN,
MEMERR("ldns_rr_list_push_rr_list");
}
}
- if (protocol == LDNS_DANE_PROTOCOL_UNSPEC ||
- (protocol & LDNS_DANE_PROTOCOL_IPV6)) {
+ if (ai_family == AF_UNSPEC || ai_family == AF_INET6) {
s = dane_query(&aaas, res,
dname, LDNS_RR_TYPE_AAAA, LDNS_RR_CLASS_IN,
ldns_rr* address_rr;
ldns_rdf* address;
- int protocol = LDNS_DANE_PROTOCOL_UNSPEC;
+ int ai_family = AF_UNSPEC;
int transport = LDNS_DANE_TRANSPORT_TCP;
char* name_str;
print_usage("ldns-dane");
break;
case '4':
- protocol = LDNS_DANE_PROTOCOL_IPV4;
+ ai_family = AF_INET;
break;
case '6':
- protocol = LDNS_DANE_PROTOCOL_IPV6;
+ ai_family = AF_INET6;
break;
case 'a':
s = ldns_str2rdf_a(&address, optarg);
* and IPv6 addresses when -4 was given.
*/
if (ldns_rr_list_rr_count(addresses) > 0 &&
- protocol != LDNS_DANE_PROTOCOL_UNSPEC) {
+ ai_family != AF_UNSPEC) {
/* TODO: resource leak, previous addresses */
originals = addresses;
addresses = rr_list_filter_rr_type(originals,
- (protocol == LDNS_DANE_PROTOCOL_IPV4
+ (ai_family == AF_INET
? LDNS_RR_TYPE_A : LDNS_RR_TYPE_AAAA));
ldns_rr_list_free(originals);
if (addresses == NULL) {
assume_dnssec_validity);
LDNS_ERR(s, "could not dane_setup_resolver");
ldns_rr_list_free(addresses);
- addresses = dane_lookup_addresses(res, name, protocol);
+ addresses =dane_lookup_addresses(res, name, ai_family);
ldns_resolver_free(res);
}
if (ldns_rr_list_rr_count(addresses) == 0) {
extern "C" {
#endif
+/**
+ * The different "Certificate usage" rdata field values for a TLSA RR.
+ */
enum ldns_enum_tlsa_certificate_usage
{
+ /** CA constraint */
LDNS_TLSA_USAGE_CA_CONSTRAINT = 0,
+ /** Sevice certificate constraint */
LDNS_TLSA_USAGE_SERVICE_CERTIFICATE_CONSTRAINT = 1,
+ /** Trust anchor assertion */
LDNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION = 2,
+ /** Domain issued certificate */
LDNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE = 3
};
typedef enum ldns_enum_tlsa_certificate_usage ldns_tlsa_certificate_usage;
+/**
+ * The different "Selector" rdata field values for a TLSA RR.
+ */
enum ldns_enum_tlsa_selector
{
+ /**
+ * Full certificate: the Certificate binary structure
+ * as defined in [RFC5280]
+ */
LDNS_TLSA_SELECTOR_FULL_CERTIFICATE = 0,
+
+ /**
+ * SubjectPublicKeyInfo: DER-encoded binary structure
+ * as defined in [RFC5280]
+ */
LDNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO = 1
};
typedef enum ldns_enum_tlsa_selector ldns_tlsa_selector;
+/**
+ * The different "Matching type" rdata field values for a TLSA RR.
+ */
enum ldns_enum_tlsa_matching_type
{
+ /** Exact match on selected content */
LDNS_TLSA_MATCHING_TYPE_NO_HASH_USED = 0,
+ /** SHA-256 hash of selected content [RFC6234] */
LDNS_TLSA_MATCHING_TYPE_SHA256 = 1,
+ /** SHA-512 hash of selected content [RFC6234] */
LDNS_TLSA_MATCHING_TYPE_SHA512 = 2
};
typedef enum ldns_enum_tlsa_matching_type ldns_tlsa_matching_type;
-enum ldns_enum_dane_protocol
-{
- LDNS_DANE_PROTOCOL_UNSPEC = 0,
- LDNS_DANE_PROTOCOL_IPV4 = 1,
- LDNS_DANE_PROTOCOL_IPV6 = 2,
- LDNS_DANE_PROTOCOL_IP = 3
-};
-typedef enum ldns_enum_dane_protocol ldns_dane_protocol;
-
+/**
+ * Known transports to use with TLSA owner names.
+ */
enum ldns_enum_dane_transport
{
+ /** TCP */
LDNS_DANE_TRANSPORT_TCP = 0,
+ /** UDP */
LDNS_DANE_TRANSPORT_UDP = 1,
+ /** SCTP */
LDNS_DANE_TRANSPORT_SCTP = 2
};
typedef enum ldns_enum_dane_transport ldns_dane_transport;
/**
- * Creates a dname consisting of the given name, prefixed by the service
- * port and protocol name of the transport:
- * _<port>._<protocol>.<name>
- * TODO: How to choose protocol SCTP?
+ * Creates a dname consisting of the given name, prefixed by the service port
+ * and type of transport: _<EM>port</EM>._<EM>transport</EM>.<EM>name</EM>.
*
* \param[out] tlsa_owner The created dname.
- * \param[in] name The dname that should be prefixed by the service and
- * protocol.
- * \param[in] port The service port number.
+ * \param[in] name The dname that should be prefixed.
+ * \param[in] port The service port number for wich the name should be created.
* \param[in] transport The transport for wich the name should be created.
* \return LDNS_STATUS_OK on success or an error code otherwise.
*/
* \param[in] cert The certificate from which the data is selected
* \param[in] selector The full certificate or the public key
* \param[in] matching_type The full data or the SHA256 or SHA512 hash
- * of the selected data
+ * of the selected data
* \return LDNS_STATUS_OK on success or an error code otherwise.
*/
ldns_status ldns_dane_cert2rdf(ldns_rdf** rdf, X509* cert,
* This can help to make sure that the intended (self signed)
* trust anchor is actually present in extra_certs (which is a
* DANE requirement).
+ *
* \return LDNS_STATUS_OK on success or an error code otherwise.
*/
ldns_status ldns_dane_select_certificate(X509** selected_cert,
*
* \return LDNS_STATUS_OK on success or an error code otherwise.
*/
-ldns_status
-ldns_dane_create_tlsa_rr(ldns_rr** tlsa,
+ldns_status ldns_dane_create_tlsa_rr(ldns_rr** tlsa,
ldns_tlsa_certificate_usage certificate_usage,
ldns_tlsa_selector selector,
ldns_tlsa_matching_type matching_type,
X509* cert);
+/**
+ * Verify if the given TLSA resource record matces the given certificate.
+ * Reporting on a TLSA rr mismatch (LDNS_STATUS_DANE_TLSA_DID_NOT_MATCH)
+ * is preferred over PKIX failure (LDNS_STATUS_DANE_PKIX_DID_NOT_VALIDATE).
+ * So when PKIX validation is required by the TLSA Certificate usage,
+ * but the TLSA data does not match, LDNS_STATUS_DANE_TLSA_DID_NOT_MATCH
+ * is returned whether the PKIX validated or not.
+ *
+ * \param[in] tlsa_rr The resource record that specifies what and how to
+ * match the certificate. With tlsa_rr == NULL, regular PKIX
+ * validation is performed.
+ * \param[in] cert The certificate to match (and validate)
+ * \param[in] extra_certs Intermediate certificates that might be necessary
+ * creating the validation chain.
+ * \param[in] pkix_validation_store Used when the certificate usage is
+ * "CA constraint" or "Service Certificate Constraint" to
+ * validate the certificate.
+ *
+ * \return LDNS_STATUS_OK on success,
+ * LDNS_STATUS_DANE_TLSA_DID_NOT_MATCH on TLSA data mismatch,
+ * LDNS_STATUS_DANE_PKIX_DID_NOT_VALIDATE when TLSA matched,
+ * but the PKIX validation failed, or other ldns_status errors.
+ */
ldns_status
ldns_dane_verify_rr(const ldns_rr* tlsa_rr,
X509* cert, STACK_OF(X509)* extra_certs,
X509_STORE* pkix_validation_store);
+/**
+ * Verify if any of the given TLSA resource records matces the given
+ * certificate.
+ *
+ * \param[in] tlsas The resource records that specify what and how to
+ * match the certificate. One must match for this function
+ * to succeed. With tlsas == NULL or the number of TLSA records
+ * in tlsas == 0, regular PKIX validation is performed.
+ * \param[in] cert The certificate to match (and validate)
+ * \param[in] extra_certs Intermediate certificates that might be necessary
+ * creating the validation chain.
+ * \param[in] pkix_validation_store Used when the certificate usage is
+ * "CA constraint" or "Service Certificate Constraint" to
+ * validate the certificate.
+ *
+ * \return LDNS_STATUS_OK on success,
+ * LDNS_STATUS_DANE_PKIX_DID_NOT_VALIDATE when one of the TLSA's
+ * matched but the PKIX validation failed,
+ * LDNS_STATUS_DANE_TLSA_DID_NOT_MATCH when none of the TLSA's matched,
+ * or other ldns_status errors.
+ */
ldns_status
ldns_dane_verify(ldns_rr_list* tlsas,
X509* cert, STACK_OF(X509)* extra_certs,
* \param[in] *dname a rdf representing the dname
* \return true or false
*/
-bool ldns_dname_absolute(const ldns_rdf *rdf);
+bool ldns_dname_absolute(const ldns_rdf *dname);
/**
* look inside the rdf and if it is an LDNS_RDF_TYPE_DNAME
# contain include files that are not input files but should be processed by
# the preprocessor.
-INCLUDE_PATH =
+INCLUDE_PATH = .
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the