static gboolean infinoted_startup_load_credentials(InfinotedStartup* startup, GError** error) { gnutls_x509_crt_t* certs; guint n_certs; gnutls_certificate_credentials_t creds; int res; if(startup->options->security_policy != INF_XMPP_CONNECTION_SECURITY_ONLY_UNSECURED) { startup->private_key = infinoted_startup_load_key( startup->log, startup->options->create_key, startup->options->key_file, error ); if(startup->private_key == NULL) return FALSE; certs = infinoted_startup_load_certificate( startup->log, startup->options->create_certificate, startup->private_key, startup->options->certificate_file, startup->options->certificate_chain_file, &n_certs, error ); if(certs == NULL) return FALSE; /* Takes ownership of certificates: */ startup->certificates = inf_certificate_chain_new(certs, n_certs); startup->credentials = inf_certificate_credentials_new(); creds = inf_certificate_credentials_get(startup->credentials); res = gnutls_certificate_set_x509_key( creds, inf_certificate_chain_get_raw(startup->certificates), inf_certificate_chain_get_n_certificates(startup->certificates), startup->private_key ); if(res != 0) { inf_gnutls_set_error(error, res); return FALSE; } } return TRUE; }
static void infinoted_plugin_certificate_auth_certificate_func(InfXmppConnection* xmpp, gnutls_session_t session, InfCertificateChain* chain, gpointer user_data) { InfinotedPluginCertificateAuth* plugin; int res; int verify_result; GError* error; plugin = (InfinotedPluginCertificateAuth*)user_data; if(chain != NULL) { /* Note that we don't require client certificates to be signed by a CA. * We only require them to be signed by one of the certificates in our * list, but we don't care whether that's a CA or not. A common use case * is to sign client certificates with our own server certificate. */ res = gnutls_x509_crt_list_verify( inf_certificate_chain_get_raw(chain), inf_certificate_chain_get_n_certificates(chain), plugin->cas, plugin->n_cas, NULL, 0, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT | GNUTLS_VERIFY_DISABLE_CA_SIGN, &verify_result ); error = NULL; if(res != GNUTLS_E_SUCCESS) inf_gnutls_set_error(&error, res); else if( (verify_result & GNUTLS_CERT_INVALID) != 0) inf_gnutls_certificate_verification_set_error(&error, res); if(error != NULL) { inf_xmpp_connection_certificate_verify_cancel(xmpp, error); g_error_free(error); } else { inf_xmpp_connection_certificate_verify_continue(xmpp); } } else { /* If we do not accept unauthenticated clients, then GnuTLS should have * blocked the connection already, since we set the certificate request * to GNUTLS_CERT_REQUIRE in that case. */ g_assert(plugin->accept_unauthenticated_clients == TRUE); inf_xmpp_connection_certificate_verify_continue(xmpp); } }
void Gobby::CertificateManager::make_credentials() { InfCertificateCredentials* creds = inf_certificate_credentials_new(); gnutls_certificate_credentials_t gnutls_creds = inf_certificate_credentials_get(creds); if(m_preferences.security.authentication_enabled && m_key != NULL && m_certificates != NULL) { gnutls_certificate_set_x509_key( gnutls_creds, inf_certificate_chain_get_raw(m_certificates), inf_certificate_chain_get_n_certificates(m_certificates), m_key ); } if(m_preferences.security.use_system_trust) { const int n_cas = gnutls_certificate_set_x509_system_trust( gnutls_creds); if(n_cas < 0) { g_warning("Failed to add system CAs: %s\n", gnutls_strerror(n_cas)); } } if(!m_trust.empty()) { gnutls_certificate_set_x509_trust( gnutls_creds, &m_trust[0], m_trust.size() ); } if(m_dh_params != NULL) gnutls_certificate_set_dh_params(gnutls_creds, m_dh_params); gnutls_certificate_set_verify_flags( gnutls_creds, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); InfCertificateCredentials* old_creds = m_credentials; m_credentials = creds; m_signal_credentials_changed.emit(); if(old_creds != NULL) inf_certificate_credentials_unref(old_creds); }
static void inf_gtk_certificate_manager_certificate_func(InfXmppConnection* connection, gnutls_session_t session, InfCertificateChain* chain, gpointer user_data) { InfGtkCertificateManager* manager; InfGtkCertificateManagerPrivate* priv; InfGtkCertificateDialogFlags flags; gnutls_x509_crt_t presented_cert; gnutls_x509_crt_t known_cert; gchar* hostname; gboolean match_hostname; gboolean issuer_known; gnutls_x509_crt_t root_cert; int ret; unsigned int verify; GHashTable* table; gboolean cert_equal; time_t expiration_time; InfGtkCertificateManagerQuery* query; gchar* text; GtkWidget* vbox; GtkWidget* button; GtkWidget* image; GtkWidget* label; GError* error; manager = INF_GTK_CERTIFICATE_MANAGER(user_data); priv = INF_GTK_CERTIFICATE_MANAGER_PRIVATE(manager); g_object_get(G_OBJECT(connection), "remote-hostname", &hostname, NULL); presented_cert = inf_certificate_chain_get_own_certificate(chain); match_hostname = gnutls_x509_crt_check_hostname(presented_cert, hostname); /* First, validate the certificate */ ret = gnutls_certificate_verify_peers2(session, &verify); error = NULL; if(ret != GNUTLS_E_SUCCESS) inf_gnutls_set_error(&error, ret); /* Remove the GNUTLS_CERT_ISSUER_NOT_KNOWN flag from the verification * result, and if the certificate is still invalid, then set an error. */ if(error == NULL) { issuer_known = TRUE; if(verify & GNUTLS_CERT_SIGNER_NOT_FOUND) { issuer_known = FALSE; /* Re-validate the certificate for other failure reasons -- * unfortunately the gnutls_certificate_verify_peers2() call * does not tell us whether the certificate is otherwise invalid * if a signer is not found already. */ /* TODO: Here it would be good to use the verify flags from the * certificate credentials, but GnuTLS does not have API to * retrieve them. */ root_cert = inf_certificate_chain_get_root_certificate(chain); ret = gnutls_x509_crt_list_verify( inf_certificate_chain_get_raw(chain), inf_certificate_chain_get_n_certificates(chain), &root_cert, 1, NULL, 0, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, &verify ); if(ret != GNUTLS_E_SUCCESS) inf_gnutls_set_error(&error, ret); else if(verify & GNUTLS_CERT_INVALID) inf_gnutls_certificate_verification_set_error(&error, verify); } } /* Look up the host in our database of pinned certificates if we could not * fully verify the certificate, i.e. if either the issuer is not known or * the hostname of the connection does not match the certificate. */ table = NULL; if(error == NULL) { known_cert = NULL; if(!match_hostname || !issuer_known) { /* If we cannot load the known host file, then cancel the connection. * Otherwise it might happen that someone shows us a certificate that we * tell the user we don't know, if though actually for that host we expect * a different certificate. */ table = inf_gtk_certificate_manager_ref_known_hosts(manager, &error); if(table != NULL) known_cert = g_hash_table_lookup(table, hostname); } } /* Next, configure the flags for the dialog to be shown based on the * verification result, and on whether the pinned certificate matches * the one presented by the host or not. */ flags = 0; if(error == NULL) { if(known_cert != NULL) { cert_equal = inf_gtk_certificate_manager_compare_fingerprint( known_cert, presented_cert, &error ); if(error == NULL && cert_equal == FALSE) { if(!match_hostname) flags |= INF_GTK_CERTIFICATE_DIALOG_CERT_HOSTNAME_MISMATCH; if(!issuer_known) flags |= INF_GTK_CERTIFICATE_DIALOG_CERT_ISSUER_NOT_KNOWN; flags |= INF_GTK_CERTIFICATE_DIALOG_CERT_UNEXPECTED; expiration_time = gnutls_x509_crt_get_expiration_time(known_cert); if(expiration_time != (time_t)(-1)) { expiration_time -= INF_GTK_CERTIFICATE_MANAGER_EXPIRATION_TOLERANCE; if(time(NULL) > expiration_time) { flags |= INF_GTK_CERTIFICATE_DIALOG_CERT_OLD_EXPIRED; } } } } else { if(!match_hostname) flags |= INF_GTK_CERTIFICATE_DIALOG_CERT_HOSTNAME_MISMATCH; if(!issuer_known) flags |= INF_GTK_CERTIFICATE_DIALOG_CERT_ISSUER_NOT_KNOWN; } } /* Now proceed either by accepting the connection, rejecting it, or * bothering the user with an annoying dialog. */ if(error == NULL) { if(flags == 0) { if(match_hostname && issuer_known) { /* Remove the pinned entry if we now have a valid certificate for * this host. */ if(table != NULL && g_hash_table_remove(table, hostname) == TRUE) { inf_gtk_certificate_manager_write_known_hosts_with_warning( manager, table ); } } inf_xmpp_connection_certificate_verify_continue(connection); } else { query = g_slice_new(InfGtkCertificateManagerQuery); query->manager = manager; query->known_hosts = table; query->connection = connection; query->dialog = inf_gtk_certificate_dialog_new( priv->parent_window, 0, flags, hostname, chain ); query->certificate_chain = chain; table = NULL; g_object_ref(query->connection); inf_certificate_chain_ref(chain); g_signal_connect( G_OBJECT(connection), "notify::status", G_CALLBACK(inf_gtk_certificate_manager_notify_status_cb), query ); g_signal_connect( G_OBJECT(query->dialog), "response", G_CALLBACK(inf_gtk_certificate_manager_response_cb), query ); image = gtk_image_new_from_stock(GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON); gtk_widget_show(image); button = gtk_dialog_add_button( GTK_DIALOG(query->dialog), _("_Cancel connection"), GTK_RESPONSE_REJECT ); gtk_button_set_image(GTK_BUTTON(button), image); image = gtk_image_new_from_stock(GTK_STOCK_CONNECT, GTK_ICON_SIZE_BUTTON); gtk_widget_show(image); button = gtk_dialog_add_button( GTK_DIALOG(query->dialog), _("C_ontinue connection"), GTK_RESPONSE_ACCEPT ); gtk_button_set_image(GTK_BUTTON(button), image); text = g_strdup_printf( _("Do you want to continue the connection to host \"%s\"? If you " "choose to continue, this certificate will be trusted in the " "future when connecting to this host."), hostname ); label = gtk_label_new(text); gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); gtk_label_set_line_wrap_mode(GTK_LABEL(label), PANGO_WRAP_WORD_CHAR); gtk_label_set_width_chars(GTK_LABEL(label), 60); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); gtk_widget_show(label); g_free(text); vbox = gtk_dialog_get_content_area(GTK_DIALOG(query->dialog)); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); priv->queries = g_slist_prepend(priv->queries, query); gtk_window_present(GTK_WINDOW(query->dialog)); } } else { inf_xmpp_connection_certificate_verify_cancel(connection, error); g_error_free(error); } if(table != NULL) g_hash_table_unref(table); g_free(hostname); }
void Gobby::BrowserContextCommands::on_account_created( gnutls_x509_privkey_t key, InfCertificateChain* certificate, const InfAclAccount* account) { InfBrowser* browser; g_object_get(G_OBJECT(m_dialog->gobj()), "browser", &browser, NULL); const InfAclAccount* own_account = inf_browser_get_acl_local_account(browser); const InfAclAccountId default_id = inf_acl_account_id_from_string("default"); const bool is_default_account = (own_account->id == default_id); g_object_unref(browser); gnutls_x509_crt_t cert = inf_certificate_chain_get_own_certificate(certificate); gchar* name = inf_cert_util_get_dn_by_oid( cert, GNUTLS_OID_X520_COMMON_NAME, 0); const std::string cn = name; g_free(name); std::string host; if(INFC_IS_BROWSER(browser)) { InfXmlConnection* xml = infc_browser_get_connection(INFC_BROWSER(browser)); if(INF_IS_XMPP_CONNECTION(xml)) { gchar* hostname; g_object_get(G_OBJECT(xml), "remote-hostname", &hostname, NULL); host = hostname; g_free(hostname); } } if(host.empty()) host = "local"; gnutls_x509_crt_t* certs = inf_certificate_chain_get_raw(certificate); const unsigned int n_certs = inf_certificate_chain_get_n_certificates(certificate); std::auto_ptr<Gtk::MessageDialog> dlg; try { const std::string filename = make_certificate_filename(cn, host); GError* error = NULL; inf_cert_util_write_certificate_with_key( key, certs, n_certs, filename.c_str(), &error); if(error != NULL) { const std::string message = error->message; g_error_free(error); throw std::runtime_error(message); } if(is_default_account && (m_cert_manager.get_private_key() == NULL || m_cert_manager.get_certificates() == NULL)) { m_preferences.security.key_file = filename; m_preferences.security.certificate_file = filename; dlg.reset(new Gtk::MessageDialog( m_parent, _("Account successfully created"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE)); dlg->set_secondary_text( _("When re-connecting to the server, the " "new account will be used.")); } else { // TODO: Gobby should support multiple certificates dlg.reset(new Gtk::MessageDialog( m_parent, _("Account successfully created"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE)); dlg->set_secondary_text(Glib::ustring::compose( _("The certificate has been stored at %1.\n\n" "To login to this account, set the " "certificate in Gobby's preferences " "and re-connect to the server."), filename)); } } catch(const std::exception& ex) { // This is actually a bit unfortunate, because the // account was actually created and we have a valid // certificate for it, but we cannot keep it... dlg.reset(new Gtk::MessageDialog( m_parent, _("Failed to create account"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE)); dlg->set_secondary_text(Glib::ustring::compose( _("Could not save the certificate for the " "account: %1"), ex.what())); } m_dialog = dlg; m_dialog->signal_response().connect( sigc::mem_fun( *this, &BrowserContextCommands:: on_account_created_response)); m_dialog->present(); }