summaryrefslogtreecommitdiff
path: root/libdane
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2012-10-06 04:26:05 +0200
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2012-10-06 14:57:19 +0200
commit3b58c338b2af492c2fc986458c837ebaf73007b5 (patch)
tree8f47fb6929ec86aff14655d871976a276cffbe10 /libdane
parentb2e95a9acc2045d283857f4727d78cced11eadac (diff)
downloadgnutls-3b58c338b2af492c2fc986458c837ebaf73007b5.tar.gz
Added a DANE library.
Diffstat (limited to 'libdane')
-rw-r--r--libdane/Makefile.am66
-rw-r--r--libdane/dane.c559
-rw-r--r--libdane/errors.c101
-rw-r--r--libdane/includes/Makefile.am25
-rw-r--r--libdane/includes/gnutls/dane.h162
-rw-r--r--libdane/libdane.map19
6 files changed, 932 insertions, 0 deletions
diff --git a/libdane/Makefile.am b/libdane/Makefile.am
new file mode 100644
index 0000000000..87a9413daf
--- /dev/null
+++ b/libdane/Makefile.am
@@ -0,0 +1,66 @@
+## Process this file with automake to produce Makefile.in
+# Copyright (C) 2012 KU Leuven
+#
+# Author: Nikos Mavrogiannopoulos
+#
+# This file is part of libdane.
+#
+# libdane is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 of the
+# License, or (at your option) any later version.
+#
+# GnuTLS-extra 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+
+ACLOCAL_AMFLAGS = -I ../m4 -I ../gl/m4
+
+AM_CFLAGS = $(WERROR_CFLAGS) $(WSTACK_CFLAGS) $(WARN_CFLAGS)
+AM_CPPFLAGS = \
+ -I$(srcdir)/../gl \
+ -I$(builddir)/../gl \
+ -I$(builddir)/../lib/includes \
+ -I$(srcdir)/../lib/includes \
+ -I$(srcdir)/includes \
+ -I$(builddir)/includes
+
+SUBDIRS = includes
+
+defexecdir = $(bindir)
+defexec_DATA =
+
+
+libdane_la_LDFLAGS = -no-undefined
+
+if ENABLE_DANE
+lib_LTLIBRARIES = libdane.la
+
+libdane_la_SOURCES = dane.c errors.c libdane.map
+
+libdane_la_LIBADD = ../gl/libgnu.la \
+ ../lib/libgnutls.la
+
+libdane_la_LDFLAGS += -version-info $(LT_DANE_CURRENT):$(LT_DANE_REVISION):$(LT_DANE_AGE)
+
+libdane_la_LIBADD += $(LIBSOCKET) $(UNBOUND_LIBS)
+
+if HAVE_LD_VERSION_SCRIPT
+libdane_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libdane.map
+else
+libdane_la_LDFLAGS += -export-symbols-regex '^(dane).*'
+endif
+
+if HAVE_LD_OUTPUT_DEF
+libdane_la_LDFLAGS += \
+ -Wl,--output-def,libdane-$(DLL_VERSION).def
+libdane-$(DLL_VERSION).def: libdane.la
+defexec_DATA += libdane-$(DLL_VERSION).def
+endif
+endif
+
+DISTCLEANFILES = $(defexec_DATA)
diff --git a/libdane/dane.c b/libdane/dane.c
new file mode 100644
index 0000000000..f3d28341e3
--- /dev/null
+++ b/libdane/dane.c
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2012 KU Leuven
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of libdane.
+ *
+ * libdane 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 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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 this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <unbound.h>
+#include <gnutls/dane.h>
+#include <gnutls/x509.h>
+#include <gnutls/abstract.h>
+#include <gnutls/crypto.h>
+
+#define MAX_DATA_ENTRIES 4
+
+struct dane_query_st
+{
+ unsigned int data_entries;
+ dane_cert_usage_t usage[MAX_DATA_ENTRIES];
+ dane_cert_type_t type[MAX_DATA_ENTRIES];
+ dane_match_type_t match[MAX_DATA_ENTRIES];
+ gnutls_datum_t data[MAX_DATA_ENTRIES];
+ struct ub_ctx* ctx;
+ struct ub_result* result;
+ unsigned int flags;
+ dane_query_status_t status;
+};
+
+/**
+ * dane_query_status:
+ * @q: The query structure
+ *
+ * This function will return the status of the query response.
+ * See %dane_query_status_t for the possible types.
+ *
+ * Returns: The status type.
+ **/
+dane_query_status_t dane_query_status(dane_query_t q)
+{
+ return q->status;
+}
+
+/**
+ * dane_query_entries:
+ * @q: The query structure
+ *
+ * This function will return the number of entries in a query.
+ *
+ * Returns: The number of entries.
+ **/
+unsigned int dane_query_entries(dane_query_t q)
+{
+ return q->data_entries;
+}
+
+/**
+ * dane_query_data:
+ * @q: The query structure
+ * @idx: The index of the query response.
+ * @usage: The certificate usage (see %dane_cert_usage_t)
+ * @type: The certificate type (see %dane_cert_type_t)
+ * @match: The DANE matching type (see %dane_match_type_t)
+ * @data: The DANE data.
+ *
+ * This function will provide the DANE data from the query
+ * response.
+ *
+ * Returns: On success, %DANE_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int dane_query_data(dane_query_t q, unsigned int idx,
+ unsigned int *usage, unsigned int *type,
+ unsigned int *match, gnutls_datum_t * data)
+{
+ if (idx >= q->data_entries)
+ return DANE_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ if (usage)
+ *usage = q->usage[idx];
+ if (type)
+ *type = q->type[idx];
+ if (match)
+ *match = q->match[idx];
+ if (data) {
+ data->data = q->data[idx].data;
+ data->size = q->data[idx].size;
+ }
+
+ return DANE_E_SUCCESS;
+}
+
+/**
+ * dane_query_init:
+ * @q: The structure to be initialized
+ * @flags: flags from the DANE_F_* definitions
+ *
+ * This function will initialize a DANE query structure.
+ *
+ * Returns: On success, %DANE_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int dane_query_init(dane_query_t* q, unsigned int flags)
+{
+ struct ub_ctx* ctx;
+ int ret;
+
+ *q = calloc(1, sizeof(struct dane_query_st));
+ if (*q == NULL)
+ return DANE_E_MEMORY_ERROR;
+
+ ctx = ub_ctx_create();
+ if(!ctx) {
+ ret = DANE_E_INITIALIZATION_ERROR;
+ goto cleanup;
+ }
+ ub_ctx_debugout(ctx, stderr);
+
+ if (!(flags & DANE_F_IGNORE_LOCAL_RESOLVER)) {
+ if( (ret=ub_ctx_resolvconf(ctx, NULL)) != 0) {
+ ret = DANE_E_INITIALIZATION_ERROR;
+ goto cleanup;
+ }
+
+ if( (ret=ub_ctx_hosts(ctx, NULL)) != 0) {
+ ret = DANE_E_INITIALIZATION_ERROR;
+ goto cleanup;
+ }
+ }
+
+ /* read public keys for DNSSEC verification */
+ if( (ret=ub_ctx_add_ta_file(ctx, (char*)UNBOUND_ROOT_KEY_FILE)) != 0) {
+ ret = DANE_E_INITIALIZATION_ERROR;
+ goto cleanup;
+ }
+
+ (*q)->ctx = ctx;
+ (*q)->flags = flags;
+
+ return DANE_E_SUCCESS;
+cleanup:
+
+ if (ctx)
+ ub_ctx_delete(ctx);
+ free(*q);
+
+ return ret;
+}
+
+/**
+ * dane_query_init:
+ * @q: The structure to be deinitialized
+ *
+ * This function will deinitialize a DANE query structure.
+ *
+ **/
+void dane_query_deinit(dane_query_t q)
+{
+ if (q->result)
+ ub_ctx_delete(q->ctx);
+ ub_resolve_free(q->result);
+
+ free(q);
+}
+
+/**
+ * dane_query_resolve_tlsa:
+ * @q: The query structure
+ * @host: The host name to resolve.
+ * @proto: The protocol type (tcp, udp, etc.)
+ * @port: The service port number (eg. 443).
+ *
+ * This function will query the DNS server for the TLSA (DANE)
+ * data for the given host.
+ *
+ * Returns: On success, %DANE_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int dane_query_resolve_tlsa(dane_query_t q, const char* host, const char* proto, unsigned int port)
+{
+ char ns[1024];
+ int ret;
+ unsigned int i;
+
+ if (q->result) {
+ ub_resolve_free(q->result);
+ q->result = NULL;
+ }
+
+ snprintf(ns, sizeof(ns), "_%u._%s.%s", port, proto, host);
+
+ /* query for webserver */
+ ret = ub_resolve(q->ctx, ns, 52, 1, &q->result);
+ if(ret != 0) {
+ return DANE_E_RESOLVING_ERROR;
+ }
+
+/* show first result */
+ if(!q->result->havedata) {
+ return DANE_E_NO_DANE_DATA;
+ }
+
+ i = 0;
+ do {
+
+ if (q->result->len[i] > 3)
+ ret = DANE_E_SUCCESS;
+ else {
+ return DANE_E_RECEIVED_CORRUPT_DATA;
+ }
+
+ q->usage[i] = q->result->data[i][0];
+ q->type[i] = q->result->data[i][1];
+ q->match[i] = q->result->data[i][2];
+ q->data[i].data = (void*)&q->result->data[i][3];
+ q->data[i].size = q->result->len[i];
+ i++;
+ } while(q->result->data[i] != NULL);
+
+ q->data_entries = i;
+
+ if (q->flags & DANE_F_REQUIRE_DNSSEC) {
+ if (!q->result->secure) {
+ if (q->result->bogus)
+ ret = DANE_E_INVALID_DNSSEC_SIG;
+ else
+ ret = DANE_E_NO_DNSSEC_SIG;
+ }
+ }
+
+ /* show security status */
+ if (q->result->secure)
+ q->status = DANE_QUERY_DNSSEC_VERIFIED;
+ else if (q->result->bogus)
+ q->status = DANE_QUERY_BOGUS;
+ else q->status = DANE_QUERY_NO_DNSSEC;
+
+ return ret;
+}
+
+static unsigned int matches(const gnutls_datum_t *raw1, const gnutls_datum_t *raw2,
+ dane_match_type_t match)
+{
+uint8_t digest[64];
+int ret;
+
+ if (match == DANE_MATCH_EXACT) {
+ if (raw1->size != raw2->size)
+ return 0;
+
+ if (memcmp(raw1->data, raw2->data, raw1->size) != 0)
+ return 0;
+
+ return 1;
+ } else if (match == DANE_MATCH_SHA2_256) {
+
+ if (raw2->size < 32)
+ return 0;
+
+ ret = gnutls_hash_fast(GNUTLS_DIG_SHA256, raw1->data, raw1->size, digest);
+ if (ret < 0)
+ return 0;
+
+ if (memcmp(digest, raw2->data, 32) != 0)
+ return 0;
+
+ return 1;
+ } else if (match == DANE_MATCH_SHA2_512) {
+ if (raw2->size < 64)
+ return 0;
+
+ ret = gnutls_hash_fast(GNUTLS_DIG_SHA512, raw1->data, raw1->size, digest);
+ if (ret < 0)
+ return 0;
+
+ if (memcmp(digest, raw2->data, 64) != 0)
+ return 0;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int crt_to_pubkey(const gnutls_datum_t *raw_crt, gnutls_datum_t * out)
+{
+gnutls_pubkey_t pub = NULL;
+gnutls_x509_crt_t crt = NULL;
+int ret;
+size_t size;
+
+ out->data = NULL;
+
+ ret = gnutls_x509_crt_init(&crt);
+ if (ret < 0)
+ return DANE_E_PUBKEY_ERROR;
+
+ ret = gnutls_pubkey_init( &pub);
+ if (ret < 0) {
+ ret = DANE_E_PUBKEY_ERROR;
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_crt_import(crt, raw_crt, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ ret = DANE_E_PUBKEY_ERROR;
+ goto cleanup;
+ }
+
+ ret = gnutls_pubkey_import_x509(pub, crt, 0);
+ if (ret < 0) {
+ ret = DANE_E_PUBKEY_ERROR;
+ goto cleanup;
+ }
+
+ size = 0;
+ ret = gnutls_pubkey_export(pub, GNUTLS_X509_FMT_DER, NULL, &size);
+ if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ ret = DANE_E_PUBKEY_ERROR;
+ goto cleanup;
+ }
+
+ out->data = malloc(size);
+ if (out->data == NULL) {
+ ret = DANE_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ ret = gnutls_pubkey_export(pub, GNUTLS_X509_FMT_DER, out->data, &size);
+ if (ret < 0) {
+ ret = DANE_E_PUBKEY_ERROR;
+ goto cleanup;
+ }
+
+ out->size = size;
+
+ ret = 0;
+ goto clean_certs;
+
+cleanup:
+ free(out->data);
+clean_certs:
+ if (pub)
+ gnutls_pubkey_deinit(pub);
+ if (crt)
+ gnutls_x509_crt_deinit(crt);
+
+ return ret;
+}
+
+static int verify_ca(const gnutls_datum_t *raw_crt, unsigned raw_crt_size,
+ gnutls_certificate_type_t crt_type,
+ dane_cert_type_t ctype,
+ dane_match_type_t match, gnutls_datum_t * data,
+ unsigned int *verify)
+{
+gnutls_datum_t pubkey = {NULL, 0};
+int ret;
+
+ if (raw_crt_size < 2)
+ return DANE_E_INVALID_REQUEST;
+
+ if (ctype == DANE_CERT_X509 && crt_type == GNUTLS_CRT_X509) {
+
+ if (!matches(&raw_crt[1], data, match))
+ *verify |= DANE_VERIFY_CA_CONSTRAINS_VIOLATED;
+
+ } else if (ctype == DANE_CERT_PK && crt_type == GNUTLS_CRT_X509) {
+ ret = crt_to_pubkey(&raw_crt[1], &pubkey);
+ if (ret < 0)
+ goto cleanup;
+
+ if (!matches(&pubkey, data, match))
+ *verify |= DANE_VERIFY_CA_CONSTRAINS_VIOLATED;
+ }
+
+ ret = 0;
+cleanup:
+ free(pubkey.data);
+ return ret;
+}
+
+static int verify_ee(const gnutls_datum_t *raw_crt, gnutls_certificate_type_t crt_type,
+ dane_cert_type_t ctype, dane_match_type_t match, gnutls_datum_t * data,
+ unsigned int *verify)
+{
+gnutls_datum_t pubkey = {NULL, 0};
+int ret;
+
+ if (ctype == DANE_CERT_X509 && crt_type == GNUTLS_CRT_X509) {
+
+ if (!matches(raw_crt, data, match))
+ *verify |= DANE_VERIFY_CERT_DIFFERS;
+
+ } else if (ctype == DANE_CERT_PK && crt_type == GNUTLS_CRT_X509) {
+
+ ret = crt_to_pubkey(raw_crt, &pubkey);
+ if (ret < 0)
+ goto cleanup;
+
+ if (!matches(&pubkey, data, match))
+ *verify |= DANE_VERIFY_CERT_DIFFERS;
+ }
+
+ ret = 0;
+cleanup:
+ free(pubkey.data);
+ return ret;
+}
+
+/**
+ * dane_verify_crt:
+ * @chain: A certificate chain
+ * @chain_size: The size of the chain
+ * @chain_type: The type of the certificate chain
+ * @hostname: The hostname associated with the chain
+ * @proto: The protocol of the service connecting (e.g. tcp)
+ * @port: The port of the service connecting (e.g. 443)
+ * @flags: The %DANE_F flags.
+ * @verify: An OR'ed list of %dane_verify_status_t.
+ *
+ * This function will verify the given certificate chain against the
+ * CA constrains and/or the certificate available via DANE.
+ * If no information via DANE can be obtained the flag %DANE_VERIFY_NO_DANE_INFO
+ * is set. If a DNSSEC signature is not available for the DANE
+ * record then the verify flag %DANE_VERIFY_NO_DNSSEC_DATA is set.
+ *
+ * Note that when verifying untrusted certificates, it is recommended to
+ * use the %DANE_F_REQUIRE_DNSSEC flag.
+ *
+ * Due to the many possible options of DANE, there is no single threat
+ * model countered. When notifying the user about DANE verification results
+ * it may be better to mention: DANE verification did not reject the certificate,
+ * rather than mentioning a successful DANE verication.
+ *
+ * Returns: On success, %DANE_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ **/
+int dane_verify_crt (
+ const gnutls_datum_t *chain, unsigned chain_size,
+ gnutls_certificate_type_t chain_type,
+ const char * hostname, const char* proto, unsigned int port,
+ unsigned int flags, unsigned int *verify)
+{
+dane_query_t q;
+int ret;
+unsigned int usage, type, match, idx, status;
+gnutls_datum_t data;
+
+ if (chain_type != GNUTLS_CRT_X509)
+ return DANE_E_INVALID_REQUEST;
+
+ *verify = 0;
+
+ ret = dane_query_init(&q, flags);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dane_query_resolve_tlsa(q, hostname, proto, port);
+ if (ret < 0) {
+ goto cleanup;
+ }
+
+ status = dane_query_status(q);
+ if (status == DANE_QUERY_BOGUS) {
+ *verify |= DANE_VERIFY_DNSSEC_DATA_INVALID;
+ goto cleanup;
+ } else if (status == DANE_QUERY_NO_DNSSEC) {
+ *verify |= DANE_VERIFY_NO_DNSSEC_DATA;
+ goto cleanup;
+ }
+
+ idx = 0;
+ do {
+ ret = dane_query_data(q, idx++, &usage, &type, &match, &data);
+ if (ret == DANE_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+
+ if (ret < 0) {
+ goto cleanup;
+ }
+
+ if (usage == DANE_CERT_USAGE_LOCAL_CA || usage == DANE_CERT_USAGE_CA) {
+ ret = verify_ca(chain, chain_size, chain_type, type, match, &data, verify);
+ if (ret < 0)
+ goto cleanup;
+
+ } else if (usage == DANE_CERT_USAGE_LOCAL_EE || usage == DANE_CERT_USAGE_EE) {
+ ret = verify_ee(&chain[0], chain_type, type, match, &data, verify);
+ if (ret < 0)
+ goto cleanup;
+ }
+ } while(1);
+
+ ret = 0;
+
+cleanup:
+ dane_query_deinit(q);
+ return ret;
+}
+
+/**
+ * dane_verify_session_crt:
+ * @session: A gnutls session
+ * @hostname: The hostname associated with the chain
+ * @proto: The protocol of the service connecting (e.g. tcp)
+ * @port: The port of the service connecting (e.g. 443)
+ * @flags: The %DANE_F flags.
+ * @verify: An OR'ed list of %dane_verify_status_t.
+ *
+ * This function will verify session's certificate chain against the
+ * CA constrains and/or the certificate available via DANE.
+ * See dane_verify_crt() for more information.
+ *
+ * Returns: On success, %DANE_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ **/
+int dane_verify_session_crt (
+ gnutls_session_t session,
+ const char * hostname, const char* proto, unsigned int port,
+ unsigned int flags, unsigned int *verify)
+{
+const gnutls_datum_t *cert_list;
+unsigned int cert_list_size = 0;
+unsigned int type;
+
+ cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
+ if (cert_list_size == 0) {
+ return DANE_E_NO_CERT;
+ }
+
+ type = gnutls_certificate_type_get(session);
+
+ return dane_verify_crt(cert_list, cert_list_size, type, hostname, proto, port, flags, verify);
+}
diff --git a/libdane/errors.c b/libdane/errors.c
new file mode 100644
index 0000000000..0753265883
--- /dev/null
+++ b/libdane/errors.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 KU Leuven
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of libdane.
+ *
+ * libdane 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 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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 this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <config.h>
+#include <gnutls/dane.h>
+
+/* I18n of error codes. */
+#include "gettext.h"
+#define _(String) dgettext (PACKAGE, String)
+#define N_(String) gettext_noop (String)
+
+#define ERROR_ENTRY(desc, name) \
+ { desc, #name, name}
+
+struct error_entry
+{
+ const char *desc;
+ const char *_name;
+ int number;
+};
+typedef struct error_entry error_entry;
+
+static const error_entry error_algorithms[] = {
+ ERROR_ENTRY (N_("Success."), DANE_E_SUCCESS),
+ ERROR_ENTRY (N_("There was error initializing the DNS query."),
+ DANE_E_INITIALIZATION_ERROR),
+ ERROR_ENTRY (N_("There was an error while resolving."),
+ DANE_E_RESOLVING_ERROR),
+ ERROR_ENTRY (N_("No DANE data were found."),
+ DANE_E_NO_DANE_DATA),
+ ERROR_ENTRY (N_("No DNSSEC signature was found."),
+ DANE_E_NO_DNSSEC_SIG),
+ ERROR_ENTRY (N_("Received corrupt data."),
+ DANE_E_RECEIVED_CORRUPT_DATA),
+ ERROR_ENTRY (N_("The DNSSEC signature is invalid."),
+ DANE_E_INVALID_DNSSEC_SIG),
+ ERROR_ENTRY (N_("There was a memory error."),
+ DANE_E_MEMORY_ERROR),
+ ERROR_ENTRY (N_("There requested data are not available."),
+ DANE_E_REQUESTED_DATA_NOT_AVAILABLE),
+ ERROR_ENTRY (N_("There request is invalid."),
+ DANE_E_INVALID_REQUEST),
+ ERROR_ENTRY (N_("There was an error in the public key."),
+ DANE_E_PUBKEY_ERROR),
+ ERROR_ENTRY (N_("No certificate was found."),
+ DANE_E_NO_CERT),
+ {NULL, NULL, 0}
+};
+
+/**
+ * dane_strerror:
+ * @error: is a DANE error code, a negative error code
+ *
+ * This function is similar to strerror. The difference is that it
+ * accepts an error number returned by a gnutls function; In case of
+ * an unknown error a descriptive string is sent instead of %NULL.
+ *
+ * Error codes are always a negative error code.
+ *
+ * Returns: A string explaining the DANE error message.
+ **/
+const char *
+dane_strerror (int error)
+{
+ const char *ret = NULL;
+ const error_entry *p;
+
+ for (p = error_algorithms; p->desc != NULL; p++)
+ {
+ if (p->number == error)
+ {
+ ret = p->desc;
+ break;
+ }
+ }
+
+ /* avoid prefix */
+ if (ret == NULL)
+ return _("(unknown error code)");
+
+ return _(ret);
+}
diff --git a/libdane/includes/Makefile.am b/libdane/includes/Makefile.am
new file mode 100644
index 0000000000..59fdcbe6ed
--- /dev/null
+++ b/libdane/includes/Makefile.am
@@ -0,0 +1,25 @@
+## Process this file with automake to produce Makefile.in
+# Copyright (C) 2012 KU Leuven
+#
+# Author: Nikos Mavrogiannopoulos
+#
+# This file is part of libdane.
+#
+# libdane is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 of the
+# License, or (at your option) any later version.
+#
+# GnuTLS-extra 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+
+nobase_include_HEADERS =
+
+if ENABLE_DANE
+nobase_include_HEADERS += gnutls/dane.h
+endif
diff --git a/libdane/includes/gnutls/dane.h b/libdane/includes/gnutls/dane.h
new file mode 100644
index 0000000000..fbe9b89883
--- /dev/null
+++ b/libdane/includes/gnutls/dane.h
@@ -0,0 +1,162 @@
+/* -*- c -*-
+ * Copyright (C) 2012 KU Leuven
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of libdane.
+ *
+ * libdane 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 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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 this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+#include <gnutls/gnutls.h> /* for gnutls_datum_t */
+
+/**
+ * dane_cert_usage_t:
+ * @DANE_CERT_USAGE_CA: CA constraint. The certificate/key
+ * presented must have signed the verified key.
+ * @DANE_CERT_USAGE_EE: The key or the certificate of the end
+ * entity.
+ * @DANE_CERT_USAGE_LOCAL_CA: The remote CA is local and possibly
+ * untrusted by the verifier.
+ * @DANE_CERT_USAGE_LOCAL_EE: The remote end-entity key is local
+ * and possibly untrusted by the verifier (not signed by a CA).
+ *
+ * Enumeration of different certificate usage types.
+ */
+typedef enum dane_cert_usage_t
+{
+ DANE_CERT_USAGE_CA = 0,
+ DANE_CERT_USAGE_EE = 1,
+ DANE_CERT_USAGE_LOCAL_CA = 2,
+ DANE_CERT_USAGE_LOCAL_EE = 3
+} dane_cert_usage_t;
+
+/**
+ * dane_cert_type_t:
+ * @DANE_CERT_X509: An X.509 certificate.
+ * @DANE_CERT_PK: A public key.
+ *
+ * Enumeration of different certificate types.
+ */
+typedef enum dane_cert_type_t
+{
+ DANE_CERT_X509 = 0,
+ DANE_CERT_PK = 1
+} dane_cert_type_t;
+
+/**
+ * dane_match_type_t:
+ * @DANE_MATCH_EXACT: The full content.
+ * @DANE_MATCH_SHA2_256: A SHA-256 hash of the content.
+ * @DANE_MATCH_SHA2_512: A SHA-512 hash of the content.
+ *
+ * Enumeration of different content matching types.
+ */
+typedef enum dane_match_type_t
+{
+ DANE_MATCH_EXACT = 0,
+ DANE_MATCH_SHA2_256 = 1,
+ DANE_MATCH_SHA2_512 = 2
+} dane_match_type_t;
+
+/**
+ * dane_query_status_t:
+ * @DANE_QUERY_UNKNOWN: There was no query.
+ * @DANE_QUERY_DNSSEC_VERIFIED: The query was verified using DNSSEC.
+ * @DANE_QUERY_BOGUS: The query has wrong DNSSEC signature.
+ * @DANE_QUERY_NO_DNSSEC: The query has no DNSSEC data.
+ *
+ * Enumeration of different certificate types.
+ */
+typedef enum dane_query_status_t
+{
+ DANE_QUERY_UNKNOWN = 0,
+ DANE_QUERY_DNSSEC_VERIFIED,
+ DANE_QUERY_BOGUS,
+ DANE_QUERY_NO_DNSSEC
+} dane_query_status_t;
+
+typedef struct dane_query_st *dane_query_t;
+
+
+int dane_query_init (dane_query_t* q, unsigned int flags);
+void dane_query_deinit (dane_query_t q);
+int dane_query_resolve_tlsa (dane_query_t q, const char* host, const char* proto, unsigned int port);
+int dane_query_data(dane_query_t q, unsigned int idx,
+ unsigned int *usage, unsigned int *type,
+ unsigned int *match, gnutls_datum_t * data);
+dane_query_status_t dane_query_status(dane_query_t q);
+unsigned int dane_query_entries(dane_query_t q);
+
+
+/**
+ * dane_verify_status_t:
+ * @DANE_VERIFY_CA_CONSTRAINS_VIOLATED: The CA constrains was violated.
+ * @DANE_VERIFY_CERT_DIFFERS: The certificate obtained via DNS differs.
+ * @DANE_VERIFY_NO_DANE_INFO: No DANE data were found in the DNS record.
+ * @DANE_VERIFY_DNSSEC_DATA_INVALID: The DNSSEC data are invalid.
+ * @DANE_VERIFY_NO_DNSSEC_DATA: The DNS data were not signed using DNSSEC.
+ *
+ * Enumeration of different verification status flags.
+ */
+typedef enum dane_verify_status_t
+{
+ DANE_VERIFY_CA_CONSTRAINS_VIOLATED = 1,
+ DANE_VERIFY_CERT_DIFFERS = 1<<1,
+ DANE_VERIFY_NO_DANE_INFO = 1<<2,
+ DANE_VERIFY_DNSSEC_DATA_INVALID = 1<<3,
+ DANE_VERIFY_NO_DNSSEC_DATA = 1<<4,
+} dane_verify_status_t;
+
+/**
+ * dane_verify_flags_t:
+ * @DANE_F_REQUIRE_DNSSEC: Require DNSSEC for verification.
+ * @DANE_F_IGNORE_LOCAL_RESOLVER: Many systems are not DNSSEC-ready. In that case the local resolver is ignored, and a direct recursive resolve occurs.
+ *
+ * Enumeration of different verification flags.
+ */
+typedef enum dane_verify_flags_t
+{
+ DANE_F_REQUIRE_DNSSEC = 1,
+ DANE_F_IGNORE_LOCAL_RESOLVER = 1<<2,
+} dane_verify_flags_t;
+
+int dane_verify_crt (
+ const gnutls_datum_t *chain, unsigned chain_size,
+ gnutls_certificate_type_t chain_type,
+ const char * hostname, const char* proto, unsigned int port,
+ unsigned int flags, unsigned int *verify);
+
+int dane_verify_session_crt (
+ gnutls_session_t session,
+ const char * hostname, const char* proto, unsigned int port,
+ unsigned int flags, unsigned int *verify);
+
+const char * dane_strerror (int error);
+
+#define DANE_E_SUCCESS 0
+#define DANE_E_INITIALIZATION_ERROR -1
+#define DANE_E_RESOLVING_ERROR -2
+#define DANE_E_NO_DANE_DATA -3
+#define DANE_E_RECEIVED_CORRUPT_DATA -4
+#define DANE_E_INVALID_DNSSEC_SIG -5
+#define DANE_E_NO_DNSSEC_SIG -6
+#define DANE_E_MEMORY_ERROR -7
+#define DANE_E_REQUESTED_DATA_NOT_AVAILABLE -8
+#define DANE_E_INVALID_REQUEST -9
+#define DANE_E_PUBKEY_ERROR -10
+#define DANE_E_NO_CERT -11
+
diff --git a/libdane/libdane.map b/libdane/libdane.map
new file mode 100644
index 0000000000..a5af3538d2
--- /dev/null
+++ b/libdane/libdane.map
@@ -0,0 +1,19 @@
+# libgnutls.map -- libgnutls linker version script. -*- ld-script -*-
+
+DANE_0_0
+{
+ global:
+ dane_strerror;
+ dane_verify_session_crt;
+ dane_verify_crt;
+ dane_query_init;
+ dane_query_deinit;
+ dane_query_resolve_tlsa;
+ dane_query_data;
+ dane_query_status;
+ dane_query_entries;
+
+ local:
+ *;
+};
+