* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations including
+ * the two.
+ *
+ * You must obey the GNU General Public License in all respects for all
+ * of the code used other than OpenSSL. If you modify file(s) with this
+ * exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do
+ * so, delete this exception statement from your version. If you delete
+ * this exception statement from all source files in the program, then
+ * also delete it here.
*/
#include <config.h>
/* Gnulib portability files. */
#include <read-file.h>
+#include <minmax.h>
#include <common.h>
#include "danetool-args.h"
#include "certtool-common.h"
+#include "socket.h"
+static const char* obtain_cert(const char *hostname, const char *proto, unsigned int port,
+ const char *app_proto, unsigned quiet);
static void cmd_parser(int argc, char **argv);
static void dane_info(const char *host, const char *proto,
unsigned int port, unsigned int ca,
exit(1);
}
#ifdef ENABLE_PKCS11
- pkcs11_common();
+ pkcs11_common(NULL);
#endif
memset(&cinfo, 0, sizeof(cinfo));
gnutls_global_deinit();
}
+#define MAX_CLIST_SIZE 32
static void dane_check(const char *host, const char *proto,
unsigned int port, common_info_st * cinfo)
{
#ifdef HAVE_DANE
dane_state_t s;
dane_query_t q;
- int ret, retcode = 0;
+ int ret, retcode = 1;
unsigned entries;
unsigned int flags = DANE_F_IGNORE_LOCAL_RESOLVER, i;
unsigned int usage, type, match;
gnutls_datum_t data, file;
size_t size;
+ unsigned del = 0;
unsigned vflags = DANE_VFLAG_FAIL_IF_NOT_CHECKED;
+ const char *str;
+ gnutls_x509_crt_t *clist = NULL;
+ unsigned int clist_size = 0;
+ gnutls_datum_t certs[MAX_CLIST_SIZE];
if (ENABLED_OPT(LOCAL_DNS))
flags = 0;
if (HAVE_OPT(CHECK_CA))
vflags |= DANE_VFLAG_ONLY_CHECK_CA_USAGE;
- printf("Querying %s (%s:%d)...\n", host, proto, port);
+ if (!cinfo->cert) {
+ const char *app_proto = NULL;
+ if (HAVE_OPT(APP_PROTO))
+ app_proto = OPT_ARG(APP_PROTO);
+
+ cinfo->cert = obtain_cert(host, proto, port, app_proto, HAVE_OPT(QUIET));
+ del = 1;
+ }
+
+ if (!HAVE_OPT(QUIET))
+ fprintf(stderr, "Querying DNS for %s (%s:%d)...\n", host, proto, port);
ret = dane_state_init(&s, flags);
if (ret < 0) {
fprintf(stderr, "dane_state_init: %s\n",
dane_strerror(ret));
- exit(1);
+ retcode = 1;
+ goto error;
}
if (HAVE_OPT(DLV)) {
if (ret < 0) {
fprintf(stderr, "dane_state_set_dlv_file: %s\n",
dane_strerror(ret));
- exit(1);
+ retcode = 1;
+ goto error;
}
}
if (ret < 0) {
fprintf(stderr, "dane_query_tlsa: %s\n",
dane_strerror(ret));
- exit(1);
+ retcode = 1;
+ goto error;
+ }
+
+ if (ENABLED_OPT(PRINT_RAW)) {
+ unsigned entries;
+ gnutls_datum_t t;
+ char **dane_data;
+ int *dane_data_len;
+ int secure;
+ int bogus;
+
+ ret = dane_query_to_raw_tlsa(q, &entries, &dane_data,
+ &dane_data_len, &secure, &bogus);
+ if (ret < 0) {
+ fprintf(stderr, "dane_query_to_raw_tlsa: %s\n",
+ dane_strerror(ret));
+ retcode = 1;
+ goto error;
+ }
+
+ for (i=0;i<entries;i++) {
+ char *str;
+ size_t str_size;
+ t.data = (void*)dane_data[i];
+ t.size = dane_data_len[i];
+
+ str_size = t.size * 2 + 1;
+ str = gnutls_malloc(str_size);
+
+ ret = gnutls_hex_encode(&t, str, &str_size);
+ if (ret < 0) {
+ fprintf(stderr, "gnutls_hex_encode: %s\n",
+ dane_strerror(ret));
+ retcode = 1;
+ goto error;
+ }
+ fprintf(outfile, "[%u]: %s\n", i, str);
+ gnutls_free(str);
+ }
+ fprintf(outfile, "\n");
+ }
+
+ if (cinfo->cert) {
+ ret = gnutls_load_file(cinfo->cert, &file);
+ if (ret < 0) {
+ fprintf(stderr, "gnutls_load_file: %s\n",
+ gnutls_strerror(ret));
+ retcode = 1;
+ goto error;
+ }
+
+ ret =
+ gnutls_x509_crt_list_import2(&clist,
+ &clist_size,
+ &file,
+ cinfo->
+ incert_format, 0);
+ if (ret < 0) {
+ fprintf(stderr,
+ "gnutls_x509_crt_list_import2: %s\n",
+ gnutls_strerror(ret));
+ retcode = 1;
+ goto error;
+ }
+
+ if (clist_size > 0) {
+ for (i = 0; i < MIN(MAX_CLIST_SIZE,clist_size); i++) {
+ ret =
+ gnutls_x509_crt_export2(clist
+ [i],
+ GNUTLS_X509_FMT_DER,
+ &certs
+ [i]);
+ if (ret < 0) {
+ fprintf(stderr,
+ "gnutls_x509_crt_export2: %s\n",
+ gnutls_strerror
+ (ret));
+ retcode = 1;
+ goto error;
+ }
+ }
+ }
}
entries = dane_query_entries(q);
if (ret < 0) {
fprintf(stderr, "dane_query_data: %s\n",
dane_strerror(ret));
- exit(1);
+ retcode = 1;
+ goto error;
}
-
size = lbuffer_size;
ret = gnutls_hex_encode(&data, (void *) lbuffer, &size);
if (ret < 0) {
fprintf(stderr, "gnutls_hex_encode: %s\n",
dane_strerror(ret));
- exit(1);
+ retcode = 1;
+ goto error;
}
- if (entries > 1)
- printf("\nEntry %d:\n", i + 1);
+ if (entries > 1 && !HAVE_OPT(QUIET))
+ fprintf(outfile, "\n==== Entry %d ====\n", i + 1);
fprintf(outfile,
"_%u._%s.%s. IN TLSA ( %.2x %.2x %.2x %s )\n",
port, proto, host, usage, type, match, lbuffer);
- printf("Certificate usage: %s (%.2x)\n",
- dane_cert_usage_name(usage), usage);
- printf("Certificate type: %s (%.2x)\n",
- dane_cert_type_name(type), type);
- printf("Contents: %s (%.2x)\n",
- dane_match_type_name(match), match);
- printf("Data: %s\n", lbuffer);
+
+ if (!HAVE_OPT(QUIET)) {
+ str = dane_cert_usage_name(usage);
+ if (str == NULL) str= "Unknown";
+ fprintf(outfile, "Certificate usage: %s (%.2x)\n", str, usage);
+
+ str = dane_cert_type_name(type);
+ if (str == NULL) str= "Unknown";
+ fprintf(outfile, "Certificate type: %s (%.2x)\n", str, type);
+
+ str = dane_match_type_name(match);
+ if (str == NULL) str= "Unknown";
+ fprintf(outfile, "Contents: %s (%.2x)\n", str, match);
+ fprintf(outfile, "Data: %s\n", lbuffer);
+ }
/* Verify the DANE data */
if (cinfo->cert) {
- gnutls_x509_crt_t *clist;
- unsigned int clist_size, status;
+ unsigned int status;
+ gnutls_datum_t out;
- ret = gnutls_load_file(cinfo->cert, &file);
+ ret =
+ dane_verify_crt(s, certs, clist_size,
+ GNUTLS_CRT_X509, host,
+ proto, port, 0, vflags,
+ &status);
if (ret < 0) {
- fprintf(stderr, "gnutls_load_file: %s\n",
- gnutls_strerror(ret));
- exit(1);
+ fprintf(stderr,
+ "dane_verify_crt: %s\n",
+ dane_strerror(ret));
+ retcode = 1;
+ goto error;
}
ret =
- gnutls_x509_crt_list_import2(&clist,
- &clist_size,
- &file,
- cinfo->
- incert_format, 0);
+ dane_verification_status_print(status,
+ &out,
+ 0);
if (ret < 0) {
fprintf(stderr,
- "gnutls_x509_crt_list_import2: %s\n",
- gnutls_strerror(ret));
- exit(1);
+ "dane_verification_status_print: %s\n",
+ dane_strerror(ret));
+ retcode = 1;
+ goto error;
}
- if (clist_size > 0) {
- gnutls_datum_t certs[clist_size];
- gnutls_datum_t out;
- unsigned int i;
-
- for (i = 0; i < clist_size; i++) {
- ret =
- gnutls_x509_crt_export2(clist
- [i],
- GNUTLS_X509_FMT_DER,
- &certs
- [i]);
- if (ret < 0) {
- fprintf(stderr,
- "gnutls_x509_crt_export2: %s\n",
- gnutls_strerror
- (ret));
- exit(1);
- }
- }
-
- ret =
- dane_verify_crt(s, certs, clist_size,
- GNUTLS_CRT_X509, host,
- proto, port, 0, vflags,
- &status);
- if (ret < 0) {
- fprintf(stderr,
- "dane_verify_crt: %s\n",
- dane_strerror(ret));
- exit(1);
- }
-
- ret =
- dane_verification_status_print(status,
- &out,
- 0);
- if (ret < 0) {
- fprintf(stderr,
- "dane_verification_status_print: %s\n",
- dane_strerror(ret));
- exit(1);
- }
-
- printf("\nVerification: %s\n", out.data);
- gnutls_free(out.data);
-
- if (status != 0)
- retcode = 1;
+ if (!HAVE_OPT(QUIET))
+ fprintf(outfile, "\nVerification: %s\n", out.data);
+ gnutls_free(out.data);
- for (i = 0; i < clist_size; i++) {
- gnutls_free(certs[i].data);
- gnutls_x509_crt_deinit(clist[i]);
- }
- gnutls_free(clist);
- }
+ /* if there is at least one correct accept */
+ if (status == 0)
+ retcode = 0;
} else {
fprintf(stderr,
- "\nCertificate was not verified. Use --load-certificate.\n");
+ "\nCertificate could not be obtained. You can explicitly load the certificate using --load-certificate.\n");
+ }
+ }
+
+ if (clist_size > 0) {
+ for (i = 0; i < clist_size; i++) {
+ gnutls_free(certs[i].data);
+ gnutls_x509_crt_deinit(clist[i]);
}
+ gnutls_free(clist);
}
+
dane_query_deinit(q);
dane_state_deinit(s);
+ error:
+ if (del != 0 && cinfo->cert) {
+ remove(cinfo->cert);
+ }
+
exit(retcode);
#else
fprintf(stderr,
- "This functionality was disabled (GnuTLS was not compiled with support for DANE).\n");
+ "This functionality is disabled (GnuTLS was not compiled with support for DANE).\n");
return;
#endif
}
port, proto, host, usage, selector, type, lbuffer);
}
+
+
+struct priv_st {
+ int fd;
+ int found;
+};
+
+#ifdef HAVE_DANE
+static int cert_callback(gnutls_session_t session)
+{
+ const gnutls_datum_t *cert_list;
+ unsigned int cert_list_size = 0;
+ int ret;
+ unsigned i;
+ gnutls_datum_t t;
+ struct priv_st *priv;
+
+ cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
+ if (cert_list_size == 0) {
+ fprintf(stderr, "no certificates sent by server!\n");
+ return -1;
+ }
+
+ priv = gnutls_session_get_ptr(session);
+
+ for (i=0;i<cert_list_size;i++) {
+ ret = gnutls_pem_base64_encode_alloc("CERTIFICATE", &cert_list[i], &t);
+ if (ret < 0) {
+ fprintf(stderr, "error[%d]: %s\n", __LINE__,
+ gnutls_strerror(ret));
+ exit(1);
+ }
+
+ write(priv->fd, t.data, t.size);
+ gnutls_free(t.data);
+ }
+ priv->found = 1;
+
+ return -1;
+}
+
+static int get_cert(socket_st *hd, const char *hostname, unsigned udp, int fd)
+{
+ gnutls_certificate_credentials_t xcred;
+ gnutls_session_t session;
+ int ret;
+ struct priv_st priv;
+
+ priv.found = 0;
+ priv.fd = fd;
+
+ ret = gnutls_certificate_allocate_credentials(&xcred);
+ if (ret < 0) {
+ fprintf(stderr, "error[%d]: %s\n", __LINE__,
+ gnutls_strerror(ret));
+ exit(1);
+ }
+ gnutls_certificate_set_verify_function(xcred, cert_callback);
+
+ ret = gnutls_init(&session, (udp?GNUTLS_DATAGRAM:0)|GNUTLS_CLIENT);
+ if (ret < 0) {
+ fprintf(stderr, "error[%d]: %s\n", __LINE__,
+ gnutls_strerror(ret));
+ exit(1);
+ }
+ gnutls_session_set_ptr(session, &priv);
+ gnutls_transport_set_int(session, hd->fd);
+
+ gnutls_set_default_priority(session);
+ if (hostname && is_ip(hostname)==0) {
+ gnutls_server_name_set(session, GNUTLS_NAME_DNS, hostname, strlen(hostname));
+ }
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while(ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_WARNING_ALERT_RECEIVED);
+ /* we don't care on the result */
+
+ gnutls_deinit(session);
+ gnutls_certificate_free_credentials(xcred);
+
+ if (priv.found == 0)
+ return -1;
+
+ return 0;
+}
+
+static const char *obtain_cert(const char *hostname, const char *proto, unsigned port,
+ const char *app_proto, unsigned quiet)
+{
+ socket_st hd;
+ char txt_port[16];
+ unsigned udp = 0;
+ static char tmpfile[32];
+ int fd, ret;
+ const char *str = "Obtaining certificate from";
+ const char *service;
+
+ if (strcmp(proto, "udp") == 0)
+ udp = 1;
+ else if (strcmp(proto, "tcp") != 0) {
+ /* we cannot handle this protocol */
+ return NULL;
+ }
+
+ strcpy(tmpfile, "danetool-certXXXXXX");
+
+ sockets_init();
+ snprintf(txt_port, sizeof(txt_port), "%u", port);
+
+ if (quiet)
+ str = NULL;
+ service = port_to_service(txt_port, proto);
+ socket_open(&hd, hostname, service, udp, str);
+
+ if (app_proto == NULL) app_proto = service;
+ socket_starttls(&hd, app_proto);
+
+ umask(066);
+ fd = mkstemp(tmpfile);
+ if (fd == -1) {
+ int e = errno;
+ fprintf(stderr, "error[%d]: %s\n", __LINE__,
+ strerror(e));
+ exit(1);
+ }
+
+ ret = get_cert(&hd, hostname, udp, fd);
+ close(fd);
+
+ socket_bye(&hd);
+
+ if (ret == -1)
+ return NULL;
+ else
+ return tmpfile;
+}
+#endif