*
* See the file LICENSE for the license.
*
- * TODO before release:
- * - Check for CA other than the last in OpenSSL's validation chain,
- * with "CA constraint".
*/
#include <ldns/config.h>
#include <openssl/x509v3.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)
*/
static ldns_status
ldns_dane_get_nth_cert_from_validation_chain(
- X509** cert, STACK_OF(X509)* chain, int n)
+ X509** cert, STACK_OF(X509)* chain, int n, bool ca)
{
- if (n >= sk_X509_num(chain)) {
- return LDNS_STATUS_DANE_INDEX_OUT_OF_RANGE;
+ if (n >= sk_X509_num(chain) || n < 0) {
+ return LDNS_STATUS_DANE_OFFSET_OUT_OF_RANGE;
}
for (;;) {
*cert = sk_X509_pop(chain);
}
X509_free(*cert);
}
+ if (ca && ! X509_check_ca(*cert)) {
+ return LDNS_STATUS_DANE_NON_CA_CERTIFICATE;
+ }
return LDNS_STATUS_OK;
}
ldns_dane_select_certificate(X509** selected_cert,
X509* cert, STACK_OF(X509)* extra_certs,
X509_STORE* pkix_validation_store,
- ldns_tlsa_certificate_usage cert_usage, int index)
+ ldns_tlsa_certificate_usage cert_usage, int offset)
{
ldns_status s;
STACK_OF(X509)* pkix_validation_chain = NULL;
return s;
}
if (s == LDNS_STATUS_OK) {
- *selected_cert = sk_X509_pop(pkix_validation_chain);
- if (! *selected_cert) {
- s = LDNS_STATUS_SSL_ERR;
+ if (offset == -1) {
+ offset = 0;
}
+ s = ldns_dane_get_nth_cert_from_validation_chain(
+ selected_cert, pkix_validation_chain,
+ offset, true);
}
sk_X509_pop_free(pkix_validation_chain, X509_free);
return s;
case LDNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
- if (index == -1) {
+ if (offset == -1) {
s = ldns_dane_pkix_get_last_self_signed(
selected_cert, cert, extra_certs);
return s;
s =
ldns_dane_get_nth_cert_from_validation_chain(
selected_cert, pkix_validation_chain,
- index);
+ offset, false);
} else if (! pkix_validation_chain) {
return s;
}
s = ldns_dane_match_cert_with_data(cert,
selector, matching_type, data);
if (ca && s == LDNS_STATUS_OK && ! X509_check_ca(cert)) {
- s = LDNS_STATUS_DANE_TLSA_MATCHED_NON_CA_CERTIFICATE;
+ s = LDNS_STATUS_DANE_NON_CA_CERTIFICATE;
}
X509_free(cert);
if (s != LDNS_STATUS_DANE_TLSA_DID_NOT_MATCH) {
*/
break;
}
- s = max(s, ps); /* prefer PKIX_DID_NOT_VALIDATE
- * over TLSA_DID_NOT_MATCH
- */
+ s = (s > ps ? s : ps); /* prefer PKIX_DID_NOT_VALIDATE
+ * over TLSA_DID_NOT_MATCH
+ */
}
ldns_rr_list_free(tlsas);
}
*
* See the file LICENSE for the license.
*
- * TODO before release:
- *
- * - sigchase
- * - trace up from root
- *
- * Long term wishlist:
- * - Interact with user after connect
+ * wish list:
+ * - nicer reporting (tracing of evaluation process)
+ * - verbosity levels
+ * - STARTTLS support
*/
#include "config.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
-#include <fcntl.h>
#include <signal.h>
#include <ldns/ldns.h>
ldns_err(msg, code); } while(0)
#define MEMERR(msg) do { fprintf(stderr, "memory error in %s\n", msg); \
exit(EXIT_FAILURE); } while(0)
+#define BUFSIZE 16384
-int verbosity = 3;
+/* int verbosity = 3; */
void
print_usage(const char* progname)
printf("\t-p <CApath>\t"
"use certificates in the <CApath> directory to validate\n"
);
- printf("\t-r <hintsfile>\tuse <hintsfile> to read root hints from\n");
printf("\t-s\t\tassume PKIX validity\n");
printf("\t-t <tlsafile>\tdo not use DNS, "
"but read TLSA record(s) from <tlsafile>\n"
);
printf("\t-u\t\tuse UDP in stead of TCP to TLS connect\n");
printf("\t-v\t\tshow version and exit\n");
- printf("\t-V [0-5]\tset verbosity level (defaul 3)\n");
+ /* printf("\t-V [0-5]\tset verbosity level (defaul 3)\n"); */
exit(EXIT_SUCCESS);
}
int
-usage_within_range(const char* arg, int max, const char* name)
+dane_int_within_range(const char* arg, int max, const char* name)
{
char* endptr; /* utility var for strtol usage */
int val = strtol(arg, &endptr, 10);
};
int
-usage_within_range_table(const char* arg, int max, const char* name,
+dane_int_within_range_table(const char* arg, int max, const char* name,
dane_param_choice table[])
{
dane_param_choice* t;
}
}
}
- return usage_within_range(arg, max, name);
+ return dane_int_within_range(arg, max, name);
}
void
return LDNS_STATUS_OK;
}
-#define BUFSIZE 16384
-#define MAX( a, b ) ( (a) > (b) ? (a) : (b) )
bool
-cp_line_from_file2fd_networkline(FILE* file, int fd)
+copy_line_from_file2fd_networkline(FILE* file, int fd)
{
char buf[BUFSIZE];
char* bufptr;
}
bool
-cp_ssl2file(SSL* ssl, FILE* file)
+copy_ssl2file(SSL* ssl, FILE* file)
{
char buf[BUFSIZE];
char* bufptr;
}
bool
-cp_fd2ssl(int fd, SSL* ssl)
+copy_fd2ssl(int fd, SSL* ssl)
{
char buf[BUFSIZE];
char* bufptr;
if (child == 0) { /* Child process */
close(pipefd[0]); /* close read end */
- while (cp_line_from_file2fd_networkline(stdin, pipefd[1]));
+ while (copy_line_from_file2fd_networkline(stdin, pipefd[1]));
close(pipefd[1]);
exit(EXIT_SUCCESS);
} else if (child > 0) { /* Parent process*/
close(pipefd[1]); /* close write end */
- maxfd = MAX(pipefd[0], sock) + 1;
+ maxfd = (pipefd[0] > sock ? pipefd[0] : sock) + 1;
for (;;) {
#ifndef S_SPLINT_S
FD_ZERO(&rfds);
break;
}
if (FD_ISSET(sock, &rfds)) {
- if (! cp_ssl2file(ssl, stdout)) {
+ if (! copy_ssl2file(ssl, stdout)) {
break;
}
}
if (FD_ISSET(pipefd[0], &rfds)) {
- if (! cp_fd2ssl(pipefd[0], ssl)) {
+ if (! copy_fd2ssl(pipefd[0], ssl)) {
break;
}
}
}
-/*
- * The file with the given path should contain a list of NS RRs
- * for the root zone and A records for those NS RRs.
- * Read them, check them, and append the a records to the rr list given.
- */
-ldns_rr_list *
-read_root_hints(const char *filename)
-{
- FILE *fp = NULL;
- int line_nr = 0;
- ldns_zone *z;
- ldns_status status;
- ldns_rr_list *addresses = NULL;
- ldns_rr *rr;
- size_t i;
-
- fp = fopen(filename, "r");
- if (!fp) {
- fprintf(stderr, "Unable to open %s for reading: %s\n", filename, strerror(errno));
- return NULL;
- }
-
- status = ldns_zone_new_frm_fp_l(&z, fp, NULL, 0, 0, &line_nr);
- fclose(fp);
- if (status != LDNS_STATUS_OK) {
- fprintf(stderr, "Error reading root hints file: %s\n", ldns_get_errorstr_by_id(status));
- return NULL;
- } else {
- addresses = ldns_rr_list_new();
- for (i = 0; i < ldns_rr_list_rr_count(ldns_zone_rrs(z)); i++) {
- rr = ldns_rr_list_rr(ldns_zone_rrs(z), i);
- /*
- if ((address_family == 0 || address_family == 1) &&
- ldns_rr_get_type(rr) == LDNS_RR_TYPE_A ) {
- ldns_rr_list_push_rr(addresses, ldns_rr_clone(rr));
- }
- if ((address_family == 0 || address_family == 2) &&
- ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA) {
- ldns_rr_list_push_rr(addresses, ldns_rr_clone(rr));
- }
- */
- ldns_rr_list_push_rr(addresses, ldns_rr_clone(rr));
- }
- ldns_zone_deep_free(z);
- return addresses;
- }
-}
-
-
ldns_status
dane_setup_resolver(ldns_resolver** res,
- ldns_rr_list* keys, ldns_rr_list* dns_root,
- bool dnssec_off)
+ ldns_rr_list* keys, bool dnssec_off)
{
ldns_status s;
if (s == LDNS_STATUS_OK) {
ldns_resolver_set_dnssec(*res, ! dnssec_off);
- /* anchors must trigger signature chasing */
- ldns_resolver_set_dnssec_anchors(*res, keys);
-
- if (dns_root) {
- if (ldns_resolver_nameserver_count(*res) > 0) {
- free(ldns_resolver_nameservers(*res));
- free(ldns_resolver_rtt(*res));
- ldns_resolver_set_nameserver_count(*res, 0);
- ldns_resolver_set_nameservers(*res, NULL);
- ldns_resolver_set_rtt(*res, NULL);
- }
- s = ldns_resolver_push_nameserver_rr_list(*res,
- dns_root);
-
- /* recursive set to false will trigger tracing */
- ldns_resolver_set_recursive(*res, false);
+ if (keys && ldns_rr_list_rr_count(keys) > 0) {
+ /* anchors must trigger signature chasing */
+ ldns_resolver_set_dnssec_anchors(*res, keys);
+ ldns_resolver_set_dnssec_cd(*res, true);
}
}
return s;
ldns_rdf *name, ldns_rr_type t, ldns_rr_class c,
bool insecure_is_ok)
{
- ldns_pkt* p;
+ ldns_pkt* p = NULL;
+ ldns_rr_list* keys = NULL;
+ ldns_rr_list* rrsigs = NULL;
+ ldns_rdf* signame = NULL;
+ ldns_status s;
assert(rrs != NULL);
p = ldns_resolver_query(r, name, t, c, LDNS_RD);
if (! p) {
- ldns_err("ldns_resolver_query", LDNS_STATUS_MEM_ERR);
return LDNS_STATUS_MEM_ERR;
}
*rrs = ldns_pkt_rr_list_by_type(p, t, LDNS_SECTION_ANSWER);
- if (ldns_rr_list_rr_count(*rrs) > 0 &&
- ldns_resolver_dnssec(r) && ! ldns_pkt_ad(p) &&
- ! insecure_is_ok) {
+ if (! *rrs) {
ldns_pkt_free(p);
- if (! insecure_is_ok) {
- ldns_rr_list_deep_free(*rrs);
- *rrs = NULL;
+ return LDNS_STATUS_MEM_ERR;
+ }
+ if (ldns_rr_list_rr_count(*rrs) == 0 || ! ldns_resolver_dnssec(r)) {
+ ldns_pkt_free(p);
+ return LDNS_STATUS_OK;
+ }
+ /* We have answers and we have dnssec. */
+
+ if (! ldns_pkt_cd(p)) { /* we act as stub resolver (no sigchase) */
+ if (! ldns_pkt_ad(p)) { /* Not secure */
+ goto insecure;
}
- return LDNS_STATUS_DANE_INSECURE;
+ ldns_pkt_free(p);
+ return LDNS_STATUS_OK;
+ }
+
+ /* sigchase */
+
+ /* TODO: handle cname reference check */
+
+ rrsigs = ldns_pkt_rr_list_by_type(p,
+ LDNS_RR_TYPE_RRSIG,
+ LDNS_SECTION_ANSWER);
+
+ if (! rrsigs || ldns_rr_list_rr_count(rrsigs) == 0) {
+ goto insecure;
+ }
+
+ signame = ldns_rr_rrsig_signame(ldns_rr_list_rr(rrsigs, 0));
+ if (! signame) {
+ s = LDNS_STATUS_ERR;
+ goto error;
+ }
+ /* First try with the keys we already have */
+ s = ldns_verify(*rrs, rrsigs, ldns_resolver_dnssec_anchors(r), NULL);
+ if (s == LDNS_STATUS_OK) {
+ goto cleanup;
+ }
+ /* Fetch the necessary keys and recheck */
+ keys = ldns_fetch_valid_domain_keys(r, signame,
+ ldns_resolver_dnssec_anchors(r), &s);
+
+ if (s != LDNS_STATUS_OK) {
+ goto error;
+ }
+ if (ldns_rr_list_rr_count(keys) == 0) { /* An insecure island */
+ goto insecure;
+ }
+ s = ldns_verify(*rrs, rrsigs, keys, NULL);
+ switch (s) {
+ case LDNS_STATUS_CRYPTO_BOGUS: goto bogus;
+ case LDNS_STATUS_OK : goto cleanup;
+ default : break;
+ }
+insecure:
+ s = LDNS_STATUS_DANE_INSECURE;
+bogus:
+ if (! insecure_is_ok) {
+error:
+ ldns_rr_list_deep_free(*rrs);
+ *rrs = NULL;
+ }
+cleanup:
+ if (keys) {
+ ldns_rr_list_deep_free(keys);
+ }
+ if (rrsigs) {
+ ldns_rr_list_deep_free(rrsigs);
}
ldns_pkt_free(p);
- return LDNS_STATUS_OK;
+ return s;
}
ldns_rr_list* keys = ldns_rr_list_new();
size_t nkeys = 0;
- ldns_rr_list* dns_root = NULL;
ldns_rr_list* addresses = ldns_rr_list_new();
ldns_rr* address_rr;
if (! keys || ! addresses) {
MEMERR("ldns_rr_list_new");
}
- while((c = getopt(argc, argv, "46a:bc:df:hik:no:p:r:st:uvV:")) != -1) {
+ while((c = getopt(argc, argv, "46a:bc:df:hik:no:p:st:uvV:")) != -1) {
switch(c) {
case 'h':
print_usage("ldns-dane");
case 'p':
CApath = optarg;
break;
- case 'r':
- dns_root = read_root_hints(optarg);
- if (!dns_root) {
- fprintf(stderr,
- "cannot read the root hints file\n");
- exit(EXIT_FAILURE);
- }
- break;
case 's':
assume_pkix_validity = true;
break;
LDNS_VERSION, ldns_version());
exit(EXIT_SUCCESS);
break;
- case 'V':
+/* case 'V':
verbosity = atoi(optarg);
break;
+ */
}
}
if (port_str[strlen(port_str) - 1] == '.') {
port_str[strlen(port_str) - 1] = '\000';
}
- port = (uint16_t) usage_within_range(
+ port = (uint16_t) dane_int_within_range(
port_str + 1, 65535, "port");
s = LDNS_STATUS_OK;
} while (false);
s = ldns_str2rdf_dname(&name, name_str);
LDNS_ERR(s, "could not ldns_str2rdf_dname");
- port = (uint16_t) usage_within_range(argv[1], 65535, "port");
+ port = (uint16_t)dane_int_within_range(argv[1], 65535, "port");
s = ldns_dane_create_tlsa_owner(&tlsa_owner,
name, port, transport);
LDNS_ERR(s, "could not read tlas from file");
} else {
/* lookup tlsas */
- s = dane_setup_resolver(&res, keys, dns_root,
+ s = dane_setup_resolver(&res, keys,
assume_dnssec_validity);
LDNS_ERR(s, "could not dane_setup_resolver");
s = dane_query(&tlsas, res, tlsa_owner,
tlsas = ldns_rr_list_new();
- certificate_usage = usage_within_range_table(
+ certificate_usage = dane_int_within_range_table(
argv[2], 3, "certificate usage",
dane_certificate_usage_table);
- // certificate_usage = usage_within_range(argv[2], 3, "certificate usage");
- //selector = usage_within_range(argv[3], 1, "selector");
- selector = usage_within_range_table(
+ selector = dane_int_within_range_table(
argv[3], 1, "selector",
dane_selector_table);
matching_type = 2;
} else {
- matching_type = usage_within_range(argv[4], 2,
+ matching_type = dane_int_within_range(argv[4], 2,
"matching type");
}
if ((certificate_usage == LDNS_TLSA_USAGE_CA_CONSTRAINT ||
/* We need addresses to connect to */
if (ldns_rr_list_rr_count(addresses) == 0) {
- s = dane_setup_resolver(&res, keys, dns_root,
+ s = dane_setup_resolver(&res, keys,
assume_dnssec_validity);
LDNS_ERR(s, "could not dane_setup_resolver");
ldns_rr_list_free(addresses);