/* * This file is part of GNUnet * Copyright (C) 2009-2014 GNUnet e.V. * * GNUnet 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, or (at your * option) any later version. * * GNUnet 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 General Public License * along with GNUnet; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * @file namestore/plugin_gtk_namestore_tlsa.c * @brief namestore plugin for editing TLSA records * @author Christian Grothoff * * Please note that the code of this plugin (and its XML) is * included in the BOX plugin (and Box XML) as well and thus * particular care needs to be taken when changes are made * to make sure names are consistent across the plugins. */ #include "gnunet_gtk.h" #include "gnunet_gtk_namestore_plugin.h" #include #include #include #include /** * The user has edited the target value. Enable/disable 'save' * button depending on the validity of the value. * * @param textbuffer changed text buffer * @param user_data the plugin environment */ static void tlsa_value_textbuffer_changed_cb (GtkTextBuffer *textbuffer, gpointer user_data) { struct GNUNET_GTK_NAMESTORE_PluginEnvironment *edc = user_data; edc->check_validity (edc); } /** * The user has changed the selector. Enable/disable 'save' * button depending on the validity of the value. * * @param togglebutton button that changed editing widget * @param user_data the plugin environment */ static void edit_dialog_tlsa_selector_radiobutton_toggled_cb (GtkToggleButton *togglebutton, gpointer user_data) { struct GNUNET_GTK_NAMESTORE_PluginEnvironment *edc = user_data; edc->check_validity (edc); } /** * The user has changed the usage. Enable/disable 'save' * button depending on the validity of the value. * * @param togglebutton button that changed editing widget * @param user_data the plugin environment */ static void edit_dialog_tlsa_usage_radiobutton_toggled_cb (GtkToggleButton *togglebutton, gpointer user_data) { struct GNUNET_GTK_NAMESTORE_PluginEnvironment *edc = user_data; edc->check_validity (edc); } /** * The user has changed the matching type. Enable/disable 'save' * button depending on the validity of the value. * * @param togglebutton button that changed editing widget * @param user_data the plugin environment */ static void edit_dialog_tlsa_matching_type_radiobutton_toggled_cb (GtkToggleButton *togglebutton, gpointer user_data) { struct GNUNET_GTK_NAMESTORE_PluginEnvironment *edc = user_data; edc->check_validity (edc); } #ifndef EDP_CBC_DEF #define EDP_CBC_DEF /** * The user has changed the protocol selection. Enable/disable 'save' * button depending on the validity of the value. * * @param entry editing widget * @param user_data the plugin environment */ static void edit_dialog_protocol_combobox_changed_cb (GtkEditable *entry, gpointer user_data) { struct GNUNET_GTK_NAMESTORE_PluginEnvironment *edc = user_data; edc->check_validity (edc); } #endif /** * Return the selected button from a list of radio buttons. * * @param builder builder to resolve button names * @param button_names NULL-terminated array of buttons * @return index of the selected button, -1 for none */ static int get_selected_radio_value (GtkBuilder *builder, const char *const*button_names) { GtkToggleButton *b; for (int i=0; NULL != button_names[i]; i++) { b = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, button_names[i])); if (gtk_toggle_button_get_active (b)) return i; } return -1; } /** * NULL-terminated array with the names of the "usage" buttons, * in order to match the respective value in TLSA. */ static const char *const usage_buttons[] = { "edit_dialog_tlsa_usage_ca_radiobutton", "edit_dialog_tlsa_usage_service_cert_radiobutton", "edit_dialog_tlsa_usage_trust_anchor_radiobutton", "edit_dialog_tlsa_usage_domain_issued_cert_radiobutton", NULL }; /** * NULL-terminated array with the names of the "selector" buttons, * in order to match the respective value in TLSA. */ static const char *const selector_buttons[] = { "edit_dialog_tlsa_selector_full_cert_radiobutton", "edit_dialog_tlsa_selector_subject_public_key_radiobutton", NULL }; /** * NULL-terminated array with the names of the "matching type" buttons, * in order to match the respective value in TLSA. */ static const char *const matching_type_buttons[] = { "edit_dialog_tlsa_matching_type_full_contents_radiobutton", "edit_dialog_tlsa_matching_type_sha256_radiobutton", "edit_dialog_tlsa_matching_type_sha512_radiobutton", NULL }; /** * Function that will be called to initialize the builder's * widgets from the existing record (if there is one). * The `n_value` is the existing value of the record as a string. * * @param cls the `struct GNUNET_GTK_NAMESTORE_PluginEnvironment *` * @param n_value the record as a string * @param builder the edit dialog's builder */ static void tlsa_load (void *cls, gchar *n_value, GtkBuilder *builder) { unsigned int protocol; GtkComboBox *cb; GtkTreeIter iter; GtkTreeModel *tm; unsigned int service; guint protocol_at_iter; unsigned int record_type; unsigned int usage; unsigned int selector; unsigned int matching_type; GtkTextBuffer *tb; size_t slen = strlen (n_value) + 1; char cert_data[slen]; if (7 != SSCANF (n_value, "%u %u %u %u %u %u %s", &protocol, &service, &record_type, &usage, &selector, &matching_type, cert_data)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Unable to parse (boxed) TLSA record `%s'\n"), n_value); return; } if (GNUNET_DNSPARSER_TYPE_TLSA != record_type) { GNUNET_break (0); return; } gtk_spin_button_set_value (GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "edit_dialog_port_spinbutton")), service); cb = GTK_COMBO_BOX (gtk_builder_get_object (builder, "edit_dialog_protocol_combobox")); tm = GTK_TREE_MODEL (gtk_builder_get_object (builder, "edit_dialog_protocol_liststore")); if (gtk_tree_model_get_iter_first (tm, &iter)) { do { gtk_tree_model_get (tm, &iter, 1, &protocol_at_iter, -1); if (protocol_at_iter == protocol) { gtk_combo_box_set_active_iter (cb, &iter); break; } } while (gtk_tree_model_iter_next (tm, &iter)); } switch (usage) { case 0: /* CA cert */ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "edit_dialog_tlsa_usage_ca_radiobutton")), TRUE); break; case 1: /* Entity cert */ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "edit_dialog_tlsa_usage_service_cert_radiobutton")), TRUE); break; case 2: /* Trust anchor */ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "edit_dialog_tlsa_usage_trust_anchor_radiobutton")), TRUE); break; case 3: /* Domain-issued cert */ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "edit_dialog_tlsa_usage_domain_issued_cert_radiobutton")), TRUE); break; default: GNUNET_break_op (0); break; } switch (selector) { case 0: /* full cert, binary */ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "edit_dialog_tlsa_selector_full_cert_radiobutton")), TRUE); break; case 1: /* full cert, DER */ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "edit_dialog_tlsa_selector_subject_public_key_radiobutton")), TRUE); break; } switch (matching_type) { case 0: /* exact match */ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "edit_dialog_tlsa_matching_type_full_contents_radiobutton")), TRUE); break; case 1: /* SHA-256 hash */ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "edit_dialog_tlsa_matching_type_sha256_radiobutton")), TRUE); break; case 2: /* SHA-512 hash */ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "edit_dialog_tlsa_matching_type_sha512_radiobutton")), TRUE); break; } tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gtk_builder_get_object (builder, "edit_dialog_tlsa_value_textview"))); gtk_text_buffer_set_text (tb, cert_data, -1); } /** * Function that will be called to retrieve the final value of the * record (in string format) once the dialog is being closed. * * @param cls the `struct GNUNET_GTK_NAMESTORE_PluginEnvironment *` * @param builder the edit dialog's builder * @return record value as a string, as specified in the dialog */ static gchar * tlsa_store (void *cls, GtkBuilder *builder) { unsigned int protocol; GtkComboBox *cb; GtkTreeIter iter; guint service; unsigned int usage; unsigned int selector; unsigned int matching_type; GtkTextBuffer *tb; gchar *value; char *result; GtkTreeModel *tm; GtkTextIter ti_start; GtkTextIter ti_end; service = gtk_spin_button_get_value (GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "edit_dialog_port_spinbutton"))); cb = GTK_COMBO_BOX (gtk_builder_get_object (builder, "edit_dialog_protocol_combobox")); if (! gtk_combo_box_get_active_iter (cb, &iter)) { GNUNET_break (0); return NULL; } tm = GTK_TREE_MODEL (gtk_builder_get_object (builder, "edit_dialog_protocol_liststore")); gtk_tree_model_get (tm, &iter, 1, &protocol, -1); usage = get_selected_radio_value (builder, usage_buttons); selector = get_selected_radio_value (builder, selector_buttons); matching_type = get_selected_radio_value (builder, matching_type_buttons); if ( (-1 == usage) || (-1 == selector) || (-1 == matching_type) ) { GNUNET_break (0); return NULL; } tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gtk_builder_get_object (builder, "edit_dialog_tlsa_value_textview"))); gtk_text_buffer_get_iter_at_offset (tb, &ti_start, 0); gtk_text_buffer_get_iter_at_offset (tb, &ti_end, -1); value = gtk_text_buffer_get_text (tb, &ti_start, &ti_end, FALSE); GNUNET_asprintf (&result, "%u %u %u %u %u %u %s", protocol, (unsigned int) service, GNUNET_DNSPARSER_TYPE_TLSA, usage, selector, matching_type, value); g_free (value); return result; } /** * Function to call to validate the state of the dialog. Should * return #GNUNET_OK if the information in the dialog is valid, and * #GNUNET_SYSERR if some fields contain invalid values. The * function should highlight fields with invalid inputs for the * user. * * @param cls the `struct GNUNET_GTK_NAMESTORE_PluginEnvironment *` * @param builder the edit dialog's builder * @return #GNUNET_OK if there is a valid record value in the dialog */ static int tlsa_validate (void *cls, GtkBuilder *builder) { GtkComboBox *cb; GtkTreeIter iter; gchar *value; GtkTextBuffer *tb; GtkTextIter ti_start; GtkTextIter ti_end; gnutls_datum_t datum; gnutls_x509_crt_t cert; gnutls_pubkey_t pk; int ret; unsigned int matching_type; unsigned int selector; int err; cb = GTK_COMBO_BOX (gtk_builder_get_object (builder, "edit_dialog_protocol_combobox")); if (! gtk_combo_box_get_active_iter (cb, &iter)) { return GNUNET_SYSERR; } tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gtk_builder_get_object (builder, "edit_dialog_tlsa_value_textview"))); gtk_text_buffer_get_iter_at_offset (tb, &ti_start, 0); gtk_text_buffer_get_iter_at_offset (tb, &ti_end, -1); value = gtk_text_buffer_get_text (tb, &ti_start, &ti_end, FALSE); if (0 == strlen (value)) return GNUNET_SYSERR; { size_t slen = strlen (value); uint8_t bin[slen / 2]; if (slen / 2 != GNUNET_DNSPARSER_hex_to_bin (value, bin)) { /* not hex */ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Certificate value is not in hex...\n")); return GNUNET_SYSERR; } matching_type = get_selected_radio_value (builder, matching_type_buttons); selector = get_selected_radio_value (builder, selector_buttons); switch (matching_type) { case 0: /* exact match */ datum.size = sizeof (bin); datum.data = bin; switch (selector) { case 0: /* full Cert */ if (GNUTLS_E_SUCCESS != (err = gnutls_x509_crt_init (&cert))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to initialize CERT: %s\n"), gnutls_strerror_name (err)); ret = GNUNET_SYSERR; break; } if (GNUTLS_E_SUCCESS != (err = gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_DER))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to parse CERT: %s\n"), gnutls_strerror_name (err)); ret = GNUNET_SYSERR; } else ret = GNUNET_OK; gnutls_x509_crt_deinit (cert); break; case 1: /* subject public key only */ if (GNUTLS_E_SUCCESS != (err = gnutls_pubkey_init (&pk))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to initialize PK: %s\n"), gnutls_strerror_name (err)); ret = GNUNET_SYSERR; break; } if (GNUTLS_E_SUCCESS != (err = gnutls_pubkey_import (pk, &datum, GNUTLS_X509_FMT_DER))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to parse PK: %s\n"), gnutls_strerror_name (err)); ret = GNUNET_SYSERR; } else ret = GNUNET_OK; gnutls_pubkey_deinit (pk); break; default: GNUNET_break (0); ret = GNUNET_SYSERR; break; } break; case 1: /* SHA-256 hash */ ret = (256 / 8 == slen / 2) ? GNUNET_OK : GNUNET_SYSERR; break; case 2: /* SHA-512 hash */ ret = (512 / 8 == slen / 2) ? GNUNET_OK : GNUNET_SYSERR; break; default: GNUNET_break (0); ret = GNUNET_SYSERR; break; } } return ret; } /** * Context for TLS certificate import from network. */ struct ImportContext { /** * Network handle for the session. */ struct GNUNET_NETWORK_Handle *sock; /** * DNS resolution request to resolve the domain name. */ struct GNUNET_RESOLVER_RequestHandle *rh; /** * Builder for accessing widgets. */ GtkBuilder *builder; /** * Domain name of the site we use to get the TLS cert record from. */ char *name; /** * We succeeded with our TLS handshake, ignore further DNS replies. */ int done; }; /** * We have successfully established a TLS session to * import a certificate from the server. Import the * X509 certificate into the GUI. * * @param session TLS session to import from * @param ic the import context */ static void import_x509_certificate (gnutls_session_t session, struct ImportContext *ic) { GtkBuilder *builder = ic->builder; const gnutls_datum_t *cert_list; unsigned int cert_list_size = 0; gnutls_x509_crt_t cert; unsigned int usage; unsigned int matching_type; unsigned int selector; gnutls_pubkey_t pk; char buf[4092]; size_t bsize; char *hex; gnutls_datum_t datum; uint8_t sha256[256/8]; uint8_t sha512[512/8]; size_t ssize; GtkTextBuffer *tb; unsigned int i; cert_list = gnutls_certificate_get_peers (session, &cert_list_size); if (0 == cert_list_size) { /* is it possible to succeed with TLS handshake and have NO certificates!? If so, how do we get the public key?*/ GNUNET_break (0); return; } usage = get_selected_radio_value (builder, usage_buttons); /* Find out which certificate we care about based on usage */ for (i=0;idone) return; if (NULL == addr) { if (GNUNET_YES != ic->done) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Name resolution for `%s' failed\n"), ic->name); GNUNET_free (ic->name); GNUNET_free (ic); return; } port = gtk_spin_button_get_value (GTK_SPIN_BUTTON (gtk_builder_get_object (ic->builder, "edit_dialog_port_spinbutton"))); switch (addr->sa_family) { case AF_INET: pf = PF_INET; memcpy (&v4, addr, addrlen); v4.sin_port = htons ((uint16_t) port); a = (struct sockaddr *) &v4; break; case AF_INET6: pf = PF_INET6; memcpy (&v6, addr, addrlen); v6.sin6_port = htons ((uint16_t) port); a = (struct sockaddr *) &v6; break; default: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Unsupported address family %d\n"), addr->sa_family); return; } ic->sock = GNUNET_NETWORK_socket_create (pf, SOCK_STREAM, 0); if (NULL == ic->sock) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket"); return; } GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_set_blocking (ic->sock, GNUNET_YES)); if ( (GNUNET_OK != GNUNET_NETWORK_socket_connect (ic->sock, a, addrlen)) && (EINPROGRESS != errno) ) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to connect to target address `%s': %s\n"), GNUNET_a2s (addr, addrlen), STRERROR (errno)); GNUNET_NETWORK_socket_close (ic->sock); return; } /* initialize TLS session */ gnutls_init (&session, GNUTLS_CLIENT); gnutls_session_set_ptr (session, ic); gnutls_server_name_set (session, GNUTLS_NAME_DNS, ic->name, strlen (ic->name)); gnutls_set_default_priority (session); /* Use default priorities */ gnutls_certificate_allocate_credentials (&xcred); if (GNUTLS_E_SUCCESS != (ret = gnutls_priority_set_direct (session, "PERFORMANCE", NULL))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to initialize cipher suite: %s\n"), gnutls_strerror (ret)); goto cleanup; } /* put the x509 credentials to the current session */ gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); gnutls_transport_set_int (session, GNUNET_NETWORK_get_fd (ic->sock)); gnutls_handshake_set_timeout (session, 2000 /* 2s */); /* TODO: do this in event loop, with insensitive GUI, with possibly higher timeout ... */ /* Perform the TLS handshake */ do { ret = gnutls_handshake (session); } while ( (ret < 0) && (0 == gnutls_error_is_fatal (ret)) ); /* finally, access the certificate */ if (GNUTLS_E_SUCCESS == ret) { type = gnutls_certificate_type_get (session); switch (type) { case GNUTLS_CRT_UNKNOWN: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Server certificate type not supported\n")); break; case GNUTLS_CRT_X509: import_x509_certificate (session, ic); break; case GNUTLS_CRT_OPENPGP: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Server certificate type not supported\n")); break; case GNUTLS_CRT_RAWPK: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Server certificate type not supported\n")); break; } } else { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("TLS handshake failed: %s\n"), gnutls_strerror (ret)); } gnutls_bye (session, GNUTLS_SHUT_RDWR); ic->done = GNUNET_YES; cleanup: GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ic->sock)); gnutls_deinit (session); gnutls_certificate_free_credentials (xcred); } /** * The user clicked the "import" button. Try to import * certificate from the server. * * @param button the 'import' button * @param user_data the plugin environment */ static void tlsa_import_button_clicked_cb (GtkButton *button, gpointer user_data) { struct GNUNET_GTK_NAMESTORE_PluginEnvironment *edc = user_data; struct ImportContext *ic; const gchar *name; GtkWidget *entry; entry = GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_tlsa_import_entry")); name = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1); if ( (NULL == name) || (0 == strlen (name)) || (GNUNET_OK != GNUNET_DNSPARSER_check_name (name)) ) { /* import button should not have been sensitive */ GNUNET_break (0); return; } ic = GNUNET_new (struct ImportContext); ic->builder = edc->builder; ic->name = GNUNET_strdup (name); ic->rh = GNUNET_RESOLVER_ip_get (name, AF_UNSPEC, GNUNET_TIME_UNIT_SECONDS, &import_address_cb, ic); } /** * The user has edited the hostname used for the import button. * Update the import button's sensitivity. * * @param entry edited entry * @param user_data our plugin environment */ static void edit_dialog_tlsa_import_entry_changed_cb (GtkEditable *entry, gpointer user_data) { struct GNUNET_GTK_NAMESTORE_PluginEnvironment *edc = user_data; const gchar *preedit; gboolean sens; GtkWidget *button; button = GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_tlsa_import_button")); preedit = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1); if ( (NULL == preedit) || (0 == strlen (preedit)) || (GNUNET_OK != GNUNET_DNSPARSER_check_name (preedit)) ) sens = FALSE; else sens = TRUE; gtk_widget_set_sensitive (button, sens); } /** * Entry point for the plugin. * * @param cls the `struct GNUNET_GTK_NAMESTORE_PluginEnvironment` * @return NULL on error, otherwise the plugin context */ void * libgnunet_plugin_gtk_namestore_tlsa_init (void *cls) { struct GNUNET_GTK_NAMESTORE_PluginEnvironment *env = cls; struct GNUNET_GTK_NAMESTORE_PluginFunctions *plugin; static struct GNUNET_GTK_NAMESTORE_Symbol symbols[] = { { "tlsa_value_textbuffer_changed_cb", G_CALLBACK (tlsa_value_textbuffer_changed_cb) }, { "edit_dialog_tlsa_selector_radiobutton_toggled_cb", G_CALLBACK (edit_dialog_tlsa_selector_radiobutton_toggled_cb) }, { "edit_dialog_tlsa_usage_radiobutton_toggled_cb", G_CALLBACK (edit_dialog_tlsa_usage_radiobutton_toggled_cb) }, { "edit_dialog_tlsa_matching_type_radiobutton_toggled_cb", G_CALLBACK (edit_dialog_tlsa_matching_type_radiobutton_toggled_cb) }, { "tlsa_import_button_clicked_cb", G_CALLBACK (tlsa_import_button_clicked_cb) }, { "edit_dialog_tlsa_import_entry_changed_cb", G_CALLBACK (edit_dialog_tlsa_import_entry_changed_cb) }, /* generic CBs */ { "edit_dialog_protocol_combobox_changed_cb", G_CALLBACK (edit_dialog_protocol_combobox_changed_cb) }, { NULL, NULL } }; gnutls_global_init (); plugin = GNUNET_new (struct GNUNET_GTK_NAMESTORE_PluginFunctions); plugin->cls = env; plugin->dialog_glade_filename = "gnunet_namestore_edit_tlsa.glade"; plugin->dialog_widget_name = "edit_tlsa_dialog"; plugin->symbols = symbols; plugin->load = &tlsa_load; plugin->store = &tlsa_store; plugin->validate = &tlsa_validate; /* we will not produce a 'native' TLSA record, but one in a BOX */ plugin->record_type = GNUNET_GNSRECORD_TYPE_BOX; return plugin; } /** * Exit point from the plugin. * * @param cls the plugin context (as returned by "init") * @return always NULL */ void * libgnunet_plugin_gtk_namestore_tlsa_done (void *cls) { struct GNUNET_GTK_NAMESTORE_PluginFunctions *plugin = cls; GNUNET_free (plugin); gnutls_global_deinit (); return NULL; } /* end of plugin_gtk_namestore_tlsa.c */