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 = 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 *cstr; 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(INSECURE)) flags |= DANE_F_INSECURE; if (HAVE_OPT(CHECK_EE)) vflags |= DANE_VFLAG_ONLY_CHECK_EE_USAGE; if (HAVE_OPT(CHECK_CA)) vflags |= DANE_VFLAG_ONLY_CHECK_CA_USAGE; 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)); retcode = 1; goto error; } if (HAVE_OPT(DLV)) { ret = dane_state_set_dlv_file(s, OPT_ARG(DLV)); if (ret < 0) { fprintf(stderr, "dane_state_set_dlv_file: %s\n", dane_strerror(ret)); retcode = 1; goto error; } } ret = dane_query_tlsa(s, &q, host, proto, port); if (ret < 0) { fprintf(stderr, "dane_query_tlsa: %s\n", dane_strerror(ret)); retcode = 1; goto error; } if (ENABLED_OPT(PRINT_RAW)) { 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++) { 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); for (i = 0; i < entries; i++) { ret = dane_query_data(q, i, &usage, &type, &match, &data); if (ret < 0) { fprintf(stderr, "dane_query_data: %s\n", dane_strerror(ret)); 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)); retcode = 1; goto error; } 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); if (!HAVE_OPT(QUIET)) { cstr = dane_cert_usage_name(usage); if (cstr == NULL) cstr= "Unknown"; fprintf(outfile, "Certificate usage: %s (%.2x)\n", cstr, usage); cstr = dane_cert_type_name(type); if (cstr == NULL) cstr= "Unknown"; fprintf(outfile, "Certificate type: %s (%.2x)\n", cstr, type); cstr = dane_match_type_name(match); if (cstr == NULL) cstr= "Unknown"; fprintf(outfile, "Contents: %s (%.2x)\n", cstr, match); fprintf(outfile, "Data: %s\n", lbuffer); } /* Verify the DANE data */ if (cinfo->cert) { unsigned int status; gnutls_datum_t out; 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)); retcode = 1; goto error; } ret = dane_verification_status_print(status, &out, 0); if (ret < 0) { fprintf(stderr, "dane_verification_status_print: %s\n", dane_strerror(ret)); retcode = 1; goto error; } if (!HAVE_OPT(QUIET)) fprintf(outfile, "\nVerification: %s\n", out.data); gnutls_free(out.data); /* if there is at least one correct accept */ if (status == 0) retcode = 0; } else { fprintf(stderr, "\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 is disabled (GnuTLS was not compiled with support for DANE).\n"); return; #endif }
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; 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 vflags = DANE_VFLAG_FAIL_IF_NOT_CHECKED; if (ENABLED_OPT(LOCAL_DNS)) flags = 0; if (HAVE_OPT(INSECURE)) flags |= DANE_F_INSECURE; if (HAVE_OPT(CHECK_EE)) vflags |= DANE_VFLAG_ONLY_CHECK_EE_USAGE; if (HAVE_OPT(CHECK_CA)) vflags |= DANE_VFLAG_ONLY_CHECK_CA_USAGE; printf("Querying %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); } if (HAVE_OPT(DLV)) { ret = dane_state_set_dlv_file(s, OPT_ARG(DLV)); if (ret < 0) { fprintf(stderr, "dane_state_set_dlv_file: %s\n", dane_strerror(ret)); exit(1); } } ret = dane_query_tlsa(s, &q, host, proto, port); if (ret < 0) { fprintf(stderr, "dane_query_tlsa: %s\n", dane_strerror(ret)); exit(1); } entries = dane_query_entries(q); for (i = 0; i < entries; i++) { ret = dane_query_data(q, i, &usage, &type, &match, &data); if (ret < 0) { fprintf(stderr, "dane_query_data: %s\n", dane_strerror(ret)); exit(1); } size = buffer_size; ret = gnutls_hex_encode(&data, (void *) buffer, &size); if (ret < 0) { fprintf(stderr, "gnutls_hex_encode: %s\n", dane_strerror(ret)); exit(1); } if (entries > 1) printf("\nEntry %d:\n", i + 1); fprintf(outfile, "_%u._%s.%s. IN TLSA ( %.2x %.2x %.2x %s )\n", port, proto, host, usage, type, match, buffer); 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", buffer); /* Verify the DANE data */ if (cinfo->cert) { gnutls_x509_crt_t *clist; unsigned int clist_size, status; ret = gnutls_load_file(cinfo->cert, &file); if (ret < 0) { fprintf(stderr, "gnutls_load_file: %s\n", gnutls_strerror(ret)); exit(1); } 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)); exit(1); } 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; for (i = 0; i < clist_size; i++) { gnutls_free(certs[i].data); gnutls_x509_crt_deinit(clist[i]); } gnutls_free(clist); } } else { fprintf(stderr, "\nCertificate was not verified. Use --load-certificate.\n"); } } dane_query_deinit(q); dane_state_deinit(s); exit(retcode); #else fprintf(stderr, "This functionality was disabled (GnuTLS was not compiled with support for DANE).\n"); return; #endif }
void patrol_dialog_window_load (PatrolDialogWindow *self, const gchar *host, const gchar *proto, guint16 port, GList *chains, PatrolVerifyRC result, gint chain_result, gint dane_result, gchar *app_name, gboolean notify) { PatrolDialogWindowPrivate *pv = self->pv; pv->host = host; pv->proto = proto; pv->port = port; pv->chains = chains; pv->result = result; if (!chains) return; PatrolDialogRecord *r = chains->data; /* window title */ gchar *text = g_strdup_printf("%s - %s:%u (%s)", _("Certificate Patrol"), host, port, proto); gtk_window_set_title(GTK_WINDOW(self), text); g_free(text); /* message icon & text*/ gtk_image_set_from_stock(GTK_IMAGE(pv->icon), (g_list_length(chains) > 1) ? GTK_STOCK_DIALOG_WARNING : GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG); text = g_strdup_printf( (result == PATROL_ARG_UNKNOWN) ? _("Pinned public keys for peer <b>%s:%u (%s)</b>") : (r->rec.status == PATROL_STATUS_REJECTED) ? _("Rejected public key encountered for peer <b>%s:%u (%s)</b>") : (g_list_length(chains) > 1) ? _("<b>Changed public key</b> encountered for peer <b>%s:%u (%s)</b>\n" "in application <b>%s</b>") : notify ? _("<b>New public key</b> accepted for peer <b>%s:%u (%s)</b>\n" "in application <b>%s</b>") : _("<b>New public key</b> encountered for peer <b>%s:%u (%s)</b>\n" "in application <b>%s</b>"), host, port, proto, app_name); gtk_label_set_markup(GTK_LABEL(pv->msg), text); g_free(text); /* chain_msg */ if (chain_result != PATROL_ARG_UNKNOWN) { gtk_image_set_from_stock(GTK_IMAGE(pv->chain_icon), (chain_result == PATROL_OK) ? GTK_STOCK_APPLY : GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_BUTTON); text = g_strdup_printf( "<b>%s</b>: %s.", _("Certificate chain validation"), chain_result == PATROL_OK ? _("Success") : _("Fail")); gtk_label_set_markup(GTK_LABEL(pv->chain_msg), text); g_free(text); } else { gtk_widget_hide(gtk_widget_get_parent(pv->chain_msg)); } #ifdef HAVE_GNUTLS_DANE /* dane_msg */ if (dane_result != PATROL_ARG_UNKNOWN) { gtk_image_set_from_stock(GTK_IMAGE(pv->dane_icon), (dane_result == DANE_E_SUCCESS) ? GTK_STOCK_APPLY : (dane_result == DANE_E_NO_DANE_DATA || (dane_result > 0 && dane_result & DANE_VERIFY_NO_DANE_INFO)) ? GTK_STOCK_DIALOG_INFO : (dane_result < 0) ? GTK_STOCK_DIALOG_WARNING : GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_BUTTON); gnutls_datum_t dane_status_str = { 0 }; if (dane_result >= 0) dane_verification_status_print(dane_result, &dane_status_str, 0); text = g_strdup_printf( "<b>%s</b>: %s %.*s", _("DANE validation"), dane_strerror(dane_result < 0 ? dane_result : 0), dane_status_str.size, dane_status_str.data); gnutls_free(dane_status_str.data); gtk_label_set_markup(GTK_LABEL(pv->dane_msg), text); g_free(text); } else { gtk_widget_hide(gtk_widget_get_parent(pv->dane_msg)); } #endif GList *item = chains; guint i; for (i = 0; item && item->data; item = item->next, i++) load_chain(self, item->data, i, i ? pv->old_chains : pv->new_chain); }
static int cert_verify_callback (gnutls_session_t session) { int rc; unsigned int status = 0; int ssh = ENABLED_OPT(TOFU); #ifdef HAVE_DANE int dane = ENABLED_OPT(DANE); #endif int ca_verify = ENABLED_OPT(CA_VERIFICATION); const char* txt_service; print_cert_info (session, verbose, print_cert); if (ca_verify) { rc = cert_verify(session, hostname); if (rc == 0) { printf ("*** Verifying server certificate failed...\n"); if (!insecure && !ssh) return -1; } else if (ENABLED_OPT(OCSP) && gnutls_ocsp_status_request_is_checked(session, 0) == 0) { /* off-line verification succeeded. Try OCSP */ rc = cert_verify_ocsp(session); if (rc == 0) { printf ("*** Verifying (with OCSP) server certificate failed...\n"); if (!insecure && !ssh) return -1; } else if (rc == -1) printf("*** OCSP response ignored\n"); } } if (ssh) /* try ssh auth */ { unsigned int list_size; const gnutls_datum_t * cert; cert = gnutls_certificate_get_peers(session, &list_size); if (cert == NULL) { fprintf(stderr, "Cannot obtain peer's certificate!\n"); return -1; } txt_service = port_to_service(service); rc = gnutls_verify_stored_pubkey(NULL, NULL, hostname, txt_service, GNUTLS_CRT_X509, cert, 0); if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND) { print_cert_info_compact(session); fprintf(stderr, "Host %s (%s) has never been contacted before.\n", hostname, txt_service); if (status == 0) fprintf(stderr, "Its certificate is valid for %s.\n", hostname); rc = read_yesno("Are you sure you want to trust it? (y/N): "); if (rc == 0) return -1; } else if (rc == GNUTLS_E_CERTIFICATE_KEY_MISMATCH) { print_cert_info_compact(session); fprintf(stderr, "Warning: host %s is known and it is associated with a different key.\n", hostname); fprintf(stderr, "It might be that the server has multiple keys, or an attacker replaced the key to eavesdrop this connection .\n"); if (status == 0) fprintf(stderr, "Its certificate is valid for %s.\n", hostname); rc = read_yesno("Do you trust the received key? (y/N): "); if (rc == 0) return -1; } else if (rc < 0) { fprintf(stderr, "gnutls_verify_stored_pubkey: %s\n", gnutls_strerror(rc)); return -1; } if (rc != 0) { rc = gnutls_store_pubkey(NULL, NULL, hostname, txt_service, GNUTLS_CRT_X509, cert, 0, 0); if (rc < 0) fprintf(stderr, "Could not store key: %s\n", gnutls_strerror(rc)); } } #ifdef HAVE_DANE if (dane) /* try DANE auth */ { unsigned int sflags = ENABLED_OPT(LOCAL_DNS)?0:DANE_F_IGNORE_LOCAL_RESOLVER; rc = dane_verify_session_crt( NULL, session, hostname, udp?"udp":"tcp", atoi(service), sflags, 0, &status); if (rc < 0) { fprintf(stderr, "*** DANE verification error: %s\n", dane_strerror(rc)); if (!insecure) return -1; } else { gnutls_datum_t out; rc = dane_verification_status_print( status, &out, 0); if (rc < 0) { fprintf(stderr, "*** DANE error: %s\n", dane_strerror(rc)); if (!insecure) return -1; } fprintf(stderr, "- %s\n", out.data); gnutls_free(out.data); } } #endif return 0; }