From d96e68ea53c6457dbbd441d6dbe13c5c994d7a22 Mon Sep 17 00:00:00 2001 From: James Booth Date: Thu, 24 Sep 2015 00:18:18 +0100 Subject: [PATCH] Save trusted certificates to tlscerts file with more information --- Makefile.am | 2 + src/config/preferences.c | 74 ---------------- src/config/preferences.h | 4 - src/config/tlscerts.c | 181 ++++++++++++++++++++++++++++++++++++++ src/config/tlscerts.h | 61 +++++++++++++ src/event/server_events.c | 24 +++-- src/event/server_events.h | 1 - src/profanity.c | 4 + 8 files changed, 264 insertions(+), 87 deletions(-) create mode 100644 src/config/tlscerts.c create mode 100644 src/config/tlscerts.h diff --git a/Makefile.am b/Makefile.am index 51b97dd8..3e37fe7d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,6 +32,7 @@ core_sources = \ src/tools/autocomplete.c src/tools/autocomplete.h \ src/tools/tinyurl.c src/tools/tinyurl.h \ src/config/accounts.c src/config/accounts.h \ + src/config/tlscerts.c src/config/tlscerts.h \ src/config/account.c src/config/account.h \ src/config/preferences.c src/config/preferences.h \ src/config/theme.c src/config/theme.h @@ -57,6 +58,7 @@ unittest_sources = \ src/tools/tinyurl.c src/tools/tinyurl.h \ src/config/accounts.h \ src/config/account.c src/config/account.h \ + src/config/tlscerts.c src/config/tlscerts.h \ src/config/preferences.c src/config/preferences.h \ src/config/theme.c src/config/theme.h \ src/window_list.c src/window_list.h \ diff --git a/src/config/preferences.c b/src/config/preferences.c index 3584b457..7a198ac7 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -430,80 +430,6 @@ prefs_set_pgp_char(char ch) _save_prefs(); } -GList * -prefs_get_trusted_certs(void) -{ - gsize length; - GList *fp_list = NULL; - gchar **fps = g_key_file_get_string_list(prefs, PREF_GROUP_CONNECTION, "certs", &length, NULL); - if (fps) { - int i = 0; - for (i = 0; i < length; i++) { - fp_list = g_list_append(fp_list, strdup(fps[i])); - } - g_strfreev(fps); - return fp_list; - } else { - return NULL; - } -} - -void -prefs_free_trusted_certs(GList *certs) -{ - if (certs) { - g_list_free_full(certs, free); - } -} - -void -prefs_add_trusted_cert(const char * const fp) -{ - gsize length; - gchar **list = g_key_file_get_string_list(prefs, PREF_GROUP_CONNECTION, "certs", &length, NULL); - GList *glist = NULL; - - // list found - if (list) { - int i = 0; - for (i = 0; i < length; i++) { - // item already in list, exit function - if (strcmp(list[i], fp) == 0) { - g_list_free_full(glist, g_free); - g_strfreev(list); - return; - } - // add item to our g_list - glist = g_list_append(glist, strdup(list[i])); - } - - // item not found, add to our g_list - glist = g_list_append(glist, strdup(fp)); - - // create the new list entry - const gchar* new_list[g_list_length(glist)+1]; - GList *curr = glist; - i = 0; - while (curr) { - new_list[i++] = strdup(curr->data); - curr = g_list_next(curr); - } - new_list[i] = NULL; - g_key_file_set_string_list(prefs, PREF_GROUP_CONNECTION, "certs", new_list, g_list_length(glist)); - - // list not found - } else { - const gchar* new_list[2]; - new_list[0] = strdup(fp); - new_list[1] = NULL; - g_key_file_set_string_list(prefs, PREF_GROUP_CONNECTION, "certs", new_list, 1); - } - - g_strfreev(list); - g_list_free_full(glist, g_free); - _save_prefs(); -} - gboolean prefs_add_alias(const char * const name, const char * const value) { diff --git a/src/config/preferences.h b/src/config/preferences.h index 9718cfcb..89b3fe24 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -163,8 +163,4 @@ char * prefs_get_string(preference_t pref); void prefs_free_string(char *pref); void prefs_set_string(preference_t pref, char *value); -GList* prefs_get_trusted_certs(void); -void prefs_free_trusted_certs(GList *certs); -void prefs_add_trusted_cert(const char * const fp); - #endif diff --git a/src/config/tlscerts.c b/src/config/tlscerts.c new file mode 100644 index 00000000..562e3b0b --- /dev/null +++ b/src/config/tlscerts.c @@ -0,0 +1,181 @@ +/* + * tlscerts.c + * + * Copyright (C) 2012 - 2015 James Booth + * + * This file is part of Profanity. + * + * Profanity 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 of the License, or + * (at your option) any later version. + * + * Profanity 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 Profanity. If not, see . + * + * In addition, as a special exception, the copyright holders give permission to + * link the code of portions of this program with the OpenSSL library under + * certain conditions as described in each individual source file, and + * distribute linked combinations including the two. + * + * You must obey the GNU General Public License in all respects for all of the + * code used other than OpenSSL. If you modify file(s) with this exception, you + * may extend this exception to your version of the file(s), but you are not + * obligated to do so. If you do not wish to do so, delete this exception + * statement from your version. If you delete this exception statement from all + * source files in the program, then also delete it here. + * + */ + +#include +#include + +#include +#include + +#include "config/tlscerts.h" +#include "log.h" +#include "common.h" + +static gchar *tlscerts_loc; +static GKeyFile *tlscerts; + +static gchar* _get_tlscerts_file(void); +static void _save_tlscerts(void); + +void +tlscerts_init(void) +{ + log_info("Loading TLS certificates"); + tlscerts_loc = _get_tlscerts_file(); + + if (g_file_test(tlscerts_loc, G_FILE_TEST_EXISTS)) { + g_chmod(tlscerts_loc, S_IRUSR | S_IWUSR); + } + + tlscerts = g_key_file_new(); + g_key_file_load_from_file(tlscerts, tlscerts_loc, G_KEY_FILE_KEEP_COMMENTS, NULL); +} + +gboolean +tlscerts_exists(const char * const fingerprint) +{ + return g_key_file_has_group(tlscerts, fingerprint); +} + +TLSCertificate* +tlscerts_new(const char * const fingerprint, const char * const domain, const char * const organisation, + const char * const email, const char * const notbefore, const char * const notafter) +{ + TLSCertificate *cert = malloc(sizeof(TLSCertificate)); + if (fingerprint) { + cert->fingerprint = strdup(fingerprint); + } else { + cert->fingerprint = NULL; + } + if (domain) { + cert->domain = strdup(domain); + } else { + cert->domain = NULL; + } + if (organisation) { + cert->organisation = strdup(organisation); + } else { + cert->organisation = NULL; + } + if (email) { + cert->email = strdup(email); + } else { + cert->email= NULL; + } + if (notbefore) { + cert->notbefore = strdup(notbefore); + } else { + cert->notbefore = NULL; + } + if (notafter) { + cert->notafter = strdup(notafter); + } else { + cert->notafter = NULL; + } + + return cert; +} + +void +tlscerts_add(TLSCertificate *cert) +{ + if (!cert) { + return; + } + + if (!cert->fingerprint) { + return; + } + + if (cert->domain) { + g_key_file_set_string(tlscerts, cert->fingerprint, "domain", cert->domain); + } + if (cert->organisation) { + g_key_file_set_string(tlscerts, cert->fingerprint, "organisation", cert->organisation); + } + if (cert->email) { + g_key_file_set_string(tlscerts, cert->fingerprint, "email", cert->email); + } + if (cert->notbefore) { + g_key_file_set_string(tlscerts, cert->fingerprint, "start", cert->notbefore); + } + if (cert->notafter) { + g_key_file_set_string(tlscerts, cert->fingerprint, "end", cert->notafter); + } + + _save_tlscerts(); +} + +void +tlscerts_free(TLSCertificate *cert) +{ + if (cert) { + free(cert->fingerprint); + free(cert->domain); + free(cert->organisation); + free(cert->email); + free(cert->notbefore); + free(cert->notafter); + } +} + +void +tlscerts_close(void) +{ + g_key_file_free(tlscerts); + tlscerts = NULL; +} + +static gchar * +_get_tlscerts_file(void) +{ + gchar *xdg_data = xdg_get_data_home(); + GString *tlscerts_file = g_string_new(xdg_data); + g_string_append(tlscerts_file, "/profanity/tlscerts"); + gchar *result = strdup(tlscerts_file->str); + g_free(xdg_data); + g_string_free(tlscerts_file, TRUE); + + return result; +} + +static void +_save_tlscerts(void) +{ + gsize g_data_size; + gchar *g_tlscerts_data = g_key_file_to_data(tlscerts, &g_data_size, NULL); + g_file_set_contents(tlscerts_loc, g_tlscerts_data, g_data_size, NULL); + g_chmod(tlscerts_loc, S_IRUSR | S_IWUSR); + g_free(g_tlscerts_data); +} diff --git a/src/config/tlscerts.h b/src/config/tlscerts.h new file mode 100644 index 00000000..782d4430 --- /dev/null +++ b/src/config/tlscerts.h @@ -0,0 +1,61 @@ +/* + * tlscerts.h + * + * Copyright (C) 2012 - 2015 James Booth + * + * This file is part of Profanity. + * + * Profanity 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 of the License, or + * (at your option) any later version. + * + * Profanity 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 Profanity. If not, see . + * + * In addition, as a special exception, the copyright holders give permission to + * link the code of portions of this program with the OpenSSL library under + * certain conditions as described in each individual source file, and + * distribute linked combinations including the two. + * + * You must obey the GNU General Public License in all respects for all of the + * code used other than OpenSSL. If you modify file(s) with this exception, you + * may extend this exception to your version of the file(s), but you are not + * obligated to do so. If you do not wish to do so, delete this exception + * statement from your version. If you delete this exception statement from all + * source files in the program, then also delete it here. + * + */ + +#ifndef TLSCERTS_H +#define TLSCERTS_H + +typedef struct tls_cert_t { + char *fingerprint; + char *domain; + char *organisation; + char *email; + char *notbefore; + char *notafter; +} TLSCertificate; + +void tlscerts_init(void); + +TLSCertificate *tlscerts_new(const char * const fingerprint, const char * const domain, + const char * const organisation, const char * const email, + const char * const notbefore, const char * const notafter); + +gboolean tlscerts_exists(const char * const fingerprint); + +void tlscerts_add(TLSCertificate *cert); + +void tlscerts_free(TLSCertificate *cert); + +void tlscerts_close(void); + +#endif diff --git a/src/event/server_events.c b/src/event/server_events.c index 176973ff..4eb4f785 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -44,6 +44,7 @@ #include "config/account.h" #include "roster_list.h" #include "window_list.h" +#include "config/tlscerts.h" #ifdef HAVE_LIBOTR #include "otr/otr.h" @@ -643,12 +644,9 @@ int sv_ev_certfail(const char * const errormsg, const char * const certname, const char * const certfp, const char * const notbefore, const char * const notafter) { - GList *trusted = prefs_get_trusted_certs(); - if (g_list_find_custom(trusted, certfp, (GCompareFunc)g_strcmp0)) { - prefs_free_trusted_certs(trusted); + if (tlscerts_exists(certfp)) { return 1; } - prefs_free_trusted_certs(trusted); char *domain = NULL; char *org = NULL; @@ -676,15 +674,12 @@ sv_ev_certfail(const char * const errormsg, const char * const certname, const c cons_show_error("TLS certificate verification failed: %s", errormsg); if (domain) { cons_show(" Domain : %s", domain); - free(domain); } if (org) { cons_show(" Organisation : %s", org); - free(org); } if (email) { cons_show(" Email : %s", email); - free(email); } cons_show(" Fingerprint : %s", certfp); cons_show(" Start : %s", notbefore); @@ -712,13 +707,26 @@ sv_ev_certfail(const char * const errormsg, const char * const certname, const c if (g_strcmp0(cmd, "/tls allow") == 0) { free(cmd); + free(domain); + free(org); + free(email); return 1; } else if (g_strcmp0(cmd, "/tls always") == 0) { - prefs_add_trusted_cert(certfp); + if (!tlscerts_exists(certfp)) { + TLSCertificate *cert = tlscerts_new(certfp, domain, org, email, notbefore, notafter); + tlscerts_add(cert); + tlscerts_free(cert); + } free(cmd); + free(domain); + free(org); + free(email); return 1; } else { free(cmd); + free(domain); + free(org); + free(email); return 0; } } diff --git a/src/event/server_events.h b/src/event/server_events.h index e9ada1ce..2aa90754 100644 --- a/src/event/server_events.h +++ b/src/event/server_events.h @@ -89,5 +89,4 @@ void sv_ev_roster_received(void); int sv_ev_certfail(const char * const errormsg, const char * const certname, const char * const certfp, const char * const notbefore, const char * const notafter); - #endif diff --git a/src/profanity.c b/src/profanity.c index a56eb5e9..79e008d7 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -55,6 +55,7 @@ #include "common.h" #include "contact.h" #include "roster_list.h" +#include "config/tlscerts.h" #include "log.h" #include "muc.h" #ifdef HAVE_LIBOTR @@ -68,6 +69,7 @@ #include "ui/ui.h" #include "window_list.h" #include "event/client_events.h" +#include "config/tlscerts.h" static void _check_autoaway(void); static void _init(const int disable_tls, char *log_level); @@ -248,6 +250,7 @@ _init(const int disable_tls, char *log_level) log_info("Initialising contact list"); roster_init(); muc_init(); + tlscerts_init(); #ifdef HAVE_LIBOTR otr_init(); #endif @@ -284,6 +287,7 @@ _shutdown(void) chat_log_close(); theme_close(); accounts_close(); + tlscerts_close(); cmd_uninit(); log_stderr_close(); log_close();