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); } }
static gboolean inf_gtk_certificate_manager_compare_fingerprint(gnutls_x509_crt_t cert1, gnutls_x509_crt_t cert2, GError** error) { static const unsigned int SHA256_DIGEST_SIZE = 32; size_t size; guchar cert1_fingerprint[SHA256_DIGEST_SIZE]; guchar cert2_fingerprint[SHA256_DIGEST_SIZE]; int ret; int cmp; size = SHA256_DIGEST_SIZE; ret = gnutls_x509_crt_get_fingerprint( cert1, GNUTLS_DIG_SHA256, cert1_fingerprint, &size ); if(ret == GNUTLS_E_SUCCESS) { g_assert(size == SHA256_DIGEST_SIZE); ret = gnutls_x509_crt_get_fingerprint( cert2, GNUTLS_DIG_SHA256, cert2_fingerprint, &size ); } if(ret != GNUTLS_E_SUCCESS) { inf_gnutls_set_error(error, ret); return FALSE; } cmp = memcmp(cert1_fingerprint, cert2_fingerprint, SHA256_DIGEST_SIZE); if(cmp != 0) return FALSE; return TRUE; }
static InfdXmppServer* inf_test_certificate_setup_server(InfIo* io, const char* key_file, const char* cert_file, GError** error) { InfdTcpServer* tcp; InfdXmppServer* xmpp; gnutls_x509_privkey_t key; GPtrArray* certs; InfCertificateCredentials* creds; guint i; int res; key = inf_cert_util_read_private_key(key_file, error); if(!key) return NULL; certs = inf_cert_util_read_certificate(cert_file, NULL, error); if(!certs) { gnutls_x509_privkey_deinit(key); return NULL; } creds = inf_certificate_credentials_new(); res = gnutls_certificate_set_x509_key( inf_certificate_credentials_get(creds), (gnutls_x509_crt_t*)certs->pdata, certs->len, key ); gnutls_x509_privkey_deinit(key); for(i = 0; i < certs->len; ++i) gnutls_x509_crt_deinit(certs->pdata[i]); g_ptr_array_free(certs, TRUE); if(res != 0) { inf_certificate_credentials_unref(creds); inf_gnutls_set_error(error, res); return NULL; } tcp = g_object_new( INFD_TYPE_TCP_SERVER, "io", io, "local-port", 6524, NULL ); if(infd_tcp_server_open(tcp, error) == FALSE) { inf_certificate_credentials_unref(creds); return NULL; } xmpp = infd_xmpp_server_new( tcp, INF_XMPP_CONNECTION_SECURITY_ONLY_TLS, creds, NULL, NULL ); /* Keep client connections alive */ g_signal_connect( G_OBJECT(xmpp), "new-connection", G_CALLBACK(inf_test_certificate_validate_new_connection_cb), NULL ); inf_certificate_credentials_unref(creds); return xmpp; }
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); }
static gboolean inf_gtk_certificate_manager_write_known_hosts(InfGtkCertificateManager* mgr, GHashTable* table, GError** error) { InfGtkCertificateManagerPrivate* priv; gchar* dirname; GIOChannel* channel; GIOStatus status; GHashTableIter iter; gpointer key; gpointer value; const gchar* hostname; gnutls_x509_crt_t cert; size_t size; int res; gchar* buffer; gchar* encoded_cert; priv = INF_GTK_CERTIFICATE_MANAGER_PRIVATE(mgr); /* Note that we pin the whole certificate and not only the public key of * our known hosts. This allows us to differentiate two cases when a * host presents a new certificate: * a) The old certificate has expired or is very close to expiration. In * this case we still show a message to the user asking whether they * trust the new certificate. * b) The old certificate was perfectly valid. In this case we show a * message saying that the certificate change was unexpected, and * unless it was expected the host should not be trusted. */ dirname = g_path_get_dirname(priv->known_hosts_file); if(!inf_file_util_create_directory(dirname, 0755, error)) { g_free(dirname); return FALSE; } g_free(dirname); channel = g_io_channel_new_file(priv->known_hosts_file, "w", error); if(channel == NULL) return FALSE; status = g_io_channel_set_encoding(channel, NULL, error); if(status != G_IO_STATUS_NORMAL) { g_io_channel_unref(channel); return FALSE; } g_hash_table_iter_init(&iter, table); while(g_hash_table_iter_next(&iter, &key, &value)) { hostname = (const gchar*)key; cert = (gnutls_x509_crt_t)value; size = 0; res = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, NULL, &size); g_assert(res != GNUTLS_E_SUCCESS); buffer = NULL; if(res == GNUTLS_E_SHORT_MEMORY_BUFFER) { buffer = g_malloc(size); res = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, buffer, &size); } if(res != GNUTLS_E_SUCCESS) { g_free(buffer); g_io_channel_unref(channel); inf_gnutls_set_error(error, res); return FALSE; } encoded_cert = g_base64_encode(buffer, size); g_free(buffer); status = g_io_channel_write_chars(channel, hostname, strlen(hostname), NULL, error); if(status == G_IO_STATUS_NORMAL) status = g_io_channel_write_chars(channel, ":", 1, NULL, error); if(status == G_IO_STATUS_NORMAL) status = g_io_channel_write_chars(channel, encoded_cert, strlen(encoded_cert), NULL, error); if(status == G_IO_STATUS_NORMAL) status = g_io_channel_write_chars(channel, "\n", 1, NULL, error); g_free(encoded_cert); if(status != G_IO_STATUS_NORMAL) { g_io_channel_unref(channel); return FALSE; } } g_io_channel_unref(channel); return TRUE; }
static GHashTable* inf_gtk_certificate_manager_load_known_hosts(InfGtkCertificateManager* mgr, GError** error) { InfGtkCertificateManagerPrivate* priv; GHashTable* table; gchar* content; gsize size; GError* local_error; gchar* out_buf; gsize out_buf_len; gchar* pos; gchar* prev; gchar* next; gchar* sep; gsize len; gsize out_len; gint base64_state; guint base64_save; gnutls_datum_t data; gnutls_x509_crt_t cert; int res; priv = INF_GTK_CERTIFICATE_MANAGER_PRIVATE(mgr); table = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnutls_x509_crt_deinit ); local_error = NULL; g_file_get_contents(priv->known_hosts_file, &content, &size, &local_error); if(local_error != NULL) { if(local_error->domain == G_FILE_ERROR && local_error->code == G_FILE_ERROR_NOENT) { return table; } g_propagate_prefixed_error( error, local_error, _("Failed to open known hosts file \"%s\": "), priv->known_hosts_file ); g_hash_table_destroy(table); return NULL; } out_buf = NULL; out_buf_len = 0; prev = content; for(prev = content; prev != NULL; prev = next) { pos = strchr(prev, '\n'); next = NULL; if(pos == NULL) pos = content + size; else next = pos + 1; sep = inf_gtk_certificate_manager_memrchr(prev, ':', pos - prev); if(sep == NULL) continue; /* ignore line */ *sep = '\0'; if(g_hash_table_lookup(table, prev) != NULL) { g_set_error( error, g_quark_from_static_string("INF_GTK_CERTIFICATE_MANAGER_ERROR"), INF_GTK_CERTIFICATE_MANAGER_ERROR_DUPLICATE_HOST_ENTRY, _("Certificate for host \"%s\" appears twice in " "known hosts file \"%s\""), prev, priv->known_hosts_file ); g_hash_table_destroy(table); g_free(out_buf); g_free(content); return NULL; } /* decode base64, import DER certificate */ len = (pos - (sep + 1)); out_len = len * 3 / 4; if(out_len > out_buf_len) { out_buf = g_realloc(out_buf, out_len); out_buf_len = out_len; } base64_state = 0; base64_save = 0; out_len = g_base64_decode_step( sep + 1, len, out_buf, &base64_state, &base64_save ); cert = NULL; res = gnutls_x509_crt_init(&cert); if(res == GNUTLS_E_SUCCESS) { data.data = out_buf; data.size = out_len; res = gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_DER); } if(res != GNUTLS_E_SUCCESS) { inf_gnutls_set_error(&local_error, res); g_propagate_prefixed_error( error, local_error, _("Failed to read certificate for host \"%s\" from " "known hosts file \"%s\": "), prev, priv->known_hosts_file ); if(cert != NULL) gnutls_x509_crt_deinit(cert); g_hash_table_destroy(table); g_free(out_buf); g_free(content); return NULL; } g_hash_table_insert(table, g_strdup(prev), cert); } g_free(out_buf); g_free(content); return table; }
/** * infd_acl_account_info_from_xml: * @xml: The XML node from which to read the account information. * @error: Location to store error information, if any. * * Reads information for one account from a serialized XML node. The account * info can be written to XML with the infd_acl_account_info_to_xml() * function. If the function fails it returns %NULL and @error is set. * * Returns: A #InfdAclAccountInfo, or %NULL. Free with * infd_acl_account_info_free() when no longer needed. */ InfdAclAccountInfo* infd_acl_account_info_from_xml(xmlNodePtr xml, GError** error) { InfdAclAccountInfo* info; InfAclAccount* account; GError* local_error; gboolean has_first_seen; gdouble first_seen; gboolean has_last_seen; gdouble last_seen; xmlChar* password_salt; xmlChar* password_hash; gnutls_datum_t datum; size_t hash_len; int res; gchar* binary_salt; gchar* binary_hash; xmlNodePtr child; GPtrArray* certificate_array; guint i; g_return_val_if_fail(xml != NULL, NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); local_error = NULL; has_first_seen = inf_xml_util_get_attribute_double( xml, "first-seen", &first_seen, &local_error ); if(local_error != NULL) return NULL; has_last_seen = inf_xml_util_get_attribute_double( xml, "last-seen", &last_seen, &local_error ); if(local_error != NULL) return NULL; account = inf_acl_account_from_xml(xml, error); if(account == NULL) return NULL; password_salt = inf_xml_util_get_attribute(xml, "password-salt"); password_hash = inf_xml_util_get_attribute(xml, "password-hash"); if( (password_salt != NULL && password_hash == NULL) || (password_salt == NULL && password_hash != NULL)) { g_set_error( error, inf_request_error_quark(), INF_REQUEST_ERROR_INVALID_ATTRIBUTE, "%s", _("If one of \"password-hash\" or \"password-salt\" is provided, the " "other must be provided as well.") ); if(password_salt != NULL) xmlFree(password_salt); if(password_hash != NULL) xmlFree(password_hash); inf_acl_account_free(account); return NULL; } if(password_salt != NULL && password_hash != NULL) { datum.data = password_salt; datum.size = strlen(password_salt); hash_len = 32; binary_salt = g_malloc(hash_len); res = gnutls_hex_decode(&datum, binary_salt, &hash_len); xmlFree(password_salt); if(hash_len != 32) { g_set_error( error, inf_request_error_quark(), INF_REQUEST_ERROR_INVALID_ATTRIBUTE, "%s", _("The length of the password salt is incorrect, it should " "be 32 bytes") ); xmlFree(password_hash); g_free(binary_salt); return NULL; } else if(res != GNUTLS_E_SUCCESS) { inf_gnutls_set_error(error, res); xmlFree(password_hash); g_free(binary_salt); return NULL; } datum.data = password_hash; datum.size = strlen(password_hash); hash_len = gnutls_hash_get_len(GNUTLS_DIG_SHA256); binary_hash = g_malloc(hash_len); res = gnutls_hex_decode(&datum, binary_hash, &hash_len); xmlFree(password_hash); if(hash_len != gnutls_hash_get_len(GNUTLS_DIG_SHA256)) { g_set_error( error, inf_request_error_quark(), INF_REQUEST_ERROR_INVALID_ATTRIBUTE, _("The length of the password hash is incorrect, it should be " "%u bytes"), (unsigned int)gnutls_hash_get_len(GNUTLS_DIG_SHA256) ); g_free(binary_salt); g_free(binary_hash); return NULL; } else if(res != GNUTLS_E_SUCCESS) { inf_gnutls_set_error(error, res); g_free(binary_salt); g_free(binary_hash); return NULL; } } else { binary_salt = NULL; binary_hash = NULL;\ if(password_salt != NULL) xmlFree(password_salt); if(password_hash != NULL) xmlFree(password_hash); } certificate_array = g_ptr_array_new(); for(child = xml->children; child != NULL; child = child->next) { if(child->type != XML_ELEMENT_NODE) continue; if(strcmp((const char*)child->name, "certificate") == 0) g_ptr_array_add(certificate_array, xmlNodeGetContent(child)); } info = infd_acl_account_info_new(account->id, account->name, FALSE); inf_acl_account_free(account); info->certificates = g_malloc(sizeof(gchar*) * certificate_array->len); for(i = 0; i < certificate_array->len; ++i) { info->certificates[i] = g_strdup(certificate_array->pdata[i]); xmlFree(certificate_array->pdata[i]); } info->n_certificates = certificate_array->len; g_ptr_array_free(certificate_array, TRUE); info->password_salt = binary_salt; info->password_hash = binary_hash; if(has_first_seen == TRUE) info->first_seen = first_seen * 1e6; else info->first_seen = 0; if(has_last_seen == TRUE) info->last_seen = last_seen * 1e6; else info->last_seen = 0; return info; }
/** * infd_acl_account_info_set_password: * @info: A #InfdAclAccountInfo. * @password: The new password for the account, or %NULL. * @error: Location to store error information, if any. * * Changes the password for the given account. If @password is %NULL the * password is unset, which means that it is not possible to log into this * account with password authentication. * * If @password is non-%NULL, a new random salt is generated and a SHA256 * hash of the salt and the password is stored. * * If an error occurs while changing the password the functions set @error * and returns %FALSE. * * Returns: %TRUE in case of success or %FALSE otherwise. */ gboolean infd_acl_account_info_set_password(InfdAclAccountInfo* info, const gchar* password, GError** error) { gchar* new_salt; gchar* new_hash; gchar* salted_password; guint password_len; int res; g_return_val_if_fail(info != NULL, FALSE); if(password == NULL) { g_free(info->password_salt); g_free(info->password_hash); info->password_salt = NULL; info->password_hash = NULL; } else { new_salt = g_malloc(32); res = gnutls_rnd(GNUTLS_RND_RANDOM, new_salt, 32); if(res != GNUTLS_E_SUCCESS) { g_free(new_salt); inf_gnutls_set_error(error, res); return FALSE; } password_len = strlen(password); salted_password = g_malloc(32 + password_len); memcpy(salted_password, new_salt, 16); memcpy(salted_password + 16, password, password_len); memcpy(salted_password + 16 + password_len, new_salt + 16, 16); new_hash = g_malloc(gnutls_hash_get_len(GNUTLS_DIG_SHA256)); res = gnutls_hash_fast( GNUTLS_DIG_SHA256, salted_password, 32 + password_len, new_hash ); g_free(salted_password); if(res != GNUTLS_E_SUCCESS) { g_free(new_hash); g_free(new_salt); inf_gnutls_set_error(error, res); return FALSE; } g_free(info->password_salt); g_free(info->password_hash); info->password_salt = new_salt; info->password_hash = new_hash; } return TRUE; }
static gboolean infinoted_plugin_certificate_auth_initialize(InfinotedPluginManager* manager, gpointer plugin_info, GError** error) { InfinotedPluginCertificateAuth* plugin; InfCertificateCredentials* creds; GPtrArray* read_certs; int res; guint i; gnutls_x509_crt_t* sign_certs; InfCertificateChain* sign_chain; gnutls_x509_privkey_t super_key; InfCertUtilDescription desc; gnutls_x509_crt_t super_cert; InfAclAccountId super_id; gnutls_x509_crt_t chain[2]; gboolean written; InfdDirectory* directory; InfBrowserIter iter; InfAclSheetSet sheet_set; InfAclSheet sheet; InfRequest* request; plugin = (InfinotedPluginCertificateAuth*)plugin_info; plugin->manager = manager; creds = infinoted_plugin_manager_get_credentials(manager); if(creds == NULL) { g_set_error( error, infinoted_plugin_certificate_auth_error_quark(), INFINOTED_PLUGIN_CERTIFICATE_AUTH_ERROR_NO_CREDENTIALS, "%s", _("The certificate-auth plugin can only be used when TLS is enabled " "and a server certificate has been set.") ); return FALSE; } read_certs = inf_cert_util_read_certificate(plugin->ca_list_file, NULL, error); if(read_certs == NULL) return FALSE; if(read_certs->len == 0) { g_set_error( error, infinoted_plugin_certificate_auth_error_quark(), INFINOTED_PLUGIN_CERTIFICATE_AUTH_ERROR_NO_CAS, _("File \"%s\" does not contain any CA certificates"), plugin->ca_list_file ); g_ptr_array_free(read_certs, TRUE); return FALSE; } plugin->n_cas = read_certs->len; plugin->cas = (gnutls_x509_crt_t*)g_ptr_array_free(read_certs, FALSE); res = gnutls_certificate_set_x509_trust( inf_certificate_credentials_get(creds), plugin->cas, plugin->n_cas ); if(res < 0) { inf_gnutls_set_error(error, res); return FALSE; } if(plugin->ca_key_file != NULL) { plugin->ca_key = inf_cert_util_read_private_key(plugin->ca_key_file, error); if(plugin->ca_key == NULL) return FALSE; /* Walk through certificates and find the certificate that the key * belongs to. */ for(i = 0; i < plugin->n_cas; ++i) if(inf_cert_util_check_certificate_key(plugin->cas[i], plugin->ca_key)) break; if(i == plugin->n_cas) { gnutls_x509_privkey_deinit(plugin->ca_key); plugin->ca_key = NULL; g_set_error( error, infinoted_plugin_certificate_auth_error_quark(), INFINOTED_PLUGIN_CERTIFICATE_AUTH_ERROR_NO_CA_FOR_KEY, "%s", _("The given CA key does not match with any of the CA certificates") ); return FALSE; } plugin->ca_key_index = i; /* Set the signing certificate of the directory, so that it can handle * account creation requests. Note that this takes ownership of the * certificate, so we take special care in the cleanup code in * infinoted_plugin_certificate_auth_deinitialize(). */ sign_certs = g_malloc(sizeof(gnutls_x509_crt_t)); sign_certs[0] = plugin->cas[plugin->ca_key_index]; sign_chain = inf_certificate_chain_new(sign_certs, 1); infd_directory_set_certificate( infinoted_plugin_manager_get_directory(plugin->manager), plugin->ca_key, sign_chain ); inf_certificate_chain_unref(sign_chain); } if(plugin->super_user != NULL) { if(plugin->ca_key == NULL) { g_set_error( error, infinoted_plugin_certificate_auth_error_quark(), INFINOTED_PLUGIN_CERTIFICATE_AUTH_ERROR_NO_CA_KEY, "%s", _("Cannot generate a superuser certificate without CA key") ); return FALSE; } /* Create a private key and certificate for the super user. */ infinoted_log_info( infinoted_plugin_manager_get_log(plugin->manager), _("Creating 4096-bit RSA private key for the super user account...") ); super_key = inf_cert_util_create_private_key(GNUTLS_PK_RSA, 4096, error); if(super_key == NULL) return FALSE; desc.validity = 12 * 3600; /* 12 hours */ desc.dn_common_name = "Super User"; desc.san_dnsname = NULL; super_cert = inf_cert_util_create_signed_certificate( super_key, &desc, plugin->cas[plugin->ca_key_index], plugin->ca_key, error ); if(super_cert == NULL) { gnutls_x509_privkey_deinit(super_key); return FALSE; } super_id = infd_directory_create_acl_account( infinoted_plugin_manager_get_directory(plugin->manager), _("Super User"), TRUE, /* transient */ &super_cert, 1, error ); if(super_id == 0) { gnutls_x509_crt_deinit(super_cert); gnutls_x509_privkey_deinit(super_key); return FALSE; } plugin->super_id = super_id; chain[0] = super_cert; chain[1] = plugin->cas[plugin->ca_key_index]; written = inf_cert_util_write_certificate_with_key( super_key, chain, 2, plugin->super_user, error ); gnutls_x509_crt_deinit(super_cert); gnutls_x509_privkey_deinit(super_key); if(written == FALSE) return FALSE; inf_browser_get_root( INF_BROWSER(infinoted_plugin_manager_get_directory(plugin->manager)), &iter ); directory = infinoted_plugin_manager_get_directory(plugin->manager); sheet.account = super_id; sheet.mask = INF_ACL_MASK_ALL; infd_directory_get_support_mask(directory, &sheet.perms); sheet_set.n_sheets = 1; sheet_set.own_sheets = NULL; sheet_set.sheets = &sheet; request = inf_browser_set_acl( INF_BROWSER(directory), &iter, &sheet_set, infinoted_plugin_certificate_auth_set_acl_cb, plugin ); if(request != NULL) { plugin->set_acl_request = request; g_object_ref(plugin->set_acl_request); } } return TRUE; }