diff options
author | Christian Grothoff <christian@grothoff.org> | 2014-06-07 20:03:27 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2014-06-07 20:03:27 +0000 |
commit | e71cdb38f2b520975527141f42fdd3ce3d941fe4 (patch) | |
tree | 8dc4b01418936041930f1dc07e1d87883025918a /src/namestore/plugin_gtk_namestore_tlsa.c | |
parent | e88cc91be7df92b75430d4748fd201eb03013675 (diff) | |
download | gnunet-gtk-e71cdb38f2b520975527141f42fdd3ce3d941fe4.tar.gz gnunet-gtk-e71cdb38f2b520975527141f42fdd3ce3d941fe4.zip |
fixing #2526
Diffstat (limited to 'src/namestore/plugin_gtk_namestore_tlsa.c')
-rw-r--r-- | src/namestore/plugin_gtk_namestore_tlsa.c | 375 |
1 files changed, 369 insertions, 6 deletions
diff --git a/src/namestore/plugin_gtk_namestore_tlsa.c b/src/namestore/plugin_gtk_namestore_tlsa.c index 19f88d11..d8dc60cd 100644 --- a/src/namestore/plugin_gtk_namestore_tlsa.c +++ b/src/namestore/plugin_gtk_namestore_tlsa.c | |||
@@ -32,7 +32,7 @@ | |||
32 | #include <gnutls/gnutls.h> | 32 | #include <gnutls/gnutls.h> |
33 | #include <gnutls/x509.h> | 33 | #include <gnutls/x509.h> |
34 | #include <gnutls/abstract.h> | 34 | #include <gnutls/abstract.h> |
35 | 35 | #include <gnunet/gnunet_resolver_service.h> | |
36 | 36 | ||
37 | /** | 37 | /** |
38 | * The user has edited the target value. Enable/disable 'save' | 38 | * The user has edited the target value. Enable/disable 'save' |
@@ -352,7 +352,7 @@ tlsa_load (void *cls, | |||
352 | */ | 352 | */ |
353 | static gchar * | 353 | static gchar * |
354 | tlsa_store (void *cls, | 354 | tlsa_store (void *cls, |
355 | GtkBuilder *builder) | 355 | GtkBuilder *builder) |
356 | { | 356 | { |
357 | unsigned int protocol; | 357 | unsigned int protocol; |
358 | GtkComboBox *cb; | 358 | GtkComboBox *cb; |
@@ -470,6 +470,8 @@ tlsa_validate (void *cls, | |||
470 | &ti_start, | 470 | &ti_start, |
471 | &ti_end, | 471 | &ti_end, |
472 | FALSE); | 472 | FALSE); |
473 | if (0 == strlen (value)) | ||
474 | return GNUNET_SYSERR; | ||
473 | { | 475 | { |
474 | size_t slen = strlen (value); | 476 | size_t slen = strlen (value); |
475 | uint8_t bin[slen / 2]; | 477 | uint8_t bin[slen / 2]; |
@@ -565,6 +567,335 @@ tlsa_validate (void *cls, | |||
565 | 567 | ||
566 | 568 | ||
567 | /** | 569 | /** |
570 | * We have successfully established a TLS session to | ||
571 | * import a certificate from the server. Import the | ||
572 | * X509 certificate into the GUI. | ||
573 | * | ||
574 | * @param session TLS session to import from | ||
575 | * @param builder GTK builder to update GUI | ||
576 | */ | ||
577 | static void | ||
578 | import_x509_certificate (gnutls_session_t session, | ||
579 | GtkBuilder *builder) | ||
580 | { | ||
581 | const gnutls_datum_t *cert_list; | ||
582 | unsigned int cert_list_size = 0; | ||
583 | gnutls_x509_crt_t cert; | ||
584 | unsigned int matching_type; | ||
585 | unsigned int selector; | ||
586 | gnutls_pubkey_t pk; | ||
587 | char buf[4092]; | ||
588 | size_t bsize; | ||
589 | char *hex; | ||
590 | gnutls_datum_t datum; | ||
591 | uint8_t sha256[256/8]; | ||
592 | uint8_t sha512[512/8]; | ||
593 | size_t ssize; | ||
594 | GtkTextBuffer *tb; | ||
595 | |||
596 | cert_list = gnutls_certificate_get_peers (session, | ||
597 | &cert_list_size); | ||
598 | if (0 == cert_list_size) | ||
599 | { | ||
600 | /* is it possible to succeed with TLS handshake and have | ||
601 | NO certificates!? If so, how do we get the public key?*/ | ||
602 | GNUNET_break (0); | ||
603 | return; | ||
604 | } | ||
605 | /* we only import the first certificate. */ | ||
606 | gnutls_x509_crt_init (&cert); | ||
607 | if (GNUTLS_E_SUCCESS != | ||
608 | gnutls_x509_crt_import (cert, | ||
609 | &cert_list[0], | ||
610 | GNUTLS_X509_FMT_DER)) | ||
611 | { | ||
612 | GNUNET_break (0); | ||
613 | gnutls_x509_crt_deinit (cert); | ||
614 | return; | ||
615 | } | ||
616 | selector = get_selected_radio_value (builder, | ||
617 | selector_buttons); | ||
618 | switch (selector) | ||
619 | { | ||
620 | case 0: /* full cert */ | ||
621 | bsize = sizeof (buf); | ||
622 | if (GNUTLS_E_SUCCESS != | ||
623 | gnutls_x509_crt_export (cert, | ||
624 | GNUTLS_X509_FMT_DER, | ||
625 | buf, | ||
626 | &bsize)) | ||
627 | { | ||
628 | GNUNET_break (0); | ||
629 | gnutls_x509_crt_deinit (cert); | ||
630 | return; | ||
631 | } | ||
632 | break; | ||
633 | case 1: /* subject public key only */ | ||
634 | if (GNUTLS_E_SUCCESS != | ||
635 | gnutls_pubkey_init (&pk)) | ||
636 | { | ||
637 | GNUNET_break (0); | ||
638 | gnutls_x509_crt_deinit (cert); | ||
639 | return; | ||
640 | } | ||
641 | if (GNUTLS_E_SUCCESS != | ||
642 | gnutls_pubkey_import_x509 (pk, | ||
643 | cert, | ||
644 | 0)) | ||
645 | { | ||
646 | GNUNET_break (0); | ||
647 | gnutls_x509_crt_deinit (cert); | ||
648 | gnutls_pubkey_deinit (pk); | ||
649 | return; | ||
650 | } | ||
651 | bsize = sizeof (buf); | ||
652 | if (GNUTLS_E_SUCCESS != | ||
653 | gnutls_pubkey_export (pk, | ||
654 | GNUTLS_X509_FMT_DER, | ||
655 | buf, | ||
656 | &bsize)) | ||
657 | { | ||
658 | GNUNET_break (0); | ||
659 | gnutls_x509_crt_deinit (cert); | ||
660 | gnutls_pubkey_deinit (pk); | ||
661 | return; | ||
662 | } | ||
663 | gnutls_pubkey_deinit (pk); | ||
664 | break; | ||
665 | default: | ||
666 | GNUNET_break (0); | ||
667 | gnutls_x509_crt_deinit (cert); | ||
668 | return; | ||
669 | } | ||
670 | gnutls_x509_crt_deinit (cert); | ||
671 | /* 'buf' now contains 'bsize' bytes of the binary data to | ||
672 | hash or store in the TLSA record; hash depending on | ||
673 | user preferences. */ | ||
674 | matching_type = get_selected_radio_value (builder, | ||
675 | matching_type_buttons); | ||
676 | switch (matching_type) | ||
677 | { | ||
678 | case 0: /* exact match */ | ||
679 | hex = GNUNET_DNSPARSER_bin_to_hex (buf, | ||
680 | bsize); | ||
681 | break; | ||
682 | case 1: /* SHA-256 hash */ | ||
683 | datum.size = bsize; | ||
684 | datum.data = (void *) buf; | ||
685 | ssize = sizeof (sha256); | ||
686 | GNUNET_assert (GNUTLS_E_SUCCESS == | ||
687 | gnutls_fingerprint (GNUTLS_MAC_SHA256, | ||
688 | &datum, | ||
689 | sha256, | ||
690 | &ssize)); | ||
691 | hex = GNUNET_DNSPARSER_bin_to_hex (sha256, | ||
692 | sizeof (sha256)); | ||
693 | break; | ||
694 | case 2: /* SHA-512 hash */ | ||
695 | datum.size = bsize; | ||
696 | datum.data = (void *) buf; | ||
697 | ssize = sizeof (sha512); | ||
698 | GNUNET_assert (GNUTLS_E_SUCCESS == | ||
699 | gnutls_fingerprint (GNUTLS_MAC_SHA512, | ||
700 | &datum, | ||
701 | sha512, | ||
702 | &ssize)); | ||
703 | hex = GNUNET_DNSPARSER_bin_to_hex (sha512, | ||
704 | sizeof (sha512)); | ||
705 | break; | ||
706 | default: | ||
707 | GNUNET_break (0); | ||
708 | return; | ||
709 | } | ||
710 | |||
711 | /* Finally store 'hex' to the text buffer */ | ||
712 | tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW | ||
713 | (gtk_builder_get_object (builder, | ||
714 | "edit_dialog_tlsa_value_textview"))); | ||
715 | gtk_text_buffer_set_text (tb, | ||
716 | hex, | ||
717 | -1); | ||
718 | GNUNET_free (hex); | ||
719 | } | ||
720 | |||
721 | |||
722 | /** | ||
723 | * Context for TLS certificate import from network. | ||
724 | */ | ||
725 | struct ImportContext | ||
726 | { | ||
727 | /** | ||
728 | * The TLS session. | ||
729 | */ | ||
730 | gnutls_session_t session; | ||
731 | |||
732 | /** | ||
733 | * Network handle for the session. | ||
734 | */ | ||
735 | struct GNUNET_NETWORK_Handle *sock; | ||
736 | |||
737 | /** | ||
738 | * DNS resolution request to resolve the domain name. | ||
739 | */ | ||
740 | struct GNUNET_RESOLVER_RequestHandle *rh; | ||
741 | |||
742 | /** | ||
743 | * Builder for accessing widgets. | ||
744 | */ | ||
745 | GtkBuilder *builder; | ||
746 | }; | ||
747 | |||
748 | |||
749 | /** | ||
750 | * We got an address from DNS, start TLS handshake. | ||
751 | * | ||
752 | * @param cls our `struct ImportContext` | ||
753 | * @param addr one of the addresses of the host, NULL for the last address | ||
754 | * @param addrlen length of @a addr | ||
755 | */ | ||
756 | static void | ||
757 | import_address_cb (void *cls, | ||
758 | const struct sockaddr *addr, | ||
759 | socklen_t addrlen) | ||
760 | { | ||
761 | struct ImportContext *ic = cls; | ||
762 | int pf; | ||
763 | int ret; | ||
764 | gnutls_certificate_credentials_t xcred; | ||
765 | struct sockaddr_in v4; | ||
766 | struct sockaddr_in6 v6; | ||
767 | struct sockaddr *a; | ||
768 | unsigned int port; | ||
769 | gnutls_certificate_type_t type; | ||
770 | |||
771 | if (NULL == addr) | ||
772 | { | ||
773 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
774 | _("Name resolution failed\n")); | ||
775 | GNUNET_free (ic); | ||
776 | return; | ||
777 | } | ||
778 | port = gtk_spin_button_get_value | ||
779 | (GTK_SPIN_BUTTON (gtk_builder_get_object (ic->builder, | ||
780 | "edit_dialog_port_spinbutton"))); | ||
781 | switch (addr->sa_family) | ||
782 | { | ||
783 | case AF_INET: | ||
784 | pf = PF_INET; | ||
785 | memcpy (&v4, addr, addrlen); | ||
786 | v4.sin_port = htons ((uint16_t) port); | ||
787 | a = (struct sockaddr *) &v4; | ||
788 | break; | ||
789 | case AF_INET6: | ||
790 | pf = PF_INET6; | ||
791 | memcpy (&v6, addr, addrlen); | ||
792 | v6.sin6_port = htons ((uint16_t) port); | ||
793 | a = (struct sockaddr *) &v6; | ||
794 | break; | ||
795 | default: | ||
796 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
797 | _("Unsupported address family %d\n"), | ||
798 | addr->sa_family); | ||
799 | return; | ||
800 | } | ||
801 | ic->sock = GNUNET_NETWORK_socket_create (pf, | ||
802 | SOCK_STREAM, | ||
803 | 0); | ||
804 | if (NULL == ic->sock) | ||
805 | { | ||
806 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
807 | "socket"); | ||
808 | return; | ||
809 | } | ||
810 | GNUNET_break (GNUNET_OK == | ||
811 | GNUNET_NETWORK_socket_set_blocking (ic->sock, | ||
812 | GNUNET_YES)); | ||
813 | if ( (GNUNET_OK != | ||
814 | GNUNET_NETWORK_socket_connect (ic->sock, | ||
815 | a, | ||
816 | addrlen)) && | ||
817 | (EINPROGRESS != errno) ) | ||
818 | { | ||
819 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
820 | _("Failed to connect to target address `%s': %s\n"), | ||
821 | GNUNET_a2s (addr, addrlen), | ||
822 | STRERROR (errno)); | ||
823 | goto cleanup; | ||
824 | } | ||
825 | |||
826 | GNUNET_RESOLVER_request_cancel (ic->rh); | ||
827 | |||
828 | /* Use default priorities */ | ||
829 | if (GNUTLS_E_SUCCESS != | ||
830 | (ret = gnutls_priority_set_direct (ic->session, | ||
831 | "PERFORMANCE", | ||
832 | NULL))) | ||
833 | { | ||
834 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
835 | _("Failed to initialize cipher suite: %s\n"), | ||
836 | gnutls_strerror (ret)); | ||
837 | goto cleanup; | ||
838 | } | ||
839 | /* put the x509 credentials to the current session */ | ||
840 | gnutls_certificate_allocate_credentials (&xcred); | ||
841 | gnutls_credentials_set (ic->session, | ||
842 | GNUTLS_CRD_CERTIFICATE, | ||
843 | xcred); | ||
844 | gnutls_transport_set_int (ic->session, | ||
845 | GNUNET_NETWORK_get_fd (ic->sock)); | ||
846 | gnutls_handshake_set_timeout (ic->session, | ||
847 | 2000 /* 2s */); | ||
848 | |||
849 | /* TODO: do this in event loop, with insensitive GUI, | ||
850 | with possibly higher timeout ... */ | ||
851 | /* Perform the TLS handshake */ | ||
852 | do | ||
853 | { | ||
854 | ret = gnutls_handshake (ic->session); | ||
855 | } | ||
856 | while ( (ret < 0) && (0 == gnutls_error_is_fatal (ret)) ); | ||
857 | |||
858 | /* finally, access the certificate */ | ||
859 | if (GNUTLS_E_SUCCESS == ret) | ||
860 | { | ||
861 | type = gnutls_certificate_type_get (ic->session); | ||
862 | switch (type) | ||
863 | { | ||
864 | case GNUTLS_CRT_UNKNOWN: | ||
865 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
866 | _("Server certificate type not supported\n")); | ||
867 | break; | ||
868 | case GNUTLS_CRT_X509: | ||
869 | import_x509_certificate (ic->session, | ||
870 | ic->builder); | ||
871 | break; | ||
872 | case GNUTLS_CRT_OPENPGP: | ||
873 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
874 | _("Server certificate type not supported\n")); | ||
875 | break; | ||
876 | case GNUTLS_CRT_RAW: | ||
877 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
878 | _("Server certificate type not supported\n")); | ||
879 | break; | ||
880 | } | ||
881 | } | ||
882 | else | ||
883 | { | ||
884 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
885 | _("TLS handshake failed: %s\n"), | ||
886 | gnutls_strerror (ret)); | ||
887 | } | ||
888 | gnutls_bye (ic->session, GNUTLS_SHUT_RDWR); | ||
889 | cleanup: | ||
890 | GNUNET_break (GNUNET_OK == | ||
891 | GNUNET_NETWORK_socket_close (ic->sock)); | ||
892 | gnutls_deinit (ic->session); | ||
893 | gnutls_certificate_free_credentials (xcred); | ||
894 | GNUNET_free (ic); | ||
895 | } | ||
896 | |||
897 | |||
898 | /** | ||
568 | * The user clicked the "import" button. Try to import | 899 | * The user clicked the "import" button. Try to import |
569 | * certificate from the server. | 900 | * certificate from the server. |
570 | * | 901 | * |
@@ -576,27 +907,59 @@ tlsa_import_button_clicked_cb (GtkButton *button, | |||
576 | gpointer user_data) | 907 | gpointer user_data) |
577 | { | 908 | { |
578 | struct GNUNET_GTK_NAMESTORE_PluginEnvironment *edc = user_data; | 909 | struct GNUNET_GTK_NAMESTORE_PluginEnvironment *edc = user_data; |
910 | struct ImportContext *ic; | ||
911 | const gchar *name; | ||
912 | GtkWidget *entry; | ||
579 | 913 | ||
580 | GNUNET_break (0); // FIXME: import not implemented | 914 | entry = GTK_WIDGET (gtk_builder_get_object (edc->builder, |
915 | "edit_dialog_tlsa_import_entry")); | ||
916 | name = gtk_editable_get_chars (GTK_EDITABLE (entry), | ||
917 | 0, -1); | ||
918 | if ( (NULL == name) || | ||
919 | (0 == strlen (name)) || | ||
920 | (GNUNET_OK != GNUNET_DNSPARSER_check_name (name)) ) | ||
921 | { | ||
922 | /* import button should not have been sensitive */ | ||
923 | GNUNET_break (0); | ||
924 | return; | ||
925 | } | ||
926 | ic = GNUNET_new (struct ImportContext); | ||
927 | ic->builder = edc->builder; | ||
928 | gnutls_init (&ic->session, GNUTLS_CLIENT); | ||
929 | gnutls_session_set_ptr (ic->session, ic); | ||
930 | gnutls_server_name_set (ic->session, | ||
931 | GNUTLS_NAME_DNS, | ||
932 | name, | ||
933 | strlen (name)); | ||
934 | gnutls_set_default_priority (ic->session); | ||
935 | ic->rh = GNUNET_RESOLVER_ip_get (name, | ||
936 | AF_UNSPEC, | ||
937 | GNUNET_TIME_UNIT_SECONDS, | ||
938 | &import_address_cb, | ||
939 | ic); | ||
581 | } | 940 | } |
582 | 941 | ||
583 | 942 | ||
584 | /** | 943 | /** |
585 | * The user has edited the hostname used for the import button. | 944 | * The user has edited the hostname used for the import button. |
586 | * Update the import button's sensitivity. | 945 | * Update the import button's sensitivity. |
946 | * | ||
947 | * @param entry edited entry | ||
948 | * @param user_data our plugin environment | ||
587 | */ | 949 | */ |
588 | static void | 950 | static void |
589 | edit_dialog_tlsa_import_entry_changed_cb (GtkEditable *entry, | 951 | edit_dialog_tlsa_import_entry_changed_cb (GtkEditable *entry, |
590 | gpointer user_data) | 952 | gpointer user_data) |
591 | { | 953 | { |
592 | struct GNUNET_GTK_NAMESTORE_PluginEnvironment *edc = user_data; | 954 | struct GNUNET_GTK_NAMESTORE_PluginEnvironment *edc = user_data; |
593 | GtkWidget *button; | ||
594 | const gchar *preedit; | 955 | const gchar *preedit; |
595 | gboolean sens; | 956 | gboolean sens; |
957 | GtkWidget *button; | ||
596 | 958 | ||
597 | button = GTK_WIDGET (gtk_builder_get_object (edc->builder, | 959 | button = GTK_WIDGET (gtk_builder_get_object (edc->builder, |
598 | "edit_dialog_tlsa_entry")); | 960 | "edit_dialog_tlsa_import_button")); |
599 | preedit = gtk_editable_get_chars (entry, 0, -1); | 961 | preedit = gtk_editable_get_chars (GTK_EDITABLE (entry), |
962 | 0, -1); | ||
600 | if ( (NULL == preedit) || | 963 | if ( (NULL == preedit) || |
601 | (0 == strlen (preedit)) || | 964 | (0 == strlen (preedit)) || |
602 | (GNUNET_OK != GNUNET_DNSPARSER_check_name (preedit)) ) | 965 | (GNUNET_OK != GNUNET_DNSPARSER_check_name (preedit)) ) |